avoid thunking trivial values

This commit is contained in:
2026-05-02 14:57:52 +08:00
parent 5dd160cc43
commit f39fc5fc49
7 changed files with 208 additions and 113 deletions
+3
View File
@@ -93,6 +93,9 @@ impl<'a, Ctx: DisassemblerContext> Disassembler<'a, Ctx> {
self.read_u8(); self.read_u8();
self.read_u32(); self.read_u32();
} }
BuiltinConst => {
self.read_u32();
}
Builtins => {} Builtins => {}
ReplBinding | ScopedImportBinding | WithLookup => { ReplBinding | ScopedImportBinding | WithLookup => {
self.read_u32(); self.read_u32();
+19 -2
View File
@@ -116,6 +116,7 @@ pub enum OperandType {
Const, Const,
BigInt, BigInt,
Local, Local,
BuiltinConst,
Builtins, Builtins,
ReplBinding, ReplBinding,
ScopedImportBinding, ScopedImportBinding,
@@ -147,6 +148,7 @@ pub enum InlineOperand {
Const(Const), Const(Const),
BigInt(i64), BigInt(i64),
Local { layer: u8, local: u32 }, Local { layer: u8, local: u32 },
BuiltinConst(StringId),
Builtins, Builtins,
ReplBinding(StringId), ReplBinding(StringId),
ScopedImportBinding(StringId), ScopedImportBinding(StringId),
@@ -191,8 +193,13 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
Arg { layer } => InlineOperand::Local { layer, local: 0 }, Arg { layer } => InlineOperand::Local { layer, local: 0 },
Builtin(id) => { Builtin(id) => {
let (_, arity) = BUILTINS[id as usize]; let (_, arity) = BUILTINS[id as usize];
InlineOperand::Const(Const::PrimOp { id, arity, dispatch_ip: id.entry_phase().ip() }) InlineOperand::Const(Const::PrimOp {
}, id,
arity,
dispatch_ip: id.entry_phase().ip(),
})
}
BuiltinConst(id) => InlineOperand::BuiltinConst(id),
Builtins => InlineOperand::Builtins, Builtins => InlineOperand::Builtins,
ReplBinding(id) => InlineOperand::ReplBinding(id), ReplBinding(id) => InlineOperand::ReplBinding(id),
ScopedImportBinding(id) => InlineOperand::ScopedImportBinding(id), ScopedImportBinding(id) => InlineOperand::ScopedImportBinding(id),
@@ -218,6 +225,10 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
self.emit_u8(layer); self.emit_u8(layer);
self.emit_u32(local); self.emit_u32(local);
} }
BuiltinConst(id) => {
self.emit_u8(OperandType::BuiltinConst as u8);
self.emit_str_id(id);
}
Builtins => { Builtins => {
self.emit_u8(OperandType::Builtins as u8); self.emit_u8(OperandType::Builtins as u8);
} }
@@ -698,6 +709,12 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
self.emit_op(Op::LoadBuiltin); self.emit_op(Op::LoadBuiltin);
self.emit_u8(id as u8); self.emit_u8(id as u8);
} }
BuiltinConst(id) => self.emit_select(
&Ir::Builtins,
&[Attr::Str(id, TextRange::default())],
None,
TextRange::default(),
),
Builtins => self.emit_op(Op::LoadBuiltins), Builtins => self.emit_op(Op::LoadBuiltins),
ReplBinding(name) => { ReplBinding(name) => {
self.emit_op(Op::LoadReplBinding); self.emit_op(Op::LoadReplBinding);
+60 -53
View File
@@ -39,12 +39,12 @@ impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>, T, E: std::fmt::Display>
} }
pub trait DowngradeContext<'id: 'ir, 'ir> { pub trait DowngradeContext<'id: 'ir, 'ir> {
fn new_expr(&self, expr: Ir<'ir, GhostRef<'id, 'ir>>) -> IrRef<'id, 'ir>; fn new_expr(&self, expr: Ir<'ir, GhostRoRef<'id, 'ir>>) -> GhostRoIrRef<'id, 'ir>;
fn maybe_thunk(&mut self, ir: IrRef<'id, 'ir>) -> GhostMaybeThunkRef<'id, 'ir>; fn maybe_thunk(&mut self, ir: GhostRoIrRef<'id, 'ir>) -> GhostRoMaybeThunkRef<'id, 'ir>;
fn intern_string(&mut self, sym: impl AsRef<str>) -> StringId; fn intern_string(&mut self, sym: impl AsRef<str>) -> StringId;
fn resolve_sym(&self, id: StringId) -> Symbol<'_>; fn resolve_sym(&self, id: StringId) -> Symbol<'_>;
fn lookup(&self, sym: StringId, span: TextRange) -> Result<GhostMaybeThunkRef<'id, 'ir>>; fn lookup(&self, sym: StringId, span: TextRange) -> Result<GhostRoMaybeThunkRef<'id, 'ir>>;
fn get_current_source(&self) -> Source; fn get_current_source(&self) -> Source;
@@ -53,11 +53,11 @@ pub trait DowngradeContext<'id: 'ir, 'ir> {
F: FnOnce(&mut Self) -> R; F: FnOnce(&mut Self) -> R;
fn with_let_scope<F, R>(&mut self, bindings: &[StringId], f: F) -> Result<R> fn with_let_scope<F, R>(&mut self, bindings: &[StringId], f: F) -> Result<R>
where where
F: FnOnce(&mut Self) -> Result<(Vec<'ir, IrRef<'id, 'ir>>, R)>; F: FnOnce(&mut Self) -> Result<(Vec<'ir, GhostRoMaybeThunkRef<'id, 'ir>>, R)>;
fn with_with_scope<F, R>(&mut self, f: F) -> R fn with_with_scope<F, R>(&mut self, f: F) -> R
where where
F: FnOnce(&mut Self) -> R; F: FnOnce(&mut Self) -> R;
fn with_thunk_scope<F, R>(&mut self, f: F) -> (R, Vec<'ir, (ThunkId, IrRef<'id, 'ir>)>) fn with_thunk_scope<F, R>(&mut self, f: F) -> (R, Vec<'ir, (ThunkId, GhostRoIrRef<'id, 'ir>)>)
where where
F: FnOnce(&mut Self) -> R; F: FnOnce(&mut Self) -> R;
@@ -65,11 +65,11 @@ pub trait DowngradeContext<'id: 'ir, 'ir> {
} }
pub trait Downgrade<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> { pub trait Downgrade<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> {
fn downgrade(self, ctx: &mut Ctx) -> Result<IrRef<'id, 'ir>>; fn downgrade(self, ctx: &mut Ctx) -> Result<GhostRoIrRef<'id, 'ir>>;
} }
impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> for Expr { impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> for Expr {
fn downgrade(self, ctx: &mut Ctx) -> Result<IrRef<'id, 'ir>> { fn downgrade(self, ctx: &mut Ctx) -> Result<GhostRoIrRef<'id, 'ir>> {
use Expr::*; use Expr::*;
match self { match self {
Apply(apply) => apply.downgrade(ctx), Apply(apply) => apply.downgrade(ctx),
@@ -114,7 +114,7 @@ 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::Assert { impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> for ast::Assert {
fn downgrade(self, ctx: &mut Ctx) -> Result<IrRef<'id, 'ir>> { fn downgrade(self, ctx: &mut Ctx) -> Result<GhostRoIrRef<'id, 'ir>> {
let span = self.syntax().text_range(); let span = self.syntax().text_range();
let assertion = self.condition().require(ctx, span)?; let assertion = self.condition().require(ctx, span)?;
let assertion_raw = assertion.to_string(); let assertion_raw = assertion.to_string();
@@ -130,7 +130,7 @@ 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::IfElse { impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> for ast::IfElse {
fn downgrade(self, ctx: &mut Ctx) -> Result<IrRef<'id, 'ir>> { fn downgrade(self, ctx: &mut Ctx) -> Result<GhostRoIrRef<'id, 'ir>> {
let span = self.syntax().text_range(); let span = self.syntax().text_range();
let cond = self.condition().require(ctx, span)?.downgrade(ctx)?; let cond = self.condition().require(ctx, span)?.downgrade(ctx)?;
let consq = self.body().require(ctx, span)?.downgrade(ctx)?; let consq = self.body().require(ctx, span)?.downgrade(ctx)?;
@@ -142,7 +142,7 @@ impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> fo
macro_rules! path { macro_rules! path {
($ty:ident) => { ($ty:ident) => {
impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> for ast::$ty { impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> for ast::$ty {
fn downgrade(self, ctx: &mut Ctx) -> Result<IrRef<'id, 'ir>> { fn downgrade(self, ctx: &mut Ctx) -> Result<GhostRoIrRef<'id, 'ir>> {
downgrade_path(self.parts(), ctx) downgrade_path(self.parts(), ctx)
} }
} }
@@ -152,7 +152,7 @@ path!(PathAbs);
path!(PathRel); path!(PathRel);
path!(PathHome); path!(PathHome);
impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> for ast::PathSearch { impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> for ast::PathSearch {
fn downgrade(self, ctx: &mut Ctx) -> Result<IrRef<'id, 'ir>> { fn downgrade(self, ctx: &mut Ctx) -> Result<GhostRoIrRef<'id, 'ir>> {
let span = self.syntax().text_range(); let span = self.syntax().text_range();
let path = { let path = {
let temp = self.content().require(ctx, span)?; let temp = self.content().require(ctx, span)?;
@@ -180,7 +180,7 @@ 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::Str { impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> for ast::Str {
fn downgrade(self, ctx: &mut Ctx) -> Result<IrRef<'id, 'ir>> { fn downgrade(self, ctx: &mut Ctx) -> Result<GhostRoIrRef<'id, 'ir>> {
let normalized = self.normalized_parts(); let normalized = self.normalized_parts();
let is_single_literal = normalized.len() == 1 let is_single_literal = normalized.len() == 1
&& matches!(normalized.first(), Some(ast::InterpolPart::Literal(_))); && matches!(normalized.first(), Some(ast::InterpolPart::Literal(_)));
@@ -210,7 +210,7 @@ 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<GhostRoIrRef<'id, 'ir>> {
let span = self.syntax().text_range(); let span = self.syntax().text_range();
let 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)?),
@@ -225,7 +225,7 @@ 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::Ident { impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> for ast::Ident {
fn downgrade(self, ctx: &mut Ctx) -> Result<IrRef<'id, 'ir>> { fn downgrade(self, ctx: &mut Ctx) -> Result<GhostRoIrRef<'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.intern_string(text); let sym = ctx.intern_string(text);
@@ -235,7 +235,7 @@ 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::AttrSet { impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> for ast::AttrSet {
fn downgrade(self, ctx: &mut Ctx) -> Result<IrRef<'id, 'ir>> { fn downgrade(self, ctx: &mut Ctx) -> Result<GhostRoIrRef<'id, 'ir>> {
let rec = self.rec_token().is_some(); let rec = self.rec_token().is_some();
if !rec { if !rec {
let attrs = downgrade_attrs(self, ctx)?; let attrs = downgrade_attrs(self, ctx)?;
@@ -251,7 +251,7 @@ 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::List { impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> for ast::List {
fn downgrade(self, ctx: &mut Ctx) -> Result<IrRef<'id, 'ir>> { fn downgrade(self, ctx: &mut Ctx) -> Result<GhostRoIrRef<'id, 'ir>> {
let bump = ctx.bump(); let bump = ctx.bump();
let items = self let items = self
.items() .items()
@@ -265,7 +265,7 @@ 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::BinOp { impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> for ast::BinOp {
fn downgrade(self, ctx: &mut Ctx) -> Result<IrRef<'id, 'ir>> { fn downgrade(self, ctx: &mut Ctx) -> Result<GhostRoIrRef<'id, 'ir>> {
let span = self.syntax().text_range(); let span = self.syntax().text_range();
let lhs = self.lhs().require(ctx, span)?.downgrade(ctx)?; let lhs = self.lhs().require(ctx, span)?.downgrade(ctx)?;
let rhs = self.rhs().require(ctx, span)?.downgrade(ctx)?; let rhs = self.rhs().require(ctx, span)?.downgrade(ctx)?;
@@ -275,7 +275,7 @@ 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::HasAttr { impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> for ast::HasAttr {
fn downgrade(self, ctx: &mut Ctx) -> Result<IrRef<'id, 'ir>> { fn downgrade(self, ctx: &mut Ctx) -> Result<GhostRoIrRef<'id, 'ir>> {
let span = self.syntax().text_range(); let span = self.syntax().text_range();
let lhs = self.expr().require(ctx, span)?.downgrade(ctx)?; let lhs = self.expr().require(ctx, span)?.downgrade(ctx)?;
let rhs = downgrade_attrpath(self.attrpath().require(ctx, span)?, ctx)?; let rhs = downgrade_attrpath(self.attrpath().require(ctx, span)?, ctx)?;
@@ -284,7 +284,7 @@ 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::UnaryOp { impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> for ast::UnaryOp {
fn downgrade(self, ctx: &mut Ctx) -> Result<IrRef<'id, 'ir>> { fn downgrade(self, ctx: &mut Ctx) -> Result<GhostRoIrRef<'id, 'ir>> {
let span = self.syntax().text_range(); let span = self.syntax().text_range();
let rhs = self.expr().require(ctx, span)?.downgrade(ctx)?; let rhs = self.expr().require(ctx, span)?.downgrade(ctx)?;
let kind = self.operator().require(ctx, span)?.into(); let kind = self.operator().require(ctx, span)?.into();
@@ -293,7 +293,7 @@ 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::Select { impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> for ast::Select {
fn downgrade(self, ctx: &mut Ctx) -> Result<IrRef<'id, 'ir>> { fn downgrade(self, ctx: &mut Ctx) -> Result<GhostRoIrRef<'id, 'ir>> {
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)?;
@@ -312,7 +312,7 @@ 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::LegacyLet { impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> for ast::LegacyLet {
fn downgrade(self, ctx: &mut Ctx) -> Result<IrRef<'id, 'ir>> { fn downgrade(self, ctx: &mut Ctx) -> Result<GhostRoIrRef<'id, 'ir>> {
let span = self.syntax().text_range(); let span = self.syntax().text_range();
let entries: Vec<'ir, _> = self.entries().collect_in(ctx.bump()); let entries: Vec<'ir, _> = self.entries().collect_in(ctx.bump());
let attrset_expr = downgrade_let_bindings(entries, ctx, |ctx, binding_keys| { let attrset_expr = downgrade_let_bindings(entries, ctx, |ctx, binding_keys| {
@@ -341,7 +341,7 @@ 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::LetIn { impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> for ast::LetIn {
fn downgrade(self, ctx: &mut Ctx) -> Result<IrRef<'id, 'ir>> { fn downgrade(self, ctx: &mut Ctx) -> Result<GhostRoIrRef<'id, 'ir>> {
let entries: Vec<'ir, _> = self.entries().collect_in(ctx.bump()); let entries: Vec<'ir, _> = self.entries().collect_in(ctx.bump());
let span = self.syntax().text_range(); let span = self.syntax().text_range();
let body_expr = self.body().require(ctx, span)?; let body_expr = self.body().require(ctx, span)?;
@@ -351,7 +351,7 @@ 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::With { impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> for ast::With {
fn downgrade(self, ctx: &mut Ctx) -> Result<IrRef<'id, 'ir>> { fn downgrade(self, ctx: &mut Ctx) -> Result<GhostRoIrRef<'id, 'ir>> {
let span = self.syntax().text_range(); let span = self.syntax().text_range();
let namespace = self.namespace().require(ctx, span)?.downgrade(ctx)?; let namespace = self.namespace().require(ctx, span)?.downgrade(ctx)?;
let namespace = ctx.maybe_thunk(namespace); let namespace = ctx.maybe_thunk(namespace);
@@ -370,14 +370,14 @@ 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::Lambda { impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> for ast::Lambda {
fn downgrade(self, ctx: &mut Ctx) -> Result<IrRef<'id, 'ir>> { fn downgrade(self, ctx: &mut Ctx) -> Result<GhostRoIrRef<'id, 'ir>> {
let span = self.syntax().text_range(); let span = self.syntax().text_range();
let raw_param = self.param().require(ctx, span)?; let raw_param = self.param().require(ctx, span)?;
let body_ast = self.body().require(ctx, span)?; let body_ast = self.body().require(ctx, span)?;
struct Ret<'id, 'ir> { struct Ret<'id, 'ir> {
param: Option<Param<'ir>>, param: Option<Param<'ir>>,
body: IrRef<'id, 'ir>, body: GhostRoIrRef<'id, 'ir>,
} }
let (ret, thunks) = ctx.with_thunk_scope(|ctx| { let (ret, thunks) = ctx.with_thunk_scope(|ctx| {
@@ -434,7 +434,7 @@ 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::Apply { impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> for ast::Apply {
fn downgrade(self, ctx: &mut Ctx) -> Result<IrRef<'id, 'ir>> { fn downgrade(self, ctx: &mut Ctx) -> Result<GhostRoIrRef<'id, 'ir>> {
let span = self.syntax().text_range(); let span = self.syntax().text_range();
let func = self.lambda().require(ctx, span)?.downgrade(ctx)?; let func = self.lambda().require(ctx, span)?.downgrade(ctx)?;
let arg = self.argument().require(ctx, span)?.downgrade(ctx)?; let arg = self.argument().require(ctx, span)?.downgrade(ctx)?;
@@ -836,8 +836,15 @@ 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, (GhostMaybeThunkRef<'id, 'ir>, TextRange)>, stcs: HashMap<'ir, StringId, (GhostRoMaybeThunkRef<'id, 'ir>, TextRange)>,
dyns: Vec<'ir, (IrRef<'id, 'ir>, GhostMaybeThunkRef<'id, 'ir>, TextRange)>, dyns: Vec<
'ir,
(
GhostRoIrRef<'id, 'ir>,
GhostRoMaybeThunkRef<'id, 'ir>,
TextRange,
),
>,
} }
fn downgrade_attrs<'id, 'ir>( fn downgrade_attrs<'id, 'ir>(
@@ -852,7 +859,7 @@ fn downgrade_attrs<'id, 'ir>(
fn downgrade_attr<'id, 'ir>( fn downgrade_attr<'id, 'ir>(
attr: ast::Attr, attr: ast::Attr,
ctx: &mut impl DowngradeContext<'id, 'ir>, ctx: &mut impl DowngradeContext<'id, 'ir>,
) -> Result<Attr<IrRef<'id, 'ir>>> { ) -> Result<Attr<GhostRoIrRef<'id, 'ir>>> {
use ast::Attr::*; use ast::Attr::*;
use ast::InterpolPart::*; use ast::InterpolPart::*;
match attr { match attr {
@@ -913,7 +920,7 @@ fn downgrade_attr<'id, 'ir>(
fn downgrade_attrpath<'id, 'ir>( fn downgrade_attrpath<'id, 'ir>(
attrpath: ast::Attrpath, attrpath: ast::Attrpath,
ctx: &mut impl DowngradeContext<'id, 'ir>, ctx: &mut impl DowngradeContext<'id, 'ir>,
) -> Result<Vec<'ir, Attr<IrRef<'id, 'ir>>>> { ) -> Result<Vec<'ir, Attr<GhostRoIrRef<'id, 'ir>>>> {
let bump = ctx.bump(); let bump = ctx.bump();
attrpath attrpath
.attrs() .attrs()
@@ -922,7 +929,7 @@ fn downgrade_attrpath<'id, 'ir>(
} }
struct PatternBindings<'id, 'ir> { struct PatternBindings<'id, 'ir> {
body: IrRef<'id, 'ir>, body: GhostRoIrRef<'id, 'ir>,
required: Vec<'ir, (StringId, TextRange)>, required: Vec<'ir, (StringId, TextRange)>,
optional: Vec<'ir, (StringId, TextRange)>, optional: Vec<'ir, (StringId, TextRange)>,
} }
@@ -931,7 +938,7 @@ fn downgrade_pattern_bindings<'id, 'ir, Ctx>(
pat_entries: impl Iterator<Item = ast::PatEntry>, pat_entries: impl Iterator<Item = ast::PatEntry>,
alias: Option<StringId>, alias: Option<StringId>,
ctx: &mut Ctx, ctx: &mut Ctx,
body_fn: impl FnOnce(&mut Ctx, &[StringId]) -> Result<IrRef<'id, 'ir>>, body_fn: impl FnOnce(&mut Ctx, &[StringId]) -> Result<GhostRoIrRef<'id, 'ir>>,
) -> Result<PatternBindings<'id, 'ir>> ) -> Result<PatternBindings<'id, 'ir>>
where where
Ctx: DowngradeContext<'id, 'ir>, Ctx: DowngradeContext<'id, 'ir>,
@@ -990,6 +997,7 @@ where
} }
let arg = ctx.new_expr(Ir::Arg { layer: 0 }); let arg = ctx.new_expr(Ir::Arg { layer: 0 });
let arg_thunk = ctx.maybe_thunk(arg);
ctx.with_let_scope(&keys, |ctx| { ctx.with_let_scope(&keys, |ctx| {
let vals = params let vals = params
.into_iter() .into_iter()
@@ -1001,21 +1009,16 @@ where
span, span,
} = param; } = param;
let default = default.map(|default| default.downgrade(ctx)).transpose()?; 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 { let expr = ctx.new_expr(Ir::Select {
expr: arg, expr: arg,
attrpath: Vec::from_iter_in([Attr::Str(sym, sym_span)], bump), attrpath: Vec::from_iter_in([Attr::Str(sym, sym_span)], bump),
default, default,
span, span,
})) });
Ok(ctx.maybe_thunk(expr))
}) })
.chain(alias.into_iter().map(|_| Ok(arg))) .chain(alias.into_iter().map(|_| Ok(arg_thunk)))
.collect_in::<Result<_>>(bump)?; .collect_in::<Result<_>>(bump)?;
let body = body_fn(ctx, &keys)?; let body = body_fn(ctx, &keys)?;
@@ -1035,10 +1038,10 @@ fn downgrade_let_bindings<'id, 'ir, Ctx, F>(
entries: Vec<'ir, ast::Entry>, entries: Vec<'ir, ast::Entry>,
ctx: &mut Ctx, ctx: &mut Ctx,
body_fn: F, body_fn: F,
) -> Result<IrRef<'id, 'ir>> ) -> Result<GhostRoIrRef<'id, 'ir>>
where where
Ctx: DowngradeContext<'id, 'ir>, Ctx: DowngradeContext<'id, 'ir>,
F: FnOnce(&mut Ctx, &[StringId]) -> Result<IrRef<'id, 'ir>>, F: FnOnce(&mut Ctx, &[StringId]) -> Result<GhostRoIrRef<'id, 'ir>>,
{ {
downgrade_rec_attrs_impl::<_, _, false>(entries, ctx, |ctx, binding_keys, _dyns| { downgrade_rec_attrs_impl::<_, _, false>(entries, ctx, |ctx, binding_keys, _dyns| {
body_fn(ctx, binding_keys) body_fn(ctx, binding_keys)
@@ -1048,7 +1051,7 @@ where
fn downgrade_rec_bindings<'id, 'ir, Ctx>( fn downgrade_rec_bindings<'id, 'ir, Ctx>(
entries: Vec<'ir, ast::Entry>, entries: Vec<'ir, ast::Entry>,
ctx: &mut Ctx, ctx: &mut Ctx,
) -> Result<IrRef<'id, 'ir>> ) -> Result<GhostRoIrRef<'id, 'ir>>
where where
Ctx: DowngradeContext<'id, 'ir>, Ctx: DowngradeContext<'id, 'ir>,
{ {
@@ -1069,14 +1072,18 @@ fn downgrade_rec_attrs_impl<'id, 'ir, Ctx, F, const ALLOW_DYN: bool>(
entries: Vec<'ir, ast::Entry>, entries: Vec<'ir, ast::Entry>,
ctx: &mut Ctx, ctx: &mut Ctx,
body_fn: F, body_fn: F,
) -> Result<IrRef<'id, 'ir>> ) -> Result<GhostRoIrRef<'id, 'ir>>
where where
Ctx: DowngradeContext<'id, 'ir>, Ctx: DowngradeContext<'id, 'ir>,
F: FnOnce( F: FnOnce(
&mut Ctx, &mut Ctx,
&[StringId], &[StringId],
&[(IrRef<'id, 'ir>, GhostMaybeThunkRef<'id, 'ir>, TextRange)], &[(
) -> Result<IrRef<'id, 'ir>>, GhostRoIrRef<'id, 'ir>,
GhostRoMaybeThunkRef<'id, 'ir>,
TextRange,
)],
) -> Result<GhostRoIrRef<'id, 'ir>>,
{ {
let mut pending = PendingAttrSet::new_in(ctx.bump()); let mut pending = PendingAttrSet::new_in(ctx.bump());
pending.collect_entries(entries.iter().cloned(), ctx)?; pending.collect_entries(entries.iter().cloned(), ctx)?;
@@ -1091,7 +1098,7 @@ where
let vals = { let vals = {
let mut temp = Vec::with_capacity_in(keys.len(), ctx.bump()); let mut temp = Vec::with_capacity_in(keys.len(), ctx.bump());
for sym in &keys { for sym in &keys {
temp.push(ctx.new_expr(Ir::MaybeThunk(finalized.stcs.get(sym).expect("WTF").0))); temp.push(finalized.stcs.get(sym).expect("WTF").0);
} }
temp temp
}; };
@@ -1102,7 +1109,7 @@ where
fn collect_inherit_lookups<'id: 'ir, '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, (GhostMaybeThunkRef<'id, 'ir>, TextRange)>> { ) -> Result<HashMap<'ir, StringId, (GhostRoMaybeThunkRef<'id, 'ir>, 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
@@ -1142,7 +1149,7 @@ 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, (GhostMaybeThunkRef<'id, 'ir>, TextRange)>, inherit_lookups: &HashMap<StringId, (GhostRoMaybeThunkRef<'id, 'ir>, 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());
@@ -1170,9 +1177,9 @@ 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, (GhostMaybeThunkRef<'id, 'ir>, TextRange)>, inherit_lookups: &HashMap<StringId, (GhostRoMaybeThunkRef<'id, 'ir>, TextRange)>,
ctx: &mut Ctx, ctx: &mut Ctx,
) -> Result<IrRef<'id, 'ir>> { ) -> Result<GhostRoIrRef<'id, 'ir>> {
match value { match value {
PendingValue::Expr(expr) => expr.downgrade(ctx), PendingValue::Expr(expr) => expr.downgrade(ctx),
PendingValue::InheritFrom(from_expr, sym, span) => { PendingValue::InheritFrom(from_expr, sym, span) => {
@@ -1212,7 +1219,7 @@ fn finalize_pending_value<'id, 'ir, Ctx: DowngradeContext<'id, 'ir>, const ALLOW
fn downgrade_path<'id, 'ir>( fn downgrade_path<'id, 'ir>(
parts: impl IntoIterator<Item = ast::InterpolPart<ast::PathContent>>, parts: impl IntoIterator<Item = ast::InterpolPart<ast::PathContent>>,
ctx: &mut impl DowngradeContext<'id, 'ir>, ctx: &mut impl DowngradeContext<'id, 'ir>,
) -> Result<IrRef<'id, 'ir>> { ) -> Result<GhostRoIrRef<'id, 'ir>> {
let bump = ctx.bump(); let bump = ctx.bump();
let parts = parts let parts = parts
.into_iter() .into_iter()
+54 -16
View File
@@ -14,14 +14,16 @@ 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>;
pub type IrRef<'id, 'ir> = <GhostRef<'id, 'ir> as RefExt<'ir>>::IrRef; pub type GhostIrRef<'id, 'ir> = <GhostRef<'id, 'ir> as RefExt<'ir>>::IrRef;
pub type GhostRoIrRef<'id, 'ir> = <GhostRoRef<'id, 'ir> as RefExt<'ir>>::IrRef;
pub type RawIrRef<'ir> = <RawRef<'ir> as RefExt<'ir>>::IrRef; pub type RawIrRef<'ir> = <RawRef<'ir> as RefExt<'ir>>::IrRef;
pub type GhostMaybeThunkRef<'id, 'ir> = <GhostRef<'id, 'ir> as RefExt<'ir>>::MaybeThunkRef; pub type GhostMaybeThunkRef<'id, 'ir> = <GhostRef<'id, 'ir> as RefExt<'ir>>::MaybeThunkRef;
pub type GhostRoMaybeThunkRef<'id, 'ir> = <GhostRoRef<'id, 'ir> as RefExt<'ir>>::MaybeThunkRef;
impl<'id, 'ir> Ir<'ir, GhostRef<'id, 'ir>> { impl<'id, 'ir> Ir<'ir, GhostRoRef<'id, 'ir>> {
/// Freeze a mutable IR reference into a read-only one, consuming the /// Freeze a mutable IR reference into a read-only one, consuming the
/// `GhostToken` to prevent any further mutation. /// `GhostToken` to prevent any further mutation.
pub fn freeze(this: IrRef<'id, 'ir>, _: GhostToken<'id>) -> RawIrRef<'ir> { pub fn freeze(this: GhostRoIrRef<'id, 'ir>, _: GhostToken<'id>) -> RawIrRef<'ir> {
// SAFETY: The transmute is sound because: // SAFETY: The transmute is sound because:
// - `GhostCell<'id, T>` is `#[repr(transparent)]` over `T`, so // - `GhostCell<'id, T>` is `#[repr(transparent)]` over `T`, so
// `&'ir GhostCell<'id, T>` and `&'ir T` have identical layout. // `&'ir GhostCell<'id, T>` and `&'ir T` have identical layout.
@@ -39,7 +41,39 @@ impl<'id, 'ir> Ir<'ir, GhostRef<'id, 'ir>> {
// Consuming the `GhostToken` guarantees no `borrow_mut` calls can // Consuming the `GhostToken` guarantees no `borrow_mut` calls can
// occur afterwards, so the shared `&Ir` references reachable from a // occur afterwards, so the shared `&Ir` references reachable from a
// `RawIrRef<'ir>` can never alias with mutable references. // `RawIrRef<'ir>` can never alias with mutable references.
unsafe { std::mem::transmute::<IrRef<'id, 'ir>, RawIrRef<'ir>>(this) } unsafe { std::mem::transmute::<GhostRoIrRef<'id, 'ir>, RawIrRef<'ir>>(this) }
}
}
#[repr(transparent)]
pub struct GhostRoCell<'id, T: ?Sized>(GhostCell<'id, T>);
impl<'id, T> From<GhostCell<'id, T>> for GhostRoCell<'id, T> {
fn from(value: GhostCell<'id, T>) -> Self {
Self(value)
}
}
impl<'id, T: ?Sized> From<&GhostCell<'id, T>> for &GhostRoCell<'id, T> {
fn from(value: &GhostCell<'id, T>) -> Self {
// SAFETY: `GhostRoCell` is `#[repr(transparent)]` over `GhostCell`
// TODO: document mutability
unsafe { std::mem::transmute(value) }
}
}
impl<'id, T: ?Sized> From<&T> for &GhostRoCell<'id, T> {
fn from(value: &T) -> Self {
// SAFETY: `GhostRoCell` is `#[repr(transparent)]` over `GhostCell`,
// which is `#[repr(transparent)]` over `T`
// TODO: document mutability
unsafe { std::mem::transmute(value) }
}
}
impl<'id, T: ?Sized> GhostRoCell<'id, T> {
pub fn borrow<'a>(&'a self, token: &'a GhostToken<'id>) -> &'a T {
self.0.borrow(token)
} }
} }
@@ -55,13 +89,13 @@ pub enum MaybeThunk {
Thunk(ThunkId), Thunk(ThunkId),
Arg { layer: u8 }, Arg { layer: u8 },
Builtin(BuiltinId), Builtin(BuiltinId),
BuiltinConst(StringId),
Builtins, Builtins,
ReplBinding(StringId), ReplBinding(StringId),
ScopedImportBinding(StringId), ScopedImportBinding(StringId),
WithLookup(StringId), WithLookup(StringId),
} }
pub trait Ref<'ir> { pub trait Ref<'ir> {
type Ref<T> type Ref<T>
where where
@@ -80,11 +114,15 @@ impl<'ir, T: Ref<'ir> + 'ir> RefExt<'ir> for T {
} }
pub struct GhostRef<'id, 'ir>(PhantomData<&'ir GhostCell<'id, ()>>); pub struct GhostRef<'id, 'ir>(PhantomData<&'ir GhostCell<'id, ()>>);
pub struct GhostRoRef<'id, 'ir>(PhantomData<&'ir GhostRoCell<'id, ()>>);
pub struct RawRef<'ir>(PhantomData<&'ir ()>); pub struct RawRef<'ir>(PhantomData<&'ir ()>);
impl<'id, 'ir> Ref<'ir> for GhostRef<'id, 'ir> { impl<'id, 'ir> Ref<'ir> for GhostRef<'id, 'ir> {
type Ref<T: 'ir> = &'ir GhostCell<'id, T>; type Ref<T: 'ir> = &'ir GhostCell<'id, T>;
} }
impl<'id, 'ir> Ref<'ir> for GhostRoRef<'id, 'ir> {
type Ref<T: 'ir> = &'ir GhostRoCell<'id, T>;
}
impl<'ir> Ref<'ir> for RawRef<'ir> { impl<'ir> Ref<'ir> for RawRef<'ir> {
type Ref<T: 'ir> = &'ir T; type Ref<T: 'ir> = &'ir T;
} }
@@ -285,38 +323,38 @@ pub struct Param<'ir> {
pub fn new_global_env( pub fn new_global_env(
strings: &mut DefaultStringInterner, strings: &mut DefaultStringInterner,
) -> hashbrown::HashMap<StringId, Ir<'static, RawRef<'static>>> { ) -> hashbrown::HashMap<StringId, MaybeThunk> {
let mut global_env = hashbrown::HashMap::new(); let mut global_env = hashbrown::HashMap::new();
let builtins_sym = StringId(strings.get_or_intern("builtins")); let builtins_sym = StringId(strings.get_or_intern("builtins"));
global_env.insert(builtins_sym, Ir::Builtins); global_env.insert(builtins_sym, MaybeThunk::Builtins);
for (idx, &(name, _)) in BUILTINS.iter().enumerate() { for (idx, &(name, _)) in BUILTINS.iter().enumerate() {
let id = BuiltinId::try_from_primitive(idx as u8).expect("infallible"); let id = BuiltinId::try_from_primitive(idx as u8).expect("infallible");
let name = StringId(strings.get_or_intern(name)); let name = StringId(strings.get_or_intern(name));
global_env.insert(name, Ir::Builtin(id)); global_env.insert(name, MaybeThunk::Builtin(id));
} }
let consts = [ let consts = [
( (
"__currentSystem", "__currentSystem",
Ir::BuiltinConst(StringId(strings.get_or_intern("currentSystem"))), MaybeThunk::BuiltinConst(StringId(strings.get_or_intern("currentSystem"))),
), ),
("__langVersion", Ir::Int(6)), ("__langVersion", MaybeThunk::Int(6)),
( (
"__nixVersion", "__nixVersion",
Ir::BuiltinConst(StringId(strings.get_or_intern("nixVersion"))), MaybeThunk::BuiltinConst(StringId(strings.get_or_intern("nixVersion"))),
), ),
( (
"__storeDir", "__storeDir",
Ir::BuiltinConst(StringId(strings.get_or_intern("storeDir"))), MaybeThunk::BuiltinConst(StringId(strings.get_or_intern("storeDir"))),
), ),
( (
"__nixPath", "__nixPath",
Ir::BuiltinConst(StringId(strings.get_or_intern("nixPath"))), MaybeThunk::BuiltinConst(StringId(strings.get_or_intern("nixPath"))),
), ),
("null", Ir::Null), ("null", MaybeThunk::Null),
("true", Ir::Bool(true)), ("true", MaybeThunk::Bool(true)),
("false", Ir::Bool(false)), ("false", MaybeThunk::Bool(false)),
]; ];
for (name, ir) in consts { for (name, ir) in consts {
+4
View File
@@ -112,6 +112,10 @@ impl<'a> BytecodeReader<'a> {
let idx = self.read_u32(); let idx = self.read_u32();
OperandData::Local { layer, idx } OperandData::Local { layer, idx }
} }
OperandType::BuiltinConst => {
let id = self.read_string_id();
OperandData::BuiltinConst(id)
}
OperandType::Builtins => OperandData::Builtins, OperandType::Builtins => OperandData::Builtins,
OperandType::ReplBinding => { OperandType::ReplBinding => {
let id = self.read_string_id(); let id = self.read_string_id();
+8
View File
@@ -229,6 +229,7 @@ pub(crate) enum OperandData {
Const(StaticValue), Const(StaticValue),
BigInt(i64), BigInt(i64),
Local { layer: u8, idx: u32 }, Local { layer: u8, idx: u32 },
BuiltinConst(StringId),
Builtins, Builtins,
ReplBinding(StringId), ReplBinding(StringId),
ScopedImportBinding(StringId), ScopedImportBinding(StringId),
@@ -249,6 +250,13 @@ impl OperandData {
} }
cur.borrow().locals[idx as usize] cur.borrow().locals[idx as usize]
} }
#[allow(clippy::unwrap_used)]
BuiltinConst(id) => root
.builtins
.as_gc::<AttrSet>()
.unwrap()
.lookup(id)
.unwrap(),
Builtins => root.builtins, Builtins => root.builtins,
ReplBinding(_id) => todo!(), ReplBinding(_id) => todo!(),
ScopedImportBinding(_id) => todo!(), ScopedImportBinding(_id) => todo!(),
+60 -42
View File
@@ -8,7 +8,10 @@ use fix_codegen::{BytecodeContext, InstructionPtr, Op};
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::{GhostMaybeThunkRef, GhostRef, Ir, IrRef, MaybeThunk, RawIrRef, RawRef, ThunkId}; use fix_ir::{
GhostMaybeThunkRef, GhostRoIrRef, GhostRoMaybeThunkRef, GhostRoRef, Ir, MaybeThunk, RawIrRef,
ThunkId,
};
use fix_vm::{ForceMode, StaticValue, Vm, VmCode, VmContext, VmRuntimeCtx}; use fix_vm::{ForceMode, StaticValue, Vm, VmCode, VmContext, VmRuntimeCtx};
use ghost_cell::{GhostCell, GhostToken}; use ghost_cell::{GhostCell, GhostToken};
use hashbrown::{HashMap, HashSet}; use hashbrown::{HashMap, HashSet};
@@ -30,7 +33,7 @@ pub struct CodeState {
pub sources: Vec<Source>, pub sources: Vec<Source>,
pub spans: Vec<(usize, rnix::TextRange)>, pub spans: Vec<(usize, rnix::TextRange)>,
pub thunk_count: usize, pub thunk_count: usize,
pub global_env: HashMap<StringId, Ir<'static, RawRef<'static>>>, pub global_env: HashMap<StringId, MaybeThunk>,
} }
pub struct Evaluator { pub struct Evaluator {
@@ -323,7 +326,7 @@ impl<'ctx, 'id, 'ir, R: VmRuntimeCtx> DowngradeCtx<'ctx, 'id, 'ir, R> {
bump: &'ir Bump, bump: &'ir Bump,
token: GhostToken<'id>, token: GhostToken<'id>,
runtime: &'ctx mut R, runtime: &'ctx mut R,
global: &'ctx HashMap<StringId, Ir<'static, RawRef<'static>>>, global: &'ctx HashMap<StringId, MaybeThunk>,
extra_scope: Option<Scope<'ctx, 'id, 'ir>>, extra_scope: Option<Scope<'ctx, 'id, 'ir>>,
thunk_count: &'ctx mut usize, thunk_count: &'ctx mut usize,
source: Source, source: Source,
@@ -347,11 +350,11 @@ impl<'ctx, 'id, 'ir, R: VmRuntimeCtx> DowngradeCtx<'ctx, 'id, 'ir, R> {
impl<'ctx: 'ir, 'id, 'ir, R: VmRuntimeCtx> DowngradeContext<'id, 'ir> impl<'ctx: 'ir, 'id, 'ir, R: VmRuntimeCtx> DowngradeContext<'id, 'ir>
for DowngradeCtx<'ctx, 'id, 'ir, R> for DowngradeCtx<'ctx, 'id, 'ir, R>
{ {
fn new_expr(&self, expr: Ir<'ir, GhostRef<'id, 'ir>>) -> IrRef<'id, 'ir> { fn new_expr(&self, expr: Ir<'ir, GhostRoRef<'id, 'ir>>) -> GhostRoIrRef<'id, 'ir> {
self.bump.alloc(GhostCell::new(expr)) self.bump.alloc(GhostCell::new(expr).into())
} }
fn maybe_thunk(&mut self, ir: IrRef<'id, 'ir>) -> GhostMaybeThunkRef<'id, 'ir> { fn maybe_thunk(&mut self, ir: GhostRoIrRef<'id, 'ir>) -> GhostRoMaybeThunkRef<'id, 'ir> {
use MaybeThunk::*; use MaybeThunk::*;
let expr = (|| { let expr = (|| {
let expr = match *ir.borrow(&self.token) { let expr = match *ir.borrow(&self.token) {
@@ -366,7 +369,7 @@ impl<'ctx: 'ir, 'id, 'ir, R: VmRuntimeCtx> DowngradeContext<'id, 'ir>
Ir::MaybeThunk(thunk) => return Some(thunk), Ir::MaybeThunk(thunk) => return Some(thunk),
_ => return None, _ => return None,
}; };
Some(self.bump.alloc(GhostCell::new(expr))) Some(self.bump.alloc(GhostCell::new(expr).into()))
})(); })();
if let Some(thunk) = expr { if let Some(thunk) = expr {
return thunk; return thunk;
@@ -376,8 +379,8 @@ impl<'ctx: 'ir, 'id, 'ir, R: VmRuntimeCtx> DowngradeContext<'id, 'ir>
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.bump.alloc(GhostCell::new(Thunk(id))) self.bump.alloc(GhostCell::new(Thunk(id)).into())
} }
fn intern_string(&mut self, sym: impl AsRef<str>) -> StringId { fn intern_string(&mut self, sym: impl AsRef<str>) -> StringId {
@@ -388,39 +391,35 @@ impl<'ctx: 'ir, 'id, 'ir, R: VmRuntimeCtx> DowngradeContext<'id, 'ir>
self.runtime.resolve_string(id).into() self.runtime.resolve_string(id).into()
} }
fn lookup(&self, sym: StringId, span: rnix::TextRange) -> Result<GhostMaybeThunkRef<'id, 'ir>> { fn lookup(
&self,
sym: StringId,
span: rnix::TextRange,
) -> Result<GhostRoMaybeThunkRef<'id, 'ir>> {
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 val = match expr { return Ok(expr.into());
Ir::Builtins => Builtins,
Ir::Builtin(s) => Builtin(*s),
Ir::Bool(b) => Bool(*b),
Ir::Null => Null,
_ => unreachable!("globals should only contain leaf IR nodes"),
};
return Ok(self.bump.alloc(GhostCell::new(val)));
} }
} }
&Scope::Repl(repl_bindings) => { &Scope::Repl(repl_bindings) => {
if repl_bindings.contains(&sym) { if repl_bindings.contains(&sym) {
return Ok(self return Ok(self
.bump .bump
.alloc(GhostCell::new(MaybeThunk::ReplBinding(sym)))); .alloc(GhostCell::new(MaybeThunk::ReplBinding(sym)).into()));
} }
} }
Scope::ScopedImport(scoped_bindings) => { Scope::ScopedImport(scoped_bindings) => {
if scoped_bindings.contains(&sym) { if scoped_bindings.contains(&sym) {
return Ok(self return Ok(self
.bump .bump
.alloc(GhostCell::new(MaybeThunk::ScopedImportBinding(sym)))); .alloc(GhostCell::new(MaybeThunk::ScopedImportBinding(sym)).into()));
} }
} }
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(expr); return Ok(expr.into());
} }
} }
&Scope::Param { &Scope::Param {
@@ -431,14 +430,18 @@ impl<'ctx: 'ir, 'id, 'ir, R: VmRuntimeCtx> DowngradeContext<'id, 'ir>
let layers: u8 = let layers: u8 =
self.thunk_scopes.len().try_into().expect("scope too deep!"); self.thunk_scopes.len().try_into().expect("scope too deep!");
let layer = layers - abs_layer; let layer = layers - abs_layer;
return Ok(self.bump.alloc(GhostCell::new(MaybeThunk::Arg { layer }))); return Ok(self
.bump
.alloc(GhostCell::new(MaybeThunk::Arg { layer }).into()));
} }
} }
} }
} }
if self.with_scope_count > 0 { if self.with_scope_count > 0 {
Ok(self.bump.alloc(GhostCell::new(MaybeThunk::WithLookup(sym)))) Ok(self
.bump
.alloc(GhostCell::new(MaybeThunk::WithLookup(sym)).into()))
} else { } else {
Err(Error::downgrade_error( Err(Error::downgrade_error(
format!("'{}' not found", self.resolve_sym(sym)), format!("'{}' not found", self.resolve_sym(sym)),
@@ -454,28 +457,40 @@ impl<'ctx: 'ir, 'id, 'ir, R: VmRuntimeCtx> DowngradeContext<'id, 'ir>
fn with_let_scope<F, Ret>(&mut self, keys: &[StringId], f: F) -> Result<Ret> fn with_let_scope<F, Ret>(&mut self, keys: &[StringId], f: F) -> Result<Ret>
where where
F: FnOnce(&mut Self) -> Result<(bumpalo::collections::Vec<'ir, IrRef<'id, 'ir>>, Ret)>, F: FnOnce(
&mut Self,
) -> Result<(
bumpalo::collections::Vec<'ir, GhostRoMaybeThunkRef<'id, 'ir>>,
Ret,
)>,
{ {
let base = *self.thunk_count; let base = *self.thunk_count;
*self.thunk_count = self *self.thunk_count = self
.thunk_count .thunk_count
.checked_add(keys.len()) .checked_add(keys.len())
.expect("thunk id overflow"); .expect("thunk id overflow");
let scope = { let handles = (base..base + keys.len())
let mut scope = HashMap::new(); .map(|id| {
for (offset, &key) in keys.iter().enumerate() { &*self
scope.insert(key, &*self.bump.alloc(GhostCell::new(MaybeThunk::Thunk(ThunkId(base + offset))))); .bump
} .alloc(GhostCell::new(MaybeThunk::Thunk(ThunkId(id))))
scope })
}; .collect::<Vec<_>>();
let scope = keys.iter().copied().zip(handles.iter().copied()).collect();
self.scopes.push(Scope::Let(scope)); self.scopes.push(Scope::Let(scope));
let (vals, ret) = { let (vals, ret) = { f(self)? };
let mut guard = ScopeGuard { ctx: self }; self.scopes.pop();
f(guard.as_ctx())?
};
assert_eq!(keys.len(), vals.len()); assert_eq!(keys.len(), vals.len());
let scope = self.thunk_scopes.last_mut().expect("no active thunk scope"); let scope = self.thunk_scopes.last_mut().expect("no active thunk scope");
scope.extend_bindings((base..base + keys.len()).map(ThunkId).zip(vals)); for (i, (val, handle)) in vals.into_iter().zip(handles).enumerate() {
let thunk = *val.borrow(&self.token);
*handle.borrow_mut(&mut self.token) = thunk;
let id = ThunkId(base + i);
let ir_ref = self
.bump
.alloc(GhostCell::new(Ir::MaybeThunk(handle.into())).into());
scope.add_binding(id, ir_ref);
}
Ok(ret) Ok(ret)
} }
@@ -506,7 +521,7 @@ impl<'ctx: 'ir, 'id, 'ir, R: VmRuntimeCtx> DowngradeContext<'id, 'ir>
f: F, f: F,
) -> ( ) -> (
Ret, Ret,
bumpalo::collections::Vec<'ir, (ThunkId, IrRef<'id, 'ir>)>, bumpalo::collections::Vec<'ir, (ThunkId, GhostRoIrRef<'id, 'ir>)>,
) )
where where
F: FnOnce(&mut Self) -> Ret, F: FnOnce(&mut Self) -> Ret,
@@ -546,7 +561,7 @@ impl<'id, 'ir, 'ctx: 'ir, R: VmRuntimeCtx> DowngradeCtx<'ctx, 'id, 'ir, R> {
} }
struct ThunkScope<'id, 'ir> { struct ThunkScope<'id, 'ir> {
bindings: bumpalo::collections::Vec<'ir, (ThunkId, IrRef<'id, 'ir>)>, bindings: bumpalo::collections::Vec<'ir, (ThunkId, GhostRoIrRef<'id, 'ir>)>,
} }
impl<'id, 'ir> ThunkScope<'id, 'ir> { impl<'id, 'ir> ThunkScope<'id, 'ir> {
@@ -556,17 +571,20 @@ impl<'id, 'ir> ThunkScope<'id, 'ir> {
} }
} }
fn add_binding(&mut self, id: ThunkId, ir: IrRef<'id, 'ir>, _token: &GhostToken<'id>) { fn add_binding(&mut self, id: ThunkId, ir: GhostRoIrRef<'id, 'ir>) {
self.bindings.push((id, ir)); self.bindings.push((id, ir));
} }
fn extend_bindings(&mut self, iter: impl IntoIterator<Item = (ThunkId, IrRef<'id, 'ir>)>) { fn extend_bindings(
&mut self,
iter: impl IntoIterator<Item = (ThunkId, GhostRoIrRef<'id, 'ir>)>,
) {
self.bindings.extend(iter); self.bindings.extend(iter);
} }
} }
enum Scope<'ctx, 'id, 'ir> { enum Scope<'ctx, 'id, 'ir> {
Global(&'ctx HashMap<StringId, Ir<'static, RawRef<'static>>>), Global(&'ctx HashMap<StringId, MaybeThunk>),
Repl(&'ctx HashSet<StringId>), Repl(&'ctx HashSet<StringId>),
ScopedImport(HashSet<StringId>), ScopedImport(HashSet<StringId>),
Let(HashMap<StringId, GhostMaybeThunkRef<'id, 'ir>>), Let(HashMap<StringId, GhostMaybeThunkRef<'id, 'ir>>),