refactor: use GAT in enum Ir
This commit is contained in:
@@ -79,19 +79,23 @@ impl<'a, Ctx: DisassemblerContext> Disassembler<'a, Ctx> {
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn read_operand_data(&mut self) {
|
fn read_operand_data(&mut self) {
|
||||||
|
use OperandType::*;
|
||||||
let tag = self.read_u8();
|
let tag = self.read_u8();
|
||||||
let ty = OperandType::try_from_primitive(tag).expect("invalid operand type");
|
let ty = OperandType::try_from_primitive(tag).expect("invalid operand type");
|
||||||
match ty {
|
match ty {
|
||||||
OperandType::Const => {
|
Const => {
|
||||||
self.read_u32();
|
self.read_u32();
|
||||||
}
|
}
|
||||||
OperandType::Local => {
|
BigInt => {
|
||||||
|
self.read_i64();
|
||||||
|
}
|
||||||
|
Local => {
|
||||||
self.read_u8();
|
self.read_u8();
|
||||||
self.read_u32();
|
self.read_u32();
|
||||||
}
|
}
|
||||||
OperandType::Builtins => {}
|
Builtins => {}
|
||||||
OperandType::BigInt => {
|
ReplBinding | ScopedImportBinding | WithLookup => {
|
||||||
self.read_i64();
|
self.read_u32();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+109
-36
@@ -1,6 +1,4 @@
|
|||||||
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, MaybeThunk, Param, RawIrRef, ThunkId, UnOpKind};
|
use fix_ir::{Attr, BinOpKind, Ir, MaybeThunk, Param, RawIrRef, ThunkId, UnOpKind};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
@@ -116,9 +114,12 @@ struct BytecodeEmitter<'a, Ctx: BytecodeContext> {
|
|||||||
#[derive(Debug, Clone, Copy, TryFromPrimitive)]
|
#[derive(Debug, Clone, Copy, TryFromPrimitive)]
|
||||||
pub enum OperandType {
|
pub enum OperandType {
|
||||||
Const,
|
Const,
|
||||||
|
BigInt,
|
||||||
Local,
|
Local,
|
||||||
Builtins,
|
Builtins,
|
||||||
BigInt,
|
ReplBinding,
|
||||||
|
ScopedImportBinding,
|
||||||
|
WithLookup,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Const {
|
pub enum Const {
|
||||||
@@ -126,6 +127,7 @@ pub enum Const {
|
|||||||
Float(f64),
|
Float(f64),
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
String(StringId),
|
String(StringId),
|
||||||
|
Path(StringId),
|
||||||
PrimOp {
|
PrimOp {
|
||||||
id: BuiltinId,
|
id: BuiltinId,
|
||||||
arity: u8,
|
arity: u8,
|
||||||
@@ -143,9 +145,12 @@ pub enum AttrKeyType {
|
|||||||
|
|
||||||
pub enum InlineOperand {
|
pub enum InlineOperand {
|
||||||
Const(Const),
|
Const(Const),
|
||||||
|
BigInt(i64),
|
||||||
Local { layer: u8, local: u32 },
|
Local { layer: u8, local: u32 },
|
||||||
Builtins,
|
Builtins,
|
||||||
BigInt(i64),
|
ReplBinding(StringId),
|
||||||
|
ScopedImportBinding(StringId),
|
||||||
|
WithLookup(StringId),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile_bytecode(ir: RawIrRef<'_>, ctx: &mut impl BytecodeContext) -> InstructionPtr {
|
pub fn compile_bytecode(ir: RawIrRef<'_>, ctx: &mut impl BytecodeContext) -> InstructionPtr {
|
||||||
@@ -164,9 +169,9 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn inline_maybe_thunk(&self, val: MaybeThunk) -> InlineOperand {
|
fn inline_maybe_thunk(&self, val: &MaybeThunk) -> InlineOperand {
|
||||||
use MaybeThunk::*;
|
use MaybeThunk::*;
|
||||||
match val {
|
match *val {
|
||||||
Int(x) => {
|
Int(x) => {
|
||||||
if let Ok(x) = x.try_into() {
|
if let Ok(x) = x.try_into() {
|
||||||
InlineOperand::Const(Const::Smi(x))
|
InlineOperand::Const(Const::Smi(x))
|
||||||
@@ -178,37 +183,55 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
Bool(b) => InlineOperand::Const(Const::Bool(b)),
|
Bool(b) => InlineOperand::Const(Const::Bool(b)),
|
||||||
Null => InlineOperand::Const(Const::Null),
|
Null => InlineOperand::Const(Const::Null),
|
||||||
Str(id) => InlineOperand::Const(Const::String(id)),
|
Str(id) => InlineOperand::Const(Const::String(id)),
|
||||||
|
Path(id) => InlineOperand::Const(Const::String(id)),
|
||||||
Thunk(id) => {
|
Thunk(id) => {
|
||||||
let (layer, local) = self.resolve_thunk(id);
|
let (layer, local) = self.resolve_thunk(id);
|
||||||
InlineOperand::Local { layer, local }
|
InlineOperand::Local { layer, local }
|
||||||
}
|
}
|
||||||
Arg { layer } => InlineOperand::Local {
|
Arg { layer } => InlineOperand::Local { layer, local: 0 },
|
||||||
layer,
|
Builtin(id) => {
|
||||||
local: 0,
|
let (_, arity) = BUILTINS[id as usize];
|
||||||
|
InlineOperand::Const(Const::PrimOp { id, arity, dispatch_ip: id.entry_phase().ip() })
|
||||||
},
|
},
|
||||||
_ => todo!(),
|
Builtins => InlineOperand::Builtins,
|
||||||
|
ReplBinding(id) => InlineOperand::ReplBinding(id),
|
||||||
|
ScopedImportBinding(id) => InlineOperand::ScopedImportBinding(id),
|
||||||
|
WithLookup(id) => InlineOperand::WithLookup(id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_maybe_thunk(&mut self, val: MaybeThunk) {
|
fn emit_maybe_thunk(&mut self, val: &MaybeThunk) {
|
||||||
|
use InlineOperand::*;
|
||||||
let operand = self.inline_maybe_thunk(val);
|
let operand = self.inline_maybe_thunk(val);
|
||||||
match operand {
|
match operand {
|
||||||
InlineOperand::Const(val) => {
|
Const(val) => {
|
||||||
let idx = self.ctx.add_constant(val);
|
let idx = self.ctx.add_constant(val);
|
||||||
self.emit_u8(OperandType::Const as u8);
|
self.emit_u8(OperandType::Const as u8);
|
||||||
self.emit_u32(idx);
|
self.emit_u32(idx);
|
||||||
}
|
}
|
||||||
InlineOperand::Local { layer, local } => {
|
BigInt(val) => {
|
||||||
|
self.emit_u8(OperandType::BigInt as u8);
|
||||||
|
self.emit_i64(val);
|
||||||
|
}
|
||||||
|
Local { layer, local } => {
|
||||||
self.emit_u8(OperandType::Local as u8);
|
self.emit_u8(OperandType::Local as u8);
|
||||||
self.emit_u8(layer);
|
self.emit_u8(layer);
|
||||||
self.emit_u32(local);
|
self.emit_u32(local);
|
||||||
}
|
}
|
||||||
InlineOperand::Builtins => {
|
Builtins => {
|
||||||
self.emit_u8(OperandType::Builtins as u8);
|
self.emit_u8(OperandType::Builtins as u8);
|
||||||
}
|
}
|
||||||
InlineOperand::BigInt(val) => {
|
ReplBinding(id) => {
|
||||||
self.emit_u8(OperandType::BigInt as u8);
|
self.emit_u8(OperandType::ReplBinding as u8);
|
||||||
self.emit_i64(val);
|
self.emit_str_id(id);
|
||||||
|
}
|
||||||
|
ScopedImportBinding(id) => {
|
||||||
|
self.emit_u8(OperandType::ScopedImportBinding as u8);
|
||||||
|
self.emit_str_id(id);
|
||||||
|
}
|
||||||
|
WithLookup(id) => {
|
||||||
|
self.emit_u8(OperandType::WithLookup as u8);
|
||||||
|
self.emit_str_id(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -315,7 +338,7 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn count_with_thunks(&self, ir: RawIrRef<'_>) -> usize {
|
fn count_with_thunks(&self, ir: RawIrRef<'_>) -> usize {
|
||||||
match ir.deref() {
|
match ir {
|
||||||
Ir::With { thunks, body, .. } => thunks.len() + self.count_with_thunks(*body),
|
Ir::With { thunks, body, .. } => thunks.len() + self.count_with_thunks(*body),
|
||||||
Ir::TopLevel { thunks, body } => thunks.len() + self.count_with_thunks(*body),
|
Ir::TopLevel { thunks, body } => thunks.len() + self.count_with_thunks(*body),
|
||||||
Ir::If { cond, consq, alter } => {
|
Ir::If { cond, consq, alter } => {
|
||||||
@@ -361,7 +384,7 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
ir: RawIrRef<'ir>,
|
ir: RawIrRef<'ir>,
|
||||||
out: &mut Vec<(ThunkId, RawIrRef<'ir>)>,
|
out: &mut Vec<(ThunkId, RawIrRef<'ir>)>,
|
||||||
) {
|
) {
|
||||||
match ir.deref() {
|
match ir {
|
||||||
Ir::With { thunks, body, .. } => {
|
Ir::With { thunks, body, .. } => {
|
||||||
for &(id, inner) in thunks.iter() {
|
for &(id, inner) in thunks.iter() {
|
||||||
out.push((id, inner));
|
out.push((id, inner));
|
||||||
@@ -422,7 +445,7 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn emit_toplevel(&mut self, ir: RawIrRef<'_>) {
|
fn emit_toplevel(&mut self, ir: RawIrRef<'_>) {
|
||||||
match ir.deref() {
|
match ir {
|
||||||
&Ir::TopLevel { body, ref thunks } => {
|
&Ir::TopLevel { body, ref thunks } => {
|
||||||
let with_thunk_count = self.count_with_thunks(body);
|
let with_thunk_count = self.count_with_thunks(body);
|
||||||
let total_slots = thunks.len() + with_thunk_count;
|
let total_slots = thunks.len() + with_thunk_count;
|
||||||
@@ -468,11 +491,11 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn emit_expr(&mut self, ir: RawIrRef<'_>) {
|
fn emit_expr(&mut self, ir: RawIrRef<'_>) {
|
||||||
match ir.deref() {
|
match ir {
|
||||||
&Ir::Int(x) => {
|
&Ir::Int(x) => {
|
||||||
if x <= i32::MAX as i64 {
|
if let Ok(x) = x.try_into() {
|
||||||
self.emit_op(Op::PushSmi);
|
self.emit_op(Op::PushSmi);
|
||||||
self.emit_i32(x as i32);
|
self.emit_i32(x);
|
||||||
} else {
|
} else {
|
||||||
self.emit_op(Op::PushBigInt);
|
self.emit_op(Op::PushBigInt);
|
||||||
self.emit_i64(x);
|
self.emit_i64(x);
|
||||||
@@ -567,10 +590,6 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
} => {
|
} => {
|
||||||
self.emit_select(expr, attrpath, default, span);
|
self.emit_select(expr, attrpath, default, span);
|
||||||
}
|
}
|
||||||
&Ir::Thunk(id) => {
|
|
||||||
let (layer, local) = self.resolve_thunk(id);
|
|
||||||
self.emit_load(layer, local);
|
|
||||||
}
|
|
||||||
Ir::Builtins => {
|
Ir::Builtins => {
|
||||||
self.emit_op(Op::LoadBuiltins);
|
self.emit_op(Op::LoadBuiltins);
|
||||||
}
|
}
|
||||||
@@ -580,7 +599,7 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
}
|
}
|
||||||
&Ir::BuiltinConst(id) => {
|
&Ir::BuiltinConst(id) => {
|
||||||
self.emit_select(
|
self.emit_select(
|
||||||
RawIrRef(&Ir::Builtins),
|
&Ir::Builtins,
|
||||||
&[Attr::Str(id, TextRange::default())],
|
&[Attr::Str(id, TextRange::default())],
|
||||||
None,
|
None,
|
||||||
TextRange::default(),
|
TextRange::default(),
|
||||||
@@ -642,6 +661,60 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
self.emit_op(Op::LookupWith);
|
self.emit_op(Op::LookupWith);
|
||||||
self.emit_str_id(name);
|
self.emit_str_id(name);
|
||||||
}
|
}
|
||||||
|
&Ir::MaybeThunk(thunk) => {
|
||||||
|
use MaybeThunk::*;
|
||||||
|
match *thunk {
|
||||||
|
Int(x) => {
|
||||||
|
if let Ok(x) = x.try_into() {
|
||||||
|
self.emit_op(Op::PushSmi);
|
||||||
|
self.emit_i32(x);
|
||||||
|
} else {
|
||||||
|
self.emit_op(Op::PushBigInt);
|
||||||
|
self.emit_i64(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Float(x) => {
|
||||||
|
self.emit_op(Op::PushFloat);
|
||||||
|
self.emit_f64(x);
|
||||||
|
}
|
||||||
|
Bool(true) => self.emit_op(Op::PushTrue),
|
||||||
|
Bool(false) => self.emit_op(Op::PushFalse),
|
||||||
|
Null => self.emit_op(Op::PushNull),
|
||||||
|
Str(id) => {
|
||||||
|
self.emit_op(Op::PushString);
|
||||||
|
self.emit_str_id(id);
|
||||||
|
}
|
||||||
|
Path(id) => {
|
||||||
|
self.emit_op(Op::PushString);
|
||||||
|
self.emit_str_id(id);
|
||||||
|
self.emit_op(Op::ResolvePath);
|
||||||
|
}
|
||||||
|
Thunk(id) => {
|
||||||
|
let (layer, local) = self.resolve_thunk(id);
|
||||||
|
self.emit_load(layer, local);
|
||||||
|
}
|
||||||
|
Arg { layer } => self.emit_load(layer, 0),
|
||||||
|
Builtin(id) => {
|
||||||
|
self.emit_op(Op::LoadBuiltin);
|
||||||
|
self.emit_u8(id as u8);
|
||||||
|
}
|
||||||
|
Builtins => self.emit_op(Op::LoadBuiltins),
|
||||||
|
ReplBinding(name) => {
|
||||||
|
self.emit_op(Op::LoadReplBinding);
|
||||||
|
self.emit_str_id(name);
|
||||||
|
}
|
||||||
|
ScopedImportBinding(name) => {
|
||||||
|
self.emit_op(Op::LoadScopedBinding);
|
||||||
|
self.emit_str_id(name);
|
||||||
|
}
|
||||||
|
WithLookup(name) => {
|
||||||
|
// TODO: specialize shallow with lookups
|
||||||
|
self.emit_op(Op::PrepareWith);
|
||||||
|
self.emit_op(Op::LookupWith);
|
||||||
|
self.emit_str_id(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -739,11 +812,11 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_func(
|
fn emit_func<'ir>(
|
||||||
&mut self,
|
&mut self,
|
||||||
thunks: &[(ThunkId, RawIrRef<'_>)],
|
thunks: &[(ThunkId, RawIrRef<'ir>)],
|
||||||
param: &Option<Param<'_>>,
|
param: &Option<Param<'ir>>,
|
||||||
body: RawIrRef<'_>,
|
body: RawIrRef<'ir>,
|
||||||
) {
|
) {
|
||||||
let with_thunk_count = self.count_with_thunks(body);
|
let with_thunk_count = self.count_with_thunks(body);
|
||||||
let total_slots = thunks.len() + with_thunk_count;
|
let total_slots = thunks.len() + with_thunk_count;
|
||||||
@@ -793,8 +866,8 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
|
|
||||||
fn emit_attrset(
|
fn emit_attrset(
|
||||||
&mut self,
|
&mut self,
|
||||||
stcs: &fix_ir::HashMap<'_, StringId, (MaybeThunk, TextRange)>,
|
stcs: &fix_ir::HashMap<'_, StringId, (&MaybeThunk, TextRange)>,
|
||||||
dyns: &[(RawIrRef<'_>, MaybeThunk, 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);
|
||||||
@@ -913,7 +986,7 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
|
|||||||
|
|
||||||
fn emit_with(
|
fn emit_with(
|
||||||
&mut self,
|
&mut self,
|
||||||
namespace: MaybeThunk,
|
namespace: &MaybeThunk,
|
||||||
body: RawIrRef<'_>,
|
body: RawIrRef<'_>,
|
||||||
thunks: &[(ThunkId, RawIrRef<'_>)],
|
thunks: &[(ThunkId, RawIrRef<'_>)],
|
||||||
) {
|
) {
|
||||||
|
|||||||
+15
-13
@@ -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, IrRef<'id, 'ir>>) -> IrRef<'id, 'ir>;
|
fn new_expr(&self, expr: Ir<'ir, GhostRef<'id, 'ir>>) -> IrRef<'id, 'ir>;
|
||||||
fn maybe_thunk(&mut self, ir: IrRef<'id, 'ir>) -> MaybeThunk;
|
fn maybe_thunk(&mut self, ir: IrRef<'id, 'ir>) -> GhostMaybeThunkRef<'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<MaybeThunk>;
|
fn lookup(&self, sym: StringId, span: TextRange) -> Result<GhostMaybeThunkRef<'id, 'ir>>;
|
||||||
|
|
||||||
fn get_current_source(&self) -> Source;
|
fn get_current_source(&self) -> Source;
|
||||||
|
|
||||||
@@ -229,7 +229,8 @@ 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 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);
|
||||||
ctx.lookup(sym, span).map(|thunk| thunk.to_ir(ctx))
|
ctx.lookup(sym, span)
|
||||||
|
.map(|thunk| ctx.new_expr(Ir::MaybeThunk(thunk)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -835,8 +836,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, (MaybeThunk, TextRange)>,
|
stcs: HashMap<'ir, StringId, (GhostMaybeThunkRef<'id, 'ir>, TextRange)>,
|
||||||
dyns: Vec<'ir, (IrRef<'id, 'ir>, MaybeThunk, TextRange)>,
|
dyns: Vec<'ir, (IrRef<'id, 'ir>, GhostMaybeThunkRef<'id, 'ir>, TextRange)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn downgrade_attrs<'id, 'ir>(
|
fn downgrade_attrs<'id, 'ir>(
|
||||||
@@ -1074,7 +1075,7 @@ where
|
|||||||
F: FnOnce(
|
F: FnOnce(
|
||||||
&mut Ctx,
|
&mut Ctx,
|
||||||
&[StringId],
|
&[StringId],
|
||||||
&[(IrRef<'id, 'ir>, MaybeThunk, TextRange)],
|
&[(IrRef<'id, 'ir>, GhostMaybeThunkRef<'id, 'ir>, 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());
|
||||||
@@ -1090,7 +1091,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(finalized.stcs.get(sym).expect("WTF").0.to_ir(ctx));
|
temp.push(ctx.new_expr(Ir::MaybeThunk(finalized.stcs.get(sym).expect("WTF").0)));
|
||||||
}
|
}
|
||||||
temp
|
temp
|
||||||
};
|
};
|
||||||
@@ -1101,7 +1102,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, (MaybeThunk, TextRange)>> {
|
) -> Result<HashMap<'ir, StringId, (GhostMaybeThunkRef<'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
|
||||||
@@ -1141,7 +1142,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, (MaybeThunk, TextRange)>,
|
inherit_lookups: &HashMap<StringId, (GhostMaybeThunkRef<'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());
|
||||||
@@ -1169,7 +1170,7 @@ 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, (MaybeThunk, TextRange)>,
|
inherit_lookups: &HashMap<StringId, (GhostMaybeThunkRef<'id, 'ir>, TextRange)>,
|
||||||
ctx: &mut Ctx,
|
ctx: &mut Ctx,
|
||||||
) -> Result<IrRef<'id, 'ir>> {
|
) -> Result<IrRef<'id, 'ir>> {
|
||||||
match value {
|
match value {
|
||||||
@@ -1185,9 +1186,10 @@ fn finalize_pending_value<'id, 'ir, Ctx: DowngradeContext<'id, 'ir>, const ALLOW
|
|||||||
}
|
}
|
||||||
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(expr.to_ir(ctx))
|
Ok(ctx.new_expr(Ir::MaybeThunk(expr)))
|
||||||
} else {
|
} else {
|
||||||
ctx.lookup(sym, span).map(|val| val.to_ir(ctx))
|
ctx.lookup(sym, span)
|
||||||
|
.map(|val| ctx.new_expr(Ir::MaybeThunk(val)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PendingValue::Set(set) => {
|
PendingValue::Set(set) => {
|
||||||
|
|||||||
+80
-94
@@ -1,5 +1,5 @@
|
|||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::ops::Deref;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
@@ -10,56 +10,36 @@ 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>;
|
||||||
|
|
||||||
#[repr(transparent)]
|
pub type IrRef<'id, 'ir> = <GhostRef<'id, 'ir> as RefExt<'ir>>::IrRef;
|
||||||
#[derive(Clone, Copy)]
|
pub type RawIrRef<'ir> = <RawRef<'ir> as RefExt<'ir>>::IrRef;
|
||||||
pub struct IrRef<'id, 'ir>(&'ir GhostCell<'id, Ir<'ir, Self>>);
|
pub type GhostMaybeThunkRef<'id, 'ir> = <GhostRef<'id, 'ir> as RefExt<'ir>>::MaybeThunkRef;
|
||||||
|
|
||||||
impl<'id, 'ir> IrRef<'id, 'ir> {
|
|
||||||
pub fn new(ir: &'ir GhostCell<'id, Ir<'ir, Self>>) -> Self {
|
|
||||||
Self(ir)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn alloc(bump: &'ir Bump, ir: Ir<'ir, Self>) -> Self {
|
|
||||||
Self(bump.alloc(GhostCell::new(ir)))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn borrow<'a>(&'a self, token: &'a GhostToken<'id>) -> &'a Ir<'ir, Self> {
|
|
||||||
self.0.borrow(token)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
impl<'id, 'ir> Ir<'ir, GhostRef<'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> {
|
||||||
/// # Safety
|
// SAFETY: The transmute is sound because:
|
||||||
/// The transmute is sound because:
|
// - `GhostCell<'id, T>` is `#[repr(transparent)]` over `T`, so
|
||||||
/// - `GhostCell<'id, T>` is `#[repr(transparent)]` over `T`
|
// `&'ir GhostCell<'id, T>` and `&'ir T` have identical layout.
|
||||||
/// - `IrRef<'id, 'ir>` is `#[repr(transparent)]` over
|
// - `Ir<'ir, R>` is `#[repr(C)]`, and for every field that depends on
|
||||||
/// `&'ir GhostCell<'id, Ir<'ir, Self>>`
|
// `R`, instantiating `R = GhostRef<'id, 'ir>` vs `R = RawRef<'ir>`
|
||||||
/// - `RawIrRef<'ir>` is `#[repr(transparent)]` over `&'ir Ir<'ir, Self>`
|
// produces types of identical layout:
|
||||||
/// - `Ir<'ir, Ref>` is `#[repr(C)]` and both ref types are pointer-sized
|
// - `R::IrRef` becomes `&'ir GhostCell<'id, Ir<…>>` vs `&'ir Ir<…>`
|
||||||
///
|
// - `R::MaybeThunkRef` becomes `&'ir GhostCell<'id, MaybeThunk>`
|
||||||
/// Consuming the `GhostToken` guarantees no `borrow_mut` calls can occur
|
// vs `&'ir MaybeThunk`
|
||||||
/// afterwards, so the shared `&Ir` references from `RawIrRef::Deref` can
|
// - `R::Ref<Ir<'ir, R>>` (used in `ConcatStrings::parts`) reduces
|
||||||
/// never alias with mutable references.
|
// to the same case as `R::IrRef`
|
||||||
pub fn freeze(self, _token: GhostToken<'id>) -> RawIrRef<'ir> {
|
// - Therefore `IrRef<'id, 'ir>` and `RawIrRef<'ir>` are both
|
||||||
unsafe { std::mem::transmute(self) }
|
// pointer-sized references with the same layout.
|
||||||
}
|
//
|
||||||
}
|
// Consuming the `GhostToken` guarantees no `borrow_mut` calls can
|
||||||
|
// occur afterwards, so the shared `&Ir` references reachable from a
|
||||||
#[repr(transparent)]
|
// `RawIrRef<'ir>` can never alias with mutable references.
|
||||||
#[derive(Clone, Copy, Debug)]
|
unsafe { std::mem::transmute::<IrRef<'id, 'ir>, RawIrRef<'ir>>(this) }
|
||||||
pub struct RawIrRef<'ir>(pub &'ir Ir<'ir, Self>);
|
|
||||||
|
|
||||||
impl<'ir> Deref for RawIrRef<'ir> {
|
|
||||||
type Target = Ir<'ir, RawIrRef<'ir>>;
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
self.0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,102 +61,108 @@ pub enum MaybeThunk {
|
|||||||
WithLookup(StringId),
|
WithLookup(StringId),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MaybeThunk {
|
|
||||||
fn to_ir<'id, 'ir>(self, ctx: &mut impl DowngradeContext<'id, 'ir>) -> IrRef<'id, 'ir> {
|
pub trait Ref<'ir> {
|
||||||
use MaybeThunk::*;
|
type Ref<T>
|
||||||
let ir = match self {
|
where
|
||||||
Int(x) => Ir::Int(x),
|
T: 'ir;
|
||||||
Float(x) => Ir::Float(x),
|
}
|
||||||
Bool(x) => Ir::Bool(x),
|
|
||||||
Null => Ir::Null,
|
pub trait RefExt<'ir>: Ref<'ir> {
|
||||||
Str(x) => Ir::Str(x),
|
type Ir;
|
||||||
Path(x) => Ir::Path(ctx.new_expr(Ir::Str(x))),
|
type IrRef;
|
||||||
Thunk(x) => Ir::Thunk(x),
|
type MaybeThunkRef;
|
||||||
Arg { layer } => Ir::Arg { layer },
|
}
|
||||||
Builtin(x) => Ir::Builtin(x),
|
impl<'ir, T: Ref<'ir> + 'ir> RefExt<'ir> for T {
|
||||||
Builtins => Ir::Builtins,
|
type Ir = Ir<'ir, Self>;
|
||||||
ReplBinding(x) => Ir::ReplBinding(x),
|
type IrRef = Self::Ref<Self::Ir>;
|
||||||
ScopedImportBinding(x) => Ir::ScopedImportBinding(x),
|
type MaybeThunkRef = Self::Ref<MaybeThunk>;
|
||||||
WithLookup(x) => Ir::WithLookup(x),
|
}
|
||||||
};
|
|
||||||
ctx.new_expr(ir)
|
pub struct GhostRef<'id, 'ir>(PhantomData<&'ir GhostCell<'id, ()>>);
|
||||||
}
|
pub struct RawRef<'ir>(PhantomData<&'ir ()>);
|
||||||
|
|
||||||
|
impl<'id, 'ir> Ref<'ir> for GhostRef<'id, 'ir> {
|
||||||
|
type Ref<T: 'ir> = &'ir GhostCell<'id, T>;
|
||||||
|
}
|
||||||
|
impl<'ir> Ref<'ir> for RawRef<'ir> {
|
||||||
|
type Ref<T: 'ir> = &'ir T;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Ir<'ir, Ref> {
|
pub enum Ir<'ir, R: RefExt<'ir> + ?Sized + 'ir> {
|
||||||
Int(i64),
|
Int(i64),
|
||||||
Float(f64),
|
Float(f64),
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
Null,
|
Null,
|
||||||
Str(StringId),
|
Str(StringId),
|
||||||
Path(Ref),
|
Path(R::IrRef),
|
||||||
AttrSet {
|
AttrSet {
|
||||||
stcs: HashMap<'ir, StringId, (MaybeThunk, TextRange)>,
|
stcs: HashMap<'ir, StringId, (R::MaybeThunkRef, TextRange)>,
|
||||||
dyns: Vec<'ir, (Ref, MaybeThunk, TextRange)>,
|
dyns: Vec<'ir, (R::IrRef, R::MaybeThunkRef, TextRange)>,
|
||||||
},
|
},
|
||||||
List {
|
List {
|
||||||
items: Vec<'ir, MaybeThunk>,
|
items: Vec<'ir, R::MaybeThunkRef>,
|
||||||
},
|
},
|
||||||
ConcatStrings {
|
ConcatStrings {
|
||||||
parts: Vec<'ir, Ref>,
|
parts: Vec<'ir, R::Ref<Ir<'ir, R>>>,
|
||||||
force_string: bool,
|
force_string: bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
// OPs
|
// OPs
|
||||||
UnOp {
|
UnOp {
|
||||||
rhs: Ref,
|
rhs: R::IrRef,
|
||||||
kind: UnOpKind,
|
kind: UnOpKind,
|
||||||
},
|
},
|
||||||
BinOp {
|
BinOp {
|
||||||
lhs: Ref,
|
lhs: R::IrRef,
|
||||||
rhs: Ref,
|
rhs: R::IrRef,
|
||||||
kind: BinOpKind,
|
kind: BinOpKind,
|
||||||
},
|
},
|
||||||
HasAttr {
|
HasAttr {
|
||||||
lhs: Ref,
|
lhs: R::IrRef,
|
||||||
rhs: Vec<'ir, Attr<Ref>>,
|
rhs: Vec<'ir, Attr<R::IrRef>>,
|
||||||
},
|
},
|
||||||
Select {
|
Select {
|
||||||
expr: Ref,
|
expr: R::IrRef,
|
||||||
attrpath: Vec<'ir, Attr<Ref>>,
|
attrpath: Vec<'ir, Attr<R::IrRef>>,
|
||||||
default: Option<Ref>,
|
default: Option<R::IrRef>,
|
||||||
span: TextRange,
|
span: TextRange,
|
||||||
},
|
},
|
||||||
|
|
||||||
// Conditionals
|
// Conditionals
|
||||||
If {
|
If {
|
||||||
cond: Ref,
|
cond: R::IrRef,
|
||||||
consq: Ref,
|
consq: R::IrRef,
|
||||||
alter: Ref,
|
alter: R::IrRef,
|
||||||
},
|
},
|
||||||
Assert {
|
Assert {
|
||||||
assertion: Ref,
|
assertion: R::IrRef,
|
||||||
expr: Ref,
|
expr: R::IrRef,
|
||||||
assertion_raw: String,
|
assertion_raw: String,
|
||||||
span: TextRange,
|
span: TextRange,
|
||||||
},
|
},
|
||||||
|
|
||||||
With {
|
With {
|
||||||
namespace: MaybeThunk,
|
namespace: R::MaybeThunkRef,
|
||||||
body: Ref,
|
body: R::IrRef,
|
||||||
thunks: Vec<'ir, (ThunkId, Ref)>,
|
thunks: Vec<'ir, (ThunkId, R::IrRef)>,
|
||||||
},
|
},
|
||||||
WithLookup(StringId),
|
WithLookup(StringId),
|
||||||
|
|
||||||
// Function related
|
// Function related
|
||||||
Func {
|
Func {
|
||||||
body: Ref,
|
body: R::IrRef,
|
||||||
param: Option<Param<'ir>>,
|
param: Option<Param<'ir>>,
|
||||||
thunks: Vec<'ir, (ThunkId, Ref)>,
|
thunks: Vec<'ir, (ThunkId, R::IrRef)>,
|
||||||
},
|
},
|
||||||
Arg {
|
Arg {
|
||||||
layer: u8,
|
layer: u8,
|
||||||
},
|
},
|
||||||
Call {
|
Call {
|
||||||
func: Ref,
|
func: R::IrRef,
|
||||||
arg: MaybeThunk,
|
arg: R::MaybeThunkRef,
|
||||||
span: TextRange,
|
span: TextRange,
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -187,10 +173,10 @@ pub enum Ir<'ir, Ref> {
|
|||||||
|
|
||||||
// Misc
|
// Misc
|
||||||
TopLevel {
|
TopLevel {
|
||||||
body: Ref,
|
body: R::IrRef,
|
||||||
thunks: Vec<'ir, (ThunkId, Ref)>,
|
thunks: Vec<'ir, (ThunkId, R::IrRef)>,
|
||||||
},
|
},
|
||||||
Thunk(ThunkId),
|
MaybeThunk(R::MaybeThunkRef),
|
||||||
CurPos(TextRange),
|
CurPos(TextRange),
|
||||||
ReplBinding(StringId),
|
ReplBinding(StringId),
|
||||||
ScopedImportBinding(StringId),
|
ScopedImportBinding(StringId),
|
||||||
@@ -299,7 +285,7 @@ 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, RawIrRef<'static>>> {
|
) -> hashbrown::HashMap<StringId, Ir<'static, RawRef<'static>>> {
|
||||||
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, Ir::Builtins);
|
||||||
|
|||||||
@@ -103,15 +103,27 @@ impl<'a> BytecodeReader<'a> {
|
|||||||
let id = self.read_u32();
|
let id = self.read_u32();
|
||||||
OperandData::Const(ctx.get_const(id))
|
OperandData::Const(ctx.get_const(id))
|
||||||
}
|
}
|
||||||
|
OperandType::BigInt => {
|
||||||
|
let val = self.read_i64();
|
||||||
|
OperandData::BigInt(val)
|
||||||
|
}
|
||||||
OperandType::Local => {
|
OperandType::Local => {
|
||||||
let layer = self.read_u8();
|
let layer = self.read_u8();
|
||||||
let idx = self.read_u32();
|
let idx = self.read_u32();
|
||||||
OperandData::Local { layer, idx }
|
OperandData::Local { layer, idx }
|
||||||
}
|
}
|
||||||
OperandType::Builtins => OperandData::Builtins,
|
OperandType::Builtins => OperandData::Builtins,
|
||||||
OperandType::BigInt => {
|
OperandType::ReplBinding => {
|
||||||
let val = self.read_i64();
|
let id = self.read_string_id();
|
||||||
OperandData::BigInt(val)
|
OperandData::ReplBinding(id)
|
||||||
|
}
|
||||||
|
OperandType::ScopedImportBinding => {
|
||||||
|
let id = self.read_string_id();
|
||||||
|
OperandData::ScopedImportBinding(id)
|
||||||
|
}
|
||||||
|
OperandType::WithLookup => {
|
||||||
|
let id = self.read_string_id();
|
||||||
|
OperandData::WithLookup(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-5
@@ -227,16 +227,21 @@ pub struct Vm<'gc> {
|
|||||||
|
|
||||||
pub(crate) enum OperandData {
|
pub(crate) enum OperandData {
|
||||||
Const(StaticValue),
|
Const(StaticValue),
|
||||||
|
BigInt(i64),
|
||||||
Local { layer: u8, idx: u32 },
|
Local { layer: u8, idx: u32 },
|
||||||
Builtins,
|
Builtins,
|
||||||
BigInt(i64),
|
ReplBinding(StringId),
|
||||||
|
ScopedImportBinding(StringId),
|
||||||
|
WithLookup(StringId),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OperandData {
|
impl OperandData {
|
||||||
pub(crate) fn resolve<'gc>(&self, mc: &Mutation<'gc>, root: &Vm<'gc>) -> Value<'gc> {
|
pub(crate) fn resolve<'gc>(&self, mc: &Mutation<'gc>, root: &Vm<'gc>) -> Value<'gc> {
|
||||||
|
use OperandData::*;
|
||||||
match *self {
|
match *self {
|
||||||
OperandData::Const(sv) => sv.into(),
|
Const(sv) => sv.into(),
|
||||||
OperandData::Local { layer, idx } => {
|
BigInt(val) => Value::new_gc(Gc::new(mc, val)),
|
||||||
|
Local { layer, idx } => {
|
||||||
let mut cur = root.env;
|
let mut cur = root.env;
|
||||||
for _ in 0..layer {
|
for _ in 0..layer {
|
||||||
let prev = cur.borrow().prev.expect("env chain too short");
|
let prev = cur.borrow().prev.expect("env chain too short");
|
||||||
@@ -244,8 +249,10 @@ impl OperandData {
|
|||||||
}
|
}
|
||||||
cur.borrow().locals[idx as usize]
|
cur.borrow().locals[idx as usize]
|
||||||
}
|
}
|
||||||
OperandData::Builtins => root.builtins,
|
Builtins => root.builtins,
|
||||||
OperandData::BigInt(val) => Value::new_gc(Gc::new(mc, val)),
|
ReplBinding(_id) => todo!(),
|
||||||
|
ScopedImportBinding(_id) => todo!(),
|
||||||
|
WithLookup(_id) => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+91
-58
@@ -8,7 +8,7 @@ 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::{Ir, IrRef, MaybeThunk, RawIrRef, ThunkId};
|
use fix_ir::{GhostMaybeThunkRef, GhostRef, Ir, IrRef, MaybeThunk, RawIrRef, RawRef, 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 +30,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, RawIrRef<'static>>>,
|
pub global_env: HashMap<StringId, Ir<'static, RawRef<'static>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Evaluator {
|
pub struct Evaluator {
|
||||||
@@ -85,13 +85,13 @@ impl Evaluator {
|
|||||||
source: Source,
|
source: Source,
|
||||||
scope: &HashSet<StringId>,
|
scope: &HashSet<StringId>,
|
||||||
) -> Result<fix_common::Value> {
|
) -> Result<fix_common::Value> {
|
||||||
self.do_eval(source, Some(Scope::Repl(scope)), ForceMode::Shallow)
|
self.do_eval(source, Some(ExtraScope::Repl(scope)), ForceMode::Shallow)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_eval<'ctx>(
|
fn do_eval<'ctx>(
|
||||||
&'ctx mut self,
|
&'ctx mut self,
|
||||||
source: Source,
|
source: Source,
|
||||||
extra_scope: Option<Scope<'ctx>>,
|
extra_scope: Option<ExtraScope<'ctx>>,
|
||||||
force_mode: ForceMode,
|
force_mode: ForceMode,
|
||||||
) -> Result<fix_common::Value> {
|
) -> Result<fix_common::Value> {
|
||||||
let ip = {
|
let ip = {
|
||||||
@@ -175,14 +175,14 @@ impl<'a, R: VmRuntimeCtx> CompilerCtx<'a, R> {
|
|||||||
fn compile_bytecode(
|
fn compile_bytecode(
|
||||||
&mut self,
|
&mut self,
|
||||||
source: Source,
|
source: Source,
|
||||||
extra_scope: Option<Scope>,
|
extra_scope: Option<ExtraScope>,
|
||||||
) -> Result<InstructionPtr> {
|
) -> Result<InstructionPtr> {
|
||||||
let root = self.downgrade(source, extra_scope)?;
|
let root = self.downgrade(source, extra_scope)?;
|
||||||
let ip = fix_codegen::compile_bytecode(root.as_ref(), self);
|
let ip = fix_codegen::compile_bytecode(root.as_ref(), self);
|
||||||
Ok(ip)
|
Ok(ip)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn downgrade(&mut self, source: Source, extra_scope: Option<Scope>) -> Result<OwnedIr> {
|
fn downgrade(&mut self, source: Source, extra_scope: Option<ExtraScope>) -> Result<OwnedIr> {
|
||||||
tracing::debug!("Parsing Nix expression");
|
tracing::debug!("Parsing Nix expression");
|
||||||
|
|
||||||
self.code.sources.push(source.clone());
|
self.code.sources.push(source.clone());
|
||||||
@@ -202,7 +202,7 @@ impl<'a, R: VmRuntimeCtx> CompilerCtx<'a, R> {
|
|||||||
token,
|
token,
|
||||||
self.runtime,
|
self.runtime,
|
||||||
&self.code.global_env,
|
&self.code.global_env,
|
||||||
extra_scope,
|
extra_scope.map(Into::into),
|
||||||
&mut self.code.thunk_count,
|
&mut self.code.thunk_count,
|
||||||
source,
|
source,
|
||||||
);
|
);
|
||||||
@@ -245,6 +245,7 @@ impl<'a, R: VmRuntimeCtx> BytecodeContext for CompilerCtx<'a, R> {
|
|||||||
Float(x) => StaticValue::new_float(x),
|
Float(x) => StaticValue::new_float(x),
|
||||||
Bool(x) => StaticValue::new_inline(x),
|
Bool(x) => StaticValue::new_inline(x),
|
||||||
String(x) => StaticValue::new_inline(x),
|
String(x) => StaticValue::new_inline(x),
|
||||||
|
Path(_) => todo!(),
|
||||||
PrimOp {
|
PrimOp {
|
||||||
id,
|
id,
|
||||||
arity,
|
arity,
|
||||||
@@ -310,34 +311,20 @@ struct DowngradeCtx<'ctx, 'id, 'ir, R: VmRuntimeCtx> {
|
|||||||
token: GhostToken<'id>,
|
token: GhostToken<'id>,
|
||||||
runtime: &'ctx mut R,
|
runtime: &'ctx mut R,
|
||||||
source: Source,
|
source: Source,
|
||||||
scopes: Vec<Scope<'ctx>>,
|
scopes: Vec<Scope<'ctx, 'id, 'ir>>,
|
||||||
with_scope_count: u32,
|
with_scope_count: u32,
|
||||||
arg_count: u32,
|
arg_count: u32,
|
||||||
thunk_count: &'ctx mut usize,
|
thunk_count: &'ctx mut usize,
|
||||||
thunk_scopes: Vec<ThunkScope<'id, 'ir>>,
|
thunk_scopes: Vec<ThunkScope<'id, 'ir>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_thunk<'id>(ir: IrRef<'id, '_>, token: &GhostToken<'id>) -> bool {
|
|
||||||
!matches!(
|
|
||||||
ir.borrow(token),
|
|
||||||
Ir::Builtin(_)
|
|
||||||
| Ir::Builtins
|
|
||||||
| Ir::Int(_)
|
|
||||||
| Ir::Float(_)
|
|
||||||
| Ir::Bool(_)
|
|
||||||
| Ir::Null
|
|
||||||
| Ir::Str(_)
|
|
||||||
| Ir::Thunk(_)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx, 'id, 'ir, R: VmRuntimeCtx> DowngradeCtx<'ctx, 'id, 'ir, R> {
|
impl<'ctx, 'id, 'ir, R: VmRuntimeCtx> DowngradeCtx<'ctx, 'id, 'ir, R> {
|
||||||
fn new(
|
fn new(
|
||||||
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, RawIrRef<'static>>>,
|
global: &'ctx HashMap<StringId, Ir<'static, RawRef<'static>>>,
|
||||||
extra_scope: Option<Scope<'ctx>>,
|
extra_scope: Option<Scope<'ctx, 'id, 'ir>>,
|
||||||
thunk_count: &'ctx mut usize,
|
thunk_count: &'ctx mut usize,
|
||||||
source: Source,
|
source: Source,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
@@ -360,23 +347,29 @@ 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, IrRef<'id, 'ir>>) -> IrRef<'id, 'ir> {
|
fn new_expr(&self, expr: Ir<'ir, GhostRef<'id, 'ir>>) -> IrRef<'id, 'ir> {
|
||||||
IrRef::new(self.bump.alloc(GhostCell::new(expr)))
|
self.bump.alloc(GhostCell::new(expr))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn maybe_thunk(&mut self, ir: IrRef<'id, 'ir>) -> MaybeThunk {
|
fn maybe_thunk(&mut self, ir: IrRef<'id, 'ir>) -> GhostMaybeThunkRef<'id, 'ir> {
|
||||||
use MaybeThunk::*;
|
use MaybeThunk::*;
|
||||||
match *ir.borrow(&self.token) {
|
let expr = (|| {
|
||||||
Ir::Builtin(x) => return Builtin(x),
|
let expr = match *ir.borrow(&self.token) {
|
||||||
Ir::Int(x) => return Int(x),
|
Ir::Builtin(x) => Builtin(x),
|
||||||
Ir::Float(x) => return Float(x),
|
Ir::Int(x) => Int(x),
|
||||||
Ir::Bool(x) => return Bool(x),
|
Ir::Float(x) => Float(x),
|
||||||
Ir::Str(x) => return Str(x),
|
Ir::Bool(x) => Bool(x),
|
||||||
Ir::Thunk(x) => return Thunk(x),
|
Ir::Str(x) => Str(x),
|
||||||
Ir::Arg { layer } => return Arg { layer },
|
Ir::Arg { layer } => Arg { layer },
|
||||||
Ir::Builtins => return Builtins,
|
Ir::Builtins => Builtins,
|
||||||
Ir::Null => return Null,
|
Ir::Null => Null,
|
||||||
_ => (),
|
Ir::MaybeThunk(thunk) => return Some(thunk),
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
Some(self.bump.alloc(GhostCell::new(expr)))
|
||||||
|
})();
|
||||||
|
if let Some(thunk) = expr {
|
||||||
|
return thunk;
|
||||||
}
|
}
|
||||||
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");
|
||||||
@@ -384,7 +377,7 @@ impl<'ctx: 'ir, 'id, 'ir, R: VmRuntimeCtx> DowngradeContext<'id, 'ir>
|
|||||||
.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);
|
||||||
Thunk(id)
|
self.bump.alloc(GhostCell::new(Thunk(id)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn intern_string(&mut self, sym: impl AsRef<str>) -> StringId {
|
fn intern_string(&mut self, sym: impl AsRef<str>) -> StringId {
|
||||||
@@ -395,7 +388,7 @@ 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<MaybeThunk> {
|
fn lookup(&self, sym: StringId, span: rnix::TextRange) -> Result<GhostMaybeThunkRef<'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) => {
|
||||||
@@ -408,22 +401,26 @@ impl<'ctx: 'ir, 'id, 'ir, R: VmRuntimeCtx> DowngradeContext<'id, 'ir>
|
|||||||
Ir::Null => Null,
|
Ir::Null => Null,
|
||||||
_ => unreachable!("globals should only contain leaf IR nodes"),
|
_ => unreachable!("globals should only contain leaf IR nodes"),
|
||||||
};
|
};
|
||||||
return Ok(val);
|
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(MaybeThunk::ReplBinding(sym));
|
return Ok(self
|
||||||
|
.bump
|
||||||
|
.alloc(GhostCell::new(MaybeThunk::ReplBinding(sym))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Scope::ScopedImport(scoped_bindings) => {
|
Scope::ScopedImport(scoped_bindings) => {
|
||||||
if scoped_bindings.contains(&sym) {
|
if scoped_bindings.contains(&sym) {
|
||||||
return Ok(MaybeThunk::ScopedImportBinding(sym));
|
return Ok(self
|
||||||
|
.bump
|
||||||
|
.alloc(GhostCell::new(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(MaybeThunk::Thunk(expr));
|
return Ok(expr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&Scope::Param {
|
&Scope::Param {
|
||||||
@@ -434,14 +431,14 @@ 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(MaybeThunk::Arg { layer });
|
return Ok(self.bump.alloc(GhostCell::new(MaybeThunk::Arg { layer })));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.with_scope_count > 0 {
|
if self.with_scope_count > 0 {
|
||||||
Ok(MaybeThunk::WithLookup(sym))
|
Ok(self.bump.alloc(GhostCell::new(MaybeThunk::WithLookup(sym))))
|
||||||
} else {
|
} else {
|
||||||
Err(Error::downgrade_error(
|
Err(Error::downgrade_error(
|
||||||
format!("'{}' not found", self.resolve_sym(sym)),
|
format!("'{}' not found", self.resolve_sym(sym)),
|
||||||
@@ -464,11 +461,14 @@ impl<'ctx: 'ir, 'id, 'ir, R: VmRuntimeCtx> DowngradeContext<'id, 'ir>
|
|||||||
.thunk_count
|
.thunk_count
|
||||||
.checked_add(keys.len())
|
.checked_add(keys.len())
|
||||||
.expect("thunk id overflow");
|
.expect("thunk id overflow");
|
||||||
let iter = keys
|
let scope = {
|
||||||
.iter()
|
let mut scope = HashMap::new();
|
||||||
.enumerate()
|
for (offset, &key) in keys.iter().enumerate() {
|
||||||
.map(|(offset, &key)| (key, ThunkId(base + offset)));
|
scope.insert(key, &*self.bump.alloc(GhostCell::new(MaybeThunk::Thunk(ThunkId(base + offset)))));
|
||||||
self.scopes.push(Scope::Let(iter.collect()));
|
}
|
||||||
|
scope
|
||||||
|
};
|
||||||
|
self.scopes.push(Scope::Let(scope));
|
||||||
let (vals, ret) = {
|
let (vals, ret) = {
|
||||||
let mut guard = ScopeGuard { ctx: self };
|
let mut guard = ScopeGuard { ctx: self };
|
||||||
f(guard.as_ctx())?
|
f(guard.as_ctx())?
|
||||||
@@ -538,8 +538,10 @@ impl<'id, 'ir, 'ctx: 'ir, R: VmRuntimeCtx> DowngradeCtx<'ctx, 'id, 'ir, R> {
|
|||||||
.pop()
|
.pop()
|
||||||
.expect("no thunk scope left???")
|
.expect("no thunk scope left???")
|
||||||
.bindings;
|
.bindings;
|
||||||
let ir = IrRef::alloc(self.bump, Ir::TopLevel { body, thunks });
|
Ok(Ir::freeze(
|
||||||
Ok(ir.freeze(self.token))
|
self.new_expr(Ir::TopLevel { body, thunks }),
|
||||||
|
self.token,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -563,14 +565,29 @@ impl<'id, 'ir> ThunkScope<'id, 'ir> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Scope<'ctx> {
|
enum Scope<'ctx, 'id, 'ir> {
|
||||||
Global(&'ctx HashMap<StringId, Ir<'static, RawIrRef<'static>>>),
|
Global(&'ctx HashMap<StringId, Ir<'static, RawRef<'static>>>),
|
||||||
Repl(&'ctx HashSet<StringId>),
|
Repl(&'ctx HashSet<StringId>),
|
||||||
ScopedImport(HashSet<StringId>),
|
ScopedImport(HashSet<StringId>),
|
||||||
Let(HashMap<StringId, ThunkId>),
|
Let(HashMap<StringId, GhostMaybeThunkRef<'id, 'ir>>),
|
||||||
Param { sym: StringId, abs_layer: u8 },
|
Param { sym: StringId, abs_layer: u8 },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ExtraScope<'ctx> {
|
||||||
|
Repl(&'ctx HashSet<StringId>),
|
||||||
|
ScopedImport(HashSet<StringId>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx> From<ExtraScope<'ctx>> for Scope<'ctx, '_, '_> {
|
||||||
|
fn from(value: ExtraScope<'ctx>) -> Self {
|
||||||
|
use ExtraScope::*;
|
||||||
|
match value {
|
||||||
|
ScopedImport(scope) => Scope::ScopedImport(scope),
|
||||||
|
Repl(scope) => Scope::Repl(scope),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct ScopeGuard<'a, 'ctx, 'id, 'ir, R: VmRuntimeCtx> {
|
struct ScopeGuard<'a, 'ctx, 'id, 'ir, R: VmRuntimeCtx> {
|
||||||
ctx: &'a mut DowngradeCtx<'ctx, 'id, 'ir, R>,
|
ctx: &'a mut DowngradeCtx<'ctx, 'id, 'ir, R>,
|
||||||
}
|
}
|
||||||
@@ -593,15 +610,31 @@ struct OwnedIr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl OwnedIr {
|
impl OwnedIr {
|
||||||
|
/// # Safety
|
||||||
|
/// `ir` must be an allocation backed by `bump`. The reference's
|
||||||
|
/// lifetime is extended to `'static` as a placeholder; the stored IR
|
||||||
|
/// must only be re-borrowed via [`OwnedIr::as_ref`], which narrows
|
||||||
|
/// the lifetime back to that of the `&self` borrow. Moving `bump`
|
||||||
|
/// into the struct keeps the underlying allocation live for the
|
||||||
|
/// lifetime of the `OwnedIr`.
|
||||||
unsafe fn new(ir: RawIrRef<'_>, bump: Bump) -> Self {
|
unsafe fn new(ir: RawIrRef<'_>, bump: Bump) -> Self {
|
||||||
Self {
|
Self {
|
||||||
_bump: bump,
|
_bump: bump,
|
||||||
|
// SAFETY: see function docs - caller guarantees `ir` is in `bump`,
|
||||||
|
// and the `'static` lifetime is a placeholder narrowed by `as_ref`.
|
||||||
ir: unsafe { std::mem::transmute::<RawIrRef<'_>, RawIrRef<'static>>(ir) },
|
ir: unsafe { std::mem::transmute::<RawIrRef<'_>, RawIrRef<'static>>(ir) },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_ref(&self) -> RawIrRef<'_> {
|
fn as_ref<'ir>(&'ir self) -> RawIrRef<'ir> {
|
||||||
self.ir
|
// SAFETY: narrows the placeholder `'static` lifetime stored in
|
||||||
|
// `self.ir` down to `'ir = &'ir self`. Lifetime shortening is
|
||||||
|
// logically sound for covariant positions; the transmute is only
|
||||||
|
// needed because `RawRef<'ir>` carries `'ir` through a GAT
|
||||||
|
// (`Ref::Ref<T>`), which prevents the compiler from inferring
|
||||||
|
// covariance automatically. The bump arena that backs the IR is
|
||||||
|
// owned by `self._bump`, so the data is live for at least `'ir`.
|
||||||
|
unsafe { std::mem::transmute::<RawIrRef<'static>, RawIrRef<'ir>>(self.ir) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user