diff --git a/compiler/mono/src/expr.rs b/compiler/mono/src/expr.rs index 22b87c3820..60dd4e238e 100644 --- a/compiler/mono/src/expr.rs +++ b/compiler/mono/src/expr.rs @@ -36,6 +36,38 @@ pub struct Proc<'a> { pub ret_layout: Layout<'a>, } +impl<'a> Proc<'a> { + pub fn to_doc<'b, D, A>(&'b self, alloc: &'b D, parens: bool) -> DocBuilder<'b, D, A> + where + D: DocAllocator<'b, A>, + D::Doc: Clone, + A: Clone, + { + let args_doc = self + .args + .iter() + .map(|(_, symbol)| alloc.text(format!("{}", symbol))); + + alloc + .text(format!("procedure {} (", self.name)) + .append(alloc.intersperse(args_doc, ", ")) + .append("):") + .append(alloc.hardline()) + .append(self.body.to_doc(alloc, false).indent(4)) + } + + pub fn to_pretty(&self, width: usize) -> String { + let allocator = BoxAllocator; + let mut w = std::vec::Vec::new(); + self.to_doc::<_, ()>(&allocator, false) + .1 + .render(width, &mut w) + .unwrap(); + w.push(b'\n'); + String::from_utf8(w).unwrap() + } +} + #[derive(Clone, Debug, PartialEq, Default)] pub struct Procs<'a> { pub partial_procs: MutMap>, @@ -368,433 +400,6 @@ pub enum Expr<'a> { RuntimeError(&'a str), } -fn function_r<'a>(env: &mut Env<'a, '_>, body: &'a Expr<'a>) -> Expr<'a> { - use Expr::*; - - 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); - - for (tag, stores, branch) in branches.iter() { - let new_branch = function_d(env, *cond_symbol, stack_size as _, branch); - - new_branches.push((*tag, *stores, new_branch)); - } - - let new_default_branch = ( - default_branch.0, - &*env.arena.alloc(function_d( - env, - *cond_symbol, - stack_size as _, - default_branch.1, - )), - ); - - 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 { - 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)), - ); - - let new_fail = ( - fail.0, - &*env - .arena - .alloc(function_d(env, *cond_symbol, stack_size as _, fail.1)), - ); - - 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, - } - } - - 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 - | LoadWithoutIncrement(_) - | FunctionPointer(_, _) - | RuntimeError(_) - | RuntimeErrorFunction(_) => body.clone(), - - Reset(_, _) | Reuse(_, _) => unreachable!("reset/reuse should not have been inserted yet!"), - } -} - -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, - } - */ -} - -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::*; - - 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) - } - } - - Array { .. } | Struct(_) => { - // TODO - Err(body) - } - - 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())); - } - } - } - - 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(), - }); - - if has_been_reused { - Ok(result) - } else { - Err(result) - } - } - - Cond { - cond_symbol, - cond_layout, - branching_symbol, - branching_layout, - pass, - 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, - }, - ); - - 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 result = env.arena.alloc(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, - }); - - if has_been_reused { - Ok(result) - } else { - Err(result) - } - } - - DecAfter(symbol, expr) => { - let new_expr = function_s(env, w, stack_size, expr)?; - - Ok(env.arena.alloc(DecAfter(*symbol, new_expr))) - } - - Store(stores, expr) => { - let new_expr = function_s(env, w, stack_size, expr)?; - - Ok(env.arena.alloc(Store(*stores, 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 - | LoadWithoutIncrement(_) - | FunctionPointer(_, _) - | RuntimeError(_) - | RuntimeErrorFunction(_) => Err(body), - - Reset(_, _) | Reuse(_, _) => { - unreachable!("reset/reuse should not have been introduced yet") - } - } -} - -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, _) | LoadWithoutIncrement(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) => { - 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 (expr, _) in arguments.iter() { - stack.push(expr); - } - } - - 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 -} - #[derive(Clone, Debug)] pub enum MonoProblem { PatternProblem(crate::pattern::Error), @@ -806,10 +411,12 @@ impl<'a> Expr<'a> { can_expr: roc_can::expr::Expr, procs: &mut Procs<'a>, ) -> Self { + use crate::reset_reuse; + let mut layout_cache = LayoutCache::default(); let result = from_can(env, can_expr, procs, &mut layout_cache); - function_r(env, env.arena.alloc(result)) + reset_reuse::function_r(env, env.arena.alloc(result)) } pub fn to_doc<'b, D, A>(&'b self, alloc: &'b D, parens: bool) -> DocBuilder<'b, D, A> @@ -923,6 +530,15 @@ impl<'a> Expr<'a> { alloc.intersperse(it, alloc.space()) } + Array { elems, .. } if elems.is_empty() => alloc.text("[]"), + Array { elems, .. } => { + let it = elems.iter().map(|expr| expr.to_doc(alloc, true)); + + alloc + .text("[ ") + .append(alloc.intersperse(it, ", ")) + .append(" ]") + } AccessAtIndex { index, expr, .. } if parens => alloc .text(format!("(Access @{} ", index)) .append(expr.to_doc(alloc, false)) @@ -1208,11 +824,6 @@ fn from_can<'a>( let symbols = roc_can::pattern::symbols_from_pattern(&def.loc_pattern.value); let mut result = from_can_defs(env, vec![*def], *ret_expr, layout_cache, procs); - // TODO is order important here? - for symbol in symbols { - result = decrement_refcount(env, symbol, result); - } - result } @@ -1802,11 +1413,16 @@ fn from_can_defs<'a>( } // At this point, it's safe to assume we aren't assigning a Closure to a def. // Extract Procs from the def body and the ret expression, and return the result! - let ret = from_can(env, ret_expr.value, procs, layout_cache); + let mut ret = from_can(env, ret_expr.value, procs, layout_cache); if stored.is_empty() { ret } else { + // NOTE for scoping reasons, it's important that the refcount decrement is inside of the + // Store, not outside it. + for (symbol, _, _) in stored.iter() { + ret = decrement_refcount(env, *symbol, ret); + } Expr::Store(stored.into_bump_slice(), arena.alloc(ret)) } } diff --git a/compiler/mono/src/layout.rs b/compiler/mono/src/layout.rs index ee050bf13b..68491334c9 100644 --- a/compiler/mono/src/layout.rs +++ b/compiler/mono/src/layout.rs @@ -346,8 +346,8 @@ fn layout_from_flat_type<'a>( Ok(layout_from_tag_union(arena, tags, subs, pointer_size)) } - RecursiveTagUnion(_, _, _) => { - panic!("TODO make Layout for non-empty Tag Union"); + RecursiveTagUnion(_rec_var, _tags, _ext_var) => { + panic!("TODO make Layout for empty RecursiveTagUnion"); } EmptyTagUnion => { panic!("TODO make Layout for empty Tag Union"); diff --git a/compiler/mono/src/lib.rs b/compiler/mono/src/lib.rs index 8b78927d46..391c98a3e1 100644 --- a/compiler/mono/src/lib.rs +++ b/compiler/mono/src/lib.rs @@ -13,6 +13,7 @@ pub mod expr; pub mod layout; +pub mod reset_reuse; // Temporary, while we can build up test cases and optimize the exhaustiveness checking. // For now, following this warning's advice will lead to nasty type inference errors. diff --git a/compiler/mono/src/reset_reuse.rs b/compiler/mono/src/reset_reuse.rs new file mode 100644 index 0000000000..54a2c11fec --- /dev/null +++ b/compiler/mono/src/reset_reuse.rs @@ -0,0 +1,482 @@ +use crate::expr::Env; +use crate::expr::Expr; + +use crate::layout::{Builtin, Layout}; +use bumpalo::collections::Vec; +use bumpalo::Bump; +use roc_collections::all::{MutMap, MutSet}; +use roc_module::ident::{Ident, Lowercase, TagName}; +use roc_module::symbol::{IdentIds, ModuleId, Symbol}; +use roc_region::all::{Located, Region}; + +use Expr::*; +/* +R : FnBodypure → FnBodyRC +R(let x = e; F ) = let x = e; R(F ) +R(ret x) = ret x +R(case x of F ) = case x of D(x,ni +, R(Fi)) +where ni = #fields of x in i-th branch +D : Var × N × FnBodyRC → FnBodyRC +D(z,n, case x of F ) = case x of D(z,n, F ) +D(z,n, ret x) = ret x +D(z,n, let x = e; F ) = let x = e; D(z,n, F ) +if z ∈ e or z ∈ F +D(z,n, F ) = let w = reset z; S(w,n, F ) +otherwise, if S(w,n, F ) , F for a fresh w +D(z,n, F ) = F otherwise +S : Var × N × FnBodyRC → FnBodyRC +S(w,n, let x = ctori y; F ) = let x = reuse w in ctori y; F +if | y |= n +S(w,n, let x = e; F ) = let x = e; S(w,n, F ) otherwise +S(w,n, ret x) = ret x +S(w,n, case x of F ) = case x of S(w,n, F ) + + +Maybe a : [ Nothing, Just a ] + +map : Maybe a -> (a -> b) -> Maybe b +map = \maybe f -> + when maybe is + Nothing -> Nothing + Just x -> Just (f x) + + +map : Maybe a -> (a -> b) -> Maybe b +map = \maybe f -> + when maybe is + Nothing -> + let w = reset maybe + let r = reuse w in AppliedTag("Nothing", vec![]) + Just x -> + let v = f x + let w = reset maybe + let r = reuse w in AppliedTag("Just", vec![v]) +*/ + +pub fn function_r<'a>(env: &mut Env<'a, '_>, body: &'a Expr<'a>) -> Expr<'a> { + use Expr::*; + + 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); + + for (tag, stores, branch) in branches.iter() { + let new_branch = function_d(env, *cond_symbol, stack_size as _, branch); + + new_branches.push((*tag, *stores, new_branch)); + } + + let new_default_branch = ( + default_branch.0, + &*env.arena.alloc(function_d( + env, + *cond_symbol, + stack_size as _, + default_branch.1, + )), + ); + + 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 { + 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)), + ); + + let new_fail = ( + fail.0, + &*env + .arena + .alloc(function_d(env, *cond_symbol, stack_size as _, fail.1)), + ); + + 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, + } + } + + 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 + | LoadWithoutIncrement(_) + | FunctionPointer(_, _) + | RuntimeError(_) + | RuntimeErrorFunction(_) => body.clone(), + + Reset(_, _) | Reuse(_, _) => unreachable!("reset/reuse should not have been inserted yet!"), + } +} + +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, + } + */ +} + +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::*; + + 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) + } + } + + Array { .. } | Struct(_) => { + // TODO + Err(body) + } + + 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())); + } + } + } + + 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(), + }); + + if has_been_reused { + Ok(result) + } else { + Err(result) + } + } + + Cond { + cond_symbol, + cond_layout, + branching_symbol, + branching_layout, + pass, + 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, + }, + ); + + 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 result = env.arena.alloc(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, + }); + + if has_been_reused { + Ok(result) + } else { + Err(result) + } + } + + Store(stores, expr) => { + let new_expr = function_s(env, w, stack_size, expr)?; + + Ok(env.arena.alloc(Store(*stores, new_expr))) + } + + DecAfter(symbol, expr) => { + let new_expr = function_s(env, w, stack_size, expr)?; + + 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 + | LoadWithoutIncrement(_) + | FunctionPointer(_, _) + | RuntimeError(_) + | RuntimeErrorFunction(_) => Err(body), + + Reset(_, _) | Reuse(_, _) => { + unreachable!("reset/reuse should not have been introduced yet") + } + } +} + +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, _) | LoadWithoutIncrement(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) => { + 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 (expr, _) in arguments.iter() { + stack.push(expr); + } + } + + 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 +} diff --git a/compiler/mono/tests/test_mono.rs b/compiler/mono/tests/test_mono.rs index 23abb32f8e..0767730463 100644 --- a/compiler/mono/tests/test_mono.rs +++ b/compiler/mono/tests/test_mono.rs @@ -17,7 +17,7 @@ mod test_mono { use roc_module::ident::TagName; use roc_module::symbol::{Interns, Symbol}; use roc_mono::expr::Expr::{self, *}; - use roc_mono::expr::Procs; + use roc_mono::expr::{InProgressProc, Procs}; use roc_mono::layout; use roc_mono::layout::{Builtin, Layout, LayoutCache}; use roc_types::subs::Subs; @@ -129,7 +129,21 @@ mod test_mono { // Put this module's ident_ids back in the interns interns.all_ident_ids.insert(home, ident_ids); - let result = mono_expr.to_pretty(200); + let mut procs_string = procs + .specialized + .iter() + .map(|(_, value)| { + if let InProgressProc::Done(proc) = value { + proc.to_pretty(200) + } else { + String::new() + } + }) + .collect::>(); + + procs_string.push(mono_expr.to_pretty(200)); + + let result = procs_string.join("\n"); assert_eq!(result, expected); } @@ -221,33 +235,30 @@ mod test_mono { let home = test_home(); let gen_symbol_0 = Interns::from_index(home, 0); - DecAfter( - gen_symbol_0, - &Struct(&[ - ( - CallByName { - name: gen_symbol_0, - layout: Layout::FunctionPointer( - &[Layout::Builtin(Builtin::Int64)], - &Layout::Builtin(Builtin::Int64), - ), - args: &[(Int(4), Layout::Builtin(Int64))], - }, - Layout::Builtin(Int64), - ), - ( - CallByName { - name: gen_symbol_0, - layout: Layout::FunctionPointer( - &[Layout::Builtin(Builtin::Float64)], - &Layout::Builtin(Builtin::Float64), - ), - args: &[(Float(3.14), Layout::Builtin(Float64))], - }, - Layout::Builtin(Float64), - ), - ]), - ) + Struct(&[ + ( + CallByName { + name: gen_symbol_0, + layout: Layout::FunctionPointer( + &[Layout::Builtin(Builtin::Int64)], + &Layout::Builtin(Builtin::Int64), + ), + args: &[(Int(4), Layout::Builtin(Int64))], + }, + Layout::Builtin(Int64), + ), + ( + CallByName { + name: gen_symbol_0, + layout: Layout::FunctionPointer( + &[Layout::Builtin(Builtin::Float64)], + &Layout::Builtin(Builtin::Float64), + ), + args: &[(Float(3.14), Layout::Builtin(Float64))], + }, + Layout::Builtin(Float64), + ), + ]) }, ) } @@ -360,31 +371,28 @@ mod test_mono { let gen_symbol_0 = Interns::from_index(home, 1); let symbol_x = Interns::from_index(home, 0); - DecAfter( - symbol_x, - &Store( - &[( - symbol_x, - Builtin(Str), - Store( - &[( - gen_symbol_0, - Layout::Builtin(layout::Builtin::Int1), - Expr::Bool(true), - )], - &Cond { - cond_symbol: gen_symbol_0, - branching_symbol: gen_symbol_0, - cond_layout: Builtin(Int1), - branching_layout: Builtin(Int1), - pass: (&[] as &[_], &Expr::Str("bar")), - fail: (&[] as &[_], &Expr::Str("foo")), - ret_layout: Builtin(Str), - }, - ), - )], - &Load(symbol_x), - ), + Store( + &[( + symbol_x, + Builtin(Str), + Store( + &[( + gen_symbol_0, + Layout::Builtin(layout::Builtin::Int1), + Expr::Bool(true), + )], + &Cond { + cond_symbol: gen_symbol_0, + branching_symbol: gen_symbol_0, + cond_layout: Builtin(Int1), + branching_layout: Builtin(Int1), + pass: (&[] as &[_], &Expr::Str("bar")), + fail: (&[] as &[_], &Expr::Str("foo")), + ret_layout: Builtin(Str), + }, + ), + )], + &DecAfter(symbol_x, &Load(symbol_x)), ) }, ) @@ -424,30 +432,27 @@ mod test_mono { let gen_symbol_0 = Interns::from_index(home, 0); let struct_layout = Layout::Struct(&[I64_LAYOUT, F64_LAYOUT]); - DecAfter( - gen_symbol_0, - &CallByName { - name: gen_symbol_0, - layout: Layout::FunctionPointer( - &[struct_layout.clone()], - &struct_layout.clone(), - ), - args: &[( - Struct(&[ - ( - CallByName { - name: gen_symbol_0, - layout: Layout::FunctionPointer(&[I64_LAYOUT], &I64_LAYOUT), - args: &[(Int(4), I64_LAYOUT)], - }, - I64_LAYOUT, - ), - (Float(0.1), F64_LAYOUT), - ]), - struct_layout, - )], - }, - ) + CallByName { + name: gen_symbol_0, + layout: Layout::FunctionPointer( + &[struct_layout.clone()], + &struct_layout.clone(), + ), + args: &[( + Struct(&[ + ( + CallByName { + name: gen_symbol_0, + layout: Layout::FunctionPointer(&[I64_LAYOUT], &I64_LAYOUT), + args: &[(Int(4), I64_LAYOUT)], + }, + I64_LAYOUT, + ), + (Float(0.1), F64_LAYOUT), + ]), + struct_layout, + )], + } }, ) } @@ -561,9 +566,9 @@ mod test_mono { let load = Load(var_x); - let store = Store(arena.alloc(stores), arena.alloc(load)); + let dec = DecAfter(var_x, arena.alloc(load)); - DecAfter(var_x, arena.alloc(store)) + Store(arena.alloc(stores), arena.alloc(dec)) }, ); } @@ -587,9 +592,9 @@ mod test_mono { let load = Load(var_x); - let store = Store(arena.alloc(stores), arena.alloc(load)); + let dec = DecAfter(var_x, arena.alloc(load)); - DecAfter(var_x, arena.alloc(store)) + Store(arena.alloc(stores), arena.alloc(dec)) }, ); } @@ -615,9 +620,8 @@ mod test_mono { let load = Load(var_x); - let store = Store(arena.alloc(stores), arena.alloc(load)); - - DecAfter(var_x, arena.alloc(store)) + let dec = DecAfter(var_x, arena.alloc(load)); + Store(arena.alloc(stores), arena.alloc(dec)) }, ); } @@ -843,8 +847,10 @@ mod test_mono { fn maybe_map_to_string() { compiles_to_string( r#" - maybe : [ Nothing, Just Int ] - maybe = Just 3 + Maybe a : [ Nothing, Just a ] + + maybe : Maybe Int + maybe = Just 0x3 when maybe is Just x -> Just (x + 1) @@ -852,19 +858,22 @@ mod test_mono { "#, indoc!( r#" - Store Test.0: Just 0i64 3i64 - Store Test.0: Load Test.0 - Store Test.2: Lowlevel.And (Lowlevel.Eq 0i64 (Access @0 Load Test.0)) true + procedure Num.14 (#Attr.2, #Attr.3): + Lowlevel.NumAdd (Load #Attr.2) (Load #Attr.3) - if Test.2 then - Reset Test.0 - Reuse Test.0 + Store Test.1: Just 0i64 3i64 + Store Test.1: Load Test.1 + Store Test.3: Lowlevel.And (Lowlevel.Eq 0i64 (Access @0 Load Test.1)) true + + if Test.3 then + Reset Test.1 + Reuse Test.1 Just 0i64 *magic* else - Reset Test.0 - Reuse Test.0 + Reset Test.1 + Reuse Test.1 Nothing 1i64 - Dec Test.0 + Dec Test.1 "# ), ) @@ -946,4 +955,25 @@ mod test_mono { ), ) } + + #[test] + fn list_length() { + compiles_to_string( + r#" + x = [ 1,2,3 ] + + List.len x + "#, + indoc!( + r#" + procedure List.7 (#Attr.2): + Lowlevel.ListLen (Load #Attr.2) + + Store Test.0: [ 1i64, 2i64, 3i64 ] + *magic* + Dec Test.0 + "# + ), + ) + } }