fix: inherit in recursive attribute sets

This commit is contained in:
2026-01-29 18:11:20 +08:00
parent 084968c08a
commit 9ee2dd5c08
2 changed files with 40 additions and 1 deletions

View File

@@ -398,6 +398,25 @@ where
}
}
// IMPORTANT: For `inherit x` (without `from`), we must look up `x` in the OUTER scope
// BEFORE adding the rec's slots to the scope. Otherwise, `x` would resolve to its own
// slot, causing infinite recursion.
let mut inherit_lookups: HashMap<SymId, (ExprId, TextRange)> = HashMap::new();
for entry in &entries {
if let ast::Entry::Inherit(inherit) = entry
&& inherit.from().is_none()
{
for attr in inherit.attrs() {
if let ast::Attr::Ident(ident) = attr {
let span = ident.syntax().text_range();
let sym = ctx.new_sym(ident.to_string());
let expr = ctx.lookup(sym, span)?;
inherit_lookups.insert(sym, (expr, span));
}
}
}
}
let binding_keys: Vec<_> = binding_syms.into_iter().collect();
let slots: Vec<_> = ctx.reserve_slots(binding_keys.len()).collect();
let let_bindings: HashMap<_, _> = binding_keys
@@ -421,7 +440,20 @@ where
for entry in entries {
match entry {
ast::Entry::Inherit(inherit) => {
downgrade_inherit(inherit, &mut temp_attrs.stcs, ctx)?;
if inherit.from().is_some() {
// `inherit (from) x` - process normally, `from` may reference current scope
downgrade_inherit(inherit, &mut temp_attrs.stcs, ctx)?;
} else {
// `inherit x` - use pre-looked-up expressions from outer scope
for attr in inherit.attrs() {
if let ast::Attr::Ident(ident) = attr {
let sym = ctx.new_sym(ident.to_string());
if let Some(&(expr, span)) = inherit_lookups.get(&sym) {
temp_attrs.stcs.insert(sym, (expr, span));
}
}
}
}
}
ast::Entry::AttrpathValue(value) => {
downgrade_static_attrpathvalue(value, &mut temp_attrs, ctx)?;

View File

@@ -3,6 +3,8 @@ mod utils;
use nix_js::value::Value;
use utils::eval;
use crate::utils::eval_result;
#[test]
fn arithmetic() {
assert_eq!(eval("1 + 1"), Value::Int(2));
@@ -63,3 +65,8 @@ fn nested_let() {
Value::Int(3)
);
}
#[test]
fn rec_inherit_fails() {
assert!(eval_result("{ inherit x; }").is_err());
}