fix: fixpoint

This commit is contained in:
2025-07-17 22:50:01 +08:00
parent b556f1ea2d
commit e06bcf3f9d
8 changed files with 111 additions and 107 deletions

View File

@@ -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]

View File

@@ -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.stack.push(val);
}
let val = f(self)?;
self.cache.last_mut().unwrap().insert(idx, val.clone());
val.ok()
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();
}

View File

@@ -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();

View File

@@ -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()

View File

@@ -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 }
}

View File

@@ -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,11 +1074,10 @@ impl Let {
) -> Result<Ir> {
let map = self.bindings.clone();
let env = env.enter_let(&map);
self.bindings
.into_iter().try_for_each(|(_, ir)| {
ir.resolve(self_idx, ctx, &env)?;
Ok(())
})?;
self.bindings.into_iter().try_for_each(|(_, ir)| {
ir.resolve(self_idx, ctx, &env)?;
Ok(())
})?;
self.expr.resolve(self_idx, ctx, &env)?.ok()
}
}

View File

@@ -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)| 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,21 +167,22 @@ 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) {
for (&w_id, _) in deps {
if !self.indices.contains_key(&w_id) {
self.strong_connect(w_id);
let v_low_link = *self.low_links.get(&v_id).unwrap();
let w_low_link = *self.low_links.get(&w_id).unwrap();
if w_low_link < v_low_link {
self.low_links.insert(v_id, w_low_link);
}
} else if self.on_stack.contains(&w_id) {
let v_low_link = *self.low_links.get(&v_id).unwrap();
let w_index = *self.indices.get(&w_id).unwrap();
if w_index < v_low_link {
self.low_links.insert(v_id, w_index);
}
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);
let v_low_link = *self.low_links.get(&v_id).unwrap();
let w_low_link = *self.low_links.get(&w_id).unwrap();
if w_low_link < v_low_link {
self.low_links.insert(v_id, w_low_link);
}
} else if self.on_stack.contains(&w_id) {
let v_low_link = *self.low_links.get(&v_id).unwrap();
let w_index = *self.indices.get(&w_id).unwrap();
if w_index < v_low_link {
self.low_links.insert(v_id, w_index);
}
}
}

View File

@@ -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)
}