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

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