Compare commits

...

1 Commits

Author SHA1 Message Date
imxyy1soope1 9d4c37950e refactor(downgrade): MaybeThunk 2026-04-12 17:05:44 +08:00
5 changed files with 226 additions and 221 deletions
+46 -88
View File
@@ -1,8 +1,8 @@
use std::ops::Deref; use std::ops::Deref;
use fix_builtins::{BUILTINS, BuiltinId}; use fix_builtins::BuiltinId;
use fix_common::StringId; use fix_common::StringId;
use fix_ir::{Attr, BinOpKind, Ir, Param, RawIrRef, ThunkId, UnOpKind}; use fix_ir::{Attr, BinOpKind, Ir, MaybeThunk, Param, RawIrRef, ThunkId, UnOpKind};
use hashbrown::HashMap; use hashbrown::HashMap;
use num_enum::TryFromPrimitive; use num_enum::TryFromPrimitive;
use rnix::TextRange; use rnix::TextRange;
@@ -151,40 +151,34 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
} }
} }
fn classify_value(&mut self, ir: RawIrRef<'_>) -> InlineOperand { fn inline_maybe_thunk(&mut self, val: MaybeThunk) -> InlineOperand {
match ir.deref() { use MaybeThunk::*;
&Ir::Int(x) => { match val {
Int(x) => {
if x <= i32::MAX as i64 { if x <= i32::MAX as i64 {
InlineOperand::Const(Const::Smi(x as i32)) InlineOperand::Const(Const::Smi(x as i32))
} else { } else {
InlineOperand::BigInt(x) InlineOperand::BigInt(x)
} }
} }
&Ir::Float(x) => InlineOperand::Const(Const::Float(x)), Float(x) => InlineOperand::Const(Const::Float(x)),
&Ir::Bool(b) => InlineOperand::Const(Const::Bool(b)), Bool(b) => InlineOperand::Const(Const::Bool(b)),
Ir::Null => InlineOperand::Const(Const::Null), Null => InlineOperand::Const(Const::Null),
Ir::Str(s) => { Str(id) => InlineOperand::Const(Const::String(id)),
let sid = self.ctx.intern_string(s.deref()); Thunk(id) => {
InlineOperand::Const(Const::String(sid))
}
&Ir::Thunk(id) => {
let (layer, local) = self.resolve_thunk(id); let (layer, local) = self.resolve_thunk(id);
InlineOperand::Local { layer, local } InlineOperand::Local { layer, local }
} }
&Ir::Arg { layer } => InlineOperand::Local { Arg { layer } => InlineOperand::Local {
layer: layer.try_into().expect("scope too deep!"), layer: layer.try_into().expect("scope too deep!"),
local: 0, local: 0,
}, },
&Ir::Builtin(id) => {
let arity = BUILTINS[id as usize].1;
InlineOperand::Const(Const::PrimOp { id, arity })
}
Ir::Builtins => InlineOperand::Builtins,
_ => panic!("cannot classify IR node as inline operand"), _ => panic!("cannot classify IR node as inline operand"),
} }
} }
fn emit_inline_operand(&mut self, operand: InlineOperand) { fn emit_maybe_thunk(&mut self, val: MaybeThunk) {
let operand = self.inline_maybe_thunk(val);
match operand { match operand {
InlineOperand::Const(val) => { InlineOperand::Const(val) => {
let idx = self.ctx.add_constant(val); let idx = self.ctx.add_constant(val);
@@ -320,30 +314,15 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
self.count_with_thunks(*lhs) + self.count_with_thunks(*rhs) self.count_with_thunks(*lhs) + self.count_with_thunks(*rhs)
} }
Ir::UnOp { rhs, .. } => self.count_with_thunks(*rhs), Ir::UnOp { rhs, .. } => self.count_with_thunks(*rhs),
Ir::Call { func, arg, .. } => { Ir::Call { func, .. } => self.count_with_thunks(*func),
self.count_with_thunks(*func) + self.count_with_thunks(*arg)
}
Ir::Assert { Ir::Assert {
assertion, expr, .. assertion, expr, ..
} => self.count_with_thunks(*assertion) + self.count_with_thunks(*expr), } => self.count_with_thunks(*assertion) + self.count_with_thunks(*expr),
Ir::Select { expr, default, .. } => { Ir::Select { expr, .. } => self.count_with_thunks(*expr),
self.count_with_thunks(*expr) + default.map_or(0, |d| self.count_with_thunks(d))
}
Ir::HasAttr { lhs, .. } => self.count_with_thunks(*lhs), Ir::HasAttr { lhs, .. } => self.count_with_thunks(*lhs),
Ir::ConcatStrings { parts, .. } => { Ir::ConcatStrings { parts, .. } => {
parts.iter().map(|p| self.count_with_thunks(*p)).sum() parts.iter().map(|p| self.count_with_thunks(*p)).sum()
} }
Ir::Path(p) => self.count_with_thunks(*p),
Ir::List { items } => items.iter().map(|item| self.count_with_thunks(*item)).sum(),
Ir::AttrSet { stcs, dyns } => {
stcs.iter()
.map(|(_, &(val, _))| self.count_with_thunks(val))
.sum::<usize>()
+ dyns
.iter()
.map(|&(k, v, _)| self.count_with_thunks(k) + self.count_with_thunks(v))
.sum::<usize>()
}
_ => 0, _ => 0,
} }
} }
@@ -392,9 +371,8 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
self.collect_with_thunks_recursive(*rhs, out); self.collect_with_thunks_recursive(*rhs, out);
} }
Ir::UnOp { rhs, .. } => self.collect_with_thunks_recursive(*rhs, out), Ir::UnOp { rhs, .. } => self.collect_with_thunks_recursive(*rhs, out),
Ir::Call { func, arg, .. } => { Ir::Call { func, .. } => {
self.collect_with_thunks_recursive(*func, out); self.collect_with_thunks_recursive(*func, out);
self.collect_with_thunks_recursive(*arg, out);
} }
Ir::Assert { Ir::Assert {
assertion, expr, .. assertion, expr, ..
@@ -402,11 +380,8 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
self.collect_with_thunks_recursive(*assertion, out); self.collect_with_thunks_recursive(*assertion, out);
self.collect_with_thunks_recursive(*expr, out); self.collect_with_thunks_recursive(*expr, out);
} }
Ir::Select { expr, default, .. } => { Ir::Select { expr, .. } => {
self.collect_with_thunks_recursive(*expr, out); self.collect_with_thunks_recursive(*expr, out);
if let Some(d) = default {
self.collect_with_thunks_recursive(*d, out);
}
} }
Ir::HasAttr { lhs, .. } => self.collect_with_thunks_recursive(*lhs, out), Ir::HasAttr { lhs, .. } => self.collect_with_thunks_recursive(*lhs, out),
Ir::ConcatStrings { parts, .. } => { Ir::ConcatStrings { parts, .. } => {
@@ -414,22 +389,7 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
self.collect_with_thunks_recursive(*p, out); self.collect_with_thunks_recursive(*p, out);
} }
} }
Ir::Path(p) => self.collect_with_thunks_recursive(*p, out), _ => (),
Ir::List { items } => {
for item in items.iter() {
self.collect_with_thunks_recursive(*item, out);
}
}
Ir::AttrSet { stcs, dyns } => {
for (_, &(val, _)) in stcs.iter() {
self.collect_with_thunks_recursive(val, out);
}
for &(key, val, _) in dyns.iter() {
self.collect_with_thunks_recursive(key, out);
self.collect_with_thunks_recursive(val, out);
}
}
_ => {}
} }
} }
@@ -512,10 +472,9 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
&Ir::Bool(true) => self.emit_op(Op::PushTrue), &Ir::Bool(true) => self.emit_op(Op::PushTrue),
&Ir::Bool(false) => self.emit_op(Op::PushFalse), &Ir::Bool(false) => self.emit_op(Op::PushFalse),
Ir::Null => self.emit_op(Op::PushNull), Ir::Null => self.emit_op(Op::PushNull),
Ir::Str(s) => { &Ir::Str(id) => {
let idx = self.ctx.intern_string(s.deref());
self.emit_op(Op::PushString); self.emit_op(Op::PushString);
self.emit_str_id(idx); self.emit_str_id(id);
} }
&Ir::Path(p) => { &Ir::Path(p) => {
self.emit_expr(p); self.emit_expr(p);
@@ -572,15 +531,14 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
self.emit_op(Op::MakeList); self.emit_op(Op::MakeList);
self.emit_u32(items.len() as u32); self.emit_u32(items.len() as u32);
for &item in items.iter() { for &item in items.iter() {
let operand = self.classify_value(item); self.emit_maybe_thunk(item);
self.emit_inline_operand(operand);
} }
} }
} }
&Ir::Call { func, arg, .. } => { &Ir::Call { func, arg, .. } => {
self.emit_expr(arg);
self.emit_expr(func); self.emit_expr(func);
self.emit_op(Op::Call); self.emit_op(Op::Call);
self.inline_maybe_thunk(arg);
} }
&Ir::Arg { layer } => { &Ir::Arg { layer } => {
self.emit_load(layer.try_into().expect("scope too deep!"), 0); self.emit_load(layer.try_into().expect("scope too deep!"), 0);
@@ -616,16 +574,17 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
); );
} }
&Ir::ConcatStrings { &Ir::ConcatStrings {
ref parts, parts: _,
force_string, force_string: _,
} => { } => {
self.emit_op(Op::ConcatStrings); todo!("redesign ConcatStrings");
self.emit_u16(parts.len() as u16); // self.emit_op(Op::ConcatStrings);
self.emit_u8(if force_string { 1 } else { 0 }); // self.emit_u16(parts.len() as u16);
for &part in parts.iter() { // self.emit_u8(if force_string { 1 } else { 0 });
let operand = self.classify_value(part); // for &part in parts.iter() {
self.emit_inline_operand(operand); // let operand = self.inline_maybe_thunk(part);
} // self.emit_inline_operand(operand);
// }
} }
&Ir::HasAttr { lhs, ref rhs } => { &Ir::HasAttr { lhs, ref rhs } => {
self.emit_has_attr(lhs, rhs); self.emit_has_attr(lhs, rhs);
@@ -817,8 +776,8 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
fn emit_attrset( fn emit_attrset(
&mut self, &mut self,
stcs: &fix_ir::HashMap<'_, StringId, (RawIrRef<'_>, TextRange)>, stcs: &fix_ir::HashMap<'_, StringId, (MaybeThunk, TextRange)>,
dyns: &[(RawIrRef<'_>, RawIrRef<'_>, TextRange)], dyns: &[(RawIrRef<'_>, MaybeThunk, TextRange)],
) { ) {
if stcs.is_empty() && dyns.is_empty() { if stcs.is_empty() && dyns.is_empty() {
self.emit_op(Op::MakeEmptyAttrs); self.emit_op(Op::MakeEmptyAttrs);
@@ -832,19 +791,18 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
for (&sym, &(val, span)) in stcs.iter() { for (&sym, &(val, span)) in stcs.iter() {
self.emit_u8(AttrKeyType::Static as u8); self.emit_u8(AttrKeyType::Static as u8);
self.emit_str_id(sym); self.emit_str_id(sym);
let val_operand = self.classify_value(val); self.emit_maybe_thunk(val);
self.emit_inline_operand(val_operand);
let span_id = self.ctx.register_span(span); let span_id = self.ctx.register_span(span);
self.emit_u32(span_id); self.emit_u32(span_id);
} }
for &(key, val, span) in dyns.iter() { for &(_key, _val, _span) in dyns.iter() {
self.emit_u8(AttrKeyType::Dynamic as u8); todo!("redesign dynamic attr key");
let key_operand = self.classify_value(key); // self.emit_u8(AttrKeyType::Dynamic as u8);
self.emit_inline_operand(key_operand); // self.emit_maybe_thunk(key);
let val_operand = self.classify_value(val); // let val_operand = self.inline_maybe_thunk(val);
self.emit_inline_operand(val_operand); // self.emit_maybe_thunk(val);
let span_id = self.ctx.register_span(span); // let span_id = self.ctx.register_span(span);
self.emit_u32(span_id); // self.emit_u32(span_id);
} }
} }
@@ -910,12 +868,12 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
fn emit_with( fn emit_with(
&mut self, &mut self,
namespace: RawIrRef<'_>, namespace: MaybeThunk,
body: RawIrRef<'_>, body: RawIrRef<'_>,
thunks: &[(ThunkId, RawIrRef<'_>)], thunks: &[(ThunkId, RawIrRef<'_>)],
) { ) {
self.emit_expr(namespace);
self.emit_op(Op::PushWith); self.emit_op(Op::PushWith);
self.emit_maybe_thunk(namespace);
self.emit_scope_thunks(thunks); self.emit_scope_thunks(thunks);
self.emit_expr(body); self.emit_expr(body);
self.emit_op(Op::PopWith); self.emit_op(Op::PopWith);
+84 -88
View File
@@ -1,4 +1,3 @@
use bumpalo::boxed::Box;
use bumpalo::collections::{CollectIn, Vec}; use bumpalo::collections::{CollectIn, Vec};
use fix_builtins::BuiltinId; use fix_builtins::BuiltinId;
use fix_common::Symbol; use fix_common::Symbol;
@@ -6,7 +5,7 @@ use fix_error::{Error, Result, Source};
use hashbrown::HashSet; use hashbrown::HashSet;
use hashbrown::hash_map::Entry; use hashbrown::hash_map::Entry;
use rnix::TextRange; use rnix::TextRange;
use rnix::ast::{self, Expr, HasEntry}; use rnix::ast::{self, AstToken, Expr, HasEntry};
use rowan::ast::AstNode; use rowan::ast::AstNode;
use super::*; 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> { pub trait DowngradeContext<'id: 'ir, 'ir> {
fn new_expr(&self, expr: Ir<'ir, IrRef<'id, 'ir>>) -> IrRef<'id, '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 intern_string(&mut self, sym: impl AsRef<str>) -> StringId;
fn get_sym(&self, id: StringId) -> Symbol<'_>; fn resolve_sym(&self, id: StringId) -> Symbol<'_>;
fn lookup(&self, sym: StringId, span: TextRange) -> Result<IrRef<'id, 'ir>>; fn lookup(&self, sym: StringId, span: TextRange) -> Result<MaybeThunk>;
fn get_current_source(&self) -> Source; 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 path = {
let temp = self.content().require(ctx, span)?; let temp = self.content().require(ctx, span)?;
let text = temp.text(); let text = temp.text();
ctx.new_expr(Ir::Str( let id = ctx.intern_string(&text[1..text.len() - 1]);
text[1..text.len() - 1].to_string().box_in(ctx.bump()), let expr = ctx.new_expr(Ir::Str(id));
)) ctx.maybe_thunk(expr)
}; };
// HACK: disgusting eww // HACK: disgusting eww
let find_file = ctx.new_expr(Ir::Builtin(BuiltinId::FindFile)); 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.new_expr(Ir::BuiltinConst(sym));
let nix_path = ctx.maybe_thunk(nix_path);
let call = ctx.new_expr(Ir::Call { let call = ctx.new_expr(Ir::Call {
func: find_file, func: find_file,
arg: nix_path, 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 bump = ctx.bump();
let mut parts = normalized.into_iter().map(|part| match part { 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::Literal(lit) => {
ast::InterpolPart::Interpolation(interpol) => { let id = ctx.intern_string(lit);
let inner = interpol Ok(ctx.new_expr(Ir::Str(id)))
.expr()
.require(ctx, interpol.syntax().text_range())?
.downgrade(ctx)?;
Ok(ctx.maybe_thunk(inner))
} }
ast::InterpolPart::Interpolation(interpol) => interpol
.expr()
.require(ctx, interpol.syntax().text_range())?
.downgrade(ctx),
}); });
Ok(if is_single_literal { 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 { 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>> { fn downgrade(self, ctx: &mut Ctx) -> Result<IrRef<'id, 'ir>> {
let span = self.syntax().text_range(); 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::Integer(int) => Ir::Int(int.value().require(ctx, span)?),
ast::LiteralKind::Float(float) => Ir::Float(float.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>> { fn downgrade(self, ctx: &mut Ctx) -> Result<IrRef<'id, 'ir>> {
let span = self.syntax().text_range(); let span = self.syntax().text_range();
let text = self.ident_token().require(ctx, span)?.to_string(); let text = self.ident_token().require(ctx, span)?.to_string();
let sym = ctx.new_sym(text); let sym = ctx.intern_string(text);
ctx.lookup(sym, span) 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 span = self.syntax().text_range();
let expr = self.expr().require(ctx, span)?.downgrade(ctx)?; let expr = self.expr().require(ctx, span)?.downgrade(ctx)?;
let attrpath = downgrade_attrpath(self.attrpath().require(ctx, span)?, ctx)?; let attrpath = downgrade_attrpath(self.attrpath().require(ctx, span)?, ctx)?;
let default = if let Some(default) = self.default_expr() { let default = self
let default_expr = default.downgrade(ctx)?; .default_expr()
Some(ctx.maybe_thunk(default_expr)) .map(|expr| expr.downgrade(ctx))
} else { .transpose()?;
None
};
let span = self.syntax().text_range(); let span = self.syntax().text_range();
Ok(ctx.new_expr(Ir::Select { Ok(ctx.new_expr(Ir::Select {
expr, 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 })) 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 { Ok(ctx.new_expr(Ir::Select {
expr: attrset_expr, expr: attrset_expr,
attrpath: Vec::from_iter_in( 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 { match raw_param {
ast::Param::IdentParam(id) => { ast::Param::IdentParam(id) => {
let param_sym = ctx.new_sym(id.to_string()); let param_sym = ctx.intern_string(id.to_string());
param = None; param = None;
body = ctx.with_param_scope(param_sym, |ctx| body_ast.downgrade(ctx))?; 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() .pat_bind()
.map(|alias| { .map(|alias| {
let ident = alias.ident().require(ctx, alias.syntax().text_range())?; 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()?; .transpose()?;
@@ -484,7 +479,7 @@ impl<'id: 'ir, 'ir> PendingAttrSet<'ir> {
match first { match first {
ast::Attr::Ident(ident) => { 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) self.insert_static(sym, span, rest, value, ctx)
} }
ast::Attr::Str(string) => { ast::Attr::Str(string) => {
@@ -493,7 +488,7 @@ impl<'id: 'ir, 'ir> PendingAttrSet<'ir> {
&& let ast::InterpolPart::Literal(lit) = && let ast::InterpolPart::Literal(lit) =
parts.into_iter().next().expect("len checked") 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); return self.insert_static(sym, span, rest, value, ctx);
} }
self.insert_dynamic(first.clone(), 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() { for attr in inherit.attrs() {
let span = attr.syntax().text_range(); let span = attr.syntax().text_range();
let sym = match &attr { 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) => { ast::Attr::Str(s) => {
let parts = s.normalized_parts(); let parts = s.normalized_parts();
if parts.len() == 1 if parts.len() == 1
&& let ast::InterpolPart::Literal(lit) = && let ast::InterpolPart::Literal(lit) =
parts.into_iter().next().expect("len checked") parts.into_iter().next().expect("len checked")
{ {
ctx.new_sym(lit) ctx.intern_string(lit)
} else { } else {
return Err(Error::downgrade_error( return Err(Error::downgrade_error(
"dynamic attributes not allowed in inherit".to_string(), "dynamic attributes not allowed in inherit".to_string(),
@@ -763,7 +758,7 @@ impl<'id: 'ir, 'ir> PendingAttrSet<'ir> {
if self.stcs.contains_key(&sym) { if self.stcs.contains_key(&sym) {
return Err(Error::downgrade_error( 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(), ctx.get_current_source(),
span, span,
)); ));
@@ -840,8 +835,8 @@ fn make_attrpath_value_entry<'ir>(path: Vec<'ir, ast::Attr>, value: ast::Expr) -
} }
struct FinalizedAttrSet<'id, 'ir> { struct FinalizedAttrSet<'id, 'ir> {
stcs: HashMap<'ir, StringId, (IrRef<'id, 'ir>, TextRange)>, stcs: HashMap<'ir, StringId, (MaybeThunk, TextRange)>,
dyns: Vec<'ir, (IrRef<'id, 'ir>, IrRef<'id, 'ir>, TextRange)>, dyns: Vec<'ir, (IrRef<'id, 'ir>, MaybeThunk, TextRange)>,
} }
fn downgrade_attrs<'id, 'ir>( fn downgrade_attrs<'id, 'ir>(
@@ -861,17 +856,17 @@ fn downgrade_attr<'id, 'ir>(
use ast::InterpolPart::*; use ast::InterpolPart::*;
match attr { match attr {
Ident(ident) => Ok(Attr::Str( Ident(ident) => Ok(Attr::Str(
ctx.new_sym(ident.to_string()), ctx.intern_string(ident.to_string()),
ident.syntax().text_range(), ident.syntax().text_range(),
)), )),
Str(string) => { Str(string) => {
let parts = string.normalized_parts(); let parts = string.normalized_parts();
let span = string.syntax().text_range(); let span = string.syntax().text_range();
if parts.is_empty() { 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 { } else if parts.len() == 1 {
match parts.into_iter().next().expect("len checked") { 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( Interpolation(interpol) => Ok(Attr::Dynamic(
interpol interpol
.expr() .expr()
@@ -885,7 +880,10 @@ fn downgrade_attr<'id, 'ir>(
let parts = parts let parts = parts
.into_iter() .into_iter()
.map(|part| match part { .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 Interpolation(interpol) => interpol
.expr() .expr()
.require(ctx, interpol.syntax().text_range())? .require(ctx, interpol.syntax().text_range())?
@@ -950,13 +948,13 @@ where
for entry in pat_entries { for entry in pat_entries {
let ident = entry.ident().require(ctx, entry.syntax().text_range())?; let ident = entry.ident().require(ctx, entry.syntax().text_range())?;
let sym_span = ident.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 default = entry.default();
let span = entry.syntax().text_range(); let span = entry.syntax().text_range();
if !seen_params.insert(sym) { if !seen_params.insert(sym) {
return Err(Error::downgrade_error( return Err(Error::downgrade_error(
format!("duplicate parameter '{}'", ctx.get_sym(sym)), format!("duplicate parameter '{}'", ctx.resolve_sym(sym)),
ctx.get_current_source(), ctx.get_current_source(),
span, span,
)); ));
@@ -1001,12 +999,13 @@ where
default, default,
span, span,
} = param; } = param;
let default = if let Some(default) = default { let default = default.map(|default| default.downgrade(ctx)).transpose()?;
let default = default.clone().downgrade(ctx)?; // let default = if let Some(default) = default {
Some(ctx.maybe_thunk(default)) // let default = default.clone().downgrade(ctx)?;
} else { // Some(ctx.maybe_thunk(default))
None // } else {
}; // None
// };
Ok(ctx.new_expr(Ir::Select { Ok(ctx.new_expr(Ir::Select {
expr: arg, expr: arg,
@@ -1075,7 +1074,7 @@ where
F: FnOnce( F: FnOnce(
&mut Ctx, &mut Ctx,
&[StringId], &[StringId],
&[(IrRef<'id, 'ir>, IrRef<'id, 'ir>, TextRange)], &[(IrRef<'id, 'ir>, MaybeThunk, TextRange)],
) -> Result<IrRef<'id, 'ir>>, ) -> Result<IrRef<'id, 'ir>>,
{ {
let mut pending = PendingAttrSet::new_in(ctx.bump()); let mut pending = PendingAttrSet::new_in(ctx.bump());
@@ -1088,18 +1087,21 @@ where
ctx.with_let_scope(&keys, |ctx| { ctx.with_let_scope(&keys, |ctx| {
let finalized = finalize_pending_set::<_, ALLOW_DYN>(pending, &inherit_lookups, ctx)?; let finalized = finalize_pending_set::<_, ALLOW_DYN>(pending, &inherit_lookups, ctx)?;
let vals = keys let vals = {
.iter() let mut temp = Vec::with_capacity_in(keys.len(), ctx.bump());
.map(|sym| finalized.stcs.get(sym).expect("WTF").0) for sym in &keys {
.collect_in(ctx.bump()); temp.push(finalized.stcs.get(sym).expect("WTF").0.to_ir(ctx));
}
temp
};
body_fn(ctx, &keys, &finalized.dyns).map(|body| (vals, body)) 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], entries: &[ast::Entry],
ctx: &mut Ctx, 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()); let mut inherit_lookups = HashMap::new_in(ctx.bump());
for entry in entries { for entry in entries {
if let ast::Entry::Inherit(inherit) = entry 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() { for attr in inherit.attrs() {
if let ast::Attr::Ident(ident) = attr { if let ast::Attr::Ident(ident) = attr {
let attr_span = ident.syntax().text_range(); 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)?; let expr = ctx.lookup(sym, attr_span)?;
inherit_lookups.insert(sym, (expr, 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 { for (sym, (_, span)) in &pending.stcs {
if !binding_syms.insert(*sym) { if !binding_syms.insert(*sym) {
return Err(Error::downgrade_error( 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(), ctx.get_current_source(),
*span, *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>( fn finalize_pending_set<'id, 'ir, Ctx: DowngradeContext<'id, 'ir>, const ALLOW_DYN: bool>(
pending: PendingAttrSet, pending: PendingAttrSet,
inherit_lookups: &HashMap<StringId, (IrRef<'id, 'ir>, TextRange)>, inherit_lookups: &HashMap<StringId, (MaybeThunk, TextRange)>,
ctx: &mut Ctx, ctx: &mut Ctx,
) -> Result<FinalizedAttrSet<'id, 'ir>> { ) -> Result<FinalizedAttrSet<'id, 'ir>> {
let mut stcs = HashMap::new_in(ctx.bump()); let mut stcs = HashMap::new_in(ctx.bump());
let mut dyns = Vec::new_in(ctx.bump()); let mut dyns = Vec::new_in(ctx.bump());
for (sym, (value, value_span)) in pending.stcs { for (sym, (value, value_span)) in pending.stcs {
let expr_id = finalize_pending_value::<_, ALLOW_DYN>(value, inherit_lookups, ctx)?; let expr = finalize_pending_value::<_, ALLOW_DYN>(value, inherit_lookups, ctx)?;
stcs.insert(sym, (expr_id, value_span)); stcs.insert(sym, (ctx.maybe_thunk(expr), value_span));
} }
if ALLOW_DYN { 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_id = downgrade_attr(attr, ctx)?;
let key_expr = match key_id { let key_expr = match key_id {
Attr::Dynamic(id, _) => id, Attr::Dynamic(id, _) => id,
Attr::Str(sym, _attr_span) => { Attr::Str(sym, _attr_span) => ctx.new_expr(Ir::Str(sym)),
ctx.new_expr(Ir::Str(ctx.get_sym(sym).to_string().box_in(ctx.bump())))
}
}; };
let value_id = finalize_pending_value::<_, ALLOW_DYN>(value, inherit_lookups, ctx)?; let value = finalize_pending_value::<_, ALLOW_DYN>(value, inherit_lookups, ctx)?;
dyns.push((key_expr, value_id, value_span)); 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>( fn finalize_pending_value<'id, 'ir, Ctx: DowngradeContext<'id, 'ir>, const ALLOW_DYN: bool>(
value: PendingValue, value: PendingValue,
inherit_lookups: &HashMap<StringId, (IrRef<'id, 'ir>, TextRange)>, inherit_lookups: &HashMap<StringId, (MaybeThunk, TextRange)>,
ctx: &mut Ctx, ctx: &mut Ctx,
) -> Result<IrRef<'id, 'ir>> { ) -> Result<IrRef<'id, 'ir>> {
match value { match value {
PendingValue::Expr(expr) => { PendingValue::Expr(expr) => expr.downgrade(ctx),
let id = Downgrade::downgrade(expr, ctx)?;
Ok(ctx.maybe_thunk(id))
}
PendingValue::InheritFrom(from_expr, sym, span) => { PendingValue::InheritFrom(from_expr, sym, span) => {
let from_id = Downgrade::downgrade(from_expr, ctx)?; let from = Downgrade::downgrade(from_expr, ctx)?;
let select_id = ctx.new_expr(Ir::Select { Ok(ctx.new_expr(Ir::Select {
expr: from_id, expr: from,
attrpath: Vec::from_iter_in([Attr::Str(sym, span)], ctx.bump()), attrpath: Vec::from_iter_in([Attr::Str(sym, span)], ctx.bump()),
default: None, default: None,
span, span,
}); }))
Ok(ctx.maybe_thunk(select_id))
} }
PendingValue::InheritScope(sym, span) => { PendingValue::InheritScope(sym, span) => {
if let Some(&(expr, _)) = inherit_lookups.get(&sym) { if let Some(&(expr, _)) = inherit_lookups.get(&sym) {
Ok(ctx.maybe_thunk(expr)) Ok(expr.to_ir(ctx))
} else { } else {
let lookup_id = ctx.lookup(sym, span)?; ctx.lookup(sym, span).map(|val| val.to_ir(ctx))
Ok(ctx.maybe_thunk(lookup_id))
} }
} }
PendingValue::Set(set) => { PendingValue::Set(set) => {
@@ -1221,7 +1216,8 @@ fn downgrade_path<'id, 'ir>(
.into_iter() .into_iter()
.map(|part| match part { .map(|part| match part {
ast::InterpolPart::Literal(lit) => { 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 ast::InterpolPart::Interpolation(interpol) => interpol
.expr() .expr()
+49 -8
View File
@@ -2,7 +2,6 @@ use std::hash::Hash;
use std::ops::Deref; use std::ops::Deref;
use bumpalo::Bump; use bumpalo::Bump;
use bumpalo::boxed::Box;
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
use fix_builtins::{BUILTINS, BuiltinId}; use fix_builtins::{BUILTINS, BuiltinId};
use fix_common::StringId; use fix_common::StringId;
@@ -11,6 +10,8 @@ use num_enum::TryFromPrimitive as _;
use rnix::{TextRange, ast}; use rnix::{TextRange, ast};
use string_interner::DefaultStringInterner; use string_interner::DefaultStringInterner;
use crate::downgrade::DowngradeContext;
pub mod downgrade; pub mod downgrade;
pub type HashMap<'ir, K, V> = hashbrown::HashMap<K, V, hashbrown::DefaultHashBuilder, &'ir Bump>; pub type HashMap<'ir, K, V> = hashbrown::HashMap<K, V, hashbrown::DefaultHashBuilder, &'ir Bump>;
@@ -62,6 +63,46 @@ impl<'ir> Deref for RawIrRef<'ir> {
} }
} }
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub enum MaybeThunk {
Int(i64),
Float(f64),
Bool(bool),
Null,
Str(StringId),
Path(StringId),
Thunk(ThunkId),
Arg { layer: usize },
Builtin(BuiltinId),
Builtins,
ReplBinding(StringId),
ScopedImportBinding(StringId),
WithLookup(StringId),
}
impl MaybeThunk {
fn to_ir<'id, 'ir>(self, ctx: &mut impl DowngradeContext<'id, 'ir>) -> IrRef<'id, 'ir> {
use MaybeThunk::*;
let ir = match self {
Int(x) => Ir::Int(x),
Float(x) => Ir::Float(x),
Bool(x) => Ir::Bool(x),
Null => Ir::Null,
Str(x) => Ir::Str(x),
Path(x) => Ir::Path(ctx.new_expr(Ir::Str(x))),
Thunk(x) => Ir::Thunk(x),
Arg { layer } => Ir::Arg { layer },
Builtin(x) => Ir::Builtin(x),
Builtins => Ir::Builtins,
ReplBinding(x) => Ir::ReplBinding(x),
ScopedImportBinding(x) => Ir::ScopedImportBinding(x),
WithLookup(x) => Ir::WithLookup(x),
};
ctx.new_expr(ir)
}
}
#[repr(C)] #[repr(C)]
#[derive(Debug)] #[derive(Debug)]
pub enum Ir<'ir, Ref> { pub enum Ir<'ir, Ref> {
@@ -69,15 +110,15 @@ pub enum Ir<'ir, Ref> {
Float(f64), Float(f64),
Bool(bool), Bool(bool),
Null, Null,
Str(Box<'ir, String>), Str(StringId),
Path(Ref),
AttrSet { AttrSet {
stcs: HashMap<'ir, StringId, (Ref, TextRange)>, stcs: HashMap<'ir, StringId, (MaybeThunk, TextRange)>,
dyns: Vec<'ir, (Ref, Ref, TextRange)>, dyns: Vec<'ir, (Ref, MaybeThunk, TextRange)>,
}, },
List { List {
items: Vec<'ir, Ref>, items: Vec<'ir, MaybeThunk>,
}, },
Path(Ref),
ConcatStrings { ConcatStrings {
parts: Vec<'ir, Ref>, parts: Vec<'ir, Ref>,
force_string: bool, force_string: bool,
@@ -118,7 +159,7 @@ pub enum Ir<'ir, Ref> {
}, },
With { With {
namespace: Ref, namespace: MaybeThunk,
body: Ref, body: Ref,
thunks: Vec<'ir, (ThunkId, Ref)>, thunks: Vec<'ir, (ThunkId, Ref)>,
}, },
@@ -135,7 +176,7 @@ pub enum Ir<'ir, Ref> {
}, },
Call { Call {
func: Ref, func: Ref,
arg: Ref, arg: MaybeThunk,
span: TextRange, span: TextRange,
}, },
+15 -15
View File
@@ -413,9 +413,7 @@ impl<'gc> GcRoot<'gc> {
let Ok(ty) = AttrKeyType::try_from_primitive(tag) let Ok(ty) = AttrKeyType::try_from_primitive(tag)
.map_err(|err| panic!("unknown key tag: {:#04x}", err.number)); .map_err(|err| panic!("unknown key tag: {:#04x}", err.number));
match ty { match ty {
AttrKeyType::Static => { AttrKeyType::Static => keys.push(SelectKeyData::Static(read!(StringId))),
keys.push(SelectKeyData::Static(read!(StringId)))
}
AttrKeyType::Dynamic => keys.push(SelectKeyData::Dynamic), AttrKeyType::Dynamic => keys.push(SelectKeyData::Dynamic),
}; };
} }
@@ -598,14 +596,15 @@ impl<'gc> GcRoot<'gc> {
} }
Call => { Call => {
// force func
try_force!(0, inst_start_pc); try_force!(0, inst_start_pc);
if self.call_depth > 10000 { if self.call_depth > 10000 {
return Action::Done(Err(Error::eval_error("stack overflow; max-call-depth exceeded"))) return Action::Done(Err(Error::eval_error(
"stack overflow; max-call-depth exceeded",
)));
} }
self.call_depth += 1; self.call_depth += 1;
let func = self.pop_stack(); let func = self.pop_stack();
let arg = self.pop_stack(); let arg = read!(OperandData).resolve(mc, self);
if let Some(closure) = func.as_gc::<Closure>() { if let Some(closure) = func.as_gc::<Closure>() {
let ip = closure.ip; let ip = closure.ip;
let n_locals = closure.n_locals; let n_locals = closure.n_locals;
@@ -783,16 +782,17 @@ impl<'gc> GcRoot<'gc> {
}; };
if let Some(attrset) = current_val.as_gc::<AttrSet>() if let Some(attrset) = current_val.as_gc::<AttrSet>()
&& let Some(v) = attrset.lookup(key_sid) { && let Some(v) = attrset.lookup(key_sid)
if i < n - 1 { {
current_val = v.restrict().unwrap_or_else(|_| { if i < n - 1 {
panic!("intermediate select values must be forced") current_val = v.restrict().unwrap_or_else(|_| {
}); panic!("intermediate select values must be forced")
} else { });
result_val = Some(v); } else {
} result_val = Some(v);
continue;
} }
continue;
}
use_default = true; use_default = true;
break; break;
+32 -22
View File
@@ -7,7 +7,7 @@ use fix_codegen::{BytecodeContext, InstructionPtr};
use fix_common::{StringId, Symbol}; use fix_common::{StringId, Symbol};
use fix_error::{Error, Result, Source}; use fix_error::{Error, Result, Source};
use fix_ir::downgrade::{Downgrade as _, DowngradeContext}; use fix_ir::downgrade::{Downgrade as _, DowngradeContext};
use fix_ir::{Ir, IrRef, RawIrRef, ThunkId}; use fix_ir::{Ir, IrRef, MaybeThunk, RawIrRef, ThunkId};
use fix_vm::{ForceMode, StaticValue, Vm, VmContext}; use fix_vm::{ForceMode, StaticValue, Vm, VmContext};
use ghost_cell::{GhostCell, GhostToken}; use ghost_cell::{GhostCell, GhostToken};
use hashbrown::{HashMap, HashSet}; use hashbrown::{HashMap, HashSet};
@@ -294,56 +294,66 @@ impl<'ctx: 'ir, 'id, 'ir> DowngradeContext<'id, 'ir> for DowngradeCtx<'ctx, 'id,
IrRef::new(self.bump.alloc(GhostCell::new(expr))) IrRef::new(self.bump.alloc(GhostCell::new(expr)))
} }
fn maybe_thunk(&mut self, ir: IrRef<'id, 'ir>) -> IrRef<'id, 'ir> { fn maybe_thunk(&mut self, ir: IrRef<'id, 'ir>) -> MaybeThunk {
if !should_thunk(ir, &self.token) { use MaybeThunk::*;
return ir; match *ir.borrow(&self.token) {
Ir::Builtin(x) => return Builtin(x),
Ir::Int(x) => return Int(x),
Ir::Float(x) => return Float(x),
Ir::Bool(x) => return Bool(x),
Ir::Str(x) => return Str(x),
Ir::Thunk(x) => return Thunk(x),
Ir::Arg { layer } => return Arg { layer },
Ir::Builtins => return Builtins,
Ir::Null => return Null,
_ => (),
} }
let id = ThunkId(*self.thunk_count); let id = ThunkId(*self.thunk_count);
*self.thunk_count = self.thunk_count.checked_add(1).expect("thunk id overflow"); *self.thunk_count = self.thunk_count.checked_add(1).expect("thunk id overflow");
self.thunk_scopes self.thunk_scopes
.last_mut() .last_mut()
.expect("no active cache scope") .expect("no active cache scope")
.add_binding(id, ir, &self.token); .add_binding(id, ir, &self.token);
IrRef::alloc(self.bump, Ir::Thunk(id)) Thunk(id)
} }
fn new_sym(&mut self, sym: String) -> StringId { fn intern_string(&mut self, sym: impl AsRef<str>) -> StringId {
StringId(self.strings.get_or_intern(sym)) StringId(self.strings.get_or_intern(sym))
} }
fn get_sym(&self, id: StringId) -> Symbol<'_> { fn resolve_sym(&self, id: StringId) -> Symbol<'_> {
self.strings.resolve(id.0).expect("no symbol found").into() self.strings.resolve(id.0).expect("no symbol found").into()
} }
fn lookup(&self, sym: StringId, span: rnix::TextRange) -> Result<IrRef<'id, 'ir>> { fn lookup(&self, sym: StringId, span: rnix::TextRange) -> Result<MaybeThunk> {
for scope in self.scopes.iter().rev() { for scope in self.scopes.iter().rev() {
match scope { match scope {
&Scope::Global(global_scope) => { &Scope::Global(global_scope) => {
use MaybeThunk::*;
if let Some(expr) = global_scope.get(&sym) { if let Some(expr) = global_scope.get(&sym) {
let ir = match expr { let val = match expr {
Ir::Builtins => Ir::Builtins, Ir::Builtins => Builtins,
Ir::Builtin(s) => Ir::Builtin(*s), Ir::Builtin(s) => Builtin(*s),
Ir::Bool(b) => Ir::Bool(*b), Ir::Bool(b) => Bool(*b),
Ir::Null => Ir::Null, Ir::Null => Null,
_ => unreachable!("globals should only contain leaf IR nodes"), _ => unreachable!("globals should only contain leaf IR nodes"),
}; };
return Ok(self.new_expr(ir)); return Ok(val);
} }
} }
&Scope::Repl(repl_bindings) => { &Scope::Repl(repl_bindings) => {
if repl_bindings.contains(&sym) { if repl_bindings.contains(&sym) {
return Ok(self.new_expr(Ir::ReplBinding(sym))); return Ok(MaybeThunk::ReplBinding(sym));
} }
} }
Scope::ScopedImport(scoped_bindings) => { Scope::ScopedImport(scoped_bindings) => {
if scoped_bindings.contains(&sym) { if scoped_bindings.contains(&sym) {
return Ok(self.new_expr(Ir::ScopedImportBinding(sym))); return Ok(MaybeThunk::ScopedImportBinding(sym));
} }
} }
Scope::Let(let_scope) => { Scope::Let(let_scope) => {
if let Some(&expr) = let_scope.get(&sym) { if let Some(&expr) = let_scope.get(&sym) {
return Ok(self.new_expr(Ir::Thunk(expr))); return Ok(MaybeThunk::Thunk(expr));
} }
} }
&Scope::Param { &Scope::Param {
@@ -351,19 +361,19 @@ impl<'ctx: 'ir, 'id, 'ir> DowngradeContext<'id, 'ir> for DowngradeCtx<'ctx, 'id,
abs_layer, abs_layer,
} => { } => {
if param_sym == sym { if param_sym == sym {
return Ok(self.new_expr(Ir::Arg { return Ok(MaybeThunk::Arg {
layer: self.thunk_scopes.len() - abs_layer, layer: self.thunk_scopes.len() - abs_layer,
})); });
} }
} }
} }
} }
if self.with_scope_count > 0 { if self.with_scope_count > 0 {
Ok(self.new_expr(Ir::WithLookup(sym))) Ok(MaybeThunk::WithLookup(sym))
} else { } else {
Err(Error::downgrade_error( Err(Error::downgrade_error(
format!("'{}' not found", self.get_sym(sym)), format!("'{}' not found", self.resolve_sym(sym)),
self.get_current_source(), self.get_current_source(),
span, span,
)) ))