fix: recursive attrs
This commit is contained in:
@@ -141,10 +141,9 @@ export const all =
|
||||
export const any =
|
||||
(pred: NixValue) =>
|
||||
(list: NixValue): boolean => {
|
||||
// CppNix forces `pred` eagerly
|
||||
const f = forceFunction(pred);
|
||||
const forcedList = forceList(list);
|
||||
if (forcedList.length) {
|
||||
const f = forceFunction(pred);
|
||||
return forcedList.some((e) => forceBool(f(e)));
|
||||
}
|
||||
return true;
|
||||
// `false` when no element
|
||||
return forcedList.some((e) => forceBool(f(e)));
|
||||
};
|
||||
|
||||
@@ -108,6 +108,42 @@ impl Ir {
|
||||
}
|
||||
|
||||
impl AttrSet {
|
||||
fn merge(&mut self, other: &Self, ctx: &mut impl DowngradeContext) -> Result<()> {
|
||||
for (&sym, &(val, sp)) in &other.stcs {
|
||||
if let Some(&(existing_id, _)) = self.stcs.get(&sym) {
|
||||
let mut existing_ir = ctx.extract_ir(existing_id);
|
||||
let other_ir = ctx.extract_ir(val);
|
||||
|
||||
match (
|
||||
existing_ir.as_mut().try_unwrap_attr_set(),
|
||||
other_ir.as_ref().try_unwrap_attr_set(),
|
||||
) {
|
||||
(Ok(existing_attrs), Ok(other_attrs)) => {
|
||||
existing_attrs.merge(other_attrs, ctx)?;
|
||||
ctx.replace_ir(existing_id, existing_ir);
|
||||
ctx.replace_ir(val, other_ir);
|
||||
}
|
||||
_ => {
|
||||
ctx.replace_ir(existing_id, existing_ir);
|
||||
ctx.replace_ir(val, other_ir);
|
||||
return Err(Error::downgrade_error(
|
||||
format!(
|
||||
"attribute '{}' already defined",
|
||||
format_symbol(ctx.get_sym(sym)),
|
||||
),
|
||||
ctx.get_current_source(),
|
||||
sp,
|
||||
));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.stcs.insert(sym, (val, sp));
|
||||
}
|
||||
}
|
||||
self.dyns.extend(other.dyns.iter().cloned());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn _insert(
|
||||
&mut self,
|
||||
mut path: impl Iterator<Item = Attr>,
|
||||
@@ -171,15 +207,34 @@ impl AttrSet {
|
||||
// This is the final attribute in the path, so insert the value here.
|
||||
match name {
|
||||
Attr::Str(ident, span) => {
|
||||
if self.stcs.insert(ident, (value, span)).is_some() {
|
||||
return Err(Error::downgrade_error(
|
||||
format!(
|
||||
"attribute '{}' already defined",
|
||||
format_symbol(ctx.get_sym(ident)),
|
||||
),
|
||||
ctx.get_current_source(),
|
||||
span,
|
||||
));
|
||||
if let Some(&(existing_id, _)) = self.stcs.get(&ident) {
|
||||
let mut existing_ir = ctx.extract_ir(existing_id);
|
||||
let new_ir = ctx.extract_ir(value);
|
||||
|
||||
match (
|
||||
existing_ir.as_mut().try_unwrap_attr_set(),
|
||||
new_ir.as_ref().try_unwrap_attr_set(),
|
||||
) {
|
||||
(Ok(existing_attrs), Ok(new_attrs)) => {
|
||||
existing_attrs.merge(new_attrs, ctx)?;
|
||||
ctx.replace_ir(existing_id, existing_ir);
|
||||
ctx.replace_ir(value, new_ir);
|
||||
}
|
||||
_ => {
|
||||
ctx.replace_ir(existing_id, existing_ir);
|
||||
ctx.replace_ir(value, new_ir);
|
||||
return Err(Error::downgrade_error(
|
||||
format!(
|
||||
"attribute '{}' already defined",
|
||||
format_symbol(ctx.get_sym(ident)),
|
||||
),
|
||||
ctx.get_current_source(),
|
||||
span,
|
||||
));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.stcs.insert(ident, (value, span));
|
||||
}
|
||||
}
|
||||
Attr::Dynamic(dynamic, span) => {
|
||||
|
||||
@@ -220,21 +220,7 @@ impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::AttrSet {
|
||||
|
||||
// rec { a = 1; b = a; } => let a = 1; b = a; in { inherit a b; }
|
||||
let entries: Vec<_> = self.entries().collect();
|
||||
downgrade_let_bindings(entries, ctx, span, |ctx, binding_keys| {
|
||||
// Create plain attrset as body with inherit
|
||||
let mut attrs = AttrSet {
|
||||
stcs: HashMap::new(),
|
||||
dyns: Vec::new(),
|
||||
span,
|
||||
};
|
||||
|
||||
for sym in binding_keys {
|
||||
let expr = ctx.lookup(*sym, synthetic_span())?;
|
||||
attrs.stcs.insert(*sym, (expr, synthetic_span()));
|
||||
}
|
||||
|
||||
Ok(ctx.new_expr(attrs.to_ir()))
|
||||
})
|
||||
downgrade_rec_bindings(entries, ctx, span)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,10 +307,9 @@ impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Select {
|
||||
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::LegacyLet {
|
||||
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||
let span = self.syntax().text_range();
|
||||
let bindings = downgrade_static_attrs(self, ctx)?;
|
||||
let binding_keys: Vec<_> = bindings.keys().copied().collect();
|
||||
|
||||
let attrset_expr = ctx.with_let_scope(bindings, |ctx| {
|
||||
let entries: Vec<_> = self.entries().collect();
|
||||
let attrset_expr = downgrade_let_bindings(entries, ctx, span, |ctx, binding_keys| {
|
||||
// Create plain attrset as body with inherit
|
||||
let mut attrs = AttrSet {
|
||||
stcs: HashMap::new(),
|
||||
dyns: Vec::new(),
|
||||
@@ -332,12 +317,11 @@ impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::LegacyLet {
|
||||
};
|
||||
|
||||
for sym in binding_keys {
|
||||
// FIXME: span
|
||||
let expr = ctx.lookup(sym, synthetic_span())?;
|
||||
attrs.stcs.insert(sym, (expr, synthetic_span()));
|
||||
let expr = ctx.lookup(*sym, synthetic_span())?;
|
||||
attrs.stcs.insert(*sym, (expr, synthetic_span()));
|
||||
}
|
||||
|
||||
Result::Ok(ctx.new_expr(attrs.to_ir()))
|
||||
Ok(ctx.new_expr(attrs.to_ir()))
|
||||
})?;
|
||||
|
||||
let body_sym = ctx.new_sym("body".to_string());
|
||||
|
||||
@@ -37,32 +37,6 @@ pub fn downgrade_attrs(
|
||||
Ok(attrs)
|
||||
}
|
||||
|
||||
/// Downgrades attribute set entries for a `let...in` expression.
|
||||
/// This is a stricter version of `downgrade_attrs` that disallows dynamic attributes,
|
||||
/// as `let` bindings must be statically known.
|
||||
pub fn downgrade_static_attrs(
|
||||
attrs: impl ast::HasEntry + AstNode,
|
||||
ctx: &mut impl DowngradeContext,
|
||||
) -> Result<HashMap<SymId, ExprId>> {
|
||||
let entries = attrs.entries();
|
||||
let mut attrs = AttrSet {
|
||||
stcs: HashMap::new(),
|
||||
dyns: Vec::new(),
|
||||
span: attrs.syntax().text_range(),
|
||||
};
|
||||
|
||||
for entry in entries {
|
||||
match entry {
|
||||
ast::Entry::Inherit(inherit) => downgrade_inherit(inherit, &mut attrs.stcs, ctx)?,
|
||||
ast::Entry::AttrpathValue(value) => {
|
||||
downgrade_static_attrpathvalue(value, &mut attrs, ctx)?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(attrs.stcs.into_iter().map(|(k, (v, _))| (k, v)).collect())
|
||||
}
|
||||
|
||||
/// Downgrades an `inherit` statement.
|
||||
/// `inherit (from) a b;` is translated into `a = from.a; b = from.b;`.
|
||||
/// `inherit a b;` is translated into `a = a; b = b;` (i.e., bringing variables into scope).
|
||||
@@ -102,7 +76,8 @@ pub fn downgrade_inherit(
|
||||
);
|
||||
ctx.maybe_thunk(select_expr)
|
||||
} else {
|
||||
ctx.lookup(ident, span)?
|
||||
let lookup_expr = ctx.lookup(ident, span)?;
|
||||
ctx.maybe_thunk(lookup_expr)
|
||||
};
|
||||
match stcs.entry(ident) {
|
||||
Entry::Occupied(occupied) => {
|
||||
@@ -338,20 +313,88 @@ where
|
||||
}
|
||||
|
||||
/// Helper function to downgrade entries with let bindings semantics.
|
||||
/// This extracts common logic for both `rec` attribute sets and `let...in` expressions.
|
||||
/// This extracts common logic for `let...in` expressions.
|
||||
/// For `rec` attribute sets, use `downgrade_rec_bindings` instead.
|
||||
pub fn downgrade_let_bindings<Ctx, F>(
|
||||
entries: Vec<ast::Entry>,
|
||||
ctx: &mut Ctx,
|
||||
_span: TextRange,
|
||||
span: TextRange,
|
||||
body_fn: F,
|
||||
) -> Result<ExprId>
|
||||
where
|
||||
Ctx: DowngradeContext,
|
||||
F: FnOnce(&mut Ctx, &[SymId]) -> Result<ExprId>,
|
||||
{
|
||||
downgrade_let_bindings_impl(
|
||||
entries,
|
||||
ctx,
|
||||
span,
|
||||
|ctx, binding_keys, _dyns| body_fn(ctx, binding_keys),
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
/// Helper function to downgrade `rec` attribute sets that may contain dynamic attributes.
|
||||
/// Similar to `downgrade_let_bindings`, but allows dynamic attributes.
|
||||
pub fn downgrade_rec_bindings<Ctx>(
|
||||
entries: Vec<ast::Entry>,
|
||||
ctx: &mut Ctx,
|
||||
span: TextRange,
|
||||
) -> Result<ExprId>
|
||||
where
|
||||
Ctx: DowngradeContext,
|
||||
{
|
||||
downgrade_let_bindings_impl(
|
||||
entries,
|
||||
ctx,
|
||||
span,
|
||||
|ctx, binding_keys, dyns| {
|
||||
let mut attrs = AttrSet {
|
||||
stcs: HashMap::new(),
|
||||
dyns: dyns.to_vec(),
|
||||
span,
|
||||
};
|
||||
|
||||
for sym in binding_keys {
|
||||
let expr = ctx.lookup(*sym, synthetic_span())?;
|
||||
attrs.stcs.insert(*sym, (expr, synthetic_span()));
|
||||
}
|
||||
|
||||
Ok(ctx.new_expr(attrs.to_ir()))
|
||||
},
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
fn downgrade_let_bindings_impl<Ctx, F>(
|
||||
entries: Vec<ast::Entry>,
|
||||
ctx: &mut Ctx,
|
||||
_span: TextRange,
|
||||
body_fn: F,
|
||||
allow_dynamic: bool,
|
||||
) -> Result<ExprId>
|
||||
where
|
||||
Ctx: DowngradeContext,
|
||||
F: FnOnce(&mut Ctx, &[SymId], &[(ExprId, ExprId, TextRange)]) -> Result<ExprId>,
|
||||
{
|
||||
fn is_static_entry(entry: &ast::Entry) -> bool {
|
||||
match entry {
|
||||
ast::Entry::Inherit(_) => true,
|
||||
ast::Entry::AttrpathValue(value) => {
|
||||
let attrpath = value.attrpath().unwrap();
|
||||
let first_attr = attrpath.attrs().next();
|
||||
matches!(first_attr, Some(ast::Attr::Ident(_)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut binding_syms = HashSet::new();
|
||||
|
||||
for entry in &entries {
|
||||
if !is_static_entry(entry) && allow_dynamic {
|
||||
continue;
|
||||
}
|
||||
|
||||
match entry {
|
||||
ast::Entry::Inherit(inherit) => {
|
||||
for attr in inherit.attrs() {
|
||||
@@ -438,13 +481,18 @@ where
|
||||
};
|
||||
|
||||
for entry in entries {
|
||||
if !is_static_entry(&entry) && allow_dynamic {
|
||||
if let ast::Entry::AttrpathValue(value) = entry {
|
||||
downgrade_attrpathvalue(value, &mut temp_attrs, ctx)?;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
match entry {
|
||||
ast::Entry::Inherit(inherit) => {
|
||||
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());
|
||||
@@ -456,7 +504,11 @@ where
|
||||
}
|
||||
}
|
||||
ast::Entry::AttrpathValue(value) => {
|
||||
downgrade_static_attrpathvalue(value, &mut temp_attrs, ctx)?;
|
||||
if allow_dynamic {
|
||||
downgrade_attrpathvalue(value, &mut temp_attrs, ctx)?;
|
||||
} else {
|
||||
downgrade_static_attrpathvalue(value, &mut temp_attrs, ctx)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -472,6 +524,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
body_fn(ctx, &binding_keys)
|
||||
body_fn(ctx, &binding_keys, &temp_attrs.dyns)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -572,7 +572,13 @@ impl<Ctx: RuntimeContext> Runtime<Ctx> {
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
let (is_thunk_symbol, primop_metadata_symbol, has_context_symbol, is_path_symbol, is_cycle_symbol) = {
|
||||
let (
|
||||
is_thunk_symbol,
|
||||
primop_metadata_symbol,
|
||||
has_context_symbol,
|
||||
is_path_symbol,
|
||||
is_cycle_symbol,
|
||||
) = {
|
||||
deno_core::scope!(scope, &mut js_runtime);
|
||||
Self::get_symbols(scope)?
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user