feat: SCC analysis (thunk capture WIP)
This commit is contained in:
114
src/ir/ctx.rs
114
src/ir/ctx.rs
@@ -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(),
|
||||
}
|
||||
|
||||
215
src/ir/mod.rs
215
src/ir/mod.rs
@@ -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]
|
||||
.as_ref()
|
||||
.try_unwrap_attrs()
|
||||
.map_err(|_| {
|
||||
Error::DowngradeError("can only select from a attribute set".into())
|
||||
})?
|
||||
.select(&attrpath);
|
||||
match res {
|
||||
Err(err) => {
|
||||
if let Some(default) = self.default.clone() {
|
||||
Ok(Some(default.resolve(self_idx, func_idx, ctx, env)?))
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
ok => ok,
|
||||
}?
|
||||
}
|
||||
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())),
|
||||
};
|
||||
let res = match res {
|
||||
Err(err) => {
|
||||
if let Some(default) = self.default.clone() {
|
||||
Ok(Some(default.resolve(self_idx, ctx, env)?))
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
ok => ok,
|
||||
}?;
|
||||
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()
|
||||
|
||||
@@ -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];
|
||||
@@ -52,46 +58,46 @@ impl SccGraph {
|
||||
}
|
||||
|
||||
fn sorted(self) -> Vec<SccNode> {
|
||||
let mut in_degrees: HashMap<usize, usize> = self.nodes.keys().map(|&id| (id, 0)).collect();
|
||||
for node in self.nodes.values() {
|
||||
in_degrees.insert(node.id, node.deps.len());
|
||||
}
|
||||
|
||||
let mut reverse_adj: HashMap<usize, Vec<usize>> = HashMap::new();
|
||||
for (node_id, node) in &self.nodes {
|
||||
for &dep_id in &node.deps {
|
||||
reverse_adj.entry(dep_id).or_default().push(*node_id);
|
||||
}
|
||||
}
|
||||
|
||||
let mut queue: std::collections::VecDeque<usize> = in_degrees
|
||||
.iter()
|
||||
.filter_map(|(&id, °)| if deg == 0 { Some(id) } else { None })
|
||||
.collect();
|
||||
let mut in_degrees: HashMap<usize, usize> = self.nodes.keys().map(|&id| (id, 0)).collect();
|
||||
for node in self.nodes.values() {
|
||||
in_degrees.insert(node.id, node.deps.len());
|
||||
}
|
||||
|
||||
queue.make_contiguous().sort();
|
||||
|
||||
let mut sorted_order = Vec::new();
|
||||
while let Some(u) = queue.pop_front() {
|
||||
sorted_order.push(self.nodes[&u].clone());
|
||||
|
||||
if let Some(dependents) = reverse_adj.get(&u) {
|
||||
for &v in dependents {
|
||||
if let Some(degree) = in_degrees.get_mut(&v) {
|
||||
*degree -= 1;
|
||||
if *degree == 0 {
|
||||
queue.push_back(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if sorted_order.len() != self.nodes.len() {
|
||||
panic!("Cycle detected in SCC graph, which is impossible!");
|
||||
}
|
||||
|
||||
sorted_order
|
||||
let mut reverse_adj: HashMap<usize, Vec<usize>> = HashMap::new();
|
||||
for (node_id, node) in &self.nodes {
|
||||
for &dep_id in &node.deps {
|
||||
reverse_adj.entry(dep_id).or_default().push(*node_id);
|
||||
}
|
||||
}
|
||||
|
||||
let mut queue: std::collections::VecDeque<usize> = in_degrees
|
||||
.iter()
|
||||
.filter_map(|(&id, °)| if deg == 0 { Some(id) } else { None })
|
||||
.collect();
|
||||
|
||||
queue.make_contiguous().sort();
|
||||
|
||||
let mut sorted_order = Vec::new();
|
||||
while let Some(u) = queue.pop_front() {
|
||||
sorted_order.push(self.nodes[&u].clone());
|
||||
|
||||
if let Some(dependents) = reverse_adj.get(&u) {
|
||||
for &v in dependents {
|
||||
if let Some(degree) = in_degrees.get_mut(&v) {
|
||||
*degree -= 1;
|
||||
if *degree == 0 {
|
||||
queue.push_back(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if sorted_order.len() != self.nodes.len() {
|
||||
panic!("Cycle detected in SCC graph, which is impossible!");
|
||||
}
|
||||
|
||||
sorted_order
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user