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