diff --git a/compiler/mono/src/borrow.rs b/compiler/mono/src/borrow.rs index 307d415f0f..72fca5a3e6 100644 --- a/compiler/mono/src/borrow.rs +++ b/compiler/mono/src/borrow.rs @@ -292,6 +292,19 @@ impl<'a> BorrowInfState<'a> { // the function must take it as an owned parameter self.own_args_if_param(xs); } + Reset(x) => { + self.own_var(z); + self.own_var(*x); + } + Reuse { + symbol: x, + arguments: ys, + .. + } => { + self.own_var(z); + self.own_var(*x); + self.own_args_if_param(ys); + } EmptyArray => { self.own_var(z); } diff --git a/compiler/mono/src/inc_dec.rs b/compiler/mono/src/inc_dec.rs index c0b43138f4..d60065ac27 100644 --- a/compiler/mono/src/inc_dec.rs +++ b/compiler/mono/src/inc_dec.rs @@ -113,7 +113,15 @@ pub fn occuring_variables_expr(expr: &Expr<'_>, result: &mut MutSet) { } => { result.extend(arguments.iter().copied()); } - + Reuse { + symbol, arguments, .. + } => { + result.extend(arguments.iter().copied()); + result.insert(*symbol); + } + Reset(x) => { + result.insert(*x); + } RunLowLevel(_, args) => { result.extend(args.iter()); } @@ -413,12 +421,14 @@ impl<'a> Context<'a> { live_vars.remove(&z); let new_b = match v { - Tag { arguments: ys, .. } | Struct(ys) | Array { elems: ys, .. } => self - .add_inc_before_consume_all( - ys, - self.arena.alloc(Stmt::Let(z, v, l, b)), - &b_live_vars, - ), + Reuse { arguments: ys, .. } + | Tag { arguments: ys, .. } + | Struct(ys) + | Array { elems: ys, .. } => self.add_inc_before_consume_all( + ys, + self.arena.alloc(Stmt::Let(z, v, l, b)), + &b_live_vars, + ), AccessAtIndex { structure: x, .. } => { let b = self.add_dec_if_needed(x, b, b_live_vars); let info_x = self.get_var_info(x); @@ -464,7 +474,11 @@ impl<'a> Context<'a> { self.add_inc_before(ys, ps, b, b_live_vars) } - EmptyArray | FunctionPointer(_, _) | Literal(_) | RuntimeErrorFunction(_) => { + EmptyArray + | FunctionPointer(_, _) + | Literal(_) + | Reset(_) + | RuntimeErrorFunction(_) => { // EmptyArray is always stack-allocated // function pointers are persistent self.arena.alloc(Stmt::Let(z, v, l, b)) @@ -749,7 +763,7 @@ impl<'a> Context<'a> { } #[derive(Clone, Debug, Default)] -struct LocalContext<'a> { +pub struct LocalContext<'a> { join_points: MutMap], &'a Stmt<'a>)>, } diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index d6dff5a32b..70c75365fa 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -569,6 +569,14 @@ pub enum Expr<'a> { }, EmptyArray, + Reuse { + symbol: Symbol, + tag_name: TagName, + tag_id: u8, + arguments: &'a [Symbol], + }, + Reset(Symbol), + RuntimeErrorFunction(&'a str), } @@ -666,6 +674,28 @@ impl<'a> Expr<'a> { .append(alloc.space()) .append(alloc.intersperse(it, " ")) } + Reuse { + symbol, + tag_name, + arguments, + .. + } => { + let doc_tag = match tag_name { + TagName::Global(s) => alloc.text(s.as_str()), + TagName::Private(s) => alloc.text(format!("{}", s)), + }; + + let it = arguments.iter().map(|s| symbol_to_doc(alloc, *s)); + + alloc + .text("Reuse ") + .append(symbol_to_doc(alloc, *symbol)) + .append(doc_tag) + .append(alloc.space()) + .append(alloc.intersperse(it, " ")) + } + Reset(symbol) => alloc.text("Reuse ").append(symbol_to_doc(alloc, *symbol)), + Struct(args) => { let it = args.iter().map(|s| symbol_to_doc(alloc, *s)); @@ -2728,6 +2758,9 @@ fn substitute_in_expr<'a>( None } } + + Reuse { .. } | Reset(_) => unreachable!("reset/reuse have not been introduced yet"), + Struct(args) => { let mut did_change = false; let new_args = Vec::from_iter_in( diff --git a/compiler/mono/src/lib.rs b/compiler/mono/src/lib.rs index e44b3d7fae..59ddb331c8 100644 --- a/compiler/mono/src/lib.rs +++ b/compiler/mono/src/lib.rs @@ -15,6 +15,8 @@ pub mod borrow; pub mod inc_dec; pub mod ir; pub mod layout; +pub mod live_vars; +pub mod reset_reuse; pub mod tail_recursion; // Temporary, while we can build up test cases and optimize the exhaustiveness checking. diff --git a/compiler/mono/src/reset_reuse.rs b/compiler/mono/src/reset_reuse.rs index ad2c2ef45d..2d557cdde9 100644 --- a/compiler/mono/src/reset_reuse.rs +++ b/compiler/mono/src/reset_reuse.rs @@ -1,220 +1,188 @@ -use crate::expr::Env; -use crate::expr::Expr; +use crate::inc_dec::LocalContext; +use crate::ir::{Expr, Literal, Stmt}; +use crate::layout::{Builtin, Layout}; +use crate::live_vars; use bumpalo::collections::Vec; +use bumpalo::Bump; use roc_collections::all::MutSet; use roc_module::symbol::Symbol; -pub fn function_r<'a>(env: &mut Env<'a, '_>, body: &'a Expr<'a>) -> Expr<'a> { - use Expr::*; +struct Env<'a, 'b> { + env: crate::ir::Env<'a, 'b>, + ctx: LocalContext<'a>, +} - match body { - Switch { - cond_symbol, - branches, - cond, - cond_layout, - default_branch, - ret_layout, - } => { - let stack_size = cond_layout.stack_size(env.pointer_size); - let mut new_branches = Vec::with_capacity_in(branches.len(), env.arena); +fn may_reuse<'a>(x: Layout<'a>, y: Layout<'a>) -> bool { + // a heuristic; we really only want the same "type" to be reused. + // we could also compare actual stack size. + x == y +} - for (tag, stores, branch) in branches.iter() { - let new_branch = function_d(env, *cond_symbol, stack_size as _, branch); +fn try_function_s<'a>( + env: &mut Env<'a, '_>, + x: Symbol, + layout: Layout<'a>, + stmt: &'a Stmt<'a>, +) -> &'a Stmt<'a> { + let arena = env.env.arena; - new_branches.push((*tag, *stores, new_branch)); - } + let w = env.env.unique_symbol(); - let new_default_branch = ( - default_branch.0, - &*env.arena.alloc(function_d( - env, - *cond_symbol, - stack_size as _, - default_branch.1, - )), - ); + match function_s(env, w, stmt) { + None => stmt, + Some(new) => arena.alloc(Stmt::Let(w, Expr::Reset(x), layout, new)), + } +} +fn function_s<'a>(env: &mut Env<'a, '_>, w: Symbol, stmt: &'a Stmt<'a>) -> Option<&'a Stmt<'a>> { + use Stmt::*; + + let arena = env.env.arena; + + match stmt { + _ => todo!(), + } +} + +fn function_d_main<'a>( + env: &mut Env<'a, '_>, + x: Symbol, + layout: Layout<'a>, + stmt: &'a Stmt<'a>, +) -> (Option<&'a Stmt<'a>>, bool) { + /* + let c = layout; + + use Stmt::*; + + let arena = env.env.arena; + + match stmt { Switch { - cond_symbol: *cond_symbol, - branches: new_branches.into_bump_slice(), - default_branch: new_default_branch, - ret_layout: ret_layout.clone(), - cond: *cond, - cond_layout: cond_layout.clone(), + cond_symbol, + cond_layout, + branches, + default_branch, + ret_layout, + } => { + // TODO only conditionally re-build the expression + let live = live_vars::collect_stmt(stmt, env.ctx) + + let branches = Vec::from_iter_in( + branches.iter().map(|(label, branch)| { + let branch = function_r(env, branch).unwrap_or(branch); + let branch = function_d(env, *cond_symbol, cond_layout.clone(), branch) + .unwrap_or(branch); + + (*label, branch.clone()) + }), + arena, + ) + .into_bump_slice(); + + let default_branch = function_r(env, default_branch).unwrap_or(default_branch); + let default_branch = function_d(env, *cond_symbol, cond_layout.clone(), default_branch) + .unwrap_or(default_branch); + + let switch = Switch { + cond_symbol: *cond_symbol, + branches, + default_branch, + cond_layout: cond_layout.clone(), + ret_layout: ret_layout.clone(), + }; + + Some(arena.alloc(switch)) } - } - Cond { - cond_symbol, - cond_layout, - branching_symbol, - branching_layout, - pass, - fail, - ret_layout, - } => { - let stack_size = cond_layout.stack_size(env.pointer_size); - let new_pass = ( - pass.0, - &*env - .arena - .alloc(function_d(env, *cond_symbol, stack_size as _, pass.1)), - ); + Join { .. } => todo!(), - let new_fail = ( - fail.0, - &*env - .arena - .alloc(function_d(env, *cond_symbol, stack_size as _, fail.1)), - ); + Ret(_) | Jump(_, _) | RuntimeError(_) => None, + + Inc(x, b) => match function_r(env, b) { + None => None, + Some(new_b) => Some(arena.alloc(Inc(*x, new_b))), + }, + + Dec(x, b) => match function_r(env, b) { + None => None, + Some(new_b) => Some(arena.alloc(Dec(*x, new_b))), + }, + + Let(x, v, l, b) => match function_r(env, b) { + None => None, + Some(new_b) => Some(arena.alloc(Let(*x, v.clone(), l.clone(), new_b))), + }, Cond { - cond_symbol: *cond_symbol, - cond_layout: cond_layout.clone(), - branching_symbol: *branching_symbol, - branching_layout: branching_layout.clone(), - ret_layout: ret_layout.clone(), - pass: new_pass, - fail: new_fail, + cond_symbol, + cond_layout, + branching_symbol, + branching_layout, + pass, + fail, + ret_layout, + } => { + // TODO only conditionally re-build the expression + + let pass = function_r(env, pass).unwrap_or(pass); + let pass = function_d(env, *cond_symbol, cond_layout.clone(), pass).unwrap_or(pass); + + let fail = function_r(env, fail).unwrap_or(fail); + let fail = function_d(env, *cond_symbol, cond_layout.clone(), fail).unwrap_or(fail); + + let stmt = Cond { + cond_symbol: *cond_symbol, + cond_layout: cond_layout.clone(), + branching_symbol: *branching_symbol, + branching_layout: branching_layout.clone(), + pass, + fail, + ret_layout: ret_layout.clone(), + }; + + Some(arena.alloc(stmt)) } } - - Store(stores, body) => { - let new_body = function_r(env, body); - - Store(stores, env.arena.alloc(new_body)) - } - - DecAfter(symbol, body) => { - let new_body = function_r(env, body); - - DecAfter(*symbol, env.arena.alloc(new_body)) - } - - CallByName { .. } - | CallByPointer(_, _, _) - | RunLowLevel(_, _) - | Tag { .. } - | Struct(_) - | Array { .. } - | AccessAtIndex { .. } => { - // TODO - // how often are `when` expressions in one of the above? - body.clone() - } - - Int(_) - | Float(_) - | Str(_) - | Bool(_) - | Byte(_) - | Load(_) - | EmptyArray - | Inc(_, _) - | FunctionPointer(_, _) - | RuntimeError(_) - | RuntimeErrorFunction(_) => body.clone(), - - Reset(_, _) | Reuse(_, _) => unreachable!("reset/reuse should not have been inserted yet!"), - } + */ + panic!(); } fn function_d<'a>( env: &mut Env<'a, '_>, - z: Symbol, - stack_size: usize, - body: &'a Expr<'a>, -) -> Expr<'a> { - let symbols = symbols_in_expr(body); - if symbols.contains(&z) { - return body.clone(); - } - - if let Ok(reused) = function_s(env, z, stack_size, body) { - Expr::Reset(z, env.arena.alloc(reused)) - } else { - body.clone() - } - /* - match body { - Expr::Tag { .. } => Some(env.arena.alloc(Expr::Reuse(w, body))), - _ => None, - } - */ + x: Symbol, + layout: Layout<'a>, + stmt: &'a Stmt<'a>, +) -> Option<&'a Stmt<'a>> { + let c = layout; + todo!(); } -fn function_s<'a>( - env: &mut Env<'a, '_>, - w: Symbol, - stack_size: usize, - body: &'a Expr<'a>, -) -> Result<&'a Expr<'a>, &'a Expr<'a>> { - use Expr::*; +fn function_r<'a>(env: &mut Env<'a, '_>, stmt: &'a Stmt<'a>) -> Option<&'a Stmt<'a>> { + use Stmt::*; - match body { - Tag { tag_layout, .. } => { - if tag_layout.stack_size(env.pointer_size) as usize <= stack_size { - Ok(env.arena.alloc(Expr::Reuse(w, body))) - } else { - Err(body) - } - } + let arena = env.env.arena; - Array { .. } | Struct(_) => { - // TODO - Err(body) - } + match stmt { + Join { .. } => todo!(), - Switch { - cond_symbol, - branches, - cond, - cond_layout, - default_branch, - ret_layout, - } => { - // we can re-use `w` in each branch - let mut has_been_reused = false; - let mut new_branches = Vec::with_capacity_in(branches.len(), env.arena); - for (tag, stores, branch) in branches.iter() { - match function_s(env, *cond_symbol, stack_size as _, branch) { - Ok(new_branch) => { - has_been_reused = true; - new_branches.push((*tag, *stores, new_branch.clone())); - } - Err(new_branch) => { - new_branches.push((*tag, *stores, new_branch.clone())); - } - } - } + Ret(_) | Jump(_, _) | RuntimeError(_) => None, - let new_default_branch = ( - default_branch.0, - match function_s(env, *cond_symbol, stack_size, default_branch.1) { - Ok(new) => { - has_been_reused = true; - new - } - Err(new) => new, - }, - ); - let result = env.arena.alloc(Switch { - cond_symbol: *cond_symbol, - branches: new_branches.into_bump_slice(), - default_branch: new_default_branch, - ret_layout: ret_layout.clone(), - cond: *cond, - cond_layout: cond_layout.clone(), - }); + Inc(x, b) => match function_r(env, b) { + None => None, + Some(new_b) => Some(arena.alloc(Inc(*x, new_b))), + }, - if has_been_reused { - Ok(result) - } else { - Err(result) - } - } + Dec(x, b) => match function_r(env, b) { + None => None, + Some(new_b) => Some(arena.alloc(Dec(*x, new_b))), + }, + + Let(x, v, l, b) => match function_r(env, b) { + None => None, + Some(new_b) => Some(arena.alloc(Let(*x, v.clone(), l.clone(), new_b))), + }, Cond { cond_symbol, @@ -225,424 +193,60 @@ fn function_s<'a>( fail, ret_layout, } => { - let mut has_been_reused = false; - let new_pass = ( - pass.0, - match function_s(env, *cond_symbol, stack_size, pass.1) { - Ok(new) => { - has_been_reused = true; - new - } - Err(new) => new, - }, - ); + // TODO only conditionally re-build the expression - let new_fail = ( - fail.0, - match function_s(env, *cond_symbol, stack_size, fail.1) { - Ok(new) => { - has_been_reused = true; - new - } - Err(new) => new, - }, - ); + let pass = function_r(env, pass).unwrap_or(pass); + let pass = function_d(env, *cond_symbol, cond_layout.clone(), pass).unwrap_or(pass); - let result = env.arena.alloc(Cond { + let fail = function_r(env, fail).unwrap_or(fail); + let fail = function_d(env, *cond_symbol, cond_layout.clone(), fail).unwrap_or(fail); + + let stmt = Cond { cond_symbol: *cond_symbol, cond_layout: cond_layout.clone(), branching_symbol: *branching_symbol, branching_layout: branching_layout.clone(), + pass, + fail, ret_layout: ret_layout.clone(), - pass: new_pass, - fail: new_fail, - }); + }; - if has_been_reused { - Ok(result) - } else { - Err(result) - } + Some(arena.alloc(stmt)) } + Switch { + cond_symbol, + cond_layout, + branches, + default_branch, + ret_layout, + } => { + // TODO only conditionally re-build the expression - Store(stores, expr) => { - let new_expr = function_s(env, w, stack_size, expr)?; + let branches = Vec::from_iter_in( + branches.iter().map(|(label, branch)| { + let branch = function_r(env, branch).unwrap_or(branch); + let branch = function_d(env, *cond_symbol, cond_layout.clone(), branch) + .unwrap_or(branch); - Ok(env.arena.alloc(Store(*stores, new_expr))) - } + (*label, branch.clone()) + }), + arena, + ) + .into_bump_slice(); - DecAfter(symbol, expr) => { - let new_expr = function_s(env, w, stack_size, expr)?; + let default_branch = function_r(env, default_branch).unwrap_or(default_branch); + let default_branch = function_d(env, *cond_symbol, cond_layout.clone(), default_branch) + .unwrap_or(default_branch); - Ok(env.arena.alloc(DecAfter(*symbol, new_expr))) - } - - CallByName { .. } | CallByPointer(_, _, _) | RunLowLevel(_, _) | AccessAtIndex { .. } => { - // TODO - // how often are `Tag` expressions in one of the above? - Err(body) - } - - Int(_) - | Float(_) - | Str(_) - | Bool(_) - | Byte(_) - | Load(_) - | EmptyArray - | Inc(_, _) - | FunctionPointer(_, _) - | RuntimeError(_) - | RuntimeErrorFunction(_) => Err(body), - - Reset(_, _) | Reuse(_, _) => { - unreachable!("reset/reuse should not have been introduced yet") - } - } -} - -fn free_variables<'a>(initial: &Expr<'a>) -> MutSet { - use Expr::*; - let mut seen = MutSet::default(); - let mut bound = MutSet::default(); - let mut stack = vec![initial]; - - // in other words, variables that are referenced, but not stored - - while let Some(expr) = stack.pop() { - match expr { - FunctionPointer(symbol, _) | Load(symbol) => { - seen.insert(*symbol); - } - Reset(symbol, expr) | Reuse(symbol, expr) => { - seen.insert(*symbol); - stack.push(expr) - } - - Cond { - cond_symbol, - branching_symbol, - pass, - fail, - .. - } => { - seen.insert(*cond_symbol); - seen.insert(*branching_symbol); - - for (symbol, _, expr) in pass.0.iter() { - seen.insert(*symbol); - stack.push(expr) - } - - for (symbol, _, expr) in fail.0.iter() { - seen.insert(*symbol); - stack.push(expr) - } - } - - Switch { - cond, - cond_symbol, + let switch = Switch { + cond_symbol: *cond_symbol, branches, default_branch, - .. - } => { - stack.push(cond); - seen.insert(*cond_symbol); + cond_layout: cond_layout.clone(), + ret_layout: ret_layout.clone(), + }; - for (_, stores, expr) in branches.iter() { - stack.push(expr); - - for (symbol, _, expr) in stores.iter() { - bound.insert(*symbol); - stack.push(expr) - } - } - - stack.push(default_branch.1); - for (symbol, _, expr) in default_branch.0.iter() { - seen.insert(*symbol); - stack.push(expr) - } - } - - Store(stores, body) => { - for (symbol, _, expr) in stores.iter() { - bound.insert(*symbol); - stack.push(&expr) - } - - stack.push(body) - } - - DecAfter(symbol, body) | Inc(symbol, body) => { - seen.insert(*symbol); - stack.push(body); - } - - CallByName { name, args, .. } => { - seen.insert(*name); - for (expr, _) in args.iter() { - stack.push(expr); - } - } - - CallByPointer(function, args, _) => { - stack.push(function); - stack.extend(args.iter()); - } - - RunLowLevel(_, args) => { - for (expr, _) in args.iter() { - stack.push(expr); - } - } - - Tag { arguments, .. } => { - for (symbol, _) in arguments.iter() { - seen.insert(*symbol); - } - } - - Struct(arguments) => { - for (expr, _) in arguments.iter() { - stack.push(expr); - } - } - - Array { elems, .. } => { - for expr in elems.iter() { - stack.push(expr); - } - } - - AccessAtIndex { expr, .. } => { - stack.push(expr); - } - - Int(_) - | Float(_) - | Str(_) - | Bool(_) - | Byte(_) - | EmptyArray - | RuntimeError(_) - | RuntimeErrorFunction(_) => {} + Some(arena.alloc(switch)) } } - - for symbol in bound.iter() { - seen.remove(symbol); - } - - seen -} - -fn symbols_in_expr<'a>(initial: &Expr<'a>) -> MutSet { - use Expr::*; - let mut result = MutSet::default(); - let mut stack = vec![initial]; - - while let Some(expr) = stack.pop() { - match expr { - FunctionPointer(symbol, _) | Load(symbol) => { - result.insert(*symbol); - } - - Reset(symbol, expr) | Reuse(symbol, expr) => { - result.insert(*symbol); - stack.push(expr) - } - - Cond { - cond_symbol, - branching_symbol, - pass, - fail, - .. - } => { - result.insert(*cond_symbol); - result.insert(*branching_symbol); - - for (symbol, _, expr) in pass.0.iter() { - result.insert(*symbol); - stack.push(expr) - } - - for (symbol, _, expr) in fail.0.iter() { - result.insert(*symbol); - stack.push(expr) - } - } - - Switch { - cond, - cond_symbol, - branches, - default_branch, - .. - } => { - stack.push(cond); - result.insert(*cond_symbol); - - for (_, stores, expr) in branches.iter() { - stack.push(expr); - - for (symbol, _, expr) in stores.iter() { - result.insert(*symbol); - stack.push(expr) - } - } - - stack.push(default_branch.1); - for (symbol, _, expr) in default_branch.0.iter() { - result.insert(*symbol); - stack.push(expr) - } - } - - Store(stores, body) => { - for (symbol, _, expr) in stores.iter() { - result.insert(*symbol); - stack.push(&expr) - } - - stack.push(body) - } - - DecAfter(symbol, body) | Inc(symbol, body) => { - result.insert(*symbol); - stack.push(body); - } - - CallByName { name, args, .. } => { - result.insert(*name); - for (expr, _) in args.iter() { - stack.push(expr); - } - } - - CallByPointer(function, args, _) => { - stack.push(function); - stack.extend(args.iter()); - } - - RunLowLevel(_, args) => { - for (expr, _) in args.iter() { - stack.push(expr); - } - } - - Tag { arguments, .. } => { - for (symbol, _) in arguments.iter() { - result.insert(*symbol); - } - } - - Struct(arguments) => { - for (expr, _) in arguments.iter() { - stack.push(expr); - } - } - - Array { elems, .. } => { - for expr in elems.iter() { - stack.push(expr); - } - } - - AccessAtIndex { expr, .. } => { - stack.push(expr); - } - - Int(_) - | Float(_) - | Str(_) - | Bool(_) - | Byte(_) - | EmptyArray - | RuntimeError(_) - | RuntimeErrorFunction(_) => {} - } - } - - result -} - -pub fn function_c<'a>(env: &mut Env<'a, '_>, body: Expr<'a>) -> Expr<'a> { - let fv = free_variables(&body); - - function_c_help(env, body, fv) -} - -pub fn function_c_help<'a>(env: &mut Env<'a, '_>, body: Expr<'a>, fv: MutSet) -> Expr<'a> { - use Expr::*; - - match body { - Tag { arguments, .. } => { - let symbols = arguments - .iter() - .map(|(x, _)| x) - .copied() - .collect::>(); - - function_c_app(env, &symbols, &fv, body) - } - _ => body, - } -} - -fn function_c_app<'a>( - env: &mut Env<'a, '_>, - arguments: &[Symbol], - orig_fv: &MutSet, - mut application: Expr<'a>, -) -> Expr<'a> { - // in the future, this will need to be a check - let is_owned = true; - - for (i, y) in arguments.iter().rev().enumerate() { - if is_owned { - let mut fv = orig_fv.clone(); - fv.extend(arguments[i..].iter().copied()); - - application = insert_increment(env, *y, fv, application) - } else { - unimplemented!("owned references are not implemented yet") - } - } - - application -} - -fn insert_increment<'a>( - env: &mut Env<'a, '_>, - symbol: Symbol, - live_variables: MutSet, - body: Expr<'a>, -) -> Expr<'a> { - // in the future, this will need to be a check - let is_owned = true; - - if is_owned && !live_variables.contains(&symbol) { - body - } else { - Expr::Inc(symbol, env.arena.alloc(body)) - } -} - -fn insert_decrement<'a>(env: &mut Env<'a, '_>, symbols: &[Symbol], mut body: Expr<'a>) -> Expr<'a> { - // in the future, this will need to be a check - let is_owned = true; - let fv = free_variables(&body); - - for symbol in symbols.iter() { - let is_dead = !fv.contains(&symbol); - - if is_owned && is_dead { - body = Expr::DecAfter(*symbol, env.arena.alloc(body)); - } - } - - body }