fix: fixpoint
This commit is contained in:
@@ -207,6 +207,7 @@ fn test_func() {
|
||||
"(inputs@{ x, y, ... }: x + inputs.y) { x = 1; y = 2; z = 3; }",
|
||||
int!(3),
|
||||
);
|
||||
test_expr("let fix = f: let x = f x; in x; in (fix (self: { x = 1; y = self.x + 1; })).y", int!(2));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
||||
69
src/env.rs
69
src/env.rs
@@ -8,9 +8,8 @@ use crate::ty::internal::Value;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Env {
|
||||
cache: Vec<HashMap<usize, Value>>,
|
||||
stack: Vec<Value>,
|
||||
with: Vec<Rc<HashMap<String, Value>>>,
|
||||
args: Vec<Value>,
|
||||
}
|
||||
|
||||
impl Default for Env {
|
||||
@@ -22,36 +21,37 @@ impl Default for Env {
|
||||
impl Env {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
cache: Vec::from([HashMap::new()]),
|
||||
stack: Vec::new(),
|
||||
with: Vec::new(),
|
||||
args: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_new_cache<T>(
|
||||
&mut self,
|
||||
len: usize,
|
||||
f: impl FnOnce(&mut Self) -> T,
|
||||
) -> (T, HashMap<usize, Value>) {
|
||||
self.cache.push(HashMap::new());
|
||||
) -> (T, Vec<Value>) {
|
||||
self.stack.reserve(len);
|
||||
let ret = f(self);
|
||||
(ret, self.cache.pop().unwrap())
|
||||
(ret, self.stack.split_off(self.stack.len() - len))
|
||||
}
|
||||
|
||||
pub fn with_cache<T>(
|
||||
&mut self,
|
||||
cache: HashMap<usize, Value>,
|
||||
cache: Vec<Value>,
|
||||
len: usize,
|
||||
f: impl FnOnce(&mut Self) -> T,
|
||||
) -> (T, HashMap<usize, Value>) {
|
||||
self.cache.push(cache);
|
||||
) -> (T, Vec<Value>) {
|
||||
self.stack.extend(cache);
|
||||
let ret = f(self);
|
||||
(ret, self.cache.pop().unwrap())
|
||||
(ret, self.stack.split_off(self.stack.len() - len))
|
||||
}
|
||||
|
||||
pub fn insert_cache(&mut self, idx: usize, val: Value) {
|
||||
/* pub fn insert_cache(&mut self, idx: usize, val: Value) {
|
||||
self.cache.last_mut().unwrap().insert(idx, val);
|
||||
}
|
||||
} */
|
||||
|
||||
pub fn insert_cache_lazy(
|
||||
/* pub fn insert_cache_lazy(
|
||||
&mut self,
|
||||
idx: usize,
|
||||
f: impl FnOnce(&mut Self) -> Result<Value>,
|
||||
@@ -61,25 +61,20 @@ impl Env {
|
||||
self.cache.last_mut().unwrap().insert(idx, val);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
} */
|
||||
|
||||
pub fn lookup_cache(
|
||||
&mut self,
|
||||
idx: usize,
|
||||
f: impl FnOnce(&mut Env) -> Result<Value>,
|
||||
) -> Result<Value> {
|
||||
for level in self.cache.iter().rev() {
|
||||
if let Some(ret) = level.get(&idx) {
|
||||
return ret.clone().ok();
|
||||
}
|
||||
}
|
||||
pub fn insert_stack(&mut self, iter: impl IntoIterator<Item = fn(&mut Self) -> Result<Value>>) -> Result<()> {
|
||||
let iter = iter.into_iter();
|
||||
self.stack.reserve(iter.size_hint().0);
|
||||
for f in iter {
|
||||
let val = f(self)?;
|
||||
self.cache.last_mut().unwrap().insert(idx, val.clone());
|
||||
val.ok()
|
||||
self.stack.push(val);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn lookup_arg(&self, level: usize) -> Value {
|
||||
self.args[self.args.len() - level - 1].clone()
|
||||
pub fn lookup_stack(&mut self, idx: usize) -> Value {
|
||||
self.stack.get(self.stack.len() - idx - 1).unwrap().clone()
|
||||
}
|
||||
|
||||
pub fn lookup_with(&self, symbol: &str) -> Option<Value> {
|
||||
@@ -91,22 +86,6 @@ impl Env {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn enter_arg(&mut self, arg: Value) {
|
||||
self.args.push(arg);
|
||||
}
|
||||
|
||||
pub fn pop_args(&mut self, len: usize) -> Vec<Value> {
|
||||
self.args.split_off(self.args.len() - len)
|
||||
}
|
||||
|
||||
pub fn reserve_args(&mut self, len: usize) {
|
||||
self.args.reserve(len);
|
||||
}
|
||||
|
||||
pub fn enter_args(&mut self, args: Vec<Value>) {
|
||||
self.args.extend(args);
|
||||
}
|
||||
|
||||
pub fn exit_with(&mut self) {
|
||||
self.with.pop();
|
||||
}
|
||||
|
||||
@@ -540,6 +540,12 @@ impl JITCompile for LetVar {
|
||||
}
|
||||
}
|
||||
|
||||
impl JITCompile for Cache {
|
||||
fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl JITCompile for Thunk {
|
||||
fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot {
|
||||
let slot = ctx.alloca();
|
||||
|
||||
@@ -305,6 +305,12 @@ impl Evaluate for ir::LetVar {
|
||||
}
|
||||
}
|
||||
|
||||
impl Evaluate for ir::Cache {
|
||||
fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result<Value> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Evaluate for ir::Thunk {
|
||||
fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result<Value> {
|
||||
Value::Thunk(self.idx).ok()
|
||||
|
||||
@@ -16,7 +16,6 @@ pub struct DowngradeContext {
|
||||
pub thunks: Vec<(Ir, bool)>,
|
||||
pub thunk_deps: Vec<HashMap<usize, usize>>,
|
||||
pub func_deps: Vec<HashMap<Dep, usize>>,
|
||||
pub func_arg_dep: Vec<bool>,
|
||||
pub funcs: Vec<Func>,
|
||||
}
|
||||
|
||||
@@ -148,7 +147,6 @@ impl DowngradeContext {
|
||||
thunks: Vec::new(),
|
||||
thunk_deps: Vec::new(),
|
||||
func_deps: Vec::new(),
|
||||
func_arg_dep: Vec::new(),
|
||||
funcs: Vec::new(),
|
||||
}
|
||||
}
|
||||
@@ -190,7 +188,6 @@ impl DowngradeContext {
|
||||
let idx = self.funcs.len();
|
||||
self.funcs.push(func);
|
||||
self.func_deps.push(HashMap::new());
|
||||
self.func_arg_dep.push(false);
|
||||
LoadFunc { idx }
|
||||
}
|
||||
|
||||
|
||||
@@ -147,6 +147,8 @@ ir! {
|
||||
#[derive(Copy)]
|
||||
LetVar => { level: usize, idx: usize },
|
||||
#[derive(Copy)]
|
||||
Cache => { idx: usize },
|
||||
#[derive(Copy)]
|
||||
Thunk => { idx: usize },
|
||||
Path => { expr: Box<Ir> },
|
||||
}
|
||||
@@ -362,9 +364,20 @@ impl Thunk {
|
||||
ctx: &mut DowngradeContext,
|
||||
env: &Env<'a, 'env>,
|
||||
) -> Result<Ir> {
|
||||
ctx.new_dep(self_idx, Dep::Thunk(self.idx));
|
||||
let idx = ctx.new_dep(self_idx, Dep::Thunk(self.idx));
|
||||
ctx.resolve_thunk(self.idx, env)?;
|
||||
self.ir().ok()
|
||||
Cache { idx }.ir().ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl Cache {
|
||||
fn resolve<'a, 'env>(
|
||||
self,
|
||||
self_idx: Index,
|
||||
ctx: &mut DowngradeContext,
|
||||
env: &Env<'a, 'env>,
|
||||
) -> Result<Ir> {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -468,27 +481,28 @@ where
|
||||
|
||||
impl Downgrade for Expr {
|
||||
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
|
||||
use Expr::*;
|
||||
match self {
|
||||
Expr::Apply(apply) => apply.downgrade(ctx),
|
||||
Expr::Assert(assert) => assert.downgrade(ctx),
|
||||
Expr::Error(error) => Err(Error::DowngradeError(error.to_string())),
|
||||
Expr::IfElse(ifelse) => ifelse.downgrade(ctx),
|
||||
Expr::Select(select) => select.downgrade(ctx),
|
||||
Expr::Str(str) => str.downgrade(ctx),
|
||||
Expr::Path(path) => path.downgrade(ctx),
|
||||
Expr::Literal(lit) => lit.downgrade(ctx),
|
||||
Expr::Lambda(lambda) => lambda.downgrade(ctx),
|
||||
Expr::LegacyLet(let_) => let_.downgrade(ctx),
|
||||
Expr::LetIn(letin) => letin.downgrade(ctx),
|
||||
Expr::List(list) => list.downgrade(ctx),
|
||||
Expr::BinOp(op) => op.downgrade(ctx),
|
||||
Expr::Paren(paren) => paren.expr().unwrap().downgrade(ctx),
|
||||
Expr::Root(root) => root.expr().unwrap().downgrade(ctx),
|
||||
Expr::AttrSet(attrs) => attrs.downgrade(ctx),
|
||||
Expr::UnaryOp(op) => op.downgrade(ctx),
|
||||
Expr::Ident(ident) => ident.downgrade(ctx),
|
||||
Expr::With(with) => with.downgrade(ctx),
|
||||
Expr::HasAttr(has) => has.downgrade(ctx),
|
||||
Apply(apply) => apply.downgrade(ctx),
|
||||
Assert(assert) => assert.downgrade(ctx),
|
||||
Error(error) => Err(self::Error::DowngradeError(error.to_string())),
|
||||
IfElse(ifelse) => ifelse.downgrade(ctx),
|
||||
Select(select) => select.downgrade(ctx),
|
||||
Str(str) => str.downgrade(ctx),
|
||||
Path(path) => path.downgrade(ctx),
|
||||
Literal(lit) => lit.downgrade(ctx),
|
||||
Lambda(lambda) => lambda.downgrade(ctx),
|
||||
LegacyLet(let_) => let_.downgrade(ctx),
|
||||
LetIn(letin) => letin.downgrade(ctx),
|
||||
List(list) => list.downgrade(ctx),
|
||||
BinOp(op) => op.downgrade(ctx),
|
||||
Paren(paren) => paren.expr().unwrap().downgrade(ctx),
|
||||
Root(root) => root.expr().unwrap().downgrade(ctx),
|
||||
AttrSet(attrs) => attrs.downgrade(ctx),
|
||||
UnaryOp(op) => op.downgrade(ctx),
|
||||
Ident(ident) => ident.downgrade(ctx),
|
||||
With(with) => with.downgrade(ctx),
|
||||
HasAttr(has) => has.downgrade(ctx),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -935,14 +949,13 @@ impl Select {
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
let res = match &expr {
|
||||
Ir::Attrs(attrs) => attrs.select(&attrpath),
|
||||
&Ir::Thunk(Thunk { idx }) => ctx.thunks[idx]
|
||||
.0
|
||||
.as_ref()
|
||||
.try_unwrap_attrs()
|
||||
.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())),
|
||||
&Ir::Thunk(Thunk { idx }) => {
|
||||
match ctx.thunks[idx].0.as_ref() {
|
||||
IrRef::Attrs(attrs) => attrs.select(&attrpath),
|
||||
_ => Ok(None)
|
||||
}
|
||||
}
|
||||
_ => Ok(None),
|
||||
};
|
||||
let res = match res {
|
||||
Err(err) => {
|
||||
@@ -1061,8 +1074,7 @@ impl Let {
|
||||
) -> Result<Ir> {
|
||||
let map = self.bindings.clone();
|
||||
let env = env.enter_let(&map);
|
||||
self.bindings
|
||||
.into_iter().try_for_each(|(_, ir)| {
|
||||
self.bindings.into_iter().try_for_each(|(_, ir)| {
|
||||
ir.resolve(self_idx, ctx, &env)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
|
||||
use super::*;
|
||||
@@ -22,7 +24,7 @@ pub struct SccGraph {
|
||||
}
|
||||
|
||||
impl SccGraph {
|
||||
fn from(ctx: &DowngradeContext, sccs: Vec<Vec<usize>>) -> Self {
|
||||
fn new(ctx: &DowngradeContext, sccs: Vec<Vec<usize>>) -> Self {
|
||||
let mut graph = SccGraph::default();
|
||||
let mut thunk_to_scc = HashMap::new();
|
||||
|
||||
@@ -92,11 +94,11 @@ impl SccGraph {
|
||||
}
|
||||
}
|
||||
|
||||
let mut queue: std::collections::VecDeque<usize> = in_degrees
|
||||
let mut queue = in_degrees
|
||||
.iter()
|
||||
.filter(|(_, deg)| **deg == 0)
|
||||
.filter(|&(_, °)| deg == 0)
|
||||
.map(|(&id, _)| id)
|
||||
.collect();
|
||||
.collect::<VecDeque<_>>();
|
||||
|
||||
queue.make_contiguous().sort();
|
||||
|
||||
@@ -119,7 +121,7 @@ impl SccGraph {
|
||||
}
|
||||
|
||||
if sorted_order.len() != reachable.len() {
|
||||
panic!("Cycle detected in the reachable part of SCC graph!");
|
||||
unreachable!("Cycle detected in the reachable part of SCC graph!");
|
||||
}
|
||||
|
||||
sorted_order
|
||||
@@ -155,7 +157,7 @@ impl<'ctx> SccAnalyzer<'ctx> {
|
||||
self.strong_connect(idx);
|
||||
}
|
||||
}
|
||||
SccGraph::from(self.ctx, self.sccs).sorted()
|
||||
SccGraph::new(self.ctx, self.sccs).sorted()
|
||||
}
|
||||
|
||||
fn strong_connect(&mut self, v_id: usize) {
|
||||
@@ -165,7 +167,9 @@ impl<'ctx> SccAnalyzer<'ctx> {
|
||||
self.stack.push(v_id);
|
||||
self.on_stack.insert(v_id);
|
||||
|
||||
if let Some(deps) = self.ctx.thunk_deps.get(v_id) {
|
||||
let Some(deps) = self.ctx.thunk_deps.get(v_id) else {
|
||||
unreachable!()
|
||||
};
|
||||
for (&w_id, _) in deps {
|
||||
if !self.indices.contains_key(&w_id) {
|
||||
self.strong_connect(w_id);
|
||||
@@ -182,7 +186,6 @@ impl<'ctx> SccAnalyzer<'ctx> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.low_links[&v_id] == self.indices[&v_id] {
|
||||
let mut scc = Vec::new();
|
||||
|
||||
@@ -462,7 +462,7 @@ impl Value {
|
||||
AttrSet(attrs) => attrs.select(path),
|
||||
Catchable(_) => return Ok(self),
|
||||
_ => Err(Error::EvalError(format!(
|
||||
"cannot select from {:?}",
|
||||
"can not select from {:?}",
|
||||
self.typename()
|
||||
))),
|
||||
}?;
|
||||
@@ -481,7 +481,7 @@ impl Value {
|
||||
Catchable(_) => return Ok(self),
|
||||
_ => {
|
||||
return Err(Error::EvalError(format!(
|
||||
"cannot select from {:?}",
|
||||
"can not select from {:?}",
|
||||
self.typename()
|
||||
)));
|
||||
}
|
||||
@@ -517,7 +517,7 @@ impl Value {
|
||||
|
||||
pub fn force(&mut self, engine: &mut Engine, env: &mut Env) -> Result<&mut Self> {
|
||||
if let &mut Value::Thunk(idx) = self {
|
||||
*self = env.lookup_cache(idx, |_| unreachable!())?
|
||||
*self = env.lookup_stack(idx, |_| unreachable!())?
|
||||
}
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user