refactor(downgrade): MaybeThunk

This commit is contained in:
2026-04-12 16:19:29 +08:00
parent e34cfc7add
commit 8ad4cf7c7a
6 changed files with 240 additions and 230 deletions
+84 -88
View File
@@ -1,4 +1,3 @@
use bumpalo::boxed::Box;
use bumpalo::collections::{CollectIn, Vec};
use fix_builtins::BuiltinId;
use fix_common::Symbol;
@@ -6,7 +5,7 @@ use fix_error::{Error, Result, Source};
use hashbrown::HashSet;
use hashbrown::hash_map::Entry;
use rnix::TextRange;
use rnix::ast::{self, Expr, HasEntry};
use rnix::ast::{self, AstToken, Expr, HasEntry};
use rowan::ast::AstNode;
use super::*;
@@ -39,20 +38,13 @@ impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>, T, E: std::fmt::Display>
}
}
trait BoxIn: Sized {
fn box_in(self, bump: &bumpalo::Bump) -> Box<'_, Self> {
Box::new_in(self, bump)
}
}
impl<T: Sized> BoxIn for T {}
pub trait DowngradeContext<'id: 'ir, 'ir> {
fn new_expr(&self, expr: Ir<'ir, IrRef<'id, 'ir>>) -> IrRef<'id, 'ir>;
fn maybe_thunk(&mut self, ir: IrRef<'id, 'ir>) -> IrRef<'id, 'ir>;
fn maybe_thunk(&mut self, ir: IrRef<'id, 'ir>) -> MaybeThunk;
fn new_sym(&mut self, sym: String) -> StringId;
fn get_sym(&self, id: StringId) -> Symbol<'_>;
fn lookup(&self, sym: StringId, span: TextRange) -> Result<IrRef<'id, 'ir>>;
fn intern_string(&mut self, sym: impl AsRef<str>) -> StringId;
fn resolve_sym(&self, id: StringId) -> Symbol<'_>;
fn lookup(&self, sym: StringId, span: TextRange) -> Result<MaybeThunk>;
fn get_current_source(&self) -> Source;
@@ -165,14 +157,15 @@ impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> fo
let path = {
let temp = self.content().require(ctx, span)?;
let text = temp.text();
ctx.new_expr(Ir::Str(
text[1..text.len() - 1].to_string().box_in(ctx.bump()),
))
let id = ctx.intern_string(&text[1..text.len() - 1]);
let expr = ctx.new_expr(Ir::Str(id));
ctx.maybe_thunk(expr)
};
// HACK: disgusting eww
let find_file = ctx.new_expr(Ir::Builtin(BuiltinId::FindFile));
let sym = ctx.new_sym("nixPath".into());
let sym = ctx.intern_string("nixPath");
let nix_path = ctx.new_expr(Ir::BuiltinConst(sym));
let nix_path = ctx.maybe_thunk(nix_path);
let call = ctx.new_expr(Ir::Call {
func: find_file,
arg: nix_path,
@@ -194,14 +187,14 @@ impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> fo
let bump = ctx.bump();
let mut parts = normalized.into_iter().map(|part| match part {
ast::InterpolPart::Literal(lit) => Ok(ctx.new_expr(Ir::Str(lit.box_in(bump)))),
ast::InterpolPart::Interpolation(interpol) => {
let inner = interpol
.expr()
.require(ctx, interpol.syntax().text_range())?
.downgrade(ctx)?;
Ok(ctx.maybe_thunk(inner))
ast::InterpolPart::Literal(lit) => {
let id = ctx.intern_string(lit);
Ok(ctx.new_expr(Ir::Str(id)))
}
ast::InterpolPart::Interpolation(interpol) => interpol
.expr()
.require(ctx, interpol.syntax().text_range())?
.downgrade(ctx),
});
Ok(if is_single_literal {
@@ -219,11 +212,15 @@ impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> fo
impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> for ast::Literal {
fn downgrade(self, ctx: &mut Ctx) -> Result<IrRef<'id, 'ir>> {
let span = self.syntax().text_range();
Ok(ctx.new_expr(match self.kind() {
let expr = match self.kind() {
ast::LiteralKind::Integer(int) => Ir::Int(int.value().require(ctx, span)?),
ast::LiteralKind::Float(float) => Ir::Float(float.value().require(ctx, span)?),
ast::LiteralKind::Uri(uri) => Ir::Str(uri.to_string().box_in(ctx.bump())),
}))
ast::LiteralKind::Uri(uri) => {
let id = ctx.intern_string(uri.syntax().text());
Ir::Str(id)
}
};
Ok(ctx.new_expr(expr))
}
}
@@ -231,8 +228,8 @@ impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> fo
fn downgrade(self, ctx: &mut Ctx) -> Result<IrRef<'id, 'ir>> {
let span = self.syntax().text_range();
let text = self.ident_token().require(ctx, span)?.to_string();
let sym = ctx.new_sym(text);
ctx.lookup(sym, span)
let sym = ctx.intern_string(text);
ctx.lookup(sym, span).map(|thunk| thunk.to_ir(ctx))
}
}
@@ -299,12 +296,10 @@ impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> fo
let span = self.syntax().text_range();
let expr = self.expr().require(ctx, span)?.downgrade(ctx)?;
let attrpath = downgrade_attrpath(self.attrpath().require(ctx, span)?, ctx)?;
let default = if let Some(default) = self.default_expr() {
let default_expr = default.downgrade(ctx)?;
Some(ctx.maybe_thunk(default_expr))
} else {
None
};
let default = self
.default_expr()
.map(|expr| expr.downgrade(ctx))
.transpose()?;
let span = self.syntax().text_range();
Ok(ctx.new_expr(Ir::Select {
expr,
@@ -331,7 +326,7 @@ impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> fo
Ok(ctx.new_expr(Ir::AttrSet { stcs, dyns }))
})?;
let body_sym = ctx.new_sym("body".to_string());
let body_sym = ctx.intern_string("body");
Ok(ctx.new_expr(Ir::Select {
expr: attrset_expr,
attrpath: Vec::from_iter_in(
@@ -390,7 +385,7 @@ impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> fo
match raw_param {
ast::Param::IdentParam(id) => {
let param_sym = ctx.new_sym(id.to_string());
let param_sym = ctx.intern_string(id.to_string());
param = None;
body = ctx.with_param_scope(param_sym, |ctx| body_ast.downgrade(ctx))?;
@@ -400,7 +395,7 @@ impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> fo
.pat_bind()
.map(|alias| {
let ident = alias.ident().require(ctx, alias.syntax().text_range())?;
Ok::<_, std::boxed::Box<Error>>(ctx.new_sym(ident.to_string()))
Ok::<_, std::boxed::Box<Error>>(ctx.intern_string(ident.to_string()))
})
.transpose()?;
@@ -484,7 +479,7 @@ impl<'id: 'ir, 'ir> PendingAttrSet<'ir> {
match first {
ast::Attr::Ident(ident) => {
let sym = ctx.new_sym(ident.to_string());
let sym = ctx.intern_string(ident.to_string());
self.insert_static(sym, span, rest, value, ctx)
}
ast::Attr::Str(string) => {
@@ -493,7 +488,7 @@ impl<'id: 'ir, 'ir> PendingAttrSet<'ir> {
&& let ast::InterpolPart::Literal(lit) =
parts.into_iter().next().expect("len checked")
{
let sym = ctx.new_sym(lit);
let sym = ctx.intern_string(lit);
return self.insert_static(sym, span, rest, value, ctx);
}
self.insert_dynamic(first.clone(), span, rest, value, ctx)
@@ -736,14 +731,14 @@ impl<'id: 'ir, 'ir> PendingAttrSet<'ir> {
for attr in inherit.attrs() {
let span = attr.syntax().text_range();
let sym = match &attr {
ast::Attr::Ident(ident) => ctx.new_sym(ident.to_string()),
ast::Attr::Ident(ident) => ctx.intern_string(ident.to_string()),
ast::Attr::Str(s) => {
let parts = s.normalized_parts();
if parts.len() == 1
&& let ast::InterpolPart::Literal(lit) =
parts.into_iter().next().expect("len checked")
{
ctx.new_sym(lit)
ctx.intern_string(lit)
} else {
return Err(Error::downgrade_error(
"dynamic attributes not allowed in inherit".to_string(),
@@ -763,7 +758,7 @@ impl<'id: 'ir, 'ir> PendingAttrSet<'ir> {
if self.stcs.contains_key(&sym) {
return Err(Error::downgrade_error(
format!("attribute '{}' already defined", ctx.get_sym(sym)),
format!("attribute '{}' already defined", ctx.resolve_sym(sym)),
ctx.get_current_source(),
span,
));
@@ -840,8 +835,8 @@ fn make_attrpath_value_entry<'ir>(path: Vec<'ir, ast::Attr>, value: ast::Expr) -
}
struct FinalizedAttrSet<'id, 'ir> {
stcs: HashMap<'ir, StringId, (IrRef<'id, 'ir>, TextRange)>,
dyns: Vec<'ir, (IrRef<'id, 'ir>, IrRef<'id, 'ir>, TextRange)>,
stcs: HashMap<'ir, StringId, (MaybeThunk, TextRange)>,
dyns: Vec<'ir, (IrRef<'id, 'ir>, MaybeThunk, TextRange)>,
}
fn downgrade_attrs<'id, 'ir>(
@@ -861,17 +856,17 @@ fn downgrade_attr<'id, 'ir>(
use ast::InterpolPart::*;
match attr {
Ident(ident) => Ok(Attr::Str(
ctx.new_sym(ident.to_string()),
ctx.intern_string(ident.to_string()),
ident.syntax().text_range(),
)),
Str(string) => {
let parts = string.normalized_parts();
let span = string.syntax().text_range();
if parts.is_empty() {
Ok(Attr::Str(ctx.new_sym("".to_string()), span))
Ok(Attr::Str(ctx.intern_string(""), span))
} else if parts.len() == 1 {
match parts.into_iter().next().expect("len checked") {
Literal(ident) => Ok(Attr::Str(ctx.new_sym(ident), span)),
Literal(ident) => Ok(Attr::Str(ctx.intern_string(ident), span)),
Interpolation(interpol) => Ok(Attr::Dynamic(
interpol
.expr()
@@ -885,7 +880,10 @@ fn downgrade_attr<'id, 'ir>(
let parts = parts
.into_iter()
.map(|part| match part {
Literal(lit) => Ok(ctx.new_expr(Ir::Str(lit.box_in(bump)))),
Literal(lit) => {
let id = ctx.intern_string(lit);
Ok(ctx.new_expr(Ir::Str(id)))
}
Interpolation(interpol) => interpol
.expr()
.require(ctx, interpol.syntax().text_range())?
@@ -950,13 +948,13 @@ where
for entry in pat_entries {
let ident = entry.ident().require(ctx, entry.syntax().text_range())?;
let sym_span = ident.syntax().text_range();
let sym = ctx.new_sym(ident.syntax().text().to_string());
let sym = ctx.intern_string(ident.syntax().text().to_string());
let default = entry.default();
let span = entry.syntax().text_range();
if !seen_params.insert(sym) {
return Err(Error::downgrade_error(
format!("duplicate parameter '{}'", ctx.get_sym(sym)),
format!("duplicate parameter '{}'", ctx.resolve_sym(sym)),
ctx.get_current_source(),
span,
));
@@ -1001,12 +999,13 @@ where
default,
span,
} = param;
let default = if let Some(default) = default {
let default = default.clone().downgrade(ctx)?;
Some(ctx.maybe_thunk(default))
} else {
None
};
let default = default.map(|default| default.downgrade(ctx)).transpose()?;
// let default = if let Some(default) = default {
// let default = default.clone().downgrade(ctx)?;
// Some(ctx.maybe_thunk(default))
// } else {
// None
// };
Ok(ctx.new_expr(Ir::Select {
expr: arg,
@@ -1075,7 +1074,7 @@ where
F: FnOnce(
&mut Ctx,
&[StringId],
&[(IrRef<'id, 'ir>, IrRef<'id, 'ir>, TextRange)],
&[(IrRef<'id, 'ir>, MaybeThunk, TextRange)],
) -> Result<IrRef<'id, 'ir>>,
{
let mut pending = PendingAttrSet::new_in(ctx.bump());
@@ -1088,18 +1087,21 @@ where
ctx.with_let_scope(&keys, |ctx| {
let finalized = finalize_pending_set::<_, ALLOW_DYN>(pending, &inherit_lookups, ctx)?;
let vals = keys
.iter()
.map(|sym| finalized.stcs.get(sym).expect("WTF").0)
.collect_in(ctx.bump());
let vals = {
let mut temp = Vec::with_capacity_in(keys.len(), ctx.bump());
for sym in &keys {
temp.push(finalized.stcs.get(sym).expect("WTF").0.to_ir(ctx));
}
temp
};
body_fn(ctx, &keys, &finalized.dyns).map(|body| (vals, body))
})
}
fn collect_inherit_lookups<'id, 'ir, Ctx: DowngradeContext<'id, 'ir>>(
fn collect_inherit_lookups<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>>(
entries: &[ast::Entry],
ctx: &mut Ctx,
) -> Result<HashMap<'ir, StringId, (IrRef<'id, 'ir>, TextRange)>> {
) -> Result<HashMap<'ir, StringId, (MaybeThunk, TextRange)>> {
let mut inherit_lookups = HashMap::new_in(ctx.bump());
for entry in entries {
if let ast::Entry::Inherit(inherit) = entry
@@ -1108,7 +1110,7 @@ fn collect_inherit_lookups<'id, 'ir, Ctx: DowngradeContext<'id, 'ir>>(
for attr in inherit.attrs() {
if let ast::Attr::Ident(ident) = attr {
let attr_span = ident.syntax().text_range();
let sym = ctx.new_sym(ident.to_string());
let sym = ctx.intern_string(ident.to_string());
let expr = ctx.lookup(sym, attr_span)?;
inherit_lookups.insert(sym, (expr, attr_span));
}
@@ -1127,7 +1129,7 @@ fn collect_binding_syms<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>, const AL
for (sym, (_, span)) in &pending.stcs {
if !binding_syms.insert(*sym) {
return Err(Error::downgrade_error(
format!("attribute '{}' already defined", ctx.get_sym(*sym)),
format!("attribute '{}' already defined", ctx.resolve_sym(*sym)),
ctx.get_current_source(),
*span,
));
@@ -1139,15 +1141,15 @@ fn collect_binding_syms<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>, const AL
fn finalize_pending_set<'id, 'ir, Ctx: DowngradeContext<'id, 'ir>, const ALLOW_DYN: bool>(
pending: PendingAttrSet,
inherit_lookups: &HashMap<StringId, (IrRef<'id, 'ir>, TextRange)>,
inherit_lookups: &HashMap<StringId, (MaybeThunk, TextRange)>,
ctx: &mut Ctx,
) -> Result<FinalizedAttrSet<'id, 'ir>> {
let mut stcs = HashMap::new_in(ctx.bump());
let mut dyns = Vec::new_in(ctx.bump());
for (sym, (value, value_span)) in pending.stcs {
let expr_id = finalize_pending_value::<_, ALLOW_DYN>(value, inherit_lookups, ctx)?;
stcs.insert(sym, (expr_id, value_span));
let expr = finalize_pending_value::<_, ALLOW_DYN>(value, inherit_lookups, ctx)?;
stcs.insert(sym, (ctx.maybe_thunk(expr), value_span));
}
if ALLOW_DYN {
@@ -1155,12 +1157,10 @@ fn finalize_pending_set<'id, 'ir, Ctx: DowngradeContext<'id, 'ir>, const ALLOW_D
let key_id = downgrade_attr(attr, ctx)?;
let key_expr = match key_id {
Attr::Dynamic(id, _) => id,
Attr::Str(sym, _attr_span) => {
ctx.new_expr(Ir::Str(ctx.get_sym(sym).to_string().box_in(ctx.bump())))
}
Attr::Str(sym, _attr_span) => ctx.new_expr(Ir::Str(sym)),
};
let value_id = finalize_pending_value::<_, ALLOW_DYN>(value, inherit_lookups, ctx)?;
dyns.push((key_expr, value_id, value_span));
let value = finalize_pending_value::<_, ALLOW_DYN>(value, inherit_lookups, ctx)?;
dyns.push((key_expr, ctx.maybe_thunk(value), value_span));
}
}
@@ -1169,30 +1169,25 @@ fn finalize_pending_set<'id, 'ir, Ctx: DowngradeContext<'id, 'ir>, const ALLOW_D
fn finalize_pending_value<'id, 'ir, Ctx: DowngradeContext<'id, 'ir>, const ALLOW_DYN: bool>(
value: PendingValue,
inherit_lookups: &HashMap<StringId, (IrRef<'id, 'ir>, TextRange)>,
inherit_lookups: &HashMap<StringId, (MaybeThunk, TextRange)>,
ctx: &mut Ctx,
) -> Result<IrRef<'id, 'ir>> {
match value {
PendingValue::Expr(expr) => {
let id = Downgrade::downgrade(expr, ctx)?;
Ok(ctx.maybe_thunk(id))
}
PendingValue::Expr(expr) => expr.downgrade(ctx),
PendingValue::InheritFrom(from_expr, sym, span) => {
let from_id = Downgrade::downgrade(from_expr, ctx)?;
let select_id = ctx.new_expr(Ir::Select {
expr: from_id,
let from = Downgrade::downgrade(from_expr, ctx)?;
Ok(ctx.new_expr(Ir::Select {
expr: from,
attrpath: Vec::from_iter_in([Attr::Str(sym, span)], ctx.bump()),
default: None,
span,
});
Ok(ctx.maybe_thunk(select_id))
}))
}
PendingValue::InheritScope(sym, span) => {
if let Some(&(expr, _)) = inherit_lookups.get(&sym) {
Ok(ctx.maybe_thunk(expr))
Ok(expr.to_ir(ctx))
} else {
let lookup_id = ctx.lookup(sym, span)?;
Ok(ctx.maybe_thunk(lookup_id))
ctx.lookup(sym, span).map(|val| val.to_ir(ctx))
}
}
PendingValue::Set(set) => {
@@ -1221,7 +1216,8 @@ fn downgrade_path<'id, 'ir>(
.into_iter()
.map(|part| match part {
ast::InterpolPart::Literal(lit) => {
Ok(ctx.new_expr(Ir::Str(lit.text().to_string().box_in(ctx.bump()))))
let id = ctx.intern_string(lit.text());
Ok(ctx.new_expr(Ir::Str(id)))
}
ast::InterpolPart::Interpolation(interpol) => interpol
.expr()