From 20b2b6f1ef06f94c1693ba485ab3d290289dafb9 Mon Sep 17 00:00:00 2001 From: imxyy_soope_ Date: Sun, 1 Jun 2025 09:20:04 +0800 Subject: [PATCH] feat: lookup at downgrade time works, but leaks memory --- Cargo.lock | 39 -- Cargo.toml | 2 - src/builtins/mod.rs | 30 +- src/bytecode.rs | 10 +- src/compile.rs | 31 +- src/env.rs | 78 ++-- src/ir.rs | 803 +++++++++++++++++++++++-------------- src/jit/helpers.rs | 32 +- src/jit/mod.rs | 45 ++- src/jit/test.rs | 3 +- src/ty/common.rs | 5 + src/ty/internal/attrset.rs | 11 +- src/ty/internal/func.rs | 44 +- src/ty/internal/list.rs | 36 +- src/ty/internal/mod.rs | 107 ++--- src/ty/internal/primop.rs | 20 +- src/ty/internal/string.rs | 5 +- src/ty/public.rs | 3 +- src/vm/mod.rs | 106 ++--- src/vm/test.rs | 3 +- 20 files changed, 762 insertions(+), 651 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2dc1b54..9388206 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,16 +23,6 @@ version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" -[[package]] -name = "archery" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8967cd1cc9e9e1954f644e14fbd6042fe9a37da96c52a67e44a2ac18261f8561" -dependencies = [ - "static_assertions", - "triomphe", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -334,9 +324,7 @@ dependencies = [ "inkwell", "itertools", "regex", - "replace_with", "rnix", - "rpds", "rustyline", "thiserror 2.0.12", ] @@ -410,12 +398,6 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" -[[package]] -name = "replace_with" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51743d3e274e2b18df81c4dc6caf8a5b8e15dbe799e0dca05c7617380094e884" - [[package]] name = "rnix" version = "0.12.0" @@ -438,15 +420,6 @@ dependencies = [ "text-size", ] -[[package]] -name = "rpds" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0e15515d3ce3313324d842629ea4905c25a13f81953eadb88f85516f59290a4" -dependencies = [ - "archery", -] - [[package]] name = "rustc-hash" version = "1.1.0" @@ -506,12 +479,6 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "syn" version = "2.0.101" @@ -580,12 +547,6 @@ dependencies = [ "syn", ] -[[package]] -name = "triomphe" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859eb650cfee7434994602c3a68b25d77ad9e68c8a6cd491616ef86661382eb3" - [[package]] name = "unicode-ident" version = "1.0.18" diff --git a/Cargo.toml b/Cargo.toml index b280eab..5032109 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,13 +23,11 @@ strip = true rnix = "0.12" thiserror = "2.0" itertools = "0.14" -rpds = "1.1" derive_more = { version = "2.0", features = ["full"] } ecow = "0.2" regex = "1.11" hashbrown = "0.15" inkwell = { version = "0.6.0", features = ["llvm18-1"] } gc-arena = { git = "https://github.com/kyren/gc-arena", rev = "d651e3b4363d525a2d502c2305bc73e291835c84", features= ["hashbrown"] } -replace_with = "0.1" rustyline = { version = "15.0", optional = true } diff --git a/src/builtins/mod.rs b/src/builtins/mod.rs index 66d33c1..817b2c0 100644 --- a/src/builtins/mod.rs +++ b/src/builtins/mod.rs @@ -1,13 +1,18 @@ use gc_arena::{Gc, Mutation}; use hashbrown::HashMap; -use crate::env::{IrEnv, VmEnv}; +use crate::env::VmEnv; +use crate::ir::{DowngradeContext, Ir}; +use crate::ty::common::Const; use crate::ty::internal::{AttrSet, CoW, PrimOp, Value}; use crate::vm::VM; -pub fn ir_env<'gc>(mc: &Mutation<'gc>) -> Gc<'gc, IrEnv<'gc>> { - // TODO: - IrEnv::new(Gc::new(mc, HashMap::new()), mc) +pub fn ir_env(ctx: &mut DowngradeContext) -> HashMap { + let mut map = HashMap::new(); + map.insert(ctx.new_sym("true"), ctx.new_const(Const::Bool(true)).ir()); + map.insert(ctx.new_sym("false"), ctx.new_const(Const::Bool(false)).ir()); + + map } pub fn vm_env<'gc>(vm: &VM, mc: &Mutation<'gc>) -> Gc<'gc, VmEnv<'gc>> { @@ -78,15 +83,16 @@ pub fn vm_env<'gc>(vm: &VM, mc: &Mutation<'gc>) -> Gc<'gc, VmEnv<'gc>> { map.insert(vm.new_sym(primop.name), Value::PrimOp(primop)); } let sym = vm.new_sym("builtins"); - let attrs = CoW::new_cyclic( - |this| { - map.insert(sym, Value::AttrSet(this)); - AttrSet::from_inner(map) - }, - mc, - ); + let mut attrs = CoW::new(AttrSet::new(map), mc); + unsafe { + attrs.make_cyclic(|attrs, this| { + let _ = attrs.push_attr(sym, Value::AttrSet(this)); + }, mc); + } let builtins = Value::AttrSet(attrs); env_map.insert(sym, builtins); - VmEnv::new(Gc::new(mc, env_map), mc) + // TODO: + // VmEnv::new(Gc::new(mc, env_map), mc) + VmEnv::new(Vec::new(), mc) } diff --git a/src/bytecode.rs b/src/bytecode.rs index 41e7f4f..504cb6f 100644 --- a/src/bytecode.rs +++ b/src/bytecode.rs @@ -16,6 +16,8 @@ pub enum OpCode { Const { idx: usize }, /// load a dynamic var onto stack LookUp { sym: usize }, + /// load a var from let binding onto stack + LookUpLet { level: usize, idx: usize }, /// load a thunk lazily onto stack LoadThunk { idx: usize }, /// load a thunk value onto stack @@ -31,6 +33,8 @@ pub enum OpCode { Call, /// make a function Func { idx: usize }, + /// load a function argument + Arg { level: usize }, /// consume 1 element, assert TOS is true Assert, @@ -43,14 +47,14 @@ pub enum OpCode { /// push an empty attribute set onto stack AttrSet { cap: usize }, /// finalize the recursive attribute set at TOS - FinalizeRec, + FinalizeLet, /// [ .. set value ] consume 1 element, push a static kv pair (`name`, `value`) into `set` PushStaticAttr { name: usize }, /// [ .. set name value ] consume 2 elements, push a dynamic kv pair (`name`, `value`) in to `set` PushDynamicAttr, /// push an empty list onto stack - List, + List { cap: usize }, /// [ .. list elem ] consume 1 element, push `elem` into `list` PushElem, @@ -75,8 +79,6 @@ pub enum OpCode { SelectDynamic, /// [ .. set sym default ] select `sym` from `set` or `default` SelectDynamicOrDefault, - /// enter the let environment of the attribute set at TOS - EnterLetEnv, /// enter the with environment of the attribute set at TOS EnterWithEnv, /// exit current envrironment diff --git a/src/compile.rs b/src/compile.rs index b93d301..8495716 100644 --- a/src/compile.rs +++ b/src/compile.rs @@ -96,6 +96,18 @@ impl Compile for ir::Var { } } +impl Compile for ir::Arg { + fn compile(self, comp: &mut Compiler) { + comp.push(OpCode::Arg { level: self.level }) + } +} + +impl Compile for ir::LetVar { + fn compile(self, comp: &mut Compiler) { + comp.push(OpCode::LookUpLet { level: self.level, idx: self.idx }); + } +} + impl Compile for ir::Thunk { fn compile(self, comp: &mut Compiler) { comp.push(OpCode::LoadThunk { idx: self.idx }); @@ -113,7 +125,7 @@ impl Compile for ir::Attrs { for stc in self.stcs { let thunk = stc.1.is_thunk(); stc.1.compile(comp); - if thunk && !self.rec { + if thunk { comp.push(OpCode::CaptureEnv); } comp.push(OpCode::PushStaticAttr { name: stc.0 }); @@ -122,20 +134,17 @@ impl Compile for ir::Attrs { let thunk = dynamic.1.is_thunk(); dynamic.0.compile_force(comp); dynamic.1.compile(comp); - if thunk && !self.rec { + if thunk { comp.push(OpCode::CaptureEnv); } comp.push(OpCode::PushDynamicAttr) } - if self.rec { - comp.push(OpCode::FinalizeRec); - } } } impl Compile for ir::List { fn compile(self, comp: &mut Compiler) { - comp.push(OpCode::List); + comp.push(OpCode::List { cap: self.items.len() }); for item in self.items { item.compile(comp); comp.push(OpCode::PushElem); @@ -375,8 +384,14 @@ impl Compile for ir::If { impl Compile for ir::Let { fn compile(self, comp: &mut Compiler) { - self.attrs.compile(comp); - comp.push(OpCode::EnterLetEnv); + comp.push(OpCode::List { + cap: self.bindings.len() + }); + for (_, val) in self.bindings { + val.compile(comp); + comp.push(OpCode::PushElem); + } + comp.push(OpCode::FinalizeLet); self.expr.compile(comp); comp.push(OpCode::LeaveEnv); } diff --git a/src/env.rs b/src/env.rs index 9eb11b6..b425837 100644 --- a/src/env.rs +++ b/src/env.rs @@ -8,16 +8,17 @@ use crate::{ir::Ir, ty::internal::Value}; #[derive(Collect)] #[collect(no_drop)] pub struct Env<'gc, K: Hash + Eq + Collect<'gc>, V: Collect<'gc>> { - let_: Gc<'gc, LetEnv<'gc, K, V>>, + let_: Gc<'gc, LetEnv<'gc, V>>, with: Gc<'gc, With<'gc, K, V>>, + args: Vec, last: Option>>, } #[derive(Collect)] #[collect(no_drop)] -pub struct LetEnv<'gc, K: Hash + Eq + Collect<'gc>, V: Collect<'gc>> { - map: LetNode<'gc, K, V>, - last: Option>>, +pub struct LetEnv<'gc, V: Collect<'gc>> { + map: Vec, + last: Option>>, } pub type VmEnv<'gc> = Env<'gc, usize, Value<'gc>>; @@ -34,7 +35,6 @@ pub struct With<'gc, K: Hash + Eq + Collect<'gc>, V: Collect<'gc>> { #[collect(no_drop)] enum LetNode<'gc, K: Hash + Eq + Collect<'gc>, V: Collect<'gc>> { Let(Gc<'gc, HashMap>), - SingleArg(K, V), MultiArg(Gc<'gc, HashMap>), } @@ -46,7 +46,7 @@ pub enum Type { } impl<'gc, K: Hash + Eq + Clone + Collect<'gc>, V: Clone + Collect<'gc>> Env<'gc, K, V> { - pub fn new(map: Gc<'gc, HashMap>, mc: &Mutation<'gc>) -> Gc<'gc, Self> { + pub fn new(map: Vec, mc: &Mutation<'gc>) -> Gc<'gc, Self> { Gc::new( mc, Self { @@ -58,20 +58,18 @@ impl<'gc, K: Hash + Eq + Clone + Collect<'gc>, V: Clone + Collect<'gc>> Env<'gc, last: None, }, ), + args: Vec::new(), last: None, }, ) } - pub fn lookup_slow(&self, symbol: &K) -> Option<&V> { - if let Some(val) = self.let_.lookup(symbol) { - return Some(val); - } - self.with.lookup(symbol) + pub fn lookup_arg(&self, level: usize) -> V { + self.args[self.args.len() - level - 1].clone() } - pub fn lookup_let(&self, symbol: &K) -> Option<&V> { - self.let_.lookup(symbol) + pub fn lookup_let(&self, level: usize, idx: usize) -> V { + self.let_.lookup(level, idx) } pub fn lookup_with(&self, symbol: &K) -> Option<&V> { @@ -82,20 +80,25 @@ impl<'gc, K: Hash + Eq + Clone + Collect<'gc>, V: Clone + Collect<'gc>> Env<'gc, self.with.map.is_some() } - pub fn enter_arg(self: Gc<'gc, Self>, ident: K, val: V, mc: &Mutation<'gc>) -> Gc<'gc, Self> { + #[must_use] + pub fn enter_arg(self: Gc<'gc, Self>, val: V, mc: &Mutation<'gc>) -> Gc<'gc, Self> { + let mut args = self.args.clone(); + args.push(val); Gc::new( mc, Env { - let_: self.let_.enter_arg(ident, val, mc), + let_: self.let_, with: self.with, last: Some(self), + args }, ) } + #[must_use] pub fn enter_let( self: Gc<'gc, Self>, - map: Gc<'gc, HashMap>, + map: Vec, mc: &Mutation<'gc>, ) -> Gc<'gc, Self> { Gc::new( @@ -103,11 +106,13 @@ impl<'gc, K: Hash + Eq + Clone + Collect<'gc>, V: Clone + Collect<'gc>> Env<'gc, Self { let_: self.let_.enter_let(map, mc), with: self.with, + args: self.args.clone(), last: Some(self), }, ) } + #[must_use] pub fn enter_with( self: Gc<'gc, Self>, map: Gc<'gc, HashMap>, @@ -118,6 +123,7 @@ impl<'gc, K: Hash + Eq + Clone + Collect<'gc>, V: Clone + Collect<'gc>> Env<'gc, Env { let_: self.let_, with: self.with.enter(map, mc), + args: self.args.clone(), last: Some(self), }, ) @@ -128,53 +134,35 @@ impl<'gc, K: Hash + Eq + Clone + Collect<'gc>, V: Clone + Collect<'gc>> Env<'gc, } } -impl<'gc, K: Hash + Eq + Clone + Collect<'gc>, V: Clone + Collect<'gc>> LetEnv<'gc, K, V> { - pub fn new(map: Gc<'gc, HashMap>, mc: &Mutation<'gc>) -> Gc<'gc, Self> { +impl<'gc, V: Clone + Collect<'gc>> LetEnv<'gc, V> { + pub fn new(map: Vec, mc: &Mutation<'gc>) -> Gc<'gc, Self> { Gc::new( mc, Self { - map: LetNode::Let(map), + map, last: None, }, ) } - pub fn lookup(&self, symbol: &K) -> Option<&V> { - use self::LetNode::*; - match &self.map { - Let(map) | MultiArg(map) => { - if let Some(val) = map.get(symbol) { - return Some(val); - } - } - SingleArg(sym, val) => { - if sym == symbol { - return Some(val); - } - } + pub fn lookup(&self, level: usize, idx: usize) -> V { + let mut cur = self; + for _ in 0..level { + let last = cur.last.unwrap(); + cur = last.as_ref(); } - self.last.as_ref().and_then(|env| env.lookup(symbol)) - } - - pub fn enter_arg(self: Gc<'gc, Self>, ident: K, val: V, mc: &Mutation<'gc>) -> Gc<'gc, Self> { - Gc::new( - mc, - Self { - map: LetNode::SingleArg(ident, val), - last: Some(self), - }, - ) + cur.map[idx].clone() } pub fn enter_let( self: Gc<'gc, Self>, - map: Gc<'gc, HashMap>, + map: Vec, mc: &Mutation<'gc>, ) -> Gc<'gc, Self> { Gc::new( mc, Self { - map: LetNode::Let(map), + map, last: Some(self), }, ) diff --git a/src/ir.rs b/src/ir.rs index 9c21cbb..78a287f 100644 --- a/src/ir.rs +++ b/src/ir.rs @@ -1,35 +1,29 @@ use derive_more::{IsVariant, TryUnwrap, Unwrap}; -use gc_arena::{Arena, Collect, Gc, Mutation, Rootable}; use hashbrown::HashMap; use ecow::EcoString; -use replace_with::{replace_with, replace_with_or_abort}; +use itertools::Itertools; +use rnix::ast::HasEntry; use rnix::ast::{self, Expr}; use crate::builtins::ir_env; use crate::compile::*; -use crate::env::IrEnv; use crate::error::*; use crate::ty::common as c; pub fn downgrade(expr: Expr) -> Result { - let mut arena: Arena]> = - Arena::new(|mc| DowngradeContext::new(mc)); - arena.mutate_root(|mc, ctx| { - let ir = expr.downgrade(ctx, mc)?; - let consts = std::mem::take(&mut ctx.consts).into(); - let symbols = std::mem::take(&mut ctx.symbols); - let symmap = std::mem::take(&mut ctx.symmap); - let thunks = std::mem::take(&mut ctx.thunks).into(); - let funcs = std::mem::take(&mut ctx.funcs).into(); - Ok(Downgraded { - top_level: ir, - consts, - symbols, - symmap, - thunks, - funcs, - }) + let mut ctx = DowngradeContext::new(); + let builtins = ir_env(&mut ctx); + let env = Env::new(&builtins); + let ir = expr.downgrade(&mut ctx)?; + let ir = ir.resolve(&mut ctx, &env)?; + Ok(Downgraded { + top_level: ir, + consts: ctx.consts.into(), + symbols: ctx.symbols.into(), + symmap: ctx.symmap, + thunks: ctx.thunks.into(), + funcs: ctx.funcs.into(), }) } @@ -43,9 +37,8 @@ macro_rules! ir { ) ,*$(,)? ) => { - #[derive(Clone, Debug, IsVariant, Unwrap, Collect)] - #[collect(require_static)] - pub enum Ir { + #[derive(Clone, Debug, IsVariant, Unwrap)] + pub enum Ir { $( $ty($ty), )* @@ -66,29 +59,34 @@ macro_rules! ir { } impl Ir { + #[inline] fn boxed(self) -> Box { Box::new(self) } + #[inline] fn ok(self) -> Result { Ok(self) } + #[inline] fn as_ref(&self) -> IrRef { match self { $(Ir::$ty(ir) => IrRef::$ty(ir),)* } } + #[inline] fn as_mut(&mut self) -> IrMut { match self { $(Ir::$ty(ir) => IrMut::$ty(ir),)* } } - fn resolve<'gc>(self, ctx: &mut DowngradeContext<'gc>, mc: &Mutation<'gc>) -> Result { + #[inline] + fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result { match self { - $(Ir::$ty(ir) => ir.resolve(ctx, mc),)* + $(Ir::$ty(ir) => ir.resolve(ctx, env),)* } } } @@ -104,9 +102,8 @@ macro_rules! ir { $( #[$($x)*] )* - #[derive(Clone, Debug, Collect)] - #[collect(no_drop)] - pub struct $ty { + #[derive(Clone, Debug)] + pub struct $ty { $( pub $name : $elemtype, )* @@ -122,7 +119,7 @@ macro_rules! ir { } ir! { - Attrs => { stcs: HashMap, dyns: Vec, rec: bool }, + Attrs => { stcs: HashMap, dyns: Vec }, List => { items: Vec }, HasAttr => { lhs: Box, rhs: Vec }, BinOp => { lhs: Box, rhs: Box, kind: BinOpKind }, @@ -132,47 +129,163 @@ ir! { LoadFunc => { idx: usize }, Call => { func: Box, args: Vec }, - Let => { attrs: Attrs, expr: Box }, + Let => { bindings: Vec<(usize, Ir)>, expr: Box }, With => { namespace: Box, expr: Box }, Assert => { assertion: Box, expr: Box }, ConcatStrings => { parts: Vec }, + #[derive(Copy)] Const => { idx: usize }, + #[derive(Copy)] Var => { sym: usize }, #[derive(Copy)] + Arg => { level: usize }, + #[derive(Copy)] + LetVar => { level: usize, idx: usize }, + #[derive(Copy)] Thunk => { idx: usize }, Path => { expr: Box }, } -#[derive(Clone, Debug, Collect)] -#[collect(require_static)] +#[derive(Clone, Debug)] pub struct DynamicAttrPair(pub Ir, pub Ir); -pub struct DowngradeContext<'gc> { +pub struct DowngradeContext { thunks: Vec, funcs: Vec, consts: Vec, constmap: HashMap, symbols: Vec, symmap: HashMap, - env: Gc<'gc, IrEnv<'gc>>, } -unsafe impl<'gc> Collect<'gc> for DowngradeContext<'gc> { - fn trace>(&self, cc: &mut T) { - self.env.trace(cc); +struct Env<'a, 'env> { + env: EnvNode<'a>, + prev: Option<&'env Env<'a, 'env>>, +} + +enum EnvNode<'a> { + Builtins(&'a HashMap), + Let(&'a Vec), + SingleArg(usize), + MultiArg(HashMap>, Option), + With, +} + +enum LookupResult { + Builtin(Ir), + Let { level: usize, idx: usize }, + SingleArg { level: usize }, + MultiArg { level: usize, default: Option }, + With, +} + +impl<'a, 'env> Env<'a, 'env> { + fn new(base: &'a HashMap) -> Self { + Self { + env: EnvNode::Builtins(base), + prev: None, + } + } + + fn enter_let(&'env self, map: &'a Vec) -> Self { + Self { + env: EnvNode::Let(map), + prev: Some(self), + } + } + + fn enter_single_arg(&'env self, ident: usize) -> Self { + Self { + env: EnvNode::SingleArg(ident), + prev: Some(self), + } + } + + fn enter_multi_arg(&'env self, map: HashMap>, alias: Option) -> Self { + Self { + env: EnvNode::MultiArg(map, alias), + prev: Some(self), + } + } + + fn enter_with(&'env self) -> Self { + Self { + env: EnvNode::With, + prev: Some(self), + } + } + + fn _lookup( + &self, + ident: usize, + mut arg_level: usize, + mut let_level: usize, + has_with: bool, + ) -> core::result::Result { + use EnvNode::*; + let mut has_with = has_with; + match &self.env { + Builtins(map) => { + return if let Some(ir) = map.get(&ident) { + Ok(LookupResult::Builtin(ir.clone())) + } else { + if has_with { + Ok(LookupResult::With) + } else { + Err(()) + } + }; + } + Let(map) => { + if let Ok(idx) = map.binary_search(&ident) { + return Ok(LookupResult::Let { + level: let_level, + idx, + }); + } else { + let_level += 1; + } + } + SingleArg(arg) => { + if *arg == ident { + return Ok(LookupResult::SingleArg { level: arg_level }); + } else { + arg_level += 1; + } + } + MultiArg(set, alias) => { + if let Some(default) = set.get(&ident) { + return Ok(LookupResult::MultiArg { + level: arg_level, + default: default.clone(), + }); + } else if alias.map_or(false, |alias| alias == ident) { + return Ok(LookupResult::SingleArg { level: arg_level }); + } else { + arg_level += 1; + } + } + With => has_with = true, + } + self.prev + .map(|prev| prev._lookup(ident, arg_level, let_level, has_with)) + .map_or_else(|| unreachable!(), |x| x) + } + + fn lookup(&self, ident: usize) -> core::result::Result { + self._lookup(ident, 0, 0, false) } } -impl<'gc> DowngradeContext<'gc> { - fn new(mc: &Mutation<'gc>) -> Self { +impl DowngradeContext { + fn new() -> Self { DowngradeContext { - thunks: Vec::default(), - funcs: Vec::default(), - consts: Vec::default(), - constmap: HashMap::default(), - symbols: Vec::default(), - symmap: HashMap::default(), - env: ir_env(mc), + thunks: Vec::new(), + funcs: Vec::new(), + consts: Vec::new(), + constmap: HashMap::new(), + symbols: Vec::new(), + symmap: HashMap::new(), } } } @@ -186,7 +299,7 @@ pub struct Downgraded { pub funcs: Box<[Func]>, } -impl<'gc> DowngradeContext<'gc> { +impl<'a> DowngradeContext { fn new_thunk(&mut self, thunk: Ir) -> Thunk { let idx = self.thunks.len(); self.thunks.push(thunk); @@ -199,7 +312,7 @@ impl<'gc> DowngradeContext<'gc> { LoadFunc { idx } } - fn new_const(&mut self, cnst: c::Const) -> Const { + pub fn new_const(&mut self, cnst: c::Const) -> Const { if let Some(&idx) = self.constmap.get(&cnst) { Const { idx } } else { @@ -211,7 +324,7 @@ impl<'gc> DowngradeContext<'gc> { } } - fn new_sym(&mut self, sym: impl Into) -> usize { + pub fn new_sym(&mut self, sym: impl Into) -> usize { let sym = sym.into(); if let Some(&idx) = self.symmap.get(&sym) { idx @@ -222,53 +335,47 @@ impl<'gc> DowngradeContext<'gc> { } } - fn enter_arg(&mut self, ident: usize, val: Ir, mc: &Mutation<'gc>) { - self.env = self.env.enter_arg(ident, val, mc) + fn get_sym(&self, idx: usize) -> &EcoString { + &self.symbols[idx] } - fn enter_let(&mut self, attrs: HashMap, mc: &Mutation<'gc>) { - self.env = self.env.enter_let(Gc::new(mc, attrs), mc) - } - - fn enter_with(&mut self, attrs: HashMap, mc: &Mutation<'gc>) { - self.env = self.env.enter_with(Gc::new(mc, attrs), mc) - } - - fn leave(&mut self) { - self.env = self.env.leave() - } - - fn lookup(&self, ident: usize) -> Result> { - self.env.lookup_let(&ident).map_or_else( - || { - Err(Error::DowngradeError(format!( - "{} not found", - &self.symbols[ident] - ))) + fn resolve_func(&mut self, idx: usize, env: &Env) -> Result<()> { + let self_ptr = self as *mut Self; + self.funcs.get_mut(idx).map_or_else( + || unreachable!(), + |func| { + unsafe { + let old = std::ptr::read(func); + std::ptr::write(func, old.resolve(self_ptr.as_mut().unwrap(), env)?); + } + Ok(()) }, - |val| Ok(Some(val)), ) } - fn resolve_func(&mut self, idx: usize) -> Result<()> { - self.funcs.get_mut(idx).map_or_else(|| unreachable!(), |func| { - *func = func.resolve()?; - Ok(()) - }) - } - - fn resolve_thunk(&mut self, idx: usize, mc: &Mutation<'gc>) -> Result<()> { - self.thunks.get_mut(idx).map_or_else(|| unreachable!(), |thunk| { - replace_with_or_abort(thunk, |thunk| { - thunk.resolve(self, mc) - }); - Ok(()) - }) + fn resolve_thunk(&mut self, idx: usize, env: &Env) -> Result<()> { + let self_ptr = self as *mut Self; + self.thunks.get_mut(idx).map_or_else( + || unreachable!(), + |thunk| { + unsafe { + let old = std::ptr::read(thunk); + std::ptr::write(thunk, old.resolve(self_ptr.as_mut().unwrap(), env)?); + } + Ok(()) + }, + ) } } impl Attrs { - fn _insert(&mut self, mut path: std::vec::IntoIter, name: Attr, value: Ir) -> Result<()> { + fn _insert( + &mut self, + mut path: std::vec::IntoIter, + name: Attr, + value: Ir, + ctx: &mut DowngradeContext, + ) -> Result<()> { if let Some(attr) = path.next() { match attr { Attr::Str(ident) => { @@ -280,38 +387,36 @@ impl Attrs { .try_unwrap_attrs() .or_else(|_| { Err(Error::DowngradeError(format!( - r#""{ident}" already exsists in this set"# + r#"attribute '{}' already defined"#, + ctx.get_sym(ident) ))) }) - .and_then(|attrs: &mut Attrs| attrs._insert(path, name, value)) + .and_then(|attrs: &mut Attrs| attrs._insert(path, name, value, ctx)) } else { let mut attrs = Attrs { - rec: false, stcs: HashMap::new(), dyns: Vec::new(), }; - attrs._insert(path, name, value)?; + attrs._insert(path, name, value, ctx)?; assert!(self.stcs.insert(ident, attrs.ir()).is_none()); Ok(()) } } Attr::Strs(string) => { let mut attrs = Attrs { - rec: false, stcs: HashMap::new(), dyns: Vec::new(), }; - attrs._insert(path, name, value)?; + attrs._insert(path, name, value, ctx)?; self.dyns.push(DynamicAttrPair(string.ir(), attrs.ir())); Ok(()) } Attr::Dynamic(dynamic) => { let mut attrs = Attrs { - rec: false, stcs: HashMap::new(), dyns: Vec::new(), }; - attrs._insert(path, name, value)?; + attrs._insert(path, name, value, ctx)?; self.dyns.push(DynamicAttrPair(dynamic, attrs.ir())); Ok(()) } @@ -321,7 +426,8 @@ impl Attrs { Attr::Str(ident) => { if self.stcs.get(&ident).is_some() { return Err(Error::DowngradeError(format!( - r#""{ident}" already exsists in this set"# + r#"attribute '{}' already defined"#, + ctx.get_sym(ident) ))); } self.stcs.insert(ident, value); @@ -337,10 +443,10 @@ impl Attrs { } } - pub fn insert(&mut self, path: Vec, value: Ir) -> Result<()> { + pub fn insert(&mut self, path: Vec, value: Ir, ctx: &mut DowngradeContext) -> Result<()> { let mut path = path.into_iter(); let name = path.next_back().unwrap(); - self._insert(path, name, value) + self._insert(path, name, value, ctx) } fn _has_attr(&self, mut path: std::slice::Iter, name: Attr) -> Option { @@ -365,8 +471,7 @@ impl Attrs { } } -#[derive(Clone, Debug, Collect)] -#[collect(require_static)] +#[derive(Clone, Debug, TryUnwrap)] pub enum Attr { Dynamic(Ir), Strs(ConcatStrings), @@ -374,23 +479,23 @@ pub enum Attr { } impl Attr { - fn resolve<'gc>(self, ctx: &mut DowngradeContext<'gc>, mc: &Mutation<'gc>) -> Result { + fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result { use Attr::*; Ok(match self { - Dynamic(ir) => Dynamic(ir.resolve(ctx, mc)?), - other => other + Dynamic(ir) => Dynamic(ir.resolve(ctx, env)?), + other => other, }) } } impl Thunk { - fn resolve<'gc>(self, ctx: &mut DowngradeContext<'gc>, mc: &Mutation<'gc>) -> Result { - ctx.enter_with(attrs, mc); + fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result { + ctx.resolve_thunk(self.idx, env)?; + self.ir().ok() } } -#[derive(Clone, Debug, Collect)] -#[collect(require_static)] +#[derive(Clone, Debug)] pub enum BinOpKind { Add, Sub, @@ -439,8 +544,7 @@ impl From for BinOpKind { } } -#[derive(Clone, Debug, Collect)] -#[collect(require_static)] +#[derive(Clone, Debug)] pub enum UnOpKind { Neg, Not, @@ -455,63 +559,68 @@ impl From for UnOpKind { } } -#[derive(Collect)] -#[collect(no_drop)] pub struct Func { pub param: Param, pub body: Box, } -#[derive(Clone, Debug, Collect)] -#[collect(no_drop)] +#[derive(Clone, Debug)] pub enum Param { Ident(usize), Formals { + // formals: Vec<(usize, Option)>, formals: Vec<(usize, Option)>, ellipsis: bool, alias: Option, }, } +impl LoadFunc { + fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result { + ctx.resolve_func(self.idx, env)?; + self.ir().ok() + } +} + trait Downgrade where Self: Sized, { - fn downgrade<'gc>(self, ctx: &mut DowngradeContext<'gc>, mc: &Mutation<'gc>) -> Result; + fn downgrade(self, ctx: &mut DowngradeContext) -> Result; } impl Downgrade for Expr { - fn downgrade<'gc>(self, ctx: &mut DowngradeContext<'gc>, mc: &Mutation<'gc>) -> Result { + fn downgrade(self, ctx: &mut DowngradeContext) -> Result { match self { - Expr::Apply(apply) => apply.downgrade(ctx, mc), - Expr::Assert(assert) => assert.downgrade(ctx, mc), + Expr::Apply(apply) => apply.downgrade(ctx), + Expr::Assert(assert) => assert.downgrade(ctx), Expr::Error(error) => Err(Error::DowngradeError(error.to_string())), - Expr::IfElse(ifelse) => ifelse.downgrade(ctx, mc), - Expr::Select(select) => select.downgrade(ctx, mc), - Expr::Str(str) => str.downgrade(ctx, mc), - Expr::Path(path) => path.downgrade(ctx, mc), - Expr::Literal(lit) => lit.downgrade(ctx, mc), - Expr::Lambda(lambda) => lambda.downgrade(ctx, mc), - Expr::LegacyLet(let_) => let_.downgrade(ctx, mc), - Expr::LetIn(letin) => letin.downgrade(ctx, mc), - Expr::List(list) => list.downgrade(ctx, mc), - Expr::BinOp(op) => op.downgrade(ctx, mc), - Expr::Paren(paren) => paren.expr().unwrap().downgrade(ctx, mc), - Expr::Root(root) => root.expr().unwrap().downgrade(ctx, mc), - Expr::AttrSet(attrs) => attrs.downgrade(ctx, mc), - Expr::UnaryOp(op) => op.downgrade(ctx, mc), - Expr::Ident(ident) => ident.downgrade(ctx, mc), - Expr::With(with) => with.downgrade(ctx, mc), - Expr::HasAttr(has) => has.downgrade(ctx, mc), + Expr::IfElse(ifelse) => ifelse.downgrade(ctx), + Expr::Select(select) => select.downgrade(ctx), + Expr::Str(str) => str.downgrade(ctx), + Expr::Path(path) => path.downgrade(ctx), + Expr::Literal(lit) => lit.downgrade(ctx), + Expr::Lambda(lambda) => lambda.downgrade(ctx), + Expr::LegacyLet(let_) => let_.downgrade(ctx), + Expr::LetIn(letin) => letin.downgrade(ctx), + Expr::List(list) => list.downgrade(ctx), + Expr::BinOp(op) => op.downgrade(ctx), + Expr::Paren(paren) => paren.expr().unwrap().downgrade(ctx), + Expr::Root(root) => root.expr().unwrap().downgrade(ctx), + Expr::AttrSet(attrs) => attrs.downgrade(ctx), + Expr::UnaryOp(op) => op.downgrade(ctx), + Expr::Ident(ident) => ident.downgrade(ctx), + Expr::With(with) => with.downgrade(ctx), + Expr::HasAttr(has) => has.downgrade(ctx), } } } impl Downgrade for ast::Assert { - fn downgrade<'gc>(self, ctx: &mut DowngradeContext<'gc>, mc: &Mutation<'gc>) -> Result { + fn downgrade(self, ctx: &mut DowngradeContext) -> Result { Assert { - assertion: self.condition().unwrap().downgrade(ctx, mc)?.boxed(), - expr: self.body().unwrap().downgrade(ctx, mc)?.boxed(), + assertion: self.condition().unwrap().downgrade(ctx)?.boxed(), + expr: self.body().unwrap().downgrade(ctx)?.boxed(), } .ir() .ok() @@ -519,10 +628,10 @@ impl Downgrade for ast::Assert { } impl Assert { - fn resolve<'gc>(self, ctx: &mut DowngradeContext<'gc>, mc: &Mutation<'gc>) -> Result { + fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result { Self { - assertion: self.assertion.resolve(ctx, mc)?.boxed(), - expr: self.expr.resolve(ctx, mc)?.boxed(), + assertion: self.assertion.resolve(ctx, env)?.boxed(), + expr: self.expr.resolve(ctx, env)?.boxed(), } .ir() .ok() @@ -530,11 +639,11 @@ impl Assert { } impl Downgrade for ast::IfElse { - fn downgrade<'gc>(self, ctx: &mut DowngradeContext<'gc>, mc: &Mutation<'gc>) -> Result { + fn downgrade(self, ctx: &mut DowngradeContext) -> Result { If { - cond: self.condition().unwrap().downgrade(ctx, mc)?.boxed(), - consq: self.body().unwrap().downgrade(ctx, mc)?.boxed(), - alter: self.else_body().unwrap().downgrade(ctx, mc)?.boxed(), + cond: self.condition().unwrap().downgrade(ctx)?.boxed(), + consq: self.body().unwrap().downgrade(ctx)?.boxed(), + alter: self.else_body().unwrap().downgrade(ctx)?.boxed(), } .ir() .ok() @@ -542,11 +651,11 @@ impl Downgrade for ast::IfElse { } impl If { - fn resolve<'gc>(self, ctx: &mut DowngradeContext<'gc>, mc: &Mutation<'gc>) -> Result { + fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result { If { - cond: self.cond.resolve(ctx, mc)?.boxed(), - consq: self.consq.resolve(ctx, mc)?.boxed(), - alter: self.alter.resolve(ctx, mc)?.boxed(), + cond: self.cond.resolve(ctx, env)?.boxed(), + consq: self.consq.resolve(ctx, env)?.boxed(), + alter: self.alter.resolve(ctx, env)?.boxed(), } .ir() .ok() @@ -554,13 +663,13 @@ impl If { } impl Downgrade for ast::Path { - fn downgrade<'gc>(self, ctx: &mut DowngradeContext<'gc>, mc: &Mutation<'gc>) -> Result { + fn downgrade(self, ctx: &mut DowngradeContext) -> Result { let parts = self .parts() .map(|part| match part { ast::InterpolPart::Literal(lit) => ctx.new_const(lit.to_string().into()).ir().ok(), ast::InterpolPart::Interpolation(interpol) => { - interpol.expr().unwrap().downgrade(ctx, mc) + interpol.expr().unwrap().downgrade(ctx) } }) .collect::>>()?; @@ -579,9 +688,9 @@ impl Downgrade for ast::Path { } impl Path { - fn resolve<'gc>(self, ctx: &mut DowngradeContext<'gc>, mc: &Mutation<'gc>) -> Result { + fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result { Self { - expr: self.expr.resolve(ctx, mc)?.boxed(), + expr: self.expr.resolve(ctx, env)?.boxed(), } .ir() .ok() @@ -589,14 +698,14 @@ impl Path { } impl Downgrade for ast::Str { - fn downgrade<'gc>(self, ctx: &mut DowngradeContext<'gc>, mc: &Mutation<'gc>) -> Result { + fn downgrade(self, ctx: &mut DowngradeContext) -> Result { let parts = self .normalized_parts() .into_iter() .map(|part| match part { ast::InterpolPart::Literal(lit) => ctx.new_const(lit.into()).ir().ok(), ast::InterpolPart::Interpolation(interpol) => { - interpol.expr().unwrap().downgrade(ctx, mc) + interpol.expr().unwrap().downgrade(ctx) } }) .collect::>>()?; @@ -609,12 +718,12 @@ impl Downgrade for ast::Str { } impl ConcatStrings { - fn resolve<'gc>(self, ctx: &mut DowngradeContext<'gc>, mc: &Mutation<'gc>) -> Result { + fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result { Self { parts: self .parts .into_iter() - .map(|ir| ir.resolve(ctx, mc)) + .map(|ir| ir.resolve(ctx, env)) .collect::>>()?, } .ir() @@ -623,7 +732,7 @@ impl ConcatStrings { } impl Downgrade for ast::Literal { - fn downgrade<'gc>(self, ctx: &mut DowngradeContext<'gc>, _: &Mutation<'gc>) -> Result { + fn downgrade(self, ctx: &mut DowngradeContext) -> Result { match self.kind() { ast::LiteralKind::Integer(int) => ctx.new_const(int.value().unwrap().into()), ast::LiteralKind::Float(float) => ctx.new_const(float.value().unwrap().into()), @@ -635,52 +744,89 @@ impl Downgrade for ast::Literal { } impl Const { - fn resolve<'gc>(self, ctx: &mut DowngradeContext<'gc>, mc: &Mutation<'gc>) -> Result { + fn resolve<'a, 'env>(self, _: &mut DowngradeContext, _: &Env<'a, 'env>) -> Result { self.ir().ok() } } impl Downgrade for ast::Ident { - fn downgrade<'gc>(self, ctx: &mut DowngradeContext<'gc>, _: &Mutation<'gc>) -> Result { + fn downgrade(self, ctx: &mut DowngradeContext) -> Result { let sym = ctx.new_sym(self.ident_token().unwrap().text()); Var { sym }.ir().ok() } } impl Var { - fn resolve<'gc>(self, ctx: &mut DowngradeContext<'gc>, mc: &Mutation<'gc>) -> Result { - ctx.lookup(self.sym)? - .map_or(Var { ..self }.ir().ok(), |val| val.clone().ok()) + fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result { + use LookupResult::*; + match env + .lookup(self.sym) + .map_err(|_| Error::DowngradeError(format!("{} not found", ctx.get_sym(self.sym))))? + { + Builtin(ir) => ir, + Let { level, idx } => LetVar { level, idx }.ir(), + SingleArg { level } => Arg { level }.ir(), + MultiArg { level, default } => Select { + expr: Arg { level }.ir().boxed(), + attrpath: vec![Attr::Str(self.sym)], + default: default.map(Box::new), + } + .ir(), + With => self.ir(), + } + .ok() + } +} + +impl Arg { + fn resolve<'a, 'env>(self, _: &mut DowngradeContext, _: &Env<'a, 'env>) -> Result { + unreachable!() + } +} + +impl LetVar { + fn resolve<'a, 'env>(self, _: &mut DowngradeContext, _: &Env<'a, 'env>) -> Result { + unreachable!() } } impl Downgrade for ast::AttrSet { - fn downgrade<'gc>(self, ctx: &mut DowngradeContext<'gc>, mc: &Mutation<'gc>) -> Result { + fn downgrade(self, ctx: &mut DowngradeContext) -> Result { let rec = self.rec_token().is_some(); - downgrade_has_entry(self, rec, ctx, mc).map(|attrs| attrs.ir()) + let attrs = downgrade_attrs(self, ctx)?; + if rec { + let bindings = attrs.stcs.into_iter().sorted_by_key(|(k, _)| *k).collect::>(); + let stcs = bindings + .iter() + .map(|&(sym, _)| (sym, Var { sym }.ir())) + .collect(); + Let { + bindings, + expr: Attrs { stcs, ..attrs }.ir().boxed(), + } + .ir() + } else { + attrs.ir() + } + .ok() } } impl Attrs { - fn resolve<'gc>(self, ctx: &mut DowngradeContext<'gc>, mc: &Mutation<'gc>) -> Result { - if self.rec { - todo!() - } else { - Self { - stcs: self - .stcs - .into_iter() - .map(|(k, v)| Ok((k, v.resolve(ctx, mc)?))) - .collect::>()?, - dyns: self - .dyns - .into_iter() - .map(|DynamicAttrPair(k, v)| { - Ok(DynamicAttrPair(k.resolve(ctx, mc)?, v.resolve(ctx, mc)?)) - }) - .collect::>()?, - rec: false, - } + fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result { + Self { + stcs: self + .stcs + .into_iter() + .map(|(k, v)| Ok((k, v.resolve(ctx, env)?))) + .collect::>()?, + dyns: self + .dyns + .into_iter() + .map(|DynamicAttrPair(k, v)| { + Ok(DynamicAttrPair(k.resolve(ctx, env)?, v.resolve(ctx, env)?)) + }) + .collect::>()?, } .ir() .ok() @@ -688,22 +834,22 @@ impl Attrs { } impl Downgrade for ast::List { - fn downgrade<'gc>(self, ctx: &mut DowngradeContext<'gc>, mc: &Mutation<'gc>) -> Result { + fn downgrade(self, ctx: &mut DowngradeContext) -> Result { let mut items = Vec::with_capacity(self.items().size_hint().0); for item in self.items() { - items.push(item.downgrade(ctx, mc)?) + items.push(item.downgrade(ctx)?) } List { items }.ir().ok() } } impl List { - fn resolve<'gc>(self, ctx: &mut DowngradeContext<'gc>, mc: &Mutation<'gc>) -> Result { + fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result { Self { items: self .items .into_iter() - .map(|item| item.resolve(ctx, mc)) + .map(|item| item.resolve(ctx, env)) .collect::>()?, } .ir() @@ -712,10 +858,10 @@ impl List { } impl Downgrade for ast::BinOp { - fn downgrade<'gc>(self, ctx: &mut DowngradeContext<'gc>, mc: &Mutation<'gc>) -> Result { + fn downgrade(self, ctx: &mut DowngradeContext) -> Result { BinOp { - lhs: self.lhs().unwrap().downgrade(ctx, mc)?.boxed(), - rhs: self.rhs().unwrap().downgrade(ctx, mc)?.boxed(), + lhs: self.lhs().unwrap().downgrade(ctx)?.boxed(), + rhs: self.rhs().unwrap().downgrade(ctx)?.boxed(), kind: self.operator().unwrap().into(), } .ir() @@ -724,10 +870,10 @@ impl Downgrade for ast::BinOp { } impl BinOp { - fn resolve<'gc>(self, ctx: &mut DowngradeContext<'gc>, mc: &Mutation<'gc>) -> Result { + fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result { Self { - lhs: self.lhs.resolve(ctx, mc)?.boxed(), - rhs: self.rhs.resolve(ctx, mc)?.boxed(), + lhs: self.lhs.resolve(ctx, env)?.boxed(), + rhs: self.rhs.resolve(ctx, env)?.boxed(), ..self } .ir() @@ -736,9 +882,9 @@ impl BinOp { } impl Downgrade for ast::HasAttr { - fn downgrade<'gc>(self, ctx: &mut DowngradeContext<'gc>, mc: &Mutation<'gc>) -> Result { - let attrs = self.expr().unwrap().downgrade(ctx, mc)?; - let path = downgrade_attrpath(self.attrpath().unwrap(), ctx, mc)?; + fn downgrade(self, ctx: &mut DowngradeContext) -> Result { + let attrs = self.expr().unwrap().downgrade(ctx)?; + let path = downgrade_attrpath(self.attrpath().unwrap(), ctx)?; HasAttr { lhs: attrs.boxed(), rhs: path, @@ -749,10 +895,14 @@ impl Downgrade for ast::HasAttr { } impl HasAttr { - fn resolve<'gc>(self, ctx: &mut DowngradeContext<'gc>, mc: &Mutation<'gc>) -> Result { + fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result { Self { - lhs: self.lhs.resolve(ctx, mc)?.boxed(), - rhs: self.rhs.into_iter().map(|attr| attr.resolve(ctx, mc)).collect::>()?, + lhs: self.lhs.resolve(ctx, env)?.boxed(), + rhs: self + .rhs + .into_iter() + .map(|attr| attr.resolve(ctx, env)) + .collect::>()?, } .ir() .ok() @@ -760,9 +910,9 @@ impl HasAttr { } impl Downgrade for ast::UnaryOp { - fn downgrade<'gc>(self, ctx: &mut DowngradeContext<'gc>, mc: &Mutation<'gc>) -> Result { + fn downgrade(self, ctx: &mut DowngradeContext) -> Result { UnOp { - rhs: self.expr().unwrap().downgrade(ctx, mc)?.boxed(), + rhs: self.expr().unwrap().downgrade(ctx)?.boxed(), kind: self.operator().unwrap().into(), } .ir() @@ -771,9 +921,9 @@ impl Downgrade for ast::UnaryOp { } impl UnOp { - fn resolve<'gc>(self, ctx: &mut DowngradeContext<'gc>, mc: &Mutation<'gc>) -> Result { + fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result { Self { - rhs: self.rhs.resolve(ctx, mc)?.boxed(), + rhs: self.rhs.resolve(ctx, env)?.boxed(), ..self } .ir() @@ -782,12 +932,12 @@ impl UnOp { } impl Downgrade for ast::Select { - fn downgrade<'gc>(self, ctx: &mut DowngradeContext<'gc>, mc: &Mutation<'gc>) -> Result { + fn downgrade(self, ctx: &mut DowngradeContext) -> Result { Select { - expr: self.expr().unwrap().downgrade(ctx, mc)?.boxed(), - attrpath: downgrade_attrpath(self.attrpath().unwrap(), ctx, mc)?, + expr: self.expr().unwrap().downgrade(ctx)?.boxed(), + attrpath: downgrade_attrpath(self.attrpath().unwrap(), ctx)?, default: match self.default_expr() { - Some(default) => Some(default.downgrade(ctx, mc)?.boxed()), + Some(default) => Some(default.downgrade(ctx)?.boxed()), None => None, }, } @@ -797,16 +947,20 @@ impl Downgrade for ast::Select { } impl Select { - fn resolve<'gc>(self, ctx: &mut DowngradeContext<'gc>, mc: &Mutation<'gc>) -> Result { + fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result { let default = if let Some(default) = self.default { - Some(default.resolve(ctx, mc)?.boxed()) + Some(default.resolve(ctx, env)?.boxed()) } else { None }; Self { - expr: self.expr.resolve(ctx, mc)?.boxed(), - attrpath: self.attrpath.into_iter().map(|attr| attr.resolve(ctx, mc)).collect::>()?, - default + expr: self.expr.resolve(ctx, env)?.boxed(), + attrpath: self + .attrpath + .into_iter() + .map(|attr| attr.resolve(ctx, env)) + .collect::>()?, + default, } .ir() .ok() @@ -814,8 +968,8 @@ impl Select { } impl Downgrade for ast::LegacyLet { - fn downgrade<'gc>(self, ctx: &mut DowngradeContext<'gc>, mc: &Mutation<'gc>) -> Result { - let attrs = downgrade_has_entry(self, true, ctx, mc)?; + fn downgrade(self, ctx: &mut DowngradeContext) -> Result { + let attrs = downgrade_attrs(self, ctx)?; Select { expr: attrs.ir().boxed(), attrpath: vec![Attr::Str(ctx.new_sym("body"))], @@ -827,98 +981,170 @@ impl Downgrade for ast::LegacyLet { } impl Downgrade for ast::LetIn { - fn downgrade<'gc>(self, ctx: &mut DowngradeContext<'gc>, mc: &Mutation<'gc>) -> Result { - let body = self.body().unwrap(); - let attrs = downgrade_has_entry(self, true, ctx, mc)?; - let expr = body.downgrade(ctx, mc)?.boxed(); - Let { attrs, expr }.ir().ok() + fn downgrade(self, ctx: &mut DowngradeContext) -> Result { + let expr = self.body().unwrap().downgrade(ctx)?.boxed(); + let mut bindings = HashMap::new(); + for entry in self.entries() { + match entry { + ast::Entry::Inherit(inherit) => downgrade_inherit(inherit, &mut bindings, ctx)?, + ast::Entry::AttrpathValue(value) => { + let mut path = downgrade_attrpath(value.attrpath().unwrap(), ctx)?.into_iter(); + let value = value.value().unwrap().downgrade(ctx)?; + let unwrap_ident = |ident: Attr| { + ident.try_unwrap_str().map_err(|_| { + Error::DowngradeError("dynamic attributes not allowed in let".into()) + }) + }; + let ident = unwrap_ident(path.next().unwrap())?; + if path.len() > 1 { + let mut attrs = bindings + .entry(ident) + .or_insert_with(|| { + Attrs { + stcs: HashMap::new(), + dyns: Vec::new(), + } + .ir() + }) + .as_mut() + .try_unwrap_attrs() + .map_err(|_| { + Error::DowngradeError(format!( + r#"attribute '{}' already defined"#, + ctx.get_sym(ident) + )) + })?; + while path.len() > 1 { + attrs = attrs + .stcs + .entry(unwrap_ident(path.next().unwrap())?) + .or_insert_with(|| { + Attrs { + stcs: HashMap::new(), + dyns: Vec::new(), + } + .ir() + }) + .as_mut() + .try_unwrap_attrs() + .unwrap(); + } + attrs + .stcs + .insert(unwrap_ident(path.next().unwrap())?, value); + } else { + bindings.insert(ident, value); + } + } + } + } + let bindings = bindings.into_iter().sorted_by_key(|(k, _)| *k).collect(); + Let { bindings, expr }.ir().ok() } } impl Let { - fn resolve<'gc>(self, ctx: &mut DowngradeContext<'gc>, mc: &Mutation<'gc>) -> Result { - let attrs = self.attrs.resolve(ctx, mc)?.unwrap_attrs(); - ctx.enter_let(attrs.stcs.clone(), mc); - let expr = self.expr.resolve(ctx, mc)?.boxed(); - ctx.leave(); - - Self { attrs, expr }.ir().ok() + fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result { + let map = self + .bindings + .iter() + .map(|(sym, _)| *sym) + .sorted() + .collect(); + let env = env.enter_let(&map); + let bindings = self + .bindings + .into_iter() + .map(|(k, v)| Ok((k, match v.resolve(ctx, &env)? { + ir @ Ir::Const(_) => ir, + ir => ctx.new_thunk(ir).ir() + }))) + .collect::>()?; + let expr = self.expr.resolve(ctx, &env)?.boxed(); + Self { bindings, expr }.ir().ok() } } impl Downgrade for ast::With { - fn downgrade<'gc>(self, ctx: &mut DowngradeContext<'gc>, mc: &Mutation<'gc>) -> Result { - let namespace = self.namespace().unwrap().downgrade(ctx, mc)?; - if let Ir::Attrs(attrs) = namespace { - let expr = self.body().unwrap().downgrade(ctx, mc)?.boxed(); - Let { attrs, expr }.ir().ok() - } else { - let namespace = namespace.boxed(); - let expr = self.body().unwrap().downgrade(ctx, mc)?.boxed(); - With { namespace, expr }.ir().ok() - } + fn downgrade(self, ctx: &mut DowngradeContext) -> Result { + let namespace = self.namespace().unwrap().downgrade(ctx)?.boxed(); + let expr = self.body().unwrap().downgrade(ctx)?.boxed(); + With { namespace, expr }.ir().ok() } } impl With { - fn resolve<'gc>(self, ctx: &mut DowngradeContext<'gc>, mc: &Mutation<'gc>) -> Result { - let namespace = self.namespace.resolve(ctx, mc)?.boxed(); - ctx.enter_with(HashMap::new(), mc); - let expr = self.expr.resolve(ctx, mc)?.boxed(); + fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result { + let namespace = self.namespace.resolve(ctx, env)?.boxed(); + let expr = self.expr.resolve(ctx, &env.enter_with())?.boxed(); Self { namespace, expr }.ir().ok() } } impl Downgrade for ast::Lambda { - fn downgrade<'gc>(self, ctx: &mut DowngradeContext<'gc>, mc: &Mutation<'gc>) -> Result { + fn downgrade(self, ctx: &mut DowngradeContext) -> Result { let body = self.body().unwrap(); - let param = downgrade_param(self.param().unwrap(), ctx, mc)?; - let body = body.downgrade(ctx, mc)?.boxed(); + let param = downgrade_param(self.param().unwrap(), ctx)?; + let body = body.downgrade(ctx)?.boxed(); ctx.new_func(Func { param, body }).ir().ok() } } +impl Func { + fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result { + let env = match &self.param { + Param::Ident(ident) => env.enter_single_arg(*ident), + Param::Formals { formals, alias, .. } => env.enter_multi_arg( + formals + .iter() + .cloned() + .map(|(ident, default)| (ident, default.map(Ir::Thunk))) + .collect(), + *alias, + ), + }; + let body = self.body.resolve(ctx, &env)?.boxed(); + Ok(Self { body, ..self }) + } +} + impl Downgrade for ast::Apply { - fn downgrade<'gc>(self, ctx: &mut DowngradeContext<'gc>, mc: &Mutation<'gc>) -> Result { - let mut args = vec![self.argument().unwrap().downgrade(ctx, mc)?]; + fn downgrade(self, ctx: &mut DowngradeContext) -> Result { + let mut args = vec![self.argument().unwrap().downgrade(ctx)?]; let mut func = self.lambda().unwrap(); while let ast::Expr::Apply(call) = func { func = call.lambda().unwrap(); - args.push(call.argument().unwrap().downgrade(ctx, mc)?); + args.push(call.argument().unwrap().downgrade(ctx)?); } - let func = func.downgrade(ctx, mc)?.boxed(); + let func = func.downgrade(ctx)?.boxed(); args.reverse(); Call { func, args }.ir().ok() } } impl Call { - fn resolve<'gc>(self, ctx: &mut DowngradeContext<'gc>, mc: &Mutation<'gc>) -> Result { + fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result { Self { - func: self.func.resolve(ctx, mc)?.boxed(), - args: self.args.into_iter().map(|arg| arg.resolve(ctx, mc)).collect::>()?, + func: self.func.resolve(ctx, env)?.boxed(), + args: self + .args + .into_iter() + .map(|arg| arg.resolve(ctx, env)) + .collect::>()?, } .ir() .ok() } } -fn downgrade_param<'gc>( - param: ast::Param, - ctx: &mut DowngradeContext<'gc>, - mc: &Mutation<'gc>, -) -> Result { +fn downgrade_param(param: ast::Param, ctx: &mut DowngradeContext) -> Result { match param { ast::Param::IdentParam(ident) => Ok(Param::Ident(ctx.new_sym(ident.to_string()))), - ast::Param::Pattern(pattern) => downgrade_pattern(pattern, ctx, mc), + ast::Param::Pattern(pattern) => downgrade_pattern(pattern, ctx), } } -fn downgrade_pattern<'gc>( - pattern: ast::Pattern, - ctx: &mut DowngradeContext<'gc>, - mc: &Mutation<'gc>, -) -> Result { +fn downgrade_pattern(pattern: ast::Pattern, ctx: &mut DowngradeContext) -> Result { let formals = pattern .pat_entries() .map(|entry| { @@ -929,8 +1155,9 @@ fn downgrade_pattern<'gc>( entry .default() .unwrap() - .downgrade(ctx, mc) + .downgrade(ctx) .map(|ok| (ident, Some(ctx.new_thunk(ok)))) + // .map(|ok| (ident, Some(ok))) } }) .collect::>>()?; @@ -945,45 +1172,36 @@ fn downgrade_pattern<'gc>( }) } -fn downgrade_has_entry<'gc>( - has_entry: impl ast::HasEntry, - rec: bool, - ctx: &mut DowngradeContext<'gc>, - mc: &Mutation<'gc>, -) -> Result { +fn downgrade_attrs(has_entry: impl ast::HasEntry, ctx: &mut DowngradeContext) -> Result { let entires = has_entry.entries(); let mut attrs = Attrs { - rec, stcs: HashMap::new(), dyns: Vec::new(), }; for entry in entires { match entry { - ast::Entry::Inherit(inherit) => downgrade_inherit(inherit, &mut attrs.stcs, ctx, mc)?, - ast::Entry::AttrpathValue(value) => { - downgrade_attrpathvalue(value, &mut attrs, ctx, mc)? - } + ast::Entry::Inherit(inherit) => downgrade_inherit(inherit, &mut attrs.stcs, ctx)?, + ast::Entry::AttrpathValue(value) => downgrade_attrpathvalue(value, &mut attrs, ctx)?, } } Ok(attrs) } -fn downgrade_inherit<'gc>( +fn downgrade_inherit( inherit: ast::Inherit, stcs: &mut HashMap, - ctx: &mut DowngradeContext<'gc>, - mc: &Mutation<'gc>, + ctx: &mut DowngradeContext, ) -> Result<()> { let from = if let Some(from) = inherit.from() { - let from = from.expr().unwrap().downgrade(ctx, mc)?; + let from = from.expr().unwrap().downgrade(ctx)?; Some(ctx.new_thunk(from)) } else { None }; for attr in inherit.attrs() { - let ident = match downgrade_attr(attr, ctx, mc)? { + let ident = match downgrade_attr(attr, ctx)? { Attr::Str(ident) => ident, _ => { return Err(Error::DowngradeError( @@ -1007,11 +1225,7 @@ fn downgrade_inherit<'gc>( Ok(()) } -fn downgrade_attr<'gc>( - attr: ast::Attr, - ctx: &mut DowngradeContext<'gc>, - mc: &Mutation<'gc>, -) -> Result { +fn downgrade_attr(attr: ast::Attr, ctx: &mut DowngradeContext) -> Result { use ast::Attr::*; use ast::InterpolPart::*; match attr { @@ -1024,7 +1238,7 @@ fn downgrade_attr<'gc>( match parts.into_iter().next().unwrap() { Literal(ident) => Ok(Attr::Str(ctx.new_sym(ident))), Interpolation(interpol) => { - Ok(Attr::Dynamic(interpol.expr().unwrap().downgrade(ctx, mc)?)) + Ok(Attr::Dynamic(interpol.expr().unwrap().downgrade(ctx)?)) } } } else { @@ -1032,38 +1246,33 @@ fn downgrade_attr<'gc>( .into_iter() .map(|part| match part { Literal(lit) => ctx.new_const(lit.into()).ir().ok(), - Interpolation(interpol) => interpol.expr().unwrap().downgrade(ctx, mc), + Interpolation(interpol) => interpol.expr().unwrap().downgrade(ctx), }) .collect::>>()?; Ok(Attr::Strs(ConcatStrings { parts })) } } - Dynamic(dynamic) => Ok(Attr::Dynamic(dynamic.expr().unwrap().downgrade(ctx, mc)?)), + Dynamic(dynamic) => Ok(Attr::Dynamic(dynamic.expr().unwrap().downgrade(ctx)?)), } } -fn downgrade_attrpath<'gc>( - attrpath: ast::Attrpath, - ctx: &mut DowngradeContext<'gc>, - mc: &Mutation<'gc>, -) -> Result> { +fn downgrade_attrpath(attrpath: ast::Attrpath, ctx: &mut DowngradeContext) -> Result> { attrpath .attrs() - .map(|attr| downgrade_attr(attr, ctx, mc)) + .map(|attr| downgrade_attr(attr, ctx)) .collect::>>() } -fn downgrade_attrpathvalue<'gc>( +fn downgrade_attrpathvalue( value: ast::AttrpathValue, attrs: &mut Attrs, - ctx: &mut DowngradeContext<'gc>, - mc: &Mutation<'gc>, + ctx: &mut DowngradeContext, ) -> Result<()> { - let path = downgrade_attrpath(value.attrpath().unwrap(), ctx, mc)?; - let value = value.value().unwrap().downgrade(ctx, mc)?; + let path = downgrade_attrpath(value.attrpath().unwrap(), ctx)?; + let value = value.value().unwrap().downgrade(ctx)?; let value = match value { x @ Ir::Const(_) => x, x => ctx.new_thunk(x).ir(), }; - attrs.insert(path, value) + attrs.insert(path, value, ctx) } diff --git a/src/jit/helpers.rs b/src/jit/helpers.rs index 867511e..927b79f 100644 --- a/src/jit/helpers.rs +++ b/src/jit/helpers.rs @@ -34,6 +34,8 @@ pub struct Helpers<'ctx> { pub eq: FunctionValue<'ctx>, pub or: FunctionValue<'ctx>, pub call: FunctionValue<'ctx>, + pub arg: FunctionValue<'ctx>, + pub lookup_let: FunctionValue<'ctx>, pub lookup: FunctionValue<'ctx>, pub force: FunctionValue<'ctx>, } @@ -113,6 +115,16 @@ impl<'ctx> Helpers<'ctx> { ), None, ); + let arg = module.add_function( + "arg", + value_type.fn_type(&[ptr_int_type.into(), ptr_type.into()], false), + None, + ); + let lookup_let = module.add_function( + "lookup_let", + value_type.fn_type(&[ptr_int_type.into(), ptr_int_type.into(), ptr_type.into()], false), + None, + ); let lookup = module.add_function( "lookup", value_type.fn_type(&[ptr_int_type.into(), ptr_type.into()], false), @@ -142,6 +154,8 @@ impl<'ctx> Helpers<'ctx> { execution_engine.add_global_mapping(&eq, helper_eq as _); execution_engine.add_global_mapping(&or, helper_or as _); execution_engine.add_global_mapping(&call, helper_call as _); + execution_engine.add_global_mapping(&arg, helper_arg as _); + execution_engine.add_global_mapping(&lookup_let, helper_lookup_let as _); execution_engine.add_global_mapping(&lookup, helper_lookup as _); execution_engine.add_global_mapping(&force, helper_force as _); @@ -165,6 +179,8 @@ impl<'ctx> Helpers<'ctx> { eq, or, call, + arg, + lookup_let, lookup, force, } @@ -348,9 +364,21 @@ extern "C" fn helper_call<'gc>( } } +extern "C" fn helper_arg(idx: usize, env: *const VmEnv) -> JITValue { + let env = unsafe { env.as_ref() }.unwrap(); + let val: JITValue = env.lookup_arg(idx).into(); + val +} + +extern "C" fn helper_lookup_let(level: usize, idx: usize, env: *const VmEnv) -> JITValue { + let env = unsafe { env.as_ref() }.unwrap(); + let val: JITValue = env.lookup_let(level, idx).into(); + val +} + extern "C" fn helper_lookup(sym: usize, env: *const VmEnv) -> JITValue { let env = unsafe { env.as_ref() }.unwrap(); - let val: JITValue = env.lookup_slow(&sym).unwrap().into(); + let val: JITValue = env.lookup_with(&sym).unwrap().into(); val } @@ -373,7 +401,7 @@ extern "C" fn helper_force<'gc>( let (opcodes, env) = thunk.suspend(mc).unwrap(); let func = unsafe { jit.as_ref() } .unwrap() - .compile_seq(opcodes.iter().copied(), vm) + .compile_seq(opcodes.iter().copied().rev(), vm) .unwrap(); let val = unsafe { func.call(env.as_ref() as *const _, mc as *const _) }; thunk.insert_value(val.into(), mc); diff --git a/src/jit/mod.rs b/src/jit/mod.rs index b4be69d..ef5cd3f 100644 --- a/src/jit/mod.rs +++ b/src/jit/mod.rs @@ -199,7 +199,7 @@ impl<'gc> JITContext<'gc> { pub fn compile_seq( &self, - opcodes: impl ExactSizeIterator + DoubleEndedIterator, + mut opcodes: impl ExactSizeIterator, vm: &'gc VM<'gc>, ) -> Result> { let mut stack = Stack::<_, STACK_SIZE>::new(); @@ -211,7 +211,7 @@ impl<'gc> JITContext<'gc> { let entry = self.context.append_basic_block(func_, "entry"); self.builder.position_at_end(entry); let len = opcodes.len(); - self.build_expr(&mut opcodes.rev(), vm, env, mc, &mut stack, func_, len)?; + self.build_expr(&mut opcodes, vm, env, mc, &mut stack, func_, len)?; assert_eq!(stack.len(), 1); let value = stack.pop(); @@ -437,6 +437,47 @@ impl<'gc> JITContext<'gc> { _ => todo!("BinOp::{:?} not implemented in JIT", op), } } + OpCode::Arg { level } => stack.push( + self.builder + .build_direct_call( + self.helpers.arg, + &[ + self.helpers + .ptr_int_type + .const_int(level as u64, false) + .into(), + env.into(), + ], + "call_arg", + ) + .unwrap() + .try_as_basic_value() + .left() + .unwrap(), + )?, + OpCode::LookUpLet { level, idx } => stack.push( + self.builder + .build_direct_call( + self.helpers.lookup_let, + &[ + self.helpers + .ptr_int_type + .const_int(level as u64, false) + .into(), + self.helpers + .ptr_int_type + .const_int(idx as u64, false) + .into(), + env.into(), + ], + "call_lookup_let", + ) + .unwrap() + .try_as_basic_value() + .left() + .unwrap(), + )?, + OpCode::LookUp { sym } => stack.push( self.builder .build_direct_call( diff --git a/src/jit/test.rs b/src/jit/test.rs index 97c318c..cfda948 100644 --- a/src/jit/test.rs +++ b/src/jit/test.rs @@ -8,7 +8,6 @@ use hashbrown::{HashMap, HashSet}; use inkwell::context::Context; use ecow::EcoString; -use rpds::vector_sync; use crate::builtins::vm_env; use crate::compile::compile; @@ -77,7 +76,7 @@ macro_rules! symbol { macro_rules! list { ($($x:tt)*) => ( - Value::List(List::new(vector_sync![$($x)*])) + Value::List(List::new(vec![$($x)*])) ); } diff --git a/src/ty/common.rs b/src/ty/common.rs index 6042c79..134cce6 100644 --- a/src/ty/common.rs +++ b/src/ty/common.rs @@ -112,3 +112,8 @@ impl PartialEq for Const { } impl Eq for Const {} + +pub enum MaybeThunk { + Thunk(usize), + Const(Const) +} diff --git a/src/ty/internal/attrset.rs b/src/ty/internal/attrset.rs index fd03105..04fdffe 100644 --- a/src/ty/internal/attrset.rs +++ b/src/ty/internal/attrset.rs @@ -2,11 +2,10 @@ use std::ops::Deref; use std::rc::Rc; use derive_more::Constructor; -use gc_arena::{Collect, Gc, Mutation}; +use gc_arena::Collect; use hashbrown::{HashMap, HashSet}; use itertools::Itertools; -use crate::env::VmEnv; use crate::vm::VM; use super::super::public as p; @@ -58,14 +57,6 @@ impl<'gc> AttrSet<'gc> { self.data.get(&sym).is_some() } - pub fn capture(&mut self, env: Gc<'gc, VmEnv<'gc>>, mc: &Mutation<'gc>) { - self.data.iter().for_each(|(_, v)| { - if let Value::Thunk(ref thunk) = v.clone() { - thunk.capture_env(env, mc); - } - }) - } - pub fn update(&mut self, other: &AttrSet<'gc>) { for (k, v) in other.data.iter() { self.push_attr_force(*k, v.clone()) diff --git a/src/ty/internal/func.rs b/src/ty/internal/func.rs index 75c2ef4..21bc071 100644 --- a/src/ty/internal/func.rs +++ b/src/ty/internal/func.rs @@ -2,15 +2,13 @@ use std::cell::Cell; use gc_arena::lock::{GcRefLock, RefLock}; use gc_arena::{Collect, Gc, Mutation}; -use hashbrown::HashMap; -use itertools::Itertools; use crate::bytecode::Func as BFunc; use crate::env::VmEnv; use crate::error::Result; use crate::ir; use crate::jit::JITFunc; -use crate::ty::internal::{Thunk, Value}; +use crate::ty::internal::Value; use crate::vm::VM; #[derive(Debug, Clone, Collect)] @@ -75,45 +73,7 @@ impl<'gc> Func<'gc> { vm: &'gc VM<'gc>, mc: &Mutation<'gc>, ) -> Result> { - use Param::*; - - let mut env = self.env; - env = match self.func.param.clone() { - Ident(ident) => env.enter_arg(ident, arg, mc), - Formals { - formals, - ellipsis, - alias, - } => { - let arg = arg.unwrap_attr_set(); - let mut new = HashMap::with_capacity(formals.len() + alias.iter().len()); - if !ellipsis - && arg - .as_inner() - .iter() - .map(|(k, _)| k) - .sorted() - .ne(formals.iter().map(|(k, _)| k).sorted()) - { - todo!() - } - for (formal, default) in formals { - let arg = arg - .select(formal) - .or_else(|| { - default - .map(|idx| Value::Thunk(Thunk::new(vm.get_thunk(idx), mc))) - }) - .unwrap(); - new.insert(formal, arg); - } - if let Some(alias) = alias { - new.insert(alias, Value::AttrSet(arg)); - } - env.enter_let(Gc::new(mc, new), mc) - } - }; - + let env = self.env.enter_arg(arg, mc); let compiled = self .compiled .borrow_mut(mc) diff --git a/src/ty/internal/list.rs b/src/ty/internal/list.rs index f7c7cc1..8925d2b 100644 --- a/src/ty/internal/list.rs +++ b/src/ty/internal/list.rs @@ -1,17 +1,15 @@ use hashbrown::HashSet; - -use derive_more::Constructor; -use gc_arena::Collect; -use rpds::Vector; +use gc_arena::{Collect, Gc, Mutation}; use crate::ty::public as p; use crate::vm::VM; +use crate::env::VmEnv; use super::Value; -#[derive(Constructor, Clone, PartialEq)] +#[derive(Clone, PartialEq)] pub struct List<'gc> { - data: Vector>, + data: Vec>, } unsafe impl<'gc> Collect<'gc> for List<'gc> { @@ -23,22 +21,40 @@ unsafe impl<'gc> Collect<'gc> for List<'gc> { } impl<'gc> List<'gc> { - pub fn empty() -> Self { + pub fn new() -> Self { List { - data: Vector::new(), + data: Vec::new(), + } + } + + pub fn with_capacity(cap: usize) -> Self { + List { + data: Vec::with_capacity(cap), } } pub fn push(&mut self, elem: Value<'gc>) { - self.data.push_back_mut(elem); + self.data.push(elem); } pub fn concat(&mut self, other: &List<'gc>) { for elem in other.data.iter() { - self.data.push_back_mut(elem.clone()); + self.data.push(elem.clone()); } } + pub fn capture(&mut self, env: Gc<'gc, VmEnv<'gc>>, mc: &Mutation<'gc>) { + self.data.iter().for_each(|v| { + if let Value::Thunk(ref thunk) = v.clone() { + thunk.capture_env(env, mc); + } + }) + } + + pub fn into_inner(self) -> Vec> { + self.data + } + pub fn to_public(&self, vm: &VM<'gc>, seen: &mut HashSet>) -> p::Value { p::Value::List(p::List::new( self.data diff --git a/src/ty/internal/mod.rs b/src/ty/internal/mod.rs index 09e404e..0e18479 100644 --- a/src/ty/internal/mod.rs +++ b/src/ty/internal/mod.rs @@ -1,7 +1,5 @@ use std::cell::Cell; use std::hash::Hash; -use std::marker::PhantomData; -use std::mem::MaybeUninit; use std::ops::Deref; use derive_more::{IsVariant, Unwrap}; @@ -35,66 +33,49 @@ pub struct CoW<'gc, T: Clone + Collect<'gc>> { inner: Gc<'gc, CoWInner<'gc, T>>, } +#[derive(Collect)] +#[collect(no_drop)] struct CoWInner<'gc, T: Clone + Collect<'gc>> { ref_count: Cell, - data: MaybeUninit, - _marker: PhantomData>, -} - -unsafe impl<'gc, T: Clone + Collect<'gc>> Collect<'gc> for CoWInner<'gc, T> { - fn trace>(&self, cc: &mut Tr) { - unsafe { self.data.assume_init_ref() }.trace(cc); - } + data: GcRefLock<'gc, T>, } #[allow(mutable_transmutes)] impl<'gc, T: Clone + Collect<'gc>> CoW<'gc, T> { pub fn new(data: T, mc: &Mutation<'gc>) -> Self { Self { - inner: Gc::new(mc, CoWInner::new(data)), + inner: Gc::new(mc, CoWInner::new(data, mc)), } } - pub fn new_cyclic(datafn: impl FnOnce(Self) -> T, mc: &Mutation<'gc>) -> Self { - let inner = Gc::new( - mc, - CoWInner { - ref_count: Cell::new(1), - data: MaybeUninit::uninit(), - _marker: PhantomData, - }, - ); - let data = datafn(CoW { inner }); - unsafe { std::mem::transmute::<_, &mut MaybeUninit<_>>(&inner.data) }.write(data); - Self { inner } + pub unsafe fn make_cyclic(&mut self, f: impl FnOnce(&mut T, Self), mc: &Mutation<'gc>) { + let inner = self.inner; + self.make_mut(|self_mut| f(self_mut, Self { inner }), mc); + self.inner.ref_count.set(self.inner.ref_count.get() + 1); } - pub fn make_mut(&mut self, mc: &Mutation<'gc>) -> &mut T { + pub fn make_mut(&mut self, f: impl FnOnce(&mut T) -> U, mc: &Mutation<'gc>) -> U { if self.inner.ref_count.get() == 1 { - unsafe { std::mem::transmute(self.inner.data.assume_init_ref()) } + f(&mut *self.inner.data.borrow_mut(mc)) } else { - unsafe { - *self = CoW::new(self.inner.data.assume_init_ref().clone(), mc); - std::mem::transmute(self.inner.data.assume_init_ref()) - } + *self = CoW::new(self.inner.data.borrow().clone(), mc); + f(&mut *self.inner.data.borrow_mut(mc)) } } pub fn as_ptr(&self) -> *const T { - self.as_ref() as *const T + &*self.as_ref() as *const T } -} -impl<'gc, T: Clone + Collect<'gc>> AsRef for CoW<'gc, T> { - fn as_ref(&self) -> &T { - unsafe { self.inner.data.assume_init_ref() } + pub fn as_ref(&self) -> std::cell::Ref<'_, T> { + self.inner.data.borrow() } } impl<'gc, T: Clone + Collect<'gc>> Deref for CoW<'gc, T> { type Target = T; fn deref(&self) -> &Self::Target { - self.as_ref() + unsafe { self.as_ptr().as_ref() }.unwrap() } } @@ -112,11 +93,10 @@ impl<'gc, T: Clone + Collect<'gc>> Drop for CoW<'gc, T> { } impl<'gc, T: Clone + Collect<'gc>> CoWInner<'gc, T> { - fn new(data: T) -> Self { + fn new(data: T, mc: &Mutation<'gc>) -> Self { Self { ref_count: Cell::new(1), - data: MaybeUninit::new(data), - _marker: PhantomData, + data: Gc::new(mc, RefLock::new(data)) } } } @@ -153,7 +133,7 @@ impl Hash for Value<'_> { List(x) => x.as_ptr().hash(state), Catchable(x) => x.hash(state), PrimOp(x) => (x.as_ref() as *const self::PrimOp).hash(state), - PartialPrimOp(x) => (x.as_ref() as *const self::PartialPrimOp).hash(state), + PartialPrimOp(x) => x.as_ptr().hash(state), Func(x) => (x.as_ref() as *const self::Func).hash(state), } } @@ -206,22 +186,6 @@ pub enum ValueAsRef<'v, 'gc> { Func(&'v Func<'gc>), } -#[derive(IsVariant, Unwrap)] -pub enum ValueAsMut<'v, 'gc> { - Int(i64), - Float(f64), - Bool(bool), - String(&'v str), - Null, - Thunk(&'v Thunk<'gc>), - AttrSet(&'v mut AttrSet<'gc>), - List(&'v mut List<'gc>), - Catchable(&'v str), - PrimOp(&'v PrimOp), - PartialPrimOp(&'v mut PartialPrimOp<'gc>), - Func(&'v Func<'gc>), -} - impl<'gc, 'v> Value<'gc> { pub fn as_ref(&'v self) -> ValueAsRef<'v, 'gc> { use Value::*; @@ -241,25 +205,6 @@ impl<'gc, 'v> Value<'gc> { Func(x) => R::Func(x), } } - - pub fn as_mut(&'v mut self, mc: &Mutation<'gc>) -> ValueAsMut<'v, 'gc> { - use Value::*; - use ValueAsMut as M; - match self { - Int(x) => M::Int(*x), - Float(x) => M::Float(*x), - Bool(x) => M::Bool(*x), - String(x) => M::String(x), - Null => M::Null, - Thunk(x) => M::Thunk(x), - AttrSet(x) => M::AttrSet(x.make_mut(mc)), - List(x) => M::List(x.make_mut(mc)), - Catchable(x) => M::Catchable(x), - PrimOp(x) => M::PrimOp(x), - PartialPrimOp(x) => M::PartialPrimOp(x.make_mut(mc)), - Func(x) => M::Func(x), - } - } } impl<'gc> Value<'gc> { pub fn ok(self) -> Result { @@ -382,7 +327,7 @@ impl<'gc> Value<'gc> { (Float(a), Float(b)) => Float(a + b), (String(a), String(b)) => { let mut a = a.clone(); - a.make_mut(mc).push_str(b.as_str()); + a.make_mut(|a| a.push_str(b.as_str()), mc); String(a) } (Value::Catchable(_), _) => return, @@ -426,7 +371,7 @@ impl<'gc> Value<'gc> { match (self.coerce_to_string(), other.coerce_to_string()) { (Value::String(a), Value::String(b)) => { let mut a = a.clone(); - a.make_mut(mc).push_str(b.as_str()) + a.make_mut(|a| a.push_str(b.as_str()), mc); } (_, Value::Catchable(_)) => *self = other, (Value::Catchable(_), _) => (), @@ -437,7 +382,7 @@ impl<'gc> Value<'gc> { pub fn push(&mut self, elem: Self, mc: &Mutation<'gc>) -> &mut Self { if let Value::List(list) = self { - list.make_mut(mc).push(elem); + list.make_mut(|list| list.push(elem), mc); } else if let Value::Catchable(_) = self { } else if let Value::Catchable(_) = elem { *self = elem; @@ -454,7 +399,7 @@ impl<'gc> Value<'gc> { } match (self, other) { (Value::List(a), Value::List(b)) => { - a.make_mut(mc).concat(b.as_ref()); + a.make_mut(|a| a.concat(&b), mc); } (Value::Catchable(_), _) => (), _ => todo!(), @@ -463,7 +408,7 @@ impl<'gc> Value<'gc> { pub fn push_attr(&mut self, sym: usize, val: Self, mc: &Mutation<'gc>) -> &mut Self { if let Value::AttrSet(attrs) = self { - attrs.make_mut(mc).push_attr(sym, val); + attrs.make_mut(|attrs| attrs.push_attr(sym, val), mc) } else if let Value::Catchable(_) = self { } else if let Value::Catchable(_) = val { *self = val @@ -480,7 +425,7 @@ impl<'gc> Value<'gc> { } match (self, other) { (Value::AttrSet(a), Value::AttrSet(b)) => { - a.make_mut(mc).update(b.as_ref()); + a.make_mut(|a| a.update(&b), mc) } (Value::Catchable(_), _) => (), _ => todo!(), @@ -556,7 +501,7 @@ impl<'gc> Value<'gc> { Int(x) => Value::Const(Const::Int(*x)), Float(x) => Value::Const(Const::Float(*x)), Bool(x) => Value::Const(Const::Bool(*x)), - String(x) => Value::Const(Const::String(x.as_ref().into())), + String(x) => Value::Const(Const::String(x.as_str().into())), Null => Value::Const(Const::Null), Thunk(_) => Value::Thunk, PrimOp(primop) => Value::PrimOp(primop.name), diff --git a/src/ty/internal/primop.rs b/src/ty/internal/primop.rs index 392754c..cc0e018 100644 --- a/src/ty/internal/primop.rs +++ b/src/ty/internal/primop.rs @@ -73,13 +73,17 @@ impl<'gc> PartialPrimOp<'gc> { mc: &Mutation<'gc>, ) -> Result> { let func = self.func; - let self_mut = self.make_mut(mc); - self_mut.args.push(arg); - self_mut.arity -= 1; - if self_mut.arity > 0 { - Value::PartialPrimOp(self.clone()).ok() - } else { - func(std::mem::take(&mut self_mut.args), vm, mc) - } + let Some(ret) = self.make_mut(|self_mut| { + self_mut.args.push(arg); + self_mut.arity -= 1; + if self_mut.arity == 0 { + Some(func(std::mem::take(&mut self_mut.args), vm, mc)) + } else { + None + } + }, mc) else { + return Value::PartialPrimOp(self.clone()).ok(); + }; + ret } } diff --git a/src/ty/internal/string.rs b/src/ty/internal/string.rs index c2e829a..25a61dc 100644 --- a/src/ty/internal/string.rs +++ b/src/ty/internal/string.rs @@ -1,16 +1,15 @@ // TODO: Contextful String use ecow::EcoString; -use rpds::List; pub struct StringContext { - context: List<()>, + context: Vec<()>, } impl StringContext { pub fn new() -> StringContext { StringContext { - context: List::new(), + context: Vec::new(), } } } diff --git a/src/ty/public.rs b/src/ty/public.rs index 5f97ac2..22a17ee 100644 --- a/src/ty/public.rs +++ b/src/ty/public.rs @@ -6,7 +6,6 @@ use std::sync::LazyLock; use derive_more::{Constructor, IsVariant, Unwrap}; use ecow::EcoString; use regex::Regex; -use rpds::VectorSync; use super::common::*; @@ -93,7 +92,7 @@ impl Display for AttrSet { #[derive(Constructor, Clone, Debug, PartialEq)] pub struct List { - data: VectorSync, + data: Vec, } impl Display for List { diff --git a/src/vm/mod.rs b/src/vm/mod.rs index f241a36..a4027bd 100644 --- a/src/vm/mod.rs +++ b/src/vm/mod.rs @@ -3,7 +3,6 @@ use std::cell::RefCell; use gc_arena::{Arena, Collect, Gc, Mutation, Rootable}; use hashbrown::{HashMap, HashSet}; use inkwell::context::Context; -use itertools::Itertools; use crate::builtins::vm_env; use crate::bytecode::{BinOp, Func as F, OpCode, OpCodes, Program, UnOp}; @@ -88,49 +87,9 @@ pub fn eval FnOnce(Value<'gc>, &mut GcRoot<'gc>, &Mutation<'gc>) } Consq::PopEnv => _ = root.envs.pop().unwrap(), Consq::Call => { - use Param::*; let arg = root.stack.pop(); let func = root.stack.pop().unwrap_func(); - let env = func.env; - let env = match func.func.param.clone() { - Ident(ident) => env.enter_arg(ident, arg, mc), - Formals { - formals, - ellipsis, - alias, - } => { - let arg = arg.unwrap_attr_set(); - let mut new = - HashMap::with_capacity(formals.len() + alias.iter().len()); - if !ellipsis - && arg - .iter() - .map(|(k, _)| k) - .sorted() - .ne(formals.iter().map(|(k, _)| k).sorted()) - { - todo!() - } - for (formal, default) in formals { - // TODO: rec env - let arg = arg - .select(formal) - .or_else(|| { - default.map(|idx| { - Value::Thunk( - Thunk::new(root.vm.get_thunk(idx), mc), - ) - }) - }) - .unwrap(); - new.insert(formal, arg); - } - if let Some(alias) = alias { - new.insert(alias, Value::AttrSet(arg)); - } - env.enter_let(Gc::new(mc, new), mc) - } - }; + let env = func.env.enter_arg(arg, mc); let count = func.count.get(); func.count.set(count + 1); if count >= 1 { @@ -200,13 +159,12 @@ fn single_op<'gc, const CAP: usize>( let Some(val) = stack.tos().as_ref().unwrap_thunk().get_value() else { return Ok(Consq::Force); }; - stack.pop(); - stack.push(val)?; + *stack.tos_mut() = val; } OpCode::InsertValue => { let val = stack.pop(); - stack.pop().unwrap_thunk().insert_value(val.clone(), mc); - let _ = stack.push(val); + stack.tos().as_ref().unwrap_thunk().insert_value(val.clone(), mc); + *stack.tos_mut() = val; } OpCode::Jmp { step } => return Ok(Consq::Jmp(step)), OpCode::JmpIfFalse { step } => { @@ -218,7 +176,7 @@ fn single_op<'gc, const CAP: usize>( let arg = stack.pop(); let func = stack.tos_mut(); if func.is_func() { - stack.push(arg)?; + let _ = stack.push(arg); return Ok(Consq::Call); } func.call(arg, vm, mc)?; @@ -227,6 +185,9 @@ fn single_op<'gc, const CAP: usize>( let func = vm.get_func(idx); stack.push(Value::Func(Gc::new(mc, Func::new(func, *env, mc))))?; } + OpCode::Arg { level } => { + stack.push(env.lookup_arg(level))?; + } OpCode::UnOp { op } => { use UnOp::*; let value = stack.tos_mut(); @@ -262,8 +223,8 @@ fn single_op<'gc, const CAP: usize>( OpCode::Path => { todo!() } - OpCode::List => { - stack.push(Value::List(CoW::new(List::empty(), mc)))?; + OpCode::List { cap } => { + stack.push(Value::List(CoW::new(List::with_capacity(cap), mc)))?; } OpCode::PushElem => { let elem = stack.pop(); @@ -272,22 +233,14 @@ fn single_op<'gc, const CAP: usize>( OpCode::AttrSet { cap } => { stack.push(Value::AttrSet(CoW::new(AttrSet::with_capacity(cap), mc)))?; } - OpCode::FinalizeRec => { - let mut new = HashMap::new(); - stack - .tos() + OpCode::FinalizeLet => { + let mut list = stack.pop().unwrap_list(); + let map = list .as_ref() - .unwrap_attr_set() - .as_inner() - .iter() - .map(|(&k, v)| (k, v.clone())) - .collect_into(&mut new); - *env = env.enter_let(Gc::new(mc, new), mc); - stack - .tos_mut() - .as_mut(mc) - .unwrap_attr_set() - .capture(*env, mc); + .clone() + .into_inner(); + *env = env.enter_let(map, mc); + list.make_mut(|list| list.capture(*env, mc), mc); } OpCode::PushStaticAttr { name } => { let val = stack.pop(); @@ -296,7 +249,7 @@ fn single_op<'gc, const CAP: usize>( OpCode::PushDynamicAttr => { let val = stack.pop(); let sym = stack.pop(); - let sym = vm.new_sym(sym.unwrap_string().as_ref()); + let sym = vm.new_sym::<&str>(&sym.unwrap_string()); stack.tos_mut().push_attr(sym, val, mc); } OpCode::Select { sym } => { @@ -309,14 +262,14 @@ fn single_op<'gc, const CAP: usize>( OpCode::SelectDynamic => { let mut val = stack.pop(); val.coerce_to_string(); - let sym = vm.new_sym(val.unwrap_string().as_ref()); + let sym = vm.new_sym::<&str>(&val.unwrap_string()); stack.tos_mut().select(sym, vm)?; } OpCode::SelectDynamicOrDefault => { let default = stack.pop(); let mut val = stack.pop(); val.coerce_to_string(); - let sym = vm.new_sym(val.unwrap_string().as_ref()); + let sym = vm.new_sym::<&str>(&val.unwrap_string()); stack.tos_mut().select_with_default(sym, default)?; } OpCode::HasAttr { sym } => { @@ -325,25 +278,18 @@ fn single_op<'gc, const CAP: usize>( OpCode::HasDynamicAttr => { let mut val = stack.pop(); val.coerce_to_string(); - let sym = vm.new_sym(val.unwrap_string().as_ref()); + let sym = vm.new_sym::<&str>(&val.unwrap_string()); stack.tos_mut().has_attr(sym); } OpCode::LookUp { sym } => { stack.push( - env.lookup_slow(&sym) + env.lookup_with(&sym) .ok_or_else(|| Error::EvalError(format!("{} not found", vm.get_sym(sym))))? .clone(), )?; } - OpCode::EnterLetEnv => { - let mut new = HashMap::new(); - stack - .pop() - .unwrap_attr_set() - .iter() - .map(|(&k, v)| (k, v.clone())) - .collect_into(&mut new); - *env = env.enter_let(Gc::new(mc, new), mc); + OpCode::LookUpLet { level, idx } => { + stack.push(env.lookup_let(level, idx))?; } OpCode::LeaveEnv => *env = env.leave(), OpCode::EnterWithEnv => { @@ -401,7 +347,7 @@ impl<'gc> VM<'gc> { self.symbols.borrow()[idx].clone().into() } - pub fn new_sym(&self, sym: impl Into) -> usize { + pub fn new_sym>(&self, sym: T) -> usize { let sym = sym.into(); if let Some(&idx) = self.symmap.borrow().get(&sym) { idx @@ -420,7 +366,7 @@ impl<'gc> VM<'gc> { pub fn compile_func(&'gc self, func: &'gc F) -> JITFunc<'gc> { self.jit - .compile_seq(func.opcodes.iter().copied(), self) + .compile_seq(func.opcodes.iter().copied().rev(), self) .unwrap() } } diff --git a/src/vm/test.rs b/src/vm/test.rs index 8c2b5dc..22e854e 100644 --- a/src/vm/test.rs +++ b/src/vm/test.rs @@ -7,7 +7,6 @@ use hashbrown::HashMap; use test::{Bencher, black_box}; use ecow::EcoString; -use rpds::vector_sync; use crate::compile::compile; use crate::ir::downgrade; @@ -75,7 +74,7 @@ macro_rules! symbol { macro_rules! list { ($($x:tt)*) => ( - Value::List(List::new(vector_sync![$($x)*])) + Value::List(List::new(vec![$($x)*])) ); }