feat: SCC analysis (thunk capture WIP)

This commit is contained in:
2025-06-17 11:53:54 +08:00
parent b2d2490327
commit 7f6848c9e5
19 changed files with 501 additions and 458 deletions

View File

@@ -28,6 +28,8 @@
libffi
libxml2
ncurses
gdb
valgrind
];
LLVM_SYS_181_PREFIX = toString pkgs.llvm_18.dev;
LD_LIBRARY_PATH = let

View File

@@ -22,7 +22,7 @@ fn main() -> Result<()> {
));
}
let expr = root.tree().expr().unwrap();
let (downgraded, _) = downgrade(expr)?;
let downgraded = downgrade(expr)?;
println!("{}", eval(downgraded)?);
Ok(())

View File

@@ -2,10 +2,9 @@ use itertools::Itertools;
use rustyline::error::ReadlineError;
use rustyline::{DefaultEditor, Result};
use nixjit::compile::compile;
use nixjit::error::Error;
use nixjit::ir::downgrade;
use nixjit::vm::run;
use nixjit::error::Error;
use nixjit::engine::eval;
macro_rules! unwrap {
($e:expr) => {
@@ -41,8 +40,7 @@ fn main() -> Result<()> {
}
let expr = root.tree().expr().unwrap();
let downgraded = unwrap!(downgrade(expr));
let prog = compile(downgraded);
println!("{}", unwrap!(run(prog)));
println!("{}", unwrap!(eval(downgraded)));
}
Err(ReadlineError::Interrupted) => {
println!("CTRL-C");

View File

@@ -38,8 +38,8 @@ fn main() -> Result<()> {
continue;
}
let expr = root.tree().expr().unwrap();
let (_, graph) = unwrap!(downgrade(expr));
println!("{:?}", graph);
let downgraded = unwrap!(downgrade(expr));
println!("{:?}", downgraded.graph);
}
Err(ReadlineError::Interrupted) => {
println!("CTRL-C");

View File

@@ -3,7 +3,7 @@ use hashbrown::HashMap;
use crate::ir::{Const, DowngradeContext, Ir};
pub fn ir_env(ctx: &mut DowngradeContext) -> HashMap<EcoString, Ir> {
pub fn ir_env(_: &mut DowngradeContext) -> HashMap<EcoString, Ir> {
let mut map = HashMap::new();
map.insert("true".into(), Const::from(true).ir());
map.insert("false".into(), Const::from(false).ir());

View File

@@ -1,14 +1,13 @@
use std::rc::Rc;
use hashbrown::HashSet;
use lru::LruCache;
use priority_queue::PriorityQueue;
use crate::env::VmEnv;
use crate::error::Result;
use crate::eval::Evaluate;
use crate::ir::{Downgraded, Ir};
use crate::ty::internal as i;
use crate::ir::{Dep, Downgraded, Ir, SccNode};
use crate::ty::internal::{self as i, ThunkRef};
use crate::ty::public::Value;
#[cfg(test)]
@@ -18,40 +17,63 @@ type ThunkIdx = usize;
type EnvIdx = usize;
pub struct Engine {
thunks: Box<[Ir]>,
pub thunks: Box<[Ir]>,
pub func_offset: usize,
pub func_deps: Vec<HashSet<Dep>>,
tasks: PriorityQueue<CompileTask, usize>,
lru: LruCache<(ThunkIdx, EnvIdx), i::Value>,
}
pub fn eval(downgraded: Downgraded) -> Result<Value> {
let mut engine = Engine::new(downgraded.thunks, downgraded.func_offset);
engine
.eval()
.map(|mut val| {
Ok(val
.force(&mut engine)?
.to_public(&engine, &mut HashSet::new()))
})?
let mut engine = Engine::new(downgraded.thunks, downgraded.func_offset, downgraded.func_deps);
engine.eval(downgraded.graph)
}
impl Engine {
pub fn new(thunks: Box<[Ir]>, func_offset: usize) -> Self {
pub fn new(thunks: Box<[Ir]>, func_offset: usize, func_deps: Vec<HashSet<Dep>>) -> Self {
Self {
lru: LruCache::new(thunks.len().try_into().unwrap()),
tasks: PriorityQueue::new(),
thunks,
func_offset,
tasks: PriorityQueue::new(),
func_deps
}
}
pub fn eval(&mut self) -> Result<i::Value> {
self.thunks.last().unwrap().clone().eval(self, &mut VmEnv::new())
pub fn eval(&mut self, graph: Vec<SccNode>) -> Result<Value> {
let mut env = VmEnv::new();
let last = graph.last().unwrap().members[0];
for SccNode { members, .. } in graph.into_iter() {
// TODO:
assert!(members.len() == 1);
for member in members.into_iter() {
let val = self.thunks[member].clone().eval(self, &mut env)?;
env.insert_cache(member, val);
}
}
env.lookup_cache(last, |_| unreachable!())
.map(|mut val| Ok(val.force(self, &mut env)?.to_public(self, &mut HashSet::new())))?
}
pub fn eval_thunk(&mut self, idx: usize, env: &mut VmEnv) -> Result<i::Value> {
self.thunks[idx].clone().eval(self, env)
}
pub fn eval_func_deps(&mut self, idx: usize, env: &mut VmEnv) -> Result<()> {
for dep in self.func_deps[idx - self.func_offset].clone() {
match dep {
Dep::Arg(idx) => {
if let i::Value::Thunk(ThunkRef { idx }) = env.lookup_arg(idx) {
let val = self.thunks[idx].clone().eval(self, env)?;
env.insert_cache(idx, val)
}
},
Dep::Thunk(idx) =>{
let val = self.thunks[idx].clone().eval(self, env)?;
env.insert_cache(idx, val)
}
}
}
Ok(())
}
}
enum Thunk {

View File

@@ -16,7 +16,9 @@ use super::eval;
#[inline]
fn test_expr(expr: &str, expected: Value) {
let (downgraded, _) = downgrade(rnix::Root::parse(expr).tree().expr().unwrap()).unwrap();
println!("{expr}");
let downgraded = downgrade(rnix::Root::parse(expr).tree().expr().unwrap()).unwrap();
println!("{downgraded:?}");
assert_eq!(eval(downgraded).unwrap(), expected);
}
@@ -148,6 +150,13 @@ fn test_attrs() {
symbol!("a") => int!(1)
},
);
test_expr(
"rec { a = 1; b = a; }",
attrs! {
symbol!("a") => int!(1),
symbol!("b") => thunk!()
},
);
test_expr("{ a = 1; }.a", int!(1));
test_expr("{ a = 1; }.b or 1", int!(1));
test_expr(
@@ -193,7 +202,7 @@ fn test_let() {
fn test_func() {
test_expr("(x: x) 1", int!(1));
test_expr("(x: x) (x: x) 1", int!(1));
test_expr("(x: y: x + y) 1 1", int!(2));
test_expr("(x: y: x / y) 1 2", int!(0));
test_expr("({ x, y }: x + y) { x = 1; y = 2; }", int!(3));
test_expr("({ x, y, ... }: x + y) { x = 1; y = 2; z = 3; }", int!(3));
test_expr(

View File

@@ -6,14 +6,14 @@ use hashbrown::HashMap;
use crate::stack::Stack;
use crate::ty::internal::{EnvRef, Value};
use crate::error::{Error, Result};
#[derive(Clone)]
pub struct VmEnv {
let_: Rc<RefCell<Stack<Vec<Value>, 1000>>>,
with: Rc<With>,
args: Rc<RefCell<Stack<Value, 1000>>>,
new: bool,
pub id: usize,
cache: Vec<HashMap<usize, Value>>,
with: Vec<Rc<HashMap<EcoString, Value>>>,
args: Vec<Value>,
pub args_len: usize,
}
#[derive(Clone)]
@@ -41,104 +41,71 @@ pub enum Type {
impl VmEnv {
pub fn new() -> Self {
Self {
let_: Rc::default(),
with: With {
map: None,
last: None,
}
.into(),
args: Rc::default(),
new: false,
id: 0,
cache: Vec::from([HashMap::new()]),
with: Vec::new(),
args: Vec::new(),
args_len: 0,
}
}
pub fn lookup_let(&self, level: usize, idx: usize) -> Value {
self.let_.borrow()[level][idx].clone()
pub fn enter_cache_level<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T {
self.cache.push(HashMap::new());
let ret = f(self);
self.cache.pop();
ret
}
pub fn pop_cache_level(&mut self) {
self.cache.pop();
}
pub fn insert_cache(&mut self, idx: usize, val: Value) {
self.cache.last_mut().unwrap().insert(idx, val);
}
pub fn lookup_cache(&mut self, idx: usize, f: impl FnOnce(&mut VmEnv) -> Result<Value>) -> Result<Value> {
for level in self.cache.iter().rev() {
if let Some(ret) = level.get(&idx) {
return ret.clone().ok();
}
}
let val = f(self)?;
self.cache.last_mut().unwrap().insert(idx, val.clone());
val.ok()
}
pub fn lookup_arg(&self, level: usize) -> Value {
self.args.borrow()[level].clone()
self.args[self.args.len() - level - 1].clone()
}
pub fn lookup_with(&self, symbol: &EcoString) -> Option<Value> {
self.with.lookup(symbol)
for with in self.with.iter().rev() {
if let Some(ret) = with.get(symbol) {
return Some(ret.clone());
}
}
None
}
pub fn has_with(&self) -> bool {
self.with.map.is_some()
pub fn enter_arg(&mut self, arg: Value) {
self.args.push(arg)
}
pub fn enter_let(&mut self, mut map: Vec<Value>) {
if Rc::strong_count(&self.let_) > 1 {
self.let_ = Rc::new_cyclic(|weak| {
let weak = VmEnvWeak {
let_: weak.clone(),
with: Rc::downgrade(&self.with),
args: Rc::downgrade(&self.args),
new: self.new,
id: self.id,
};
map.iter_mut().for_each(|val| {
if let Value::Thunk(thunk) = val {
thunk.capture(EnvRef::Weak(weak.clone()));
}
});
let new = self.let_.as_ref().clone();
new.borrow_mut().push(map).unwrap();
new
})
} else {
let weak = self.downgrade();
map.iter_mut().for_each(|val| {
if let Value::Thunk(thunk) = val {
thunk.capture(EnvRef::Weak(weak.clone()));
}
});
let _ = self.let_.borrow_mut().push(map);
}
pub fn pop_args(&mut self, len: usize) -> Vec<Value> {
self.args.split_off(self.args.len() - len)
}
pub fn enter_arg(&mut self, val: Value) {
if Rc::strong_count(&self.args) > 1 {
self.args = Rc::new(self.args.as_ref().clone());
}
self.args.borrow_mut().push(val).unwrap()
pub fn enter_args(&mut self, args: Vec<Value>) {
self.args.extend(args);
}
pub fn pop_let(&mut self) {
if Rc::strong_count(&self.let_) > 1 {
self.let_ = Rc::new(self.let_.as_ref().clone());
}
self.let_.borrow_mut().pop();
pub fn pop_with(&mut self) {
self.with.pop();
}
pub fn pop_arg(&mut self) {
if Rc::strong_count(&self.args) > 1 {
self.args = Rc::new(self.args.as_ref().clone());
}
self.args.borrow_mut().pop();
}
#[must_use]
pub fn enter_with(&self, map: Rc<HashMap<EcoString, Value>>) -> Self {
Self {
let_: self.let_.clone(),
with: self.with.clone().enter(map),
args: self.args.clone(),
new: self.new,
id: self.id + 1,
}
}
pub fn downgrade(&self) -> VmEnvWeak {
VmEnvWeak {
let_: Rc::downgrade(&self.let_),
with: Rc::downgrade(&self.with),
args: Rc::downgrade(&self.args),
id: self.id,
new: self.new,
}
pub fn enter_with(&mut self, map: Rc<HashMap<EcoString, Value>>) {
self.with.push(map)
}
}
@@ -157,15 +124,3 @@ impl With {
})
}
}
impl VmEnvWeak {
pub fn upgrade(&self) -> VmEnv {
VmEnv {
let_: self.let_.upgrade().unwrap(),
with: self.with.upgrade().unwrap(),
args: self.args.upgrade().unwrap(),
id: self.id,
new: self.new,
}
}
}

View File

@@ -1,4 +1,4 @@
use inkwell::values::{StructValue, FunctionValue};
use inkwell::values::{FunctionValue, StructValue};
use crate::ir::*;
use crate::ty::common as c;
@@ -41,11 +41,18 @@ impl JITCompile for UnOp {
let tag = ctx.get_tag(rhs);
let fallback = ctx.context.append_basic_block(func, "fallback");
let ret = ctx.context.append_basic_block(func, "fallback");
let res = ctx.builder.build_alloca(ctx.helpers.value_type, "res_alloca").unwrap();
let res = ctx
.builder
.build_alloca(ctx.helpers.value_type, "res_alloca")
.unwrap();
ctx.builder.build_switch(tag, fallback, &[]).unwrap();
ctx.builder.position_at_end(fallback);
ctx.builder.position_at_end(ret);
ctx.builder.build_load(ctx.helpers.value_type, res, "load_res").unwrap().try_into().unwrap()
ctx.builder
.build_load(ctx.helpers.value_type, res, "load_res")
.unwrap()
.try_into()
.unwrap()
}
}

View File

@@ -184,7 +184,7 @@ impl<'ctx> Helpers<'ctx> {
self.value_type
.const_named_struct(&[
self.int_type.const_int(ValueTag::Int as _, false).into(),
self.const_int(int).into()
self.const_int(int).into(),
])
.into()
}
@@ -197,7 +197,7 @@ impl<'ctx> Helpers<'ctx> {
self.value_type
.const_named_struct(&[
self.int_type.const_int(ValueTag::Float as _, false).into(),
self.const_float(float).into()
self.const_float(float).into(),
])
.into()
}

View File

@@ -6,7 +6,9 @@ use inkwell::builder::Builder;
use inkwell::context::Context;
use inkwell::execution_engine::ExecutionEngine;
use inkwell::module::Module;
use inkwell::values::{AnyValue, AsValueRef, BasicMetadataValueEnum, BasicValueEnum, IntValue, StructValue};
use inkwell::values::{
AnyValue, AsValueRef, BasicMetadataValueEnum, BasicValueEnum, IntValue, StructValue,
};
use crate::env::VmEnv;
use crate::ir::{Ir, UnOpKind};
@@ -145,20 +147,24 @@ impl<'ctx> JITContext<'ctx> {
pub fn get_tag(&self, val: StructValue<'ctx>) -> IntValue<'ctx> {
let alloca = self
.builder
.build_alloca(self.helpers.value_type, "get_tag_alloca").unwrap();
.build_alloca(self.helpers.value_type, "get_tag_alloca")
.unwrap();
self.builder.build_store(alloca, val).unwrap();
self.builder
.build_load(
self.context.bool_type(),
self.builder
.build_struct_gep(self.helpers.value_type, alloca, 1, "get_tag_gep").unwrap(),
.build_struct_gep(self.helpers.value_type, alloca, 1, "get_tag_gep")
.unwrap(),
"get_tag",
).unwrap()
)
.unwrap()
.into_int_value()
}
pub fn call(&self, args: &[BasicMetadataValueEnum<'ctx>]) -> StructValue<'ctx> {
self.builder.build_call(self.helpers.call, args, "call")
self.builder
.build_call(self.helpers.call, args, "call")
.unwrap()
.as_any_value_enum()
.into_struct_value()

View File

@@ -3,7 +3,8 @@ use ecow::EcoVec;
use crate::engine::Engine;
use crate::env::VmEnv;
use crate::error::{Error, Result};
use crate::ir::{self, DynAttr}; use crate::ty::common::Const;
use crate::ir::{self, DynAttr};
use crate::ty::common::Const;
use crate::ty::internal::{AttrSet, EnvRef, List, ThunkRef, Value};
use crate::ty::public::Symbol;
@@ -18,20 +19,12 @@ impl Evaluate for ir::Attrs {
let mut attrs = AttrSet::new(
self.stcs
.into_iter()
.map(|(k, v)| {
Ok((k, {
let mut val = v.eval(engine, env)?;
if let Value::Thunk(thunk) = &mut val {
thunk.capture(EnvRef::Strong(env.clone()));
}
val
}))
})
.map(|(k, v)| Ok((k, v.eval(engine, env)?)))
.collect::<Result<_>>()?,
);
for DynAttr(k, v) in self.dyns {
let mut k = k.eval(engine, env)?;
k.force(engine)?.coerce_to_string();
k.force(engine, env)?.coerce_to_string();
attrs.push_attr(k.unwrap_string(), v.eval(engine, env)?);
}
Value::AttrSet(attrs.into()).ok()
@@ -43,13 +36,7 @@ impl Evaluate for ir::List {
Value::List(List::from(
self.items
.into_iter()
.map(|val| {
let mut val = val.eval(engine, env)?;
if let Value::Thunk(thunk) = &mut val {
thunk.capture(EnvRef::Strong(env.clone()));
}
val.ok()
})
.map(|val| val.eval(engine, env))
.collect::<Result<EcoVec<_>>>()?,
))
.ok()
@@ -66,7 +53,7 @@ impl Evaluate for ir::HasAttr {
Strs(expr) => expr.eval(engine, env)?.unwrap_string(),
Dynamic(expr) => {
let mut val = expr.eval(engine, env)?;
val.force(engine)?.coerce_to_string();
val.force(engine, env)?.coerce_to_string();
val.unwrap_string()
}
})
@@ -80,8 +67,8 @@ impl Evaluate for ir::BinOp {
use ir::BinOpKind::*;
let mut lhs = self.lhs.eval(engine, env)?;
let mut rhs = self.rhs.eval(engine, env)?;
lhs.force(engine)?;
rhs.force(engine)?;
lhs.force(engine, env)?;
rhs.force(engine, env)?;
match self.kind {
Add => lhs.add(rhs),
Sub => {
@@ -117,9 +104,9 @@ impl Evaluate for ir::BinOp {
}
Con => lhs.concat(rhs),
Upd => lhs.update(rhs),
PipeL => lhs.call(rhs, engine)?,
PipeL => lhs.call(vec![rhs], engine, env)?,
PipeR => {
rhs.call(lhs, engine)?;
rhs.call(vec![lhs], engine, env)?;
lhs = rhs;
}
}
@@ -131,7 +118,7 @@ impl Evaluate for ir::UnOp {
fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result<Value> {
use ir::UnOpKind::*;
let mut rhs = self.rhs.eval(engine, env)?;
rhs.force(engine)?;
rhs.force(engine, env)?;
match self.kind {
Neg => rhs.neg(),
Not => rhs.not(),
@@ -146,14 +133,14 @@ impl Evaluate for ir::Select {
let mut val = self.expr.eval(engine, env)?;
if let Some(default) = self.default {
let default = default.eval(engine, env)?;
val.force(engine)?.select_with_default(
val.force(engine, env)?.select_with_default(
self.attrpath.into_iter().map(|attr| {
Ok(match attr {
Str(ident) => ident,
Strs(expr) => expr.eval(engine, env)?.unwrap_string(),
Dynamic(expr) => {
let mut val = expr.eval(engine, env)?;
val.force(engine)?.coerce_to_string();
val.force(engine, env)?.coerce_to_string();
val.unwrap_string()
}
})
@@ -161,14 +148,14 @@ impl Evaluate for ir::Select {
default,
)?;
} else {
val.force(engine)?
val.force(engine, env)?
.select(self.attrpath.into_iter().map(|attr| {
Ok(match attr {
Str(ident) => ident,
Strs(expr) => expr.eval(engine, env)?.unwrap_string(),
Dynamic(expr) => {
let mut val = expr.eval(engine, env)?;
val.force(engine)?.coerce_to_string();
val.force(engine, env)?.coerce_to_string();
val.unwrap_string()
}
})
@@ -192,10 +179,8 @@ impl Evaluate for ir::If {
impl Evaluate for ir::LoadFunc {
fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result<Value> {
Value::Func(ThunkRef {
idx: engine.func_offset + self.idx,
env: Some(EnvRef::Strong(env.clone())).into(),
})
let idx = engine.func_offset + self.idx;
Value::Func(idx)
.ok()
}
}
@@ -203,26 +188,19 @@ impl Evaluate for ir::LoadFunc {
impl Evaluate for ir::Call {
fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result<Value> {
let mut func = self.func.eval(engine, env)?;
func.force(engine)?;
func.force(engine, env)?;
func.call(self.args.into_iter().map(|arg| arg.eval(engine, env)).collect::<Result<_>>()?, engine, env)?;
// FIXME: Modify Value::call
for arg in self.args {
func.call(arg.eval(engine, env)?, engine)?;
}
// for arg in self.args {
// func.call(arg.eval(engine, env)?, engine, env)?;
// }
func.ok()
}
}
impl Evaluate for ir::Let {
fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result<Value> {
let bindings = self
.bindings
.into_iter()
.map(|(_, v)| v.eval(engine, env))
.collect::<Result<Vec<_>>>()?;
env.enter_let(bindings);
let val = self.expr.eval(engine, env)?;
env.pop_let();
Ok(val)
unreachable!()
}
}
@@ -230,10 +208,10 @@ impl Evaluate for ir::With {
fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result<Value> {
let namespace = self.namespace.eval(engine, env)?;
// TODO: Error Handling
self.expr.eval(
engine,
&mut env.enter_with(namespace.unwrap_attr_set().into_inner()),
)
env.enter_with(namespace.unwrap_attr_set().into_inner());
let ret = self.expr.eval(engine, env);
env.pop_with();
ret
}
}
@@ -250,7 +228,7 @@ impl Evaluate for ir::ConcatStrings {
.into_iter()
.map(|part| {
let mut part = part.eval(engine, env)?;
part.force(engine)?.coerce_to_string();
part.force(engine, env)?.coerce_to_string();
part.ok()
})
.collect::<Result<Vec<_>>>()?
@@ -286,7 +264,7 @@ impl Evaluate for ir::Const {
impl Evaluate for ir::Var {
fn eval(self, _: &mut Engine, env: &mut VmEnv) -> Result<Value> {
env.lookup_with(&self.sym)
.ok_or_else(|| Error::EvalError(format!("{} not found", Symbol::from(self.sym))))
.ok_or_else(|| Error::EvalError(format!("variable {} not found", Symbol::from(self.sym))))
}
}
@@ -298,11 +276,7 @@ impl Evaluate for ir::Arg {
impl Evaluate for ir::LetVar {
fn eval(self, _: &mut Engine, env: &mut VmEnv) -> Result<Value> {
let mut ret = env.lookup_let(self.level, self.idx);
if let Value::Thunk(thunk) = &mut ret {
thunk.upgrade();
}
ret.ok()
unreachable!()
}
}
@@ -316,7 +290,8 @@ impl Evaluate for ir::MaybeThunk {
fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result<Value> {
match self {
ir::MaybeThunk::Const(cnst) => cnst.eval(engine, env),
ir::MaybeThunk::Thunk(thunk) => thunk.eval(engine, env)
ir::MaybeThunk::String(string) => string.eval(engine, env),
ir::MaybeThunk::Thunk(thunk) => thunk.eval(engine, env),
}
}
}

View File

@@ -1,21 +1,28 @@
use derive_more::Unwrap;
use ecow::EcoString;
use hashbrown::{HashMap, HashSet};
use crate::error::Result;
use crate::{error::{Error, Result}, ty::common::Const};
use super::{Func, Ir, LoadFunc, MaybeThunk, Thunk};
use super::{Dep, Func, Ir, LoadFunc, MaybeThunk, SccAnalyzer, SccNode, Thunk};
#[derive(Clone, Copy, Unwrap)]
pub enum Index {
Thunk(usize),
Func(usize),
}
pub struct DowngradeContext {
pub thunks: Vec<Ir>,
pub deps: Vec<HashSet<usize>>,
pub thunks: Vec<(Ir, bool)>,
pub thunk_deps: Vec<HashSet<usize>>,
pub func_deps: Vec<HashSet<Dep>>,
pub func_arg_dep: Vec<bool>,
pub funcs: Vec<Func>,
}
pub struct Env<'a, 'env> {
env: EnvNode<'a>,
prev: Option<&'env Env<'a, 'env>>,
arg_level: usize,
let_level: usize,
}
enum EnvNode<'a> {
@@ -40,8 +47,6 @@ impl<'a, 'env> Env<'a, 'env> {
Self {
env: EnvNode::Builtins(base),
prev: None,
arg_level: 0,
let_level: 0,
}
}
@@ -49,8 +54,6 @@ impl<'a, 'env> Env<'a, 'env> {
Self {
env: EnvNode::Let(map),
prev: Some(self),
arg_level: self.arg_level,
let_level: self.let_level + 1,
}
}
@@ -58,8 +61,6 @@ impl<'a, 'env> Env<'a, 'env> {
Self {
env: EnvNode::SingleArg(ident),
prev: Some(self),
arg_level: self.arg_level + 1,
let_level: self.let_level,
}
}
@@ -71,8 +72,6 @@ impl<'a, 'env> Env<'a, 'env> {
Self {
env: EnvNode::MultiArg(map, alias),
prev: Some(self),
arg_level: self.arg_level + 1,
let_level: 0,
}
}
@@ -80,8 +79,6 @@ impl<'a, 'env> Env<'a, 'env> {
Self {
env: EnvNode::With,
prev: Some(self),
arg_level: self.arg_level,
let_level: self.let_level,
}
}
@@ -89,7 +86,6 @@ impl<'a, 'env> Env<'a, 'env> {
&self,
ident: &EcoString,
mut arg_level: usize,
mut let_level: usize,
has_with: bool,
) -> core::result::Result<LookupResult, ()> {
use EnvNode::*;
@@ -106,47 +102,41 @@ impl<'a, 'env> Env<'a, 'env> {
}
Let(map) => {
if let Ok(idx) = map.binary_search_by(|(k, _)| k.cmp(ident)) {
return Ok(LookupResult::MaybeThunk(map[idx].1))
/* return Ok(LookupResult::Let {
level: let_level - 1,
idx,
}); */
} else {
let_level -= 1;
return Ok(LookupResult::MaybeThunk(map[idx].1.clone()));
}
}
SingleArg(arg) => {
if arg == ident {
return Ok(LookupResult::SingleArg {
level: arg_level - 1,
level: arg_level,
});
} else {
arg_level -= 1;
arg_level += 1;
}
}
MultiArg(set, alias) => {
if let Some(default) = set.get(ident) {
return Ok(LookupResult::MultiArg {
level: arg_level - 1,
level: arg_level,
default: default.clone(),
});
} else if alias.as_ref() == Some(ident) {
return Ok(LookupResult::SingleArg {
level: arg_level - 1,
level: arg_level,
});
} else {
arg_level -= 1;
arg_level += 1;
}
}
With => has_with = true,
}
self.prev
.map(|prev| prev._lookup(ident, arg_level, let_level, has_with))
.map(|prev| prev._lookup(ident, arg_level, has_with))
.map_or_else(|| unreachable!(), |x| x)
}
pub fn lookup(&self, ident: &EcoString) -> core::result::Result<LookupResult, ()> {
self._lookup(ident, self.arg_level, self.let_level, false)
self._lookup(ident, 0, false)
}
}
@@ -154,7 +144,9 @@ impl DowngradeContext {
pub fn new() -> Self {
DowngradeContext {
thunks: Vec::new(),
deps: Vec::new(),
thunk_deps: Vec::new(),
func_deps: Vec::new(),
func_arg_dep: Vec::new(),
funcs: Vec::new(),
}
}
@@ -163,51 +155,79 @@ impl DowngradeContext {
impl DowngradeContext {
pub fn new_thunk(&mut self, thunk: Ir) -> Thunk {
let idx = self.thunks.len();
self.thunks.push(thunk);
self.deps.push(HashSet::new());
self.thunks.push((thunk, false));
self.thunk_deps.push(HashSet::new());
Thunk { idx }
}
pub fn maybe_thunk(&mut self, ir: Ir) -> MaybeThunk {
match ir {
Ir::Const(cnst) => MaybeThunk::Const(cnst),
Ir::String(string) => MaybeThunk::String(string),
Ir::Thunk(thunk) => MaybeThunk::Thunk(thunk),
ir => MaybeThunk::Thunk(self.new_thunk(ir))
ir => MaybeThunk::Thunk(self.new_thunk(ir)),
}
}
pub fn new_dep(&mut self, this: usize, dep: usize) {
self.deps[this].insert(dep);
pub fn new_dep(&mut self, this: Index, dep: Dep) -> Result<()> {
match this {
Index::Thunk(idx) => {
if dep == Dep::Thunk(idx) {
return Err(Error::DowngradeError("infinite recursion encountered".into()))
}
self.thunk_deps[idx].insert(dep.unwrap_thunk())
},
Index::Func(idx) => self.func_deps[idx].insert(dep),
};
Ok(())
}
pub fn new_func(&mut self, func: Func) -> LoadFunc {
let idx = self.funcs.len();
self.funcs.push(func);
self.func_deps.push(HashSet::new());
self.func_arg_dep.push(false);
LoadFunc { idx }
}
pub fn resolve_func(&mut self, thunk_idx: usize, func_idx: usize, env: &Env) -> Result<()> {
pub fn resolve_func(&mut self, idx: usize, env: &Env) -> Result<()> {
let self_ptr = self as *mut Self;
self.funcs.get_mut(func_idx).map_or_else(
self.funcs.get_mut(idx).map_or_else(
|| unreachable!(),
|func| {
unsafe {
let old = std::ptr::read(func);
std::ptr::write(func, old.resolve(thunk_idx, Some(func_idx), self_ptr.as_mut().unwrap(), env)?);
match old.resolve(Index::Func(idx), self_ptr.as_mut().unwrap(), env) {
Ok(ok) => std::ptr::write(func, ok),
Err(err) => {
std::ptr::write(func, Func { param: crate::ir::Param::Ident(EcoString::new()), body: super::Const { val: Const::Null }.ir().boxed() });
return Err(err)
},
}
}
Ok(())
},
)
}
pub fn resolve_thunk(&mut self, thunk_idx: usize, func_idx: Option<usize>, env: &Env) -> Result<()> {
pub fn resolve_thunk(&mut self, idx: usize, env: &Env) -> Result<()> {
if self.thunks[idx].1 {
return Ok(())
}
self.thunks[idx].1 = true;
let self_ptr = self as *mut Self;
self.thunks.get_mut(thunk_idx).map_or_else(
self.thunks.get_mut(idx).map_or_else(
|| unreachable!(),
|thunk| {
unsafe {
let old = std::ptr::read(thunk);
std::ptr::write(thunk, old.resolve(thunk_idx, func_idx, self_ptr.as_mut().unwrap(), env)?);
let (old, _) = std::ptr::read(thunk);
match old.resolve(Index::Thunk(idx), self_ptr.as_mut().unwrap(), env) {
Ok(ok) => std::ptr::write(&mut thunk.0, ok),
Err(err) => {
std::ptr::write(&mut thunk.0, Ir::Const(super::Const { val: Const::Null }));
return Err(err)
},
}
}
Ok(())
},
@@ -215,18 +235,24 @@ impl DowngradeContext {
}
}
#[derive(Debug)]
pub struct Downgraded {
pub thunks: Box<[Ir]>,
pub func_offset: usize,
pub func_deps: Vec<HashSet<Dep>>,
pub graph: Vec<SccNode>,
}
impl Downgraded {
pub fn new(ctx: DowngradeContext) -> Self {
Self {
graph: SccAnalyzer::new(&ctx).analyze(),
func_offset: ctx.thunks.len(),
func_deps: ctx.func_deps,
thunks: ctx
.thunks
.into_iter()
.map(|(ir, _)| ir)
.chain(ctx.funcs.into_iter().map(|Func { body, .. }| *body))
.collect(),
}

View File

@@ -1,6 +1,6 @@
use derive_more::{IsVariant, TryUnwrap, Unwrap};
use ecow::EcoString;
use hashbrown::{HashMap, HashSet};
use hashbrown::HashMap;
use inkwell::values::{FunctionValue, StructValue};
use itertools::Itertools;
use rnix::ast::HasEntry;
@@ -25,15 +25,14 @@ use utils::*;
pub use ctx::{DowngradeContext, Downgraded};
pub use scc::*;
pub fn downgrade(expr: Expr) -> Result<(Downgraded, Vec<SccNode>)> {
pub fn downgrade(expr: Expr) -> Result<Downgraded> {
let mut ctx = DowngradeContext::new();
let builtins = ir_env(&mut ctx);
let env = Env::new(&builtins);
let top_level = expr.downgrade(&mut ctx)?;
let Thunk { idx } = ctx.new_thunk(top_level);
ctx.resolve_thunk(idx, None, &env)?;
let scc = SccAnalyzer::new(&ctx).analyze();
Ok((Downgraded::new(ctx), scc))
ctx.resolve_thunk(idx, &env)?;
Ok(Downgraded::new(ctx))
}
macro_rules! ir {
@@ -83,9 +82,9 @@ macro_rules! ir {
}
#[inline]
fn resolve<'a, 'env>(self, self_idx: usize, func_idx: Option<usize>, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
fn resolve<'a, 'env>(self, self_idx: Index, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
match self {
$(Ir::$ty(ir) => ir.resolve(self_idx, func_idx, ctx, env),)*
$(Ir::$ty(ir) => ir.resolve(self_idx, ctx, env),)*
}
}
}
@@ -166,23 +165,24 @@ impl Ir {
}
}
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone)]
pub enum MaybeThunk {
Const(Const),
Thunk(Thunk)
String(String),
Thunk(Thunk),
}
impl MaybeThunk {
fn resolve<'a, 'env>(
self,
self_idx: usize,
func_idx: Option<usize>,
self_idx: Index,
ctx: &mut DowngradeContext,
env: &Env<'a, 'env>,
) -> Result<Ir> {
match self {
MaybeThunk::Thunk(thunk) => thunk.resolve(self_idx, func_idx, ctx, env),
MaybeThunk::Const(cnst) => cnst.ir().ok()
MaybeThunk::Thunk(thunk) => thunk.resolve(self_idx, ctx, env),
MaybeThunk::Const(cnst) => cnst.ir().ok(),
MaybeThunk::String(string) => string.ir().ok(),
}
}
}
@@ -199,7 +199,7 @@ pub struct DynAttr(pub Ir, pub Ir);
impl Attrs {
fn _insert(
&mut self,
mut path: std::vec::IntoIter<Attr>,
mut path: impl Iterator<Item = Attr>,
name: Attr,
value: Ir,
ctx: &mut DowngradeContext,
@@ -299,7 +299,10 @@ impl Attrs {
.get(ident)
.and_then(|attrs| attrs.as_ref().try_unwrap_attrs().ok())
.ok_or_else(|| {
Error::DowngradeError(format!("{} not found", Symbol::from(ident.clone())))
Error::DowngradeError(format!(
"attribute {} not found",
Symbol::from(ident.clone())
))
})?
._select(path, name),
None => match name {
@@ -313,7 +316,7 @@ impl Attrs {
Ok(None)
} else {
Err(Error::DowngradeError(format!(
"{} not found",
"attribute {} not found",
Symbol::from(ident.clone())
)))
}
@@ -343,14 +346,13 @@ pub enum Attr {
impl Attr {
fn resolve<'a, 'env>(
self,
self_idx: usize,
func_idx: Option<usize>,
self_idx: Index,
ctx: &mut DowngradeContext,
env: &Env<'a, 'env>,
) -> Result<Attr> {
use Attr::*;
Ok(match self {
Dynamic(ir) => Dynamic(ir.resolve(self_idx, func_idx, ctx, env)?),
Dynamic(ir) => Dynamic(ir.resolve(self_idx, ctx, env)?),
other => other,
})
}
@@ -359,13 +361,12 @@ impl Attr {
impl Thunk {
fn resolve<'a, 'env>(
self,
self_idx: usize,
func_idx: Option<usize>,
self_idx: Index,
ctx: &mut DowngradeContext,
env: &Env<'a, 'env>,
) -> Result<Ir> {
ctx.new_dep(self_idx, self.idx);
ctx.resolve_thunk(self.idx, func_idx, env)?;
ctx.new_dep(self_idx, Dep::Thunk(self.idx))?;
ctx.resolve_thunk(self.idx, env)?;
self.ir().ok()
}
}
@@ -452,12 +453,11 @@ pub enum Param {
impl LoadFunc {
fn resolve<'a, 'env>(
self,
self_idx: usize,
_: Option<usize>,
_: Index,
ctx: &mut DowngradeContext,
env: &Env<'a, 'env>,
) -> Result<Ir> {
ctx.resolve_func(self_idx, self.idx, env)?;
ctx.resolve_func(self.idx, env)?;
self.ir().ok()
}
}
@@ -510,17 +510,13 @@ impl Downgrade for ast::Assert {
impl Assert {
fn resolve<'a, 'env>(
self,
self_idx: usize,
func_idx: Option<usize>,
self_idx: Index,
ctx: &mut DowngradeContext,
env: &Env<'a, 'env>,
) -> Result<Ir> {
Self {
assertion: self
.assertion
.resolve(self_idx, func_idx, ctx, env)?
.boxed(),
expr: self.expr.resolve(self_idx, func_idx, ctx, env)?.boxed(),
assertion: self.assertion.resolve(self_idx, ctx, env)?.boxed(),
expr: self.expr.resolve(self_idx, ctx, env)?.boxed(),
}
.ir()
.ok()
@@ -542,15 +538,14 @@ impl Downgrade for ast::IfElse {
impl If {
fn resolve<'a, 'env>(
self,
self_idx: usize,
func_idx: Option<usize>,
self_idx: Index,
ctx: &mut DowngradeContext,
env: &Env<'a, 'env>,
) -> Result<Ir> {
If {
cond: self.cond.resolve(self_idx, func_idx, ctx, env)?.boxed(),
consq: self.consq.resolve(self_idx, func_idx, ctx, env)?.boxed(),
alter: self.alter.resolve(self_idx, func_idx, ctx, env)?.boxed(),
cond: self.cond.resolve(self_idx, ctx, env)?.boxed(),
consq: self.consq.resolve(self_idx, ctx, env)?.boxed(),
alter: self.alter.resolve(self_idx, ctx, env)?.boxed(),
}
.ir()
.ok()
@@ -589,13 +584,12 @@ impl Downgrade for ast::Path {
impl Path {
fn resolve<'a, 'env>(
self,
self_idx: usize,
func_idx: Option<usize>,
self_idx: Index,
ctx: &mut DowngradeContext,
env: &Env<'a, 'env>,
) -> Result<Ir> {
Self {
expr: self.expr.resolve(self_idx, func_idx, ctx, env)?.boxed(),
expr: self.expr.resolve(self_idx, ctx, env)?.boxed(),
}
.ir()
.ok()
@@ -625,8 +619,7 @@ impl Downgrade for ast::Str {
impl ConcatStrings {
fn resolve<'a, 'env>(
self,
self_idx: usize,
func_idx: Option<usize>,
self_idx: Index,
ctx: &mut DowngradeContext,
env: &Env<'a, 'env>,
) -> Result<Ir> {
@@ -634,7 +627,7 @@ impl ConcatStrings {
parts: self
.parts
.into_iter()
.map(|ir| ir.resolve(self_idx, func_idx, ctx, env))
.map(|ir| ir.resolve(self_idx, ctx, env))
.collect::<Result<Vec<_>>>()?,
}
.ir()
@@ -659,8 +652,7 @@ impl Downgrade for ast::Literal {
impl Const {
fn resolve<'a, 'env>(
self,
_: usize,
_: Option<usize>,
_: Index,
_: &mut DowngradeContext,
_: &Env<'a, 'env>,
) -> Result<Ir> {
@@ -671,8 +663,7 @@ impl Const {
impl String {
fn resolve<'a, 'env>(
self,
_: usize,
_: Option<usize>,
_: Index,
_: &mut DowngradeContext,
_: &Env<'a, 'env>,
) -> Result<Ir> {
@@ -690,15 +681,14 @@ impl Downgrade for ast::Ident {
impl Var {
fn resolve<'a, 'env>(
self,
self_idx: usize,
func_idx: Option<usize>,
self_idx: Index,
ctx: &mut DowngradeContext,
env: &Env<'a, 'env>,
) -> Result<Ir> {
use LookupResult::*;
let Ok(res) = env.lookup(&self.sym) else {
return Err(Error::DowngradeError(format!(
"{} not found",
"variable {} not found",
Symbol::from(self.sym)
)));
};
@@ -712,7 +702,7 @@ impl Var {
default: default.map(Box::new),
}
.ir(),
MaybeThunk(thunk) => thunk.resolve(self_idx, func_idx, ctx, env)?,
MaybeThunk(thunk) => thunk.resolve(self_idx, ctx, env)?,
With => self.ir(),
}
.ok()
@@ -722,8 +712,7 @@ impl Var {
impl Arg {
fn resolve<'a, 'env>(
self,
_: usize,
_: Option<usize>,
_: Index,
_: &mut DowngradeContext,
_: &Env<'a, 'env>,
) -> Result<Ir> {
@@ -734,8 +723,7 @@ impl Arg {
impl LetVar {
fn resolve<'a, 'env>(
self,
_: usize,
_: Option<usize>,
_: Index,
_: &mut DowngradeContext,
_: &Env<'a, 'env>,
) -> Result<Ir> {
@@ -773,8 +761,7 @@ impl Downgrade for ast::AttrSet {
impl Attrs {
fn resolve<'a, 'env>(
self,
self_idx: usize,
func_idx: Option<usize>,
self_idx: Index,
ctx: &mut DowngradeContext,
env: &Env<'a, 'env>,
) -> Result<Ir> {
@@ -782,15 +769,15 @@ impl Attrs {
stcs: self
.stcs
.into_iter()
.map(|(k, v)| Ok((k, v.resolve(self_idx, func_idx, ctx, env)?)))
.map(|(k, v)| Ok((k, v.resolve(self_idx, ctx, env)?)))
.collect::<Result<_>>()?,
dyns: self
.dyns
.into_iter()
.map(|DynAttr(k, v)| {
Ok(DynAttr(
k.resolve(self_idx, func_idx, ctx, env)?,
v.resolve(self_idx, func_idx, ctx, env)?,
k.resolve(self_idx, ctx, env)?,
v.resolve(self_idx, ctx, env)?,
))
})
.collect::<Result<_>>()?,
@@ -813,8 +800,7 @@ impl Downgrade for ast::List {
impl List {
fn resolve<'a, 'env>(
self,
self_idx: usize,
func_idx: Option<usize>,
self_idx: Index,
ctx: &mut DowngradeContext,
env: &Env<'a, 'env>,
) -> Result<Ir> {
@@ -822,7 +808,7 @@ impl List {
items: self
.items
.into_iter()
.map(|item| item.resolve(self_idx, func_idx, ctx, env))
.map(|item| item.resolve(self_idx, ctx, env))
.collect::<Result<_>>()?,
}
.ir()
@@ -845,14 +831,13 @@ impl Downgrade for ast::BinOp {
impl BinOp {
fn resolve<'a, 'env>(
self,
self_idx: usize,
func_idx: Option<usize>,
self_idx: Index,
ctx: &mut DowngradeContext,
env: &Env<'a, 'env>,
) -> Result<Ir> {
Self {
lhs: self.lhs.resolve(self_idx, func_idx, ctx, env)?.boxed(),
rhs: self.rhs.resolve(self_idx, func_idx, ctx, env)?.boxed(),
lhs: self.lhs.resolve(self_idx, ctx, env)?.boxed(),
rhs: self.rhs.resolve(self_idx, ctx, env)?.boxed(),
..self
}
.ir()
@@ -876,17 +861,16 @@ impl Downgrade for ast::HasAttr {
impl HasAttr {
fn resolve<'a, 'env>(
self,
self_idx: usize,
func_idx: Option<usize>,
self_idx: Index,
ctx: &mut DowngradeContext,
env: &Env<'a, 'env>,
) -> Result<Ir> {
Self {
lhs: self.lhs.resolve(self_idx, func_idx, ctx, env)?.boxed(),
lhs: self.lhs.resolve(self_idx, ctx, env)?.boxed(),
rhs: self
.rhs
.into_iter()
.map(|attr| attr.resolve(self_idx, func_idx, ctx, env))
.map(|attr| attr.resolve(self_idx, ctx, env))
.collect::<Result<_>>()?,
}
.ir()
@@ -908,13 +892,12 @@ impl Downgrade for ast::UnaryOp {
impl UnOp {
fn resolve<'a, 'env>(
self,
self_idx: usize,
func_idx: Option<usize>,
self_idx: Index,
ctx: &mut DowngradeContext,
env: &Env<'a, 'env>,
) -> Result<Ir> {
Self {
rhs: self.rhs.resolve(self_idx, func_idx, ctx, env)?.boxed(),
rhs: self.rhs.resolve(self_idx, ctx, env)?.boxed(),
..self
}
.ir()
@@ -940,40 +923,37 @@ impl Downgrade for ast::Select {
impl Select {
fn resolve<'a, 'env>(
self,
self_idx: usize,
func_idx: Option<usize>,
self_idx: Index,
ctx: &mut DowngradeContext,
env: &Env<'a, 'env>,
) -> Result<Ir> {
let expr = self.expr.resolve(self_idx, func_idx, ctx, env)?;
let expr = self.expr.resolve(self_idx, ctx, env)?;
let attrpath = self
.attrpath
.into_iter()
.map(|attr| attr.resolve(self_idx, func_idx, ctx, env))
.map(|attr| attr.resolve(self_idx, ctx, env))
.collect::<Result<Vec<_>>>()?;
let res = match &expr {
Ir::Attrs(attrs) => attrs.select(&attrpath)?,
&Ir::Thunk(Thunk { idx }) => {
let res = ctx.thunks[idx]
Ir::Attrs(attrs) => attrs.select(&attrpath),
&Ir::Thunk(Thunk { idx }) => ctx.thunks[idx]
.0
.as_ref()
.try_unwrap_attrs()
.map_err(|_| {
Error::DowngradeError("can only select from a attribute set".into())
})?
.select(&attrpath);
match res {
.map_err(|_| Error::DowngradeError("can not select from <type>".into()))?
.select(&attrpath),
Ir::Arg(_) => Ok(None),
_ => return Err(Error::DowngradeError("can not select from <type>".into())),
};
let res = match res {
Err(err) => {
if let Some(default) = self.default.clone() {
Ok(Some(default.resolve(self_idx, func_idx, ctx, env)?))
Ok(Some(default.resolve(self_idx, ctx, env)?))
} else {
Err(err)
}
}
ok => ok,
}?
}
_ => return Err(Error::DowngradeError("can not select from <type>".into())),
};
}?;
if let Some(res) = res {
res.ok()
} else {
@@ -981,7 +961,7 @@ impl Select {
expr: expr.boxed(),
attrpath,
default: if let Some(default) = self.default {
Some(default.resolve(self_idx, func_idx, ctx, env)?.boxed())
Some(default.resolve(self_idx, ctx, env)?.boxed())
} else {
None
},
@@ -1075,17 +1055,13 @@ impl Downgrade for ast::LetIn {
impl Let {
fn resolve<'a, 'env>(
self,
self_idx: usize,
func_idx: Option<usize>,
self_idx: Index,
ctx: &mut DowngradeContext,
env: &Env<'a, 'env>,
) -> Result<Ir> {
let map = self
.bindings
.clone();
let map = self.bindings.clone();
let env = env.enter_let(&map);
let expr = self.expr.resolve(self_idx, func_idx, ctx, &env)?.boxed();
Self { expr, ..self }.ir().ok()
self.expr.resolve(self_idx, ctx, &env)?.ok()
}
}
@@ -1100,19 +1076,12 @@ impl Downgrade for ast::With {
impl With {
fn resolve<'a, 'env>(
self,
self_idx: usize,
func_idx: Option<usize>,
self_idx: Index,
ctx: &mut DowngradeContext,
env: &Env<'a, 'env>,
) -> Result<Ir> {
let namespace = self
.namespace
.resolve(self_idx, func_idx, ctx, env)?
.boxed();
let expr = self
.expr
.resolve(self_idx, func_idx, ctx, &env.enter_with())?
.boxed();
let namespace = self.namespace.resolve(self_idx, ctx, env)?.boxed();
let expr = self.expr.resolve(self_idx, ctx, &env.enter_with())?.boxed();
Self { namespace, expr }.ir().ok()
}
}
@@ -1129,8 +1098,7 @@ impl Downgrade for ast::Lambda {
impl Func {
fn resolve<'a, 'env>(
self,
self_idx: usize,
func_idx: Option<usize>,
self_idx: Index,
ctx: &mut DowngradeContext,
env: &Env<'a, 'env>,
) -> Result<Func> {
@@ -1145,7 +1113,7 @@ impl Func {
alias,
),
};
let body = self.body.resolve(self_idx, func_idx, ctx, &env)?.boxed();
let body = self.body.resolve(self_idx, ctx, &env)?.boxed();
Ok(Self { body, ..self })
}
}
@@ -1167,17 +1135,16 @@ impl Downgrade for ast::Apply {
impl Call {
fn resolve<'a, 'env>(
self,
self_idx: usize,
func_idx: Option<usize>,
self_idx: Index,
ctx: &mut DowngradeContext,
env: &Env<'a, 'env>,
) -> Result<Ir> {
Self {
func: self.func.resolve(self_idx, func_idx, ctx, env)?.boxed(),
func: self.func.resolve(self_idx, ctx, env)?.boxed(),
args: self
.args
.into_iter()
.map(|arg| arg.resolve(self_idx, func_idx, ctx, env))
.map(|arg| arg.resolve(self_idx, ctx, env))
.collect::<Result<_>>()?,
}
.ir()

View File

@@ -4,9 +4,15 @@ use super::*;
#[derive(Debug, Clone)]
pub struct SccNode {
id: usize,
members: Vec<usize>,
deps: HashSet<usize>,
pub id: usize,
pub members: Vec<usize>,
pub deps: HashSet<usize>,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Unwrap)]
pub enum Dep {
Thunk(usize),
Arg(usize)
}
#[derive(Default, Debug)]
@@ -33,7 +39,7 @@ impl SccGraph {
);
}
for (from_node_id, from_deps) in ctx.deps.iter().enumerate() {
for (from_node_id, from_deps) in ctx.thunk_deps.iter().enumerate() {
let from_scc_id = thunk_to_scc[&from_node_id];
for &to_node_id in from_deps {
let to_scc_id = thunk_to_scc[&to_node_id];
@@ -134,7 +140,7 @@ impl<'ctx> SccAnalyzer<'ctx> {
self.stack.push(v_id);
self.on_stack.insert(v_id);
if let Some(deps) = self.ctx.deps.get(v_id) {
if let Some(deps) = self.ctx.thunk_deps.get(v_id) {
for &w_id in deps {
if !self.indices.contains_key(&w_id) {
self.strong_connect(w_id);

View File

@@ -14,7 +14,7 @@ use super::super::public as p;
use super::Value;
#[repr(transparent)]
#[derive(Constructor, Clone, PartialEq)]
#[derive(Constructor, Clone, PartialEq, Debug)]
pub struct AttrSet {
data: HashMap<EcoString, Value>,
}
@@ -61,7 +61,7 @@ impl AttrSet {
let item = item?;
let Some(Value::AttrSet(attrs)) = data.get(&item) else {
return Err(Error::EvalError(format!(
"{} not found",
"attribute {} not found",
Symbol::from(item)
)));
};
@@ -73,7 +73,10 @@ impl AttrSet {
.ok_or_else(|| Error::EvalError(format!("{} not found", Symbol::from(last))))
}
pub fn has_attr(&self, mut path: impl DoubleEndedIterator<Item = Result<EcoString>>) -> Result<bool> {
pub fn has_attr(
&self,
mut path: impl DoubleEndedIterator<Item = Result<EcoString>>,
) -> Result<bool> {
let mut data = &self.data;
let last = path.nth_back(0).unwrap();
for item in path {

View File

@@ -10,7 +10,7 @@ use crate::ty::public as p;
use super::Value;
#[derive(Clone, PartialEq)]
#[derive(Clone, PartialEq, Debug)]
pub struct List {
data: EcoVec<Value>,
}
@@ -57,15 +57,6 @@ impl List {
}
}
pub fn capture(&mut self, env: &Weak<VmEnv>) {
self.data.iter().for_each(|v| {
if let Value::Thunk(ref thunk) = v.clone() {
todo!()
// thunk.capture_env_weak(env.clone());
}
})
}
pub fn into_inner(self) -> EcoVec<Value> {
self.data
}

View File

@@ -1,4 +1,3 @@
use std::cell::RefCell;
use std::hash::Hash;
use std::rc::Rc;
@@ -30,17 +29,19 @@ pub enum EnvRef {
Weak(VmEnvWeak),
}
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct ThunkRef {
pub idx: usize,
pub env: Option<EnvRef>,
// pub env: Option<EnvRef>,
}
impl ThunkRef {
pub fn new(idx: usize) -> Self {
ThunkRef { idx, env: None }
ThunkRef {
idx, /* env: None */
}
}
/*
pub fn capture(&mut self, env: EnvRef) {
let _ = self.env.insert(env);
}
@@ -49,9 +50,9 @@ impl ThunkRef {
replace_with_or_abort(&mut self.env, |env| {
env.map(|env| EnvRef::Strong(env.upgraded()))
});
}
} */
}
/*
impl EnvRef {
pub fn upgraded(self) -> VmEnv {
match self {
@@ -59,9 +60,9 @@ impl EnvRef {
EnvRef::Strong(strong) => strong,
}
}
}
} */
#[derive(IsVariant, Unwrap, Clone)]
#[derive(IsVariant, Unwrap, Clone, Debug)]
pub enum Value {
Int(i64),
Float(f64),
@@ -74,7 +75,8 @@ pub enum Value {
Catchable(EcoString),
PrimOp(Rc<PrimOp>),
PartialPrimOp(Rc<PartialPrimOp>),
Func(ThunkRef),
Func(usize),
PartialFunc(usize, Vec<Value>),
}
impl Hash for Value {
@@ -133,7 +135,8 @@ pub enum ValueAsRef<'v> {
Catchable(&'v str),
PrimOp(&'v PrimOp),
PartialPrimOp(&'v PartialPrimOp),
Func(&'v ThunkRef),
Func(usize),
PartialFunc(usize, &'v Vec<Value>),
}
impl Value {
@@ -152,7 +155,8 @@ impl Value {
Catchable(x) => R::Catchable(x),
PrimOp(x) => R::PrimOp(x),
PartialPrimOp(x) => R::PartialPrimOp(x),
Func(x) => R::Func(x),
Func(x) => R::Func(*x),
PartialFunc(x, args) => R::PartialFunc(*x, args),
}
}
}
@@ -176,6 +180,7 @@ impl Value {
PrimOp(_) => "lambda",
PartialPrimOp(_) => "lambda",
Func(_) => "lambda",
PartialFunc(..) => "lambda",
}
}
@@ -187,24 +192,85 @@ impl Value {
}
}
pub fn call(&mut self, arg: Self, engine: &mut Engine) -> Result<()> {
pub fn call(&mut self, args: Vec<Self>, engine: &mut Engine, env: &mut VmEnv) -> Result<()> {
use Value::*;
for arg in args.iter() {
if matches!(arg, Value::Catchable(_)) {
*self = arg;
*self = arg.clone();
return Ok(());
}
}
*self = match self {
PrimOp(func) => func.call(arg, engine),
PartialPrimOp(func) => func.call(arg, engine),
Func(func) => {
let mut env = func.env.take().unwrap().upgraded().clone();
env.enter_arg(arg);
let val = engine.eval_thunk(func.idx, &mut env);
env.pop_arg();
val
PrimOp(func) => func.call(args, engine),
PartialPrimOp(func) => func.call(args, engine),
PartialFunc(idx, old) => {
let idx = *idx;
let len = args.len() + old.len();
env.enter_args(std::mem::take(old));
env.enter_cache_level(|env| {
let mut args = args.into_iter().peekable();
env.enter_arg(args.next().unwrap());
let mut ret = engine.eval_thunk(idx, env)?;
while args.peek().is_some() {
match ret {
Value::Func(func) => {
env.enter_arg(args.next().unwrap());
ret = engine.eval_thunk(func, env)?;
}
Value::PrimOp(primop) => {
ret = primop.call(args.collect(), engine)?;
break;
}
Value::PartialPrimOp(mut primop) => {
ret = primop.call(args.collect(), engine)?;
break;
}
_ => todo!(),
}
}
let args = env.pop_args(len);
if let Value::Func(idx) = ret {
ret = Value::PartialFunc(idx, args)
} else if let Value::PartialFunc(_, old) = &mut ret {
old.extend(args);
}
ret.ok()
})
}
&mut Func(idx) => {
let len = args.len();
let mut args = args.into_iter().peekable();
env.enter_cache_level(|env| {
env.enter_arg(args.next().unwrap());
let mut ret = engine.eval_thunk(idx, env)?;
while args.peek().is_some() {
match ret {
Value::Func(func) => {
env.enter_arg(args.next().unwrap());
ret = engine.eval_thunk(func, env)?;
}
Value::PrimOp(primop) => {
ret = primop.call(args.collect(), engine)?;
break;
}
Value::PartialPrimOp(mut primop) => {
ret = primop.call(args.collect(), engine)?;
break;
}
_ => todo!(),
}
}
let args = env.pop_args(len);
if let Value::Func(idx) = ret {
ret = Value::PartialFunc(idx, args)
} else if let Value::PartialFunc(_, old) = &mut ret {
old.extend(args);
}
ret.ok()
})
}
Catchable(_) => return Ok(()),
_ => todo!(),
other => todo!("{}", other.typename()),
}?;
Ok(())
}
@@ -421,7 +487,10 @@ impl Value {
Ok(self)
}
pub fn has_attr(&mut self, path: impl DoubleEndedIterator<Item = Result<EcoString>>) -> Result<()> {
pub fn has_attr(
&mut self,
path: impl DoubleEndedIterator<Item = Result<EcoString>>,
) -> Result<()> {
if let Value::AttrSet(attrs) = self {
let val = Value::Bool(attrs.has_attr(path)?);
*self = val;
@@ -441,15 +510,17 @@ impl Value {
self
}
pub fn force(&mut self, engine: &mut Engine) -> Result<&mut Self> {
pub fn force(&mut self, engine: &mut Engine, env: &mut VmEnv) -> Result<&mut Self> {
if let Value::Thunk(thunk) = self {
unsafe {
let old = std::ptr::read(thunk);
let mut env = old.env.unwrap().upgraded();
std::ptr::write(
self,
engine.eval_thunk(old.idx, &mut env)?,
);
match env.lookup_cache(old.idx, |env| engine.eval_thunk(old.idx, env)) {
Ok(ok) => std::ptr::write(self, ok),
Err(err) => {
std::ptr::write(self, Self::Null);
return Err(err);
}
}
}
}
Ok(self)
@@ -480,6 +551,7 @@ impl Value {
PrimOp(primop) => Value::PrimOp(primop.name),
PartialPrimOp(primop) => Value::PartialPrimOp(primop.name),
Func(_) => Value::Func,
PartialFunc(..) => Value::Func,
}
}
}

View File

@@ -21,13 +21,14 @@ impl PartialEq for PrimOp {
}
impl PrimOp {
pub fn call(&self, arg: Value, ctx: &Engine) -> Result<Value> {
let mut args = Vec::with_capacity(self.arity);
args.push(arg);
if self.arity > 1 {
pub fn call(&self, args: Vec<Value>, ctx: &Engine) -> Result<Value> {
if args.len() > self.arity {
todo!()
}
if self.arity > args.len() {
Value::PartialPrimOp(Rc::new(PartialPrimOp {
name: self.name,
arity: self.arity - 1,
arity: self.arity - args.len(),
args,
func: self.func,
}))
@@ -38,7 +39,7 @@ impl PrimOp {
}
}
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct PartialPrimOp {
pub name: &'static str,
arity: usize,
@@ -53,12 +54,15 @@ impl PartialEq for PartialPrimOp {
}
impl PartialPrimOp {
pub fn call(self: &mut Rc<Self>, arg: Value, ctx: &Engine) -> Result<Value> {
pub fn call(self: &mut Rc<Self>, args: Vec<Value>, ctx: &Engine) -> Result<Value> {
if self.arity < args.len() {
todo!()
}
let func = self.func;
let Some(ret) = ({
let self_mut = Rc::make_mut(self);
self_mut.args.push(arg);
self_mut.arity -= 1;
self_mut.arity -= args.len();
self_mut.args.extend(args);
if self_mut.arity == 0 {
Some(func(std::mem::take(&mut self_mut.args), ctx))
} else {