From a9cfddbf5c6acd52d759912e43a1ac886734f5ea Mon Sep 17 00:00:00 2001 From: imxyy_soope_ Date: Fri, 8 Aug 2025 12:12:23 +0800 Subject: [PATCH] feat: rec attrset --- evaluator/nixjit/src/test.rs | 3 +- evaluator/nixjit_builtins/src/lib.rs | 19 +++++- evaluator/nixjit_context/src/lib.rs | 55 ++++++++-------- evaluator/nixjit_eval/src/lib.rs | 10 +-- evaluator/nixjit_eval/src/value/attrset.rs | 26 ++++---- evaluator/nixjit_eval/src/value/func.rs | 18 ++--- evaluator/nixjit_eval/src/value/list.rs | 11 +--- evaluator/nixjit_eval/src/value/mod.rs | 39 +++-------- evaluator/nixjit_hir/src/lib.rs | 4 +- evaluator/nixjit_hir/src/utils.rs | 17 +++-- evaluator/nixjit_ir/src/lib.rs | 12 +--- evaluator/nixjit_lir/src/lib.rs | 77 ++++++++++++---------- evaluator/nixjit_macros/src/builtins.rs | 36 +++++----- 13 files changed, 147 insertions(+), 180 deletions(-) diff --git a/evaluator/nixjit/src/test.rs b/evaluator/nixjit/src/test.rs index 2aa192b..ff8df6e 100644 --- a/evaluator/nixjit/src/test.rs +++ b/evaluator/nixjit/src/test.rs @@ -143,8 +143,7 @@ fn test_attrs() { "rec { a = 1; b = a; }", attrs! { symbol!("a") => int!(1), - // symbol!("b") => int!(1) - symbol!("b") => thunk!() + symbol!("b") => int!(1) }, ); test_expr("{ a = 1; }.a", int!(1)); diff --git a/evaluator/nixjit_builtins/src/lib.rs b/evaluator/nixjit_builtins/src/lib.rs index 60a8ceb..90467a6 100644 --- a/evaluator/nixjit_builtins/src/lib.rs +++ b/evaluator/nixjit_builtins/src/lib.rs @@ -27,11 +27,28 @@ pub mod builtins { }) } - pub fn import(ctx: &mut Ctx, path: Value) -> Result { + pub fn import(ctx: &mut impl BuiltinsContext, path: Value) -> Result { todo!() } fn elem_at(list: Value, idx: Value) -> Result { + let list = list + .try_unwrap_list() + .map_err(|_| Error::EvalError("expected a list but found ...".to_string()))?; + let idx = idx + .try_unwrap_int() + .map_err(|_| Error::EvalError("expected a int but found ...".to_string()))?; + list.get(idx as usize) + .ok_or_else(|| { + Error::EvalError(format!( + "'builtins.elemAt' called with index {idx} on a list of size {}", + list.len() + )) + }) + .cloned() + } + + fn elem(elem: Value, list: Value) -> Result { todo!() } } diff --git a/evaluator/nixjit_context/src/lib.rs b/evaluator/nixjit_context/src/lib.rs index 279df84..7125956 100644 --- a/evaluator/nixjit_context/src/lib.rs +++ b/evaluator/nixjit_context/src/lib.rs @@ -72,16 +72,6 @@ impl Ir { } } - unsafe fn unwrap_hir_unchecked(self) -> Hir { - if cfg!(debug_assertions) { - self.unwrap_hir() - } else if let Self::Hir(hir) = self { - hir - } else { - unsafe { core::hint::unreachable_unchecked() } - } - } - unsafe fn unwrap_lir_ref_unchecked(&self) -> &Lir { #[cfg(debug_assertions)] if let Self::Lir(lir) = self { @@ -214,8 +204,8 @@ impl Context { )); } let root = root.tree().expr().unwrap().downgrade(&mut self)?; - self.resolve(&root)?; - Ok(EvalContext::eval(&mut self, &root)?.to_public(&mut HashSet::new())) + self.resolve(root)?; + Ok(EvalContext::eval(&mut self, root)?.to_public(&mut HashSet::new())) } } @@ -223,7 +213,7 @@ impl DowngradeContext for Context { fn new_expr(&mut self, expr: Hir) -> ExprId { let id = unsafe { ExprId::from(self.irs.len()) }; self.irs.push(Ir::Hir(expr).into()); - self.nodes.push(self.graph.add_node(unsafe { id.clone() })); + self.nodes.push(self.graph.add_node(id)); self.resolved.push(false); self.compiled.push(OnceCell::new()); id @@ -234,9 +224,9 @@ impl DowngradeContext for Context { f(&self.irs[idx].borrow().unwrap_hir_ref_unchecked(), self) } } - fn with_expr_mut(&mut self, id: &ExprId, f: impl FnOnce(&mut Hir, &mut Self) -> T) -> T { + fn with_expr_mut(&mut self, id: ExprId, f: impl FnOnce(&mut Hir, &mut Self) -> T) -> T { unsafe { - let idx = id.clone().raw(); + let idx = id.raw(); let self_mut = &mut *(self as *mut Self); f( &mut self @@ -253,11 +243,12 @@ impl DowngradeContext for Context { impl ResolveContext for Context { fn lookup(&self, name: &str) -> LookupResult { let mut arg_idx = 0; + let mut has_with = false; for scope in self.scopes.iter().rev() { match scope { Scope::Let(scope) => { - if let Some(expr) = scope.get(name) { - return LookupResult::Expr(unsafe { expr.clone() }); + if let Some(&expr) = scope.get(name) { + return LookupResult::Expr(expr); } } Scope::Arg(ident) => { @@ -266,15 +257,19 @@ impl ResolveContext for Context { } arg_idx += 1; } - Scope::With => return LookupResult::Unknown, + Scope::With => has_with = true, } } - LookupResult::NotFound + if has_with { + LookupResult::Unknown + } else { + LookupResult::NotFound + } } - fn new_dep(&mut self, expr: &ExprId, dep: ExprId) { + fn new_dep(&mut self, expr: ExprId, dep: ExprId) { unsafe { - let expr = expr.clone().raw(); + let expr = expr.raw(); let dep = dep.raw(); let expr = *self.nodes.get_unchecked(expr); let dep = *self.nodes.get_unchecked(dep); @@ -282,9 +277,9 @@ impl ResolveContext for Context { } } - fn resolve(&mut self, expr: &ExprId) -> Result<()> { + fn resolve(&mut self, expr: ExprId) -> Result<()> { unsafe { - let idx = expr.clone().raw(); + let idx = expr.raw(); let self_mut = &mut *(self as *mut Self); replace_with_and_return( &mut *self.irs.get_unchecked(idx).borrow_mut(), @@ -294,7 +289,9 @@ impl ResolveContext for Context { })) }, |ir| { - let hir = ir.unwrap_hir_unchecked(); + let Ir::Hir(hir) = ir else { + return (Ok(()), ir); + }; match hir.resolve(self_mut) { Ok(lir) => (Ok(()), Ir::Lir(lir)), Err(err) => ( @@ -310,8 +307,8 @@ impl ResolveContext for Context { Ok(()) } - fn new_func(&mut self, body: &ExprId, param: Param) { - self.funcs.insert(unsafe { body.clone() }, param); + fn new_func(&mut self, body: ExprId, param: Param) { + self.funcs.insert(body, param); } fn with_let_env<'a, T>( @@ -321,7 +318,7 @@ impl ResolveContext for Context { ) -> T { let mut scope = HashMap::new(); for (name, expr) in bindings { - scope.insert(name.clone(), unsafe { expr.clone() }); + scope.insert(name.clone(), *expr); } self.scopes.push(Scope::Let(scope)); let res = f(self); @@ -347,8 +344,8 @@ impl ResolveContext for Context { } impl EvalContext for Context { - fn eval(&mut self, expr: &ExprId) -> Result { - let idx = unsafe { expr.clone().raw() }; + fn eval(&mut self, expr: ExprId) -> Result { + let idx = unsafe { expr.raw() }; let lir = unsafe { &*(self .irs diff --git a/evaluator/nixjit_eval/src/lib.rs b/evaluator/nixjit_eval/src/lib.rs index 7186f75..b621fe7 100644 --- a/evaluator/nixjit_eval/src/lib.rs +++ b/evaluator/nixjit_eval/src/lib.rs @@ -23,7 +23,7 @@ mod value; /// A trait defining the context in which LIR expressions are evaluated. pub trait EvalContext: Sized { /// Evaluates an expression by its ID. - fn eval(&mut self, expr: &ExprId) -> Result; + fn eval(&mut self, expr: ExprId) -> Result; /// Enters a `with` scope for the duration of a closure's execution. fn with_with_env( @@ -61,7 +61,7 @@ pub trait Evaluate { impl Evaluate for ExprId { /// Evaluating an `ExprId` simply delegates to the context. fn eval(&self, ctx: &mut Ctx) -> Result { - ctx.eval(self) + ctx.eval(*self) } } @@ -85,8 +85,8 @@ impl Evaluate for lir::Lir { Str(x) => x.eval(ctx), Var(x) => x.eval(ctx), Path(x) => x.eval(ctx), - ExprRef(expr) => ctx.eval(expr), - FuncRef(func) => Ok(Value::Func(unsafe { func.clone() })), + &ExprRef(expr) => ctx.eval(expr), + &FuncRef(func) => Ok(Value::Func(func)), &ArgRef(idx) => Ok(ctx.lookup_arg(idx).clone()), &PrimOp(primop) => Ok(Value::PrimOp(primop)), } @@ -109,7 +109,7 @@ impl Evaluate for ir::AttrSet { let mut k = k.eval(ctx)?; k.coerce_to_string(); let v_eval_result = v.eval(ctx)?; - attrs.push_attr(k.unwrap_string(), v_eval_result); + attrs.push_attr(k.unwrap_string(), v_eval_result)?; } let result = Value::AttrSet(attrs.into()); Ok(result) diff --git a/evaluator/nixjit_eval/src/value/attrset.rs b/evaluator/nixjit_eval/src/value/attrset.rs index 1c7efdb..cbf1013 100644 --- a/evaluator/nixjit_eval/src/value/attrset.rs +++ b/evaluator/nixjit_eval/src/value/attrset.rs @@ -5,6 +5,7 @@ use std::fmt::Debug; use std::rc::Rc; use derive_more::Constructor; +use hashbrown::hash_map::Entry; use hashbrown::{HashMap, HashSet}; use itertools::Itertools; @@ -13,14 +14,13 @@ use nixjit_value::Symbol; use nixjit_value::{self as p, format_symbol}; use super::Value; -use crate::EvalContext; /// A wrapper around a `HashMap` representing a Nix attribute set. /// /// It uses `#[repr(transparent)]` to ensure it has the same memory layout /// as `HashMap`. #[repr(transparent)] -#[derive(Constructor, PartialEq)] +#[derive(Clone, Constructor, PartialEq)] pub struct AttrSet { data: HashMap, } @@ -40,14 +40,6 @@ impl Debug for AttrSet { } } -impl Clone for AttrSet { - fn clone(&self) -> Self { - AttrSet { - data: self.data.clone(), - } - } -} - impl From> for AttrSet { fn from(data: HashMap) -> Self { Self { data } @@ -80,11 +72,17 @@ impl AttrSet { /// /// This method currently uses `todo!()` and will panic if the attribute /// already exists, indicating that duplicate attribute handling is not yet implemented. - pub fn push_attr(&mut self, sym: String, val: Value) { - if self.data.get(&sym).is_some() { - todo!() + pub fn push_attr(&mut self, sym: String, val: Value) -> Result<()> { + match self.data.entry(sym) { + Entry::Occupied(occupied) => Err(Error::EvalError(format!( + "attribute '{}' already defined", + format_symbol(occupied.key()) + ))), + Entry::Vacant(vacant) => { + vacant.insert(val); + Ok(()) + } } - self.data.insert(sym, val); } /// Performs a deep selection of an attribute from a nested set. diff --git a/evaluator/nixjit_eval/src/value/func.rs b/evaluator/nixjit_eval/src/value/func.rs index 1d3fbd5..db27051 100644 --- a/evaluator/nixjit_eval/src/value/func.rs +++ b/evaluator/nixjit_eval/src/value/func.rs @@ -13,7 +13,7 @@ use crate::EvalContext; /// /// This struct captures the state of a function that has received some, but not /// all, of its expected arguments. -#[derive(Debug, Constructor)] +#[derive(Debug, Clone, Constructor)] pub struct FuncApp { /// The expression ID of the function body to be executed. pub body: ExprId, @@ -23,16 +23,6 @@ pub struct FuncApp { pub frame: Vec, } -impl Clone for FuncApp { - fn clone(&self) -> Self { - Self { - body: unsafe { self.body.clone() }, - args: self.args.clone(), - frame: self.frame.clone(), - } - } -} - impl FuncApp { /// Applies more arguments to a partially applied function. /// @@ -51,7 +41,7 @@ impl FuncApp { let mut val; let mut args = core::mem::take(args); args.push(iter.next().unwrap()?); - let (ret_args, ret) = ctx.with_args_env(args, |ctx| ctx.eval(expr)); + let (ret_args, ret) = ctx.with_args_env(args, |ctx| ctx.eval(*expr)); args = ret_args; val = ret?; loop { @@ -63,13 +53,13 @@ impl FuncApp { }; args.push(arg?); if let Value::Func(expr) = val { - let (ret_args, ret) = ctx.with_args_env(args, |ctx| ctx.eval(&expr)); + let (ret_args, ret) = ctx.with_args_env(args, |ctx| ctx.eval(expr)); args = ret_args; val = ret?; } else if let Value::FuncApp(func) = val { let mut func = Rc::unwrap_or_clone(func); func.args.push(args.pop().unwrap()); - let (ret_args, ret) = ctx.with_args_env(func.args, |ctx| ctx.eval(&func.body)); + let (ret_args, ret) = ctx.with_args_env(func.args, |ctx| ctx.eval(func.body)); args = ret_args; val = ret?; } diff --git a/evaluator/nixjit_eval/src/value/list.rs b/evaluator/nixjit_eval/src/value/list.rs index 3cbd186..d9597f0 100644 --- a/evaluator/nixjit_eval/src/value/list.rs +++ b/evaluator/nixjit_eval/src/value/list.rs @@ -9,10 +9,9 @@ use nixjit_value::List as PubList; use nixjit_value::Value as PubValue; use super::Value; -use crate::EvalContext; /// A wrapper around a `Vec` representing a Nix list. -#[derive(Default)] +#[derive(Clone, Default)] pub struct List { data: Vec, } @@ -27,14 +26,6 @@ impl Debug for List { } } -impl Clone for List { - fn clone(&self) -> Self { - Self { - data: self.data.clone(), - } - } -} - impl>> From for List { fn from(value: T) -> Self { Self { data: value.into() } diff --git a/evaluator/nixjit_eval/src/value/mod.rs b/evaluator/nixjit_eval/src/value/mod.rs index 6cde4a7..d746535 100644 --- a/evaluator/nixjit_eval/src/value/mod.rs +++ b/evaluator/nixjit_eval/src/value/mod.rs @@ -43,14 +43,14 @@ pub use primop::*; /// JIT-compiled code. It uses `#[repr(C, u64)]` to ensure a predictable layout, /// with the discriminant serving as a type tag. #[repr(C, u64)] -#[derive(IsVariant, TryUnwrap, Unwrap)] +#[derive(IsVariant, Clone, TryUnwrap, Unwrap)] pub enum Value { Int(i64), Float(f64), Bool(bool), String(String), Null, - Thunk(usize), + Thunk(ExprId), AttrSet(Rc), List(Rc), Catchable(String), @@ -81,27 +81,6 @@ impl Debug for Value { } } -impl Clone for Value { - fn clone(&self) -> Self { - use Value::*; - match self { - AttrSet(attrs) => AttrSet(attrs.clone()), - List(list) => List(list.clone()), - Catchable(catchable) => Catchable(catchable.clone()), - &Int(x) => Int(x), - &Float(x) => Float(x), - &Bool(x) => Bool(x), - String(x) => String(x.clone()), - Null => Null, - &Thunk(expr) => Thunk(expr), - &PrimOp(primop) => PrimOp(primop), - PrimOpApp(primop) => PrimOpApp(primop.clone()), - Func(expr) => Func(unsafe { expr.clone() }), - FuncApp(func) => FuncApp(func.clone()), - } - } -} - impl Hash for Value { fn hash(&self, state: &mut H) { use Value::*; @@ -169,7 +148,7 @@ pub enum ValueAsRef<'v> { Bool(bool), String(&'v String), Null, - Thunk(usize), + Thunk(&'v ExprId), AttrSet(&'v AttrSet), List(&'v List), Catchable(&'v str), @@ -190,7 +169,7 @@ impl Value { Bool(x) => R::Bool(*x), String(x) => R::String(x), Null => R::Null, - Thunk(x) => R::Thunk(*x), + Thunk(x) => R::Thunk(x), AttrSet(x) => R::AttrSet(x), List(x) => R::List(x), Catchable(x) => R::Catchable(x), @@ -264,7 +243,7 @@ impl Value { ctx.call_primop(func.id, iter.collect::>()?) } } - Func(expr) => { + &mut Func(expr) => { let mut val; let mut args = Vec::with_capacity(iter.len()); args.push(iter.next().unwrap()?); @@ -280,14 +259,14 @@ impl Value { }; args.push(arg?); if let Value::Func(expr) = val { - let (ret_args, ret) = ctx.with_args_env(args, |ctx| ctx.eval(&expr)); + let (ret_args, ret) = ctx.with_args_env(args, |ctx| ctx.eval(expr)); args = ret_args; val = ret?; } else if let Value::FuncApp(func) = val { let mut func = Rc::unwrap_or_clone(func); func.args.push(args.pop().unwrap()); let (ret_args, ret) = - ctx.with_args_env(func.args, |ctx| ctx.eval(&func.body)); + ctx.with_args_env(func.args, |ctx| ctx.eval(func.body)); args = ret_args; val = ret?; } @@ -545,14 +524,14 @@ impl Value { Ok(()) } - pub fn coerce_to_string(&mut self) -> &mut Self { + pub fn coerce_to_string(&mut self) -> Result<&mut Self> { use Value::*; if let String(_) = self { } else if let Catchable(_) = self { } else { todo!() } - self + Ok(self) } /// Converts the internal `Value` to its public-facing, serializable diff --git a/evaluator/nixjit_hir/src/lib.rs b/evaluator/nixjit_hir/src/lib.rs index 914637e..586053a 100644 --- a/evaluator/nixjit_hir/src/lib.rs +++ b/evaluator/nixjit_hir/src/lib.rs @@ -41,7 +41,7 @@ pub trait DowngradeContext { fn with_expr(&self, id: ExprId, f: impl FnOnce(&Hir, &Self) -> T) -> T; /// Provides temporary mutable access to an expression. - fn with_expr_mut(&mut self, id: &ExprId, f: impl FnOnce(&mut Hir, &mut Self) -> T) -> T; + fn with_expr_mut(&mut self, id: ExprId, f: impl FnOnce(&mut Hir, &mut Self) -> T) -> T; } // The `ir!` macro generates the `Hir` enum and related structs and traits. @@ -128,7 +128,7 @@ impl Attrs for AttrSet { match attr { Attr::Str(ident) => { // If the next attribute is a static string. - if let Some(id) = self.stcs.get(&ident) { + if let Some(&id) = self.stcs.get(&ident) { // If a sub-attrset already exists, recurse into it. ctx.with_expr_mut(id, |expr, ctx| { expr.as_mut() diff --git a/evaluator/nixjit_hir/src/utils.rs b/evaluator/nixjit_hir/src/utils.rs index e9dafb1..19fe234 100644 --- a/evaluator/nixjit_hir/src/utils.rs +++ b/evaluator/nixjit_hir/src/utils.rs @@ -4,7 +4,9 @@ //! They are helpers to the main `Downgrade` trait implementations. use hashbrown::HashMap; +use hashbrown::hash_map::Entry; +use nixjit_value::format_symbol; use rnix::ast; use nixjit_error::{Error, Result}; @@ -128,19 +130,22 @@ pub fn downgrade_inherit( // If `from` is None, `inherit foo;` becomes `foo = foo;`. || Var { sym: ident.clone() }.to_hir(), // If `from` is Some, `inherit (from) foo;` becomes `foo = from.foo;`. - |expr| { + |&expr| { Select { - expr: unsafe { expr.clone() }, + expr, attrpath: vec![Attr::Str(ident.clone())], default: None, } .to_hir() }, ); - if stcs.insert(ident, ctx.new_expr(expr)).is_some() { - // TODO: Handle or error on duplicate attribute definitions. - todo!() - } + match stcs.entry(ident) { + Entry::Occupied(occupied) => return Err(Error::EvalError(format!( + "attribute '{}' already defined", + format_symbol(occupied.key()) + ))), + Entry::Vacant(vacant) => vacant.insert(ctx.new_expr(expr)) + }; } Ok(()) } diff --git a/evaluator/nixjit_ir/src/lib.rs b/evaluator/nixjit_ir/src/lib.rs index a74f3e3..c785d61 100644 --- a/evaluator/nixjit_ir/src/lib.rs +++ b/evaluator/nixjit_ir/src/lib.rs @@ -20,20 +20,10 @@ use nixjit_value::Const as PubConst; /// /// Using a newtype wrapper like this prevents accidentally mixing up different kinds of indices. #[repr(transparent)] -#[derive(Debug, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct ExprId(usize); impl ExprId { - /// Creates a clone of the `ExprId`. - /// - /// # Safety - /// This is a shallow copy of the index. The caller must ensure that the lifetime - /// and validity of the expression being referenced are handled correctly. - #[inline(always)] - pub unsafe fn clone(&self) -> Self { - Self(self.0) - } - /// Returns the raw `usize` index. /// /// # Safety diff --git a/evaluator/nixjit_lir/src/lib.rs b/evaluator/nixjit_lir/src/lib.rs index c5a1f87..d818b12 100644 --- a/evaluator/nixjit_lir/src/lib.rs +++ b/evaluator/nixjit_lir/src/lib.rs @@ -64,18 +64,18 @@ pub enum LookupResult { /// scopes, dependencies, and the resolution of expressions themselves. pub trait ResolveContext { /// Records a dependency of one expression on another. - fn new_dep(&mut self, expr: &ExprId, dep: ExprId); + fn new_dep(&mut self, expr: ExprId, dep: ExprId); /// Creates a new function, associating a parameter specification with a body expression. - fn new_func(&mut self, body: &ExprId, param: Param); + fn new_func(&mut self, body: ExprId, param: Param); /// Triggers the resolution of a given expression. - fn resolve(&mut self, expr: &ExprId) -> Result<()>; + fn resolve(&mut self, expr: ExprId) -> Result<()>; /// Looks up a variable by name in the current scope. fn lookup(&self, name: &str) -> LookupResult; - /// Enters a `with` scope for the duration of a closure's execution. + /// Enters a `with` scope for the duration of a closure. fn with_with_env(&mut self, f: impl FnOnce(&mut Self) -> T) -> (bool, T); /// Enters a `let` scope with a given set of bindings for the duration of a closure. @@ -125,19 +125,26 @@ impl Resolve for hir::Hir { } } -/// Resolves an `AttrSet`. If it's recursive, resolution is more complex (and currently a TODO). -/// Otherwise, it resolves all key and value expressions. +/// Resolves an `AttrSet` by resolving all key and value expressions. impl Resolve for AttrSet { fn resolve(self, ctx: &mut Ctx) -> Result { if self.rec { - // TODO: Implement resolution for recursive attribute sets. - // This requires setting up a recursive scope where attributes can refer to each other. - todo!() + ctx.with_let_env(self.stcs.iter(), |ctx| { + for &id in self.stcs.values() { + ctx.resolve(id)?; + } + for &(k, v) in self.dyns.iter() { + ctx.resolve(k)?; + ctx.resolve(v)?; + } + Ok(()) + })?; + Ok(self.to_lir()) } else { - for (_, v) in self.stcs.iter() { + for (_, &v) in self.stcs.iter() { ctx.resolve(v)?; } - for (k, v) in self.dyns.iter() { + for &(k, v) in self.dyns.iter() { ctx.resolve(k)?; ctx.resolve(v)?; } @@ -149,7 +156,7 @@ impl Resolve for AttrSet { /// Resolves a `List` by resolving each of its items. impl Resolve for List { fn resolve(self, ctx: &mut Ctx) -> Result { - for item in self.items.iter() { + for &item in self.items.iter() { ctx.resolve(item)?; } Ok(self.to_lir()) @@ -159,9 +166,9 @@ impl Resolve for List { /// Resolves a `HasAttr` expression by resolving the LHS and any dynamic attributes in the path. impl Resolve for HasAttr { fn resolve(self, ctx: &mut Ctx) -> Result { - ctx.resolve(&self.lhs)?; + ctx.resolve(self.lhs)?; for attr in self.rhs.iter() { - if let Attr::Dynamic(expr) = attr { + if let &Attr::Dynamic(expr) = attr { ctx.resolve(expr)?; } } @@ -172,8 +179,8 @@ impl Resolve for HasAttr { /// Resolves a `BinOp` by resolving its left and right hand sides. impl Resolve for BinOp { fn resolve(self, ctx: &mut Ctx) -> Result { - ctx.resolve(&self.lhs)?; - ctx.resolve(&self.rhs)?; + ctx.resolve(self.lhs)?; + ctx.resolve(self.rhs)?; Ok(self.to_lir()) } } @@ -181,7 +188,7 @@ impl Resolve for BinOp { /// Resolves a `UnOp` by resolving its right hand side. impl Resolve for UnOp { fn resolve(self, ctx: &mut Ctx) -> Result { - ctx.resolve(&self.rhs)?; + ctx.resolve(self.rhs)?; Ok(self.to_lir()) } } @@ -190,13 +197,13 @@ impl Resolve for UnOp { /// attributes in the path, and the default value if it exists. impl Resolve for Select { fn resolve(self, ctx: &mut Ctx) -> Result { - ctx.resolve(&self.expr)?; + ctx.resolve(self.expr)?; for attr in self.attrpath.iter() { - if let Attr::Dynamic(expr) = attr { + if let &Attr::Dynamic(expr) = attr { ctx.resolve(expr)?; } } - if let Some(ref expr) = self.default { + if let Some(expr) = self.default { ctx.resolve(expr)?; } Ok(self.to_lir()) @@ -206,9 +213,9 @@ impl Resolve for Select { /// Resolves an `If` expression by resolving the condition, consequence, and alternative. impl Resolve for If { fn resolve(self, ctx: &mut Ctx) -> Result { - ctx.resolve(&self.cond)?; - ctx.resolve(&self.consq)?; - ctx.resolve(&self.alter)?; + ctx.resolve(self.cond)?; + ctx.resolve(self.consq)?; + ctx.resolve(self.alter)?; Ok(self.to_lir()) } } @@ -217,8 +224,8 @@ impl Resolve for If { /// It then registers the function with the context. impl Resolve for Func { fn resolve(self, ctx: &mut Ctx) -> Result { - ctx.with_param_env(self.param.ident.clone(), |ctx| ctx.resolve(&self.body))?; - ctx.new_func(&self.body, self.param); + ctx.with_param_env(self.param.ident.clone(), |ctx| ctx.resolve(self.body))?; + ctx.new_func(self.body, self.param); Ok(Lir::FuncRef(self.body)) } } @@ -226,8 +233,8 @@ impl Resolve for Func { /// Resolves a `Call` by resolving the function and all of its arguments. impl Resolve for Call { fn resolve(self, ctx: &mut Ctx) -> Result { - ctx.resolve(&self.func)?; - for arg in self.args.iter() { + ctx.resolve(self.func)?; + for &arg in self.args.iter() { ctx.resolve(arg)?; } Ok(self.to_lir()) @@ -238,8 +245,8 @@ impl Resolve for Call { /// The body is resolved within a special "with" scope. impl Resolve for With { fn resolve(self, ctx: &mut Ctx) -> Result { - ctx.resolve(&self.namespace)?; - let (env_used, res) = ctx.with_with_env(|ctx| ctx.resolve(&self.expr)); + ctx.resolve(self.namespace)?; + let (env_used, res) = ctx.with_with_env(|ctx| ctx.resolve(self.expr)); res?; // Optimization: if the `with` environment was not actually used by any variable // lookup in the body, we can elide the `With` node entirely. @@ -254,8 +261,8 @@ impl Resolve for With { /// Resolves an `Assert` by resolving the assertion condition and the body. impl Resolve for Assert { fn resolve(self, ctx: &mut Ctx) -> Result { - ctx.resolve(&self.assertion)?; - ctx.resolve(&self.expr)?; + ctx.resolve(self.assertion)?; + ctx.resolve(self.expr)?; Ok(self.to_lir()) } } @@ -263,7 +270,7 @@ impl Resolve for Assert { /// Resolves a `ConcatStrings` by resolving each part. impl Resolve for ConcatStrings { fn resolve(self, ctx: &mut Ctx) -> Result { - for part in self.parts.iter() { + for &part in self.parts.iter() { ctx.resolve(part)?; } Ok(self.to_lir()) @@ -289,7 +296,7 @@ impl Resolve for Var { /// Resolves a `Path` by resolving the underlying expression that defines the path's content. impl Resolve for Path { fn resolve(self, ctx: &mut Ctx) -> Result { - ctx.resolve(&self.expr)?; + ctx.resolve(self.expr)?; Ok(self.to_lir()) } } @@ -299,10 +306,10 @@ impl Resolve for Path { impl Resolve for hir::Let { fn resolve(self, ctx: &mut Ctx) -> Result { ctx.with_let_env(self.bindings.iter(), |ctx| { - for id in self.bindings.values() { + for &id in self.bindings.values() { ctx.resolve(id)?; } - ctx.resolve(&self.body) + ctx.resolve(self.body) })?; // The `let` expression itself evaluates to its body. Ok(Lir::ExprRef(self.body)) diff --git a/evaluator/nixjit_macros/src/builtins.rs b/evaluator/nixjit_macros/src/builtins.rs index 0808f90..0d694af 100644 --- a/evaluator/nixjit_macros/src/builtins.rs +++ b/evaluator/nixjit_macros/src/builtins.rs @@ -17,7 +17,9 @@ use convert_case::{Case, Casing}; use proc_macro::TokenStream; use proc_macro2::Span; use quote::{ToTokens, format_ident, quote}; -use syn::{FnArg, Item, ItemFn, ItemMod, Pat, PatType, Type, Visibility, parse_macro_input}; +use syn::{ + FnArg, Item, ItemFn, ItemMod, Pat, PatIdent, PatType, Type, Visibility, parse_macro_input, +}; /// The implementation of the `#[builtins]` macro. pub fn builtins_impl(input: TokenStream) -> TokenStream { @@ -144,9 +146,15 @@ fn generate_primop_wrapper( // Check if the first argument is a context `&mut Ctx`. let has_ctx = if let Some(FnArg::Typed(first_arg)) = user_args.peek() { - if let Type::Reference(_) = *first_arg.ty { - user_args.next(); // Consume the context argument - true + if let (Type::Reference(_), Pat::Ident(PatIdent { ident, .. })) = + (&*first_arg.ty, &*first_arg.pat) + { + if ident == "ctx" { + user_args.next(); + true + } else { + false + } } else { false } @@ -163,17 +171,7 @@ fn generate_primop_wrapper( // Generate code to unpack and convert arguments from the `Vec`. let arg_unpacks = arg_pats.iter().enumerate().map(|(i, arg)| { - let arg_name = match &arg { - FnArg::Typed(PatType { pat, .. }) => { - if let Pat::Ident(pat_ident) = &**pat { - pat_ident.ident.clone() - } else { - // Create a placeholder name if the pattern is not a simple ident. - format_ident!("arg{}", i, span = Span::call_site()) - } - } - _ => format_ident!("arg{}", i, span = Span::call_site()), - }; + let arg_name = format_ident!("_arg{}", i, span = Span::call_site()); let arg_ty = match &arg { FnArg::Typed(PatType { ty, .. }) => ty, _ => unreachable!(), @@ -190,12 +188,8 @@ fn generate_primop_wrapper( .iter() .enumerate() .map(|(i, arg)| match &arg { - FnArg::Typed(PatType { pat, .. }) => { - if let Pat::Ident(pat_ident) = &**pat { - pat_ident.ident.clone() - } else { - format_ident!("arg{}", i, span = Span::call_site()) - } + FnArg::Typed(PatType { .. }) => { + format_ident!("_arg{}", i, span = Span::call_site()) } _ => unreachable!(), })