feat: stack var (WIP)

This commit is contained in:
2025-08-09 08:12:53 +08:00
parent fd182b6233
commit d8ad7fe904
36 changed files with 1521 additions and 1058 deletions

View File

@@ -13,6 +13,7 @@
use derive_more::{IsVariant, TryUnwrap, Unwrap};
use hashbrown::HashMap;
use nixjit_error::{Error, Result};
use nixjit_hir as hir;
use nixjit_ir::*;
@@ -38,19 +39,20 @@ ir! {
Str,
Var,
Path,
PrimOp,
Arg,
PrimOp(PrimOpId),
StackRef(StackIdx),
ExprRef(ExprId),
FuncRef(ExprId),
ArgRef(ArgIdx),
Thunk(ExprId),
}
/// Represents the result of a variable lookup within the `ResolveContext`.
#[derive(Debug)]
pub enum LookupResult {
Stack(StackIdx),
/// The variable was found and resolved to a specific expression.
Expr(ExprId),
/// The variable was found and resolved to a function argument.
Arg(ArgIdx),
/// The variable could not be resolved statically, likely due to a `with` expression.
/// The lookup must be performed dynamically at evaluation time.
Unknown,
@@ -63,30 +65,36 @@ pub enum LookupResult {
/// This trait abstracts the environment in which expressions are resolved, managing
/// 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);
/// Creates a new function, associating a parameter specification with a body expression.
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_root(self, expr: ExprId) -> Result<()>;
/// Looks up a variable by name in the current scope.
fn lookup(&self, name: &str) -> LookupResult;
fn lookup(&mut self, name: &str) -> LookupResult;
fn lookup_arg(&mut self) -> StackIdx;
/// Enters a `with` scope for the duration of a closure.
fn with_with_env<T>(&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.
fn with_let_env<'a, T>(
fn with_let_env<T>(
&mut self,
bindings: impl Iterator<Item = (&'a String, &'a ExprId)>,
bindings: HashMap<String, ExprId>,
f: impl FnOnce(&mut Self) -> T,
) -> T;
/// Enters a function parameter scope for the duration of a closure.
fn with_param_env<T>(&mut self, ident: Option<String>, f: impl FnOnce(&mut Self) -> T) -> T;
fn with_param_env<T>(
&mut self,
func: ExprId,
ident: Option<String>,
f: impl FnOnce(&mut Self) -> T,
) -> T;
}
/// A trait for converting (resolving) an HIR node into an LIR expression.
@@ -117,10 +125,11 @@ impl<Ctx: ResolveContext> Resolve<Ctx> for hir::Hir {
Var(x) => x.resolve(ctx),
Path(x) => x.resolve(ctx),
Let(x) => x.resolve(ctx),
// The `Arg` in HIR is a placeholder. During resolution, it's replaced by
// a reference to the *current* function's argument. We assume index 0
// here, as the context manages the actual argument index.
Arg(_) => unsafe { Ok(Lir::ArgRef(ArgIdx::from(0))) },
Thunk(x) => {
ctx.resolve(x)?;
Ok(Lir::Thunk(x))
}
Arg(_) => Ok(Lir::StackRef(ctx.lookup_arg()))
}
}
}
@@ -128,28 +137,14 @@ impl<Ctx: ResolveContext> Resolve<Ctx> for hir::Hir {
/// Resolves an `AttrSet` by resolving all key and value expressions.
impl<Ctx: ResolveContext> Resolve<Ctx> for AttrSet {
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
if self.rec {
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() {
ctx.resolve(v)?;
}
for &(k, v) in self.dyns.iter() {
ctx.resolve(k)?;
ctx.resolve(v)?;
}
Ok(self.to_lir())
for (_, &v) in self.stcs.iter() {
ctx.resolve(v)?;
}
for &(k, v) in self.dyns.iter() {
ctx.resolve(k)?;
ctx.resolve(v)?;
}
Ok(self.to_lir())
}
}
@@ -224,7 +219,7 @@ impl<Ctx: ResolveContext> Resolve<Ctx> for If {
/// It then registers the function with the context.
impl<Ctx: ResolveContext> Resolve<Ctx> for Func {
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
ctx.with_param_env(self.param.ident.clone(), |ctx| ctx.resolve(self.body))?;
ctx.with_param_env(self.body, self.param.ident.clone(), |ctx| ctx.resolve(self.body))?;
ctx.new_func(self.body, self.param);
Ok(Lir::FuncRef(self.body))
}
@@ -234,9 +229,7 @@ impl<Ctx: ResolveContext> Resolve<Ctx> for Func {
impl<Ctx: ResolveContext> Resolve<Ctx> for Call {
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
ctx.resolve(self.func)?;
for &arg in self.args.iter() {
ctx.resolve(arg)?;
}
ctx.resolve(self.arg)?;
Ok(self.to_lir())
}
}
@@ -282,10 +275,10 @@ impl<Ctx: ResolveContext> Resolve<Ctx> for Var {
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
use LookupResult::*;
match ctx.lookup(&self.sym) {
Stack(idx) => Ok(Lir::StackRef(idx)),
Expr(expr) => Ok(Lir::ExprRef(expr)),
Arg(arg) => Ok(Lir::ArgRef(arg)),
Unknown => Ok(self.to_lir()),
NotFound => Err(Error::ResolutionError(format!(
NotFound => Err(Error::resolution_error(format!(
"undefined variable '{}'",
format_symbol(&self.sym)
))),
@@ -305,7 +298,7 @@ impl<Ctx: ResolveContext> Resolve<Ctx> for Path {
/// the bindings and the body, and then returning a reference to the body.
impl<Ctx: ResolveContext> Resolve<Ctx> for hir::Let {
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
ctx.with_let_env(self.bindings.iter(), |ctx| {
ctx.with_let_env(self.bindings.clone(), |ctx| {
for &id in self.bindings.values() {
ctx.resolve(id)?;
}