fix: recursive attrs
This commit is contained in:
@@ -141,10 +141,9 @@ export const all =
|
|||||||
export const any =
|
export const any =
|
||||||
(pred: NixValue) =>
|
(pred: NixValue) =>
|
||||||
(list: NixValue): boolean => {
|
(list: NixValue): boolean => {
|
||||||
const forcedList = forceList(list);
|
// CppNix forces `pred` eagerly
|
||||||
if (forcedList.length) {
|
|
||||||
const f = forceFunction(pred);
|
const f = forceFunction(pred);
|
||||||
|
const forcedList = forceList(list);
|
||||||
|
// `false` when no element
|
||||||
return forcedList.some((e) => forceBool(f(e)));
|
return forcedList.some((e) => forceBool(f(e)));
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -108,6 +108,42 @@ impl Ir {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AttrSet {
|
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(
|
fn _insert(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut path: impl Iterator<Item = Attr>,
|
mut path: impl Iterator<Item = Attr>,
|
||||||
@@ -171,7 +207,22 @@ impl AttrSet {
|
|||||||
// This is the final attribute in the path, so insert the value here.
|
// This is the final attribute in the path, so insert the value here.
|
||||||
match name {
|
match name {
|
||||||
Attr::Str(ident, span) => {
|
Attr::Str(ident, span) => {
|
||||||
if self.stcs.insert(ident, (value, span)).is_some() {
|
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(
|
return Err(Error::downgrade_error(
|
||||||
format!(
|
format!(
|
||||||
"attribute '{}' already defined",
|
"attribute '{}' already defined",
|
||||||
@@ -182,6 +233,10 @@ impl AttrSet {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
self.stcs.insert(ident, (value, span));
|
||||||
|
}
|
||||||
|
}
|
||||||
Attr::Dynamic(dynamic, span) => {
|
Attr::Dynamic(dynamic, span) => {
|
||||||
self.dyns.push((dynamic, value, span));
|
self.dyns.push((dynamic, value, 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; }
|
// rec { a = 1; b = a; } => let a = 1; b = a; in { inherit a b; }
|
||||||
let entries: Vec<_> = self.entries().collect();
|
let entries: Vec<_> = self.entries().collect();
|
||||||
downgrade_let_bindings(entries, ctx, span, |ctx, binding_keys| {
|
downgrade_rec_bindings(entries, ctx, span)
|
||||||
// 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()))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,10 +307,9 @@ impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Select {
|
|||||||
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::LegacyLet {
|
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::LegacyLet {
|
||||||
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||||
let span = self.syntax().text_range();
|
let span = self.syntax().text_range();
|
||||||
let bindings = downgrade_static_attrs(self, ctx)?;
|
let entries: Vec<_> = self.entries().collect();
|
||||||
let binding_keys: Vec<_> = bindings.keys().copied().collect();
|
let attrset_expr = downgrade_let_bindings(entries, ctx, span, |ctx, binding_keys| {
|
||||||
|
// Create plain attrset as body with inherit
|
||||||
let attrset_expr = ctx.with_let_scope(bindings, |ctx| {
|
|
||||||
let mut attrs = AttrSet {
|
let mut attrs = AttrSet {
|
||||||
stcs: HashMap::new(),
|
stcs: HashMap::new(),
|
||||||
dyns: Vec::new(),
|
dyns: Vec::new(),
|
||||||
@@ -332,12 +317,11 @@ impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::LegacyLet {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for sym in binding_keys {
|
for sym in binding_keys {
|
||||||
// FIXME: span
|
let expr = ctx.lookup(*sym, synthetic_span())?;
|
||||||
let expr = ctx.lookup(sym, synthetic_span())?;
|
attrs.stcs.insert(*sym, (expr, 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());
|
let body_sym = ctx.new_sym("body".to_string());
|
||||||
|
|||||||
@@ -37,32 +37,6 @@ pub fn downgrade_attrs(
|
|||||||
Ok(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.
|
/// Downgrades an `inherit` statement.
|
||||||
/// `inherit (from) a b;` is translated into `a = from.a; b = from.b;`.
|
/// `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).
|
/// `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)
|
ctx.maybe_thunk(select_expr)
|
||||||
} else {
|
} else {
|
||||||
ctx.lookup(ident, span)?
|
let lookup_expr = ctx.lookup(ident, span)?;
|
||||||
|
ctx.maybe_thunk(lookup_expr)
|
||||||
};
|
};
|
||||||
match stcs.entry(ident) {
|
match stcs.entry(ident) {
|
||||||
Entry::Occupied(occupied) => {
|
Entry::Occupied(occupied) => {
|
||||||
@@ -338,20 +313,88 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Helper function to downgrade entries with let bindings semantics.
|
/// 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>(
|
pub fn downgrade_let_bindings<Ctx, F>(
|
||||||
entries: Vec<ast::Entry>,
|
entries: Vec<ast::Entry>,
|
||||||
ctx: &mut Ctx,
|
ctx: &mut Ctx,
|
||||||
_span: TextRange,
|
span: TextRange,
|
||||||
body_fn: F,
|
body_fn: F,
|
||||||
) -> Result<ExprId>
|
) -> Result<ExprId>
|
||||||
where
|
where
|
||||||
Ctx: DowngradeContext,
|
Ctx: DowngradeContext,
|
||||||
F: FnOnce(&mut Ctx, &[SymId]) -> Result<ExprId>,
|
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();
|
let mut binding_syms = HashSet::new();
|
||||||
|
|
||||||
for entry in &entries {
|
for entry in &entries {
|
||||||
|
if !is_static_entry(entry) && allow_dynamic {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
match entry {
|
match entry {
|
||||||
ast::Entry::Inherit(inherit) => {
|
ast::Entry::Inherit(inherit) => {
|
||||||
for attr in inherit.attrs() {
|
for attr in inherit.attrs() {
|
||||||
@@ -438,13 +481,18 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
for entry in entries {
|
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 {
|
match entry {
|
||||||
ast::Entry::Inherit(inherit) => {
|
ast::Entry::Inherit(inherit) => {
|
||||||
if inherit.from().is_some() {
|
if inherit.from().is_some() {
|
||||||
// `inherit (from) x` - process normally, `from` may reference current scope
|
|
||||||
downgrade_inherit(inherit, &mut temp_attrs.stcs, ctx)?;
|
downgrade_inherit(inherit, &mut temp_attrs.stcs, ctx)?;
|
||||||
} else {
|
} else {
|
||||||
// `inherit x` - use pre-looked-up expressions from outer scope
|
|
||||||
for attr in inherit.attrs() {
|
for attr in inherit.attrs() {
|
||||||
if let ast::Attr::Ident(ident) = attr {
|
if let ast::Attr::Ident(ident) = attr {
|
||||||
let sym = ctx.new_sym(ident.to_string());
|
let sym = ctx.new_sym(ident.to_string());
|
||||||
@@ -456,10 +504,14 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::Entry::AttrpathValue(value) => {
|
ast::Entry::AttrpathValue(value) => {
|
||||||
|
if allow_dynamic {
|
||||||
|
downgrade_attrpathvalue(value, &mut temp_attrs, ctx)?;
|
||||||
|
} else {
|
||||||
downgrade_static_attrpathvalue(value, &mut temp_attrs, ctx)?;
|
downgrade_static_attrpathvalue(value, &mut temp_attrs, ctx)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (sym, slot) in binding_keys.iter().copied().zip(slots.iter()) {
|
for (sym, slot) in binding_keys.iter().copied().zip(slots.iter()) {
|
||||||
if let Some(&(expr, _)) = temp_attrs.stcs.get(&sym) {
|
if let Some(&(expr, _)) = temp_attrs.stcs.get(&sym) {
|
||||||
@@ -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()
|
..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);
|
deno_core::scope!(scope, &mut js_runtime);
|
||||||
Self::get_symbols(scope)?
|
Self::get_symbols(scope)?
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user