feat: rec attrset

This commit is contained in:
2025-08-08 12:12:23 +08:00
parent 67cdcfea33
commit a9cfddbf5c
13 changed files with 147 additions and 180 deletions

View File

@@ -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<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.
@@ -125,19 +125,26 @@ impl<Ctx: ResolveContext> Resolve<Ctx> 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<Ctx: ResolveContext> Resolve<Ctx> for AttrSet {
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
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<Ctx: ResolveContext> Resolve<Ctx> for AttrSet {
/// Resolves a `List` by resolving each of its items.
impl<Ctx: ResolveContext> Resolve<Ctx> for List {
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
for item in self.items.iter() {
for &item in self.items.iter() {
ctx.resolve(item)?;
}
Ok(self.to_lir())
@@ -159,9 +166,9 @@ impl<Ctx: ResolveContext> Resolve<Ctx> for List {
/// Resolves a `HasAttr` expression by resolving the LHS and any dynamic attributes in the path.
impl<Ctx: ResolveContext> Resolve<Ctx> for HasAttr {
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
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<Ctx: ResolveContext> Resolve<Ctx> for HasAttr {
/// Resolves a `BinOp` by resolving its left and right hand sides.
impl<Ctx: ResolveContext> Resolve<Ctx> for BinOp {
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
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<Ctx: ResolveContext> Resolve<Ctx> for BinOp {
/// Resolves a `UnOp` by resolving its right hand side.
impl<Ctx: ResolveContext> Resolve<Ctx> for UnOp {
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
ctx.resolve(&self.rhs)?;
ctx.resolve(self.rhs)?;
Ok(self.to_lir())
}
}
@@ -190,13 +197,13 @@ impl<Ctx: ResolveContext> Resolve<Ctx> for UnOp {
/// attributes in the path, and the default value if it exists.
impl<Ctx: ResolveContext> Resolve<Ctx> for Select {
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
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<Ctx: ResolveContext> Resolve<Ctx> for Select {
/// Resolves an `If` expression by resolving the condition, consequence, and alternative.
impl<Ctx: ResolveContext> Resolve<Ctx> for If {
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
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<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.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<Ctx: ResolveContext> Resolve<Ctx> for Func {
/// Resolves a `Call` by resolving the function and all of its arguments.
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(self.func)?;
for &arg in self.args.iter() {
ctx.resolve(arg)?;
}
Ok(self.to_lir())
@@ -238,8 +245,8 @@ impl<Ctx: ResolveContext> Resolve<Ctx> for Call {
/// The body is resolved within a special "with" scope.
impl<Ctx: ResolveContext> Resolve<Ctx> for With {
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
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<Ctx: ResolveContext> Resolve<Ctx> for With {
/// Resolves an `Assert` by resolving the assertion condition and the body.
impl<Ctx: ResolveContext> Resolve<Ctx> for Assert {
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
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<Ctx: ResolveContext> Resolve<Ctx> for Assert {
/// Resolves a `ConcatStrings` by resolving each part.
impl<Ctx: ResolveContext> Resolve<Ctx> for ConcatStrings {
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
for part in self.parts.iter() {
for &part in self.parts.iter() {
ctx.resolve(part)?;
}
Ok(self.to_lir())
@@ -289,7 +296,7 @@ impl<Ctx: ResolveContext> Resolve<Ctx> for Var {
/// Resolves a `Path` by resolving the underlying expression that defines the path's content.
impl<Ctx: ResolveContext> Resolve<Ctx> for Path {
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
ctx.resolve(&self.expr)?;
ctx.resolve(self.expr)?;
Ok(self.to_lir())
}
}
@@ -299,10 +306,10 @@ impl<Ctx: ResolveContext> Resolve<Ctx> for Path {
impl<Ctx: ResolveContext> Resolve<Ctx> for hir::Let {
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
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))