From e4f0dc889777b8841bf2945778ec7f190893e75b Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 16 Feb 2021 14:42:16 +0100 Subject: [PATCH 01/18] crude prototype --- compiler/load/src/file.rs | 14 ++++++-- compiler/mono/src/borrow.rs | 34 +++++++++++++++++++ compiler/mono/src/ir.rs | 68 +++++++++++++++++++++++++++++++------ 3 files changed, 103 insertions(+), 13 deletions(-) diff --git a/compiler/load/src/file.rs b/compiler/load/src/file.rs index a385eee464..ea760f8c28 100644 --- a/compiler/load/src/file.rs +++ b/compiler/load/src/file.rs @@ -751,6 +751,7 @@ enum Msg<'a> { layout_cache: LayoutCache<'a>, external_specializations_requested: MutMap, procedures: MutMap<(Symbol, Layout<'a>), Proc<'a>>, + passed_by_pointer: MutMap<(Symbol, Layout<'a>), Symbol>, problems: Vec, module_timing: ModuleTiming, subs: Subs, @@ -781,6 +782,7 @@ struct State<'a> { pub module_cache: ModuleCache<'a>, pub dependencies: Dependencies<'a>, pub procedures: MutMap<(Symbol, Layout<'a>), Proc<'a>>, + pub passed_by_pointer: MutMap<(Symbol, Layout<'a>), Symbol>, pub exposed_to_host: MutMap, /// This is the "final" list of IdentIds, after canonicalization and constraint gen @@ -1403,6 +1405,7 @@ where module_cache: ModuleCache::default(), dependencies: Dependencies::default(), procedures: MutMap::default(), + passed_by_pointer: MutMap::default(), exposed_to_host: MutMap::default(), exposed_types, headers_parsed, @@ -1931,6 +1934,7 @@ fn update<'a>( mut ident_ids, subs, procedures, + passed_by_pointer, external_specializations_requested, problems, module_timing, @@ -1945,12 +1949,17 @@ fn update<'a>( .notify(module_id, Phase::MakeSpecializations); state.procedures.extend(procedures); + state.passed_by_pointer.extend(passed_by_pointer); state.timings.insert(module_id, module_timing); if state.dependencies.solved_all() && state.goal_phase == Phase::MakeSpecializations { debug_assert!(work.is_empty(), "still work remaining {:?}", &work); - Proc::insert_refcount_operations(arena, &mut state.procedures); + Proc::insert_refcount_operations( + arena, + &mut state.procedures, + &state.passed_by_pointer, + ); Proc::optimize_refcount_operations( arena, @@ -3621,7 +3630,7 @@ fn make_specializations<'a>( ); let external_specializations_requested = procs.externals_we_need.clone(); - let procedures = procs.get_specialized_procs_without_rc(mono_env.arena); + let (procedures, passed_by_pointer) = procs.get_specialized_procs_without_rc(mono_env.arena); let make_specializations_end = SystemTime::now(); module_timing.make_specializations = make_specializations_end @@ -3633,6 +3642,7 @@ fn make_specializations<'a>( ident_ids, layout_cache, procedures, + passed_by_pointer, problems: mono_problems, subs, external_specializations_requested, diff --git a/compiler/mono/src/borrow.rs b/compiler/mono/src/borrow.rs index 14f6687ed0..4399d8aa18 100644 --- a/compiler/mono/src/borrow.rs +++ b/compiler/mono/src/borrow.rs @@ -9,11 +9,21 @@ use roc_module::symbol::Symbol; pub fn infer_borrow<'a>( arena: &'a Bump, procs: &MutMap<(Symbol, Layout<'a>), Proc<'a>>, + passed_by_pointer: &MutMap<(Symbol, Layout<'a>), Symbol>, ) -> ParamMap<'a> { let mut param_map = ParamMap { items: MutMap::default(), }; + for (key, other) in passed_by_pointer { + if let Some(proc) = procs.get(key) { + let mut proc: Proc = proc.clone(); + proc.name = *other; + + param_map.visit_proc_always_owned(arena, &proc); + } + } + for proc in procs.values() { param_map.visit_proc(arena, proc); } @@ -125,6 +135,21 @@ impl<'a> ParamMap<'a> { .into_bump_slice() } + fn init_borrow_args_always_owned( + arena: &'a Bump, + ps: &'a [(Layout<'a>, Symbol)], + ) -> &'a [Param<'a>] { + Vec::from_iter_in( + ps.iter().map(|(layout, symbol)| Param { + borrow: false, + layout: layout.clone(), + symbol: *symbol, + }), + arena, + ) + .into_bump_slice() + } + fn visit_proc(&mut self, arena: &'a Bump, proc: &Proc<'a>) { self.items.insert( Key::Declaration(proc.name), @@ -134,6 +159,15 @@ impl<'a> ParamMap<'a> { self.visit_stmt(arena, proc.name, &proc.body); } + fn visit_proc_always_owned(&mut self, arena: &'a Bump, proc: &Proc<'a>) { + self.items.insert( + Key::Declaration(proc.name), + Self::init_borrow_args_always_owned(arena, proc.args), + ); + + self.visit_stmt(arena, proc.name, &proc.body); + } + fn visit_stmt(&mut self, arena: &'a Bump, _fnid: Symbol, stmt: &Stmt<'a>) { use Stmt::*; diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 7b68fc593b..698e1332d0 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -172,8 +172,20 @@ impl<'a> Proc<'a> { pub fn insert_refcount_operations( arena: &'a Bump, procs: &mut MutMap<(Symbol, Layout<'a>), Proc<'a>>, + passed_by_pointer: &MutMap<(Symbol, Layout<'a>), Symbol>, ) { - let borrow_params = arena.alloc(crate::borrow::infer_borrow(arena, procs)); + let borrow_params = + arena.alloc(crate::borrow::infer_borrow(arena, procs, passed_by_pointer)); + + for (key, other) in passed_by_pointer { + if let Some(proc) = procs.get(key) { + let mut proc: Proc = proc.clone(); + proc.name = *other; + + let layout = key.1.clone(); + procs.insert((*other, layout), proc); + } + } for (_, proc) in procs.iter_mut() { crate::inc_dec::visit_proc(arena, borrow_params, proc); @@ -255,6 +267,7 @@ pub struct Procs<'a> { pub runtime_errors: MutMap, pub externals_others_need: ExternalSpecializations, pub externals_we_need: MutMap, + pub passed_by_pointer: MutMap<(Symbol, Layout<'a>), Symbol>, } impl<'a> Default for Procs<'a> { @@ -267,6 +280,7 @@ impl<'a> Default for Procs<'a> { runtime_errors: MutMap::default(), externals_we_need: MutMap::default(), externals_others_need: ExternalSpecializations::default(), + passed_by_pointer: MutMap::default(), } } } @@ -314,7 +328,10 @@ impl<'a> Procs<'a> { pub fn get_specialized_procs_without_rc( self, arena: &'a Bump, - ) -> MutMap<(Symbol, Layout<'a>), Proc<'a>> { + ) -> ( + MutMap<(Symbol, Layout<'a>), Proc<'a>>, + MutMap<(Symbol, Layout<'a>), Symbol>, + ) { let mut result = MutMap::with_capacity_and_hasher(self.specialized.len(), default_hasher()); for (key, in_prog_proc) in self.specialized.into_iter() { @@ -337,7 +354,7 @@ impl<'a> Procs<'a> { } } - result + (result, self.passed_by_pointer) } // TODO investigate make this an iterator? @@ -366,7 +383,11 @@ impl<'a> Procs<'a> { } } - let borrow_params = arena.alloc(crate::borrow::infer_borrow(arena, &result)); + let borrow_params = arena.alloc(crate::borrow::infer_borrow( + arena, + &result, + &self.passed_by_pointer, + )); for (_, proc) in result.iter_mut() { crate::inc_dec::visit_proc(arena, borrow_params, proc); @@ -406,7 +427,11 @@ impl<'a> Procs<'a> { } } - let borrow_params = arena.alloc(crate::borrow::infer_borrow(arena, &result)); + let borrow_params = arena.alloc(crate::borrow::infer_borrow( + arena, + &result, + &self.passed_by_pointer, + )); for (_, proc) in result.iter_mut() { crate::inc_dec::visit_proc(arena, borrow_params, proc); @@ -2435,7 +2460,8 @@ fn specialize_naked_symbol<'a>( match hole { Stmt::Jump(_, _) => todo!("not sure what to do in this case yet"), _ => { - let expr = Expr::FunctionPointer(symbol, layout.clone()); + let expr = + call_by_pointer(env, procs, layout_cache, symbol, layout.clone()); let new_symbol = env.unique_symbol(); return Stmt::Let( new_symbol, @@ -3523,7 +3549,7 @@ pub fn with_hole<'a>( // TODO should the let have layout Pointer? Stmt::Let( assigned, - Expr::FunctionPointer(name, layout.clone()), + call_by_pointer(env, procs, layout_cache, name, layout.clone()), layout, hole, ) @@ -3720,7 +3746,13 @@ pub fn with_hole<'a>( } } - let expr = Expr::FunctionPointer(name, function_ptr_layout.clone()); + let expr = call_by_pointer( + env, + procs, + layout_cache, + name, + function_ptr_layout.clone(), + ); stmt = Stmt::Let( function_pointer, @@ -3746,7 +3778,7 @@ pub fn with_hole<'a>( // TODO should the let have layout Pointer? Stmt::Let( assigned, - Expr::FunctionPointer(name, layout.clone()), + call_by_pointer(env, procs, layout_cache, name, layout.clone()), layout, hole, ) @@ -5327,7 +5359,7 @@ fn handle_variable_aliasing<'a>( .from_var(env.arena, variable, env.subs) .unwrap(); - let expr = Expr::FunctionPointer(right, layout.clone()); + let expr = call_by_pointer(env, procs, layout_cache, right, layout.clone()); Stmt::Let(left, expr, layout, env.arena.alloc(result)) } else { substitute_in_exprs(env.arena, &mut result, left, right); @@ -5375,7 +5407,7 @@ fn reuse_function_symbol<'a>( // an imported symbol is always a function pointer: // either it's a function, or a top-level 0-argument thunk - let expr = Expr::FunctionPointer(original, layout.clone()); + let expr = call_by_pointer(env, procs, layout_cache, original, layout.clone()); return Stmt::Let(symbol, expr, layout, env.arena.alloc(result)); } _ => { @@ -5572,6 +5604,20 @@ where result } +fn call_by_pointer<'a>( + env: &mut Env<'a, '_>, + procs: &mut Procs<'a>, + layout_cache: &mut LayoutCache<'a>, + symbol: Symbol, + layout: Layout<'a>, +) -> Expr<'a> { + procs + .passed_by_pointer + .insert((symbol, layout.clone()), symbol); + + Expr::FunctionPointer(symbol, layout) +} + fn add_needed_external<'a>( procs: &mut Procs<'a>, env: &mut Env<'a, '_>, From 3b267392b34195df91de9cad5baca729538f67be Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 16 Feb 2021 15:44:27 +0100 Subject: [PATCH 02/18] make closure borrowed example work --- cli/tests/cli_run.rs | 12 ++++++++++++ compiler/mono/src/ir.rs | 18 +++++++++++++----- examples/benchmarks/ClosureBorrowed.roc | 15 +++++++++++++++ 3 files changed, 40 insertions(+), 5 deletions(-) create mode 100644 examples/benchmarks/ClosureBorrowed.roc diff --git a/cli/tests/cli_run.rs b/cli/tests/cli_run.rs index ef11fade5d..37406bc71e 100644 --- a/cli/tests/cli_run.rs +++ b/cli/tests/cli_run.rs @@ -209,6 +209,18 @@ mod cli_run { ); } + #[test] + #[serial(closure_borrowed)] + fn run_closure_borrowed_not_optimized() { + check_output( + &example_file("benchmarks", "ClosureBorrowed.roc"), + "closure-borrowed", + &[], + "", + true, + ); + } + // #[test] // #[serial(effect)] // fn run_effect_unoptimized() { diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 698e1332d0..a8e427c079 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -17,7 +17,7 @@ use roc_types::subs::{Content, FlatType, Subs, Variable}; use std::collections::HashMap; use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder}; -pub const PRETTY_PRINT_IR_SYMBOLS: bool = false; +pub const PRETTY_PRINT_IR_SYMBOLS: bool = true; macro_rules! return_on_layout_error { ($env:expr, $layout_result:expr) => { @@ -5505,7 +5505,13 @@ fn reuse_function_symbol<'a>( } } - let expr = Expr::FunctionPointer(original, function_ptr_layout.clone()); + let expr = call_by_pointer( + env, + procs, + layout_cache, + original, + function_ptr_layout.clone(), + ); stmt = Stmt::Let( function_pointer, @@ -5527,7 +5533,7 @@ fn reuse_function_symbol<'a>( Stmt::Let( symbol, - Expr::FunctionPointer(original, layout.clone()), + call_by_pointer(env, procs, layout_cache, original, layout.clone()), layout, env.arena.alloc(result), ) @@ -5611,11 +5617,13 @@ fn call_by_pointer<'a>( symbol: Symbol, layout: Layout<'a>, ) -> Expr<'a> { + let other = env.unique_symbol(); + procs .passed_by_pointer - .insert((symbol, layout.clone()), symbol); + .insert((symbol, layout.clone()), other); - Expr::FunctionPointer(symbol, layout) + Expr::FunctionPointer(other, layout) } fn add_needed_external<'a>( diff --git a/examples/benchmarks/ClosureBorrowed.roc b/examples/benchmarks/ClosureBorrowed.roc new file mode 100644 index 0000000000..ebe3de2a39 --- /dev/null +++ b/examples/benchmarks/ClosureBorrowed.roc @@ -0,0 +1,15 @@ +app "closure-borrowed" + packages { base: "platform" } + imports [base.Task] + provides [ main ] to base + +# see https://github.com/rtfeldman/roc/issues/985 + +main : Task.Task {} [] +main = + Task.succeed (foo toUnitBorrowed "a long string such that it's malloced") + |> Task.map (\_ -> {}) + +toUnitBorrowed = \x -> Str.countGraphemes x + +foo = \f, x -> f x From 1ef0d82c5cf7e7a7c584ca0ff8b213ce5aff4f50 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 16 Feb 2021 16:26:04 +0100 Subject: [PATCH 03/18] apply created closure immediately --- compiler/gen/src/llvm/build.rs | 23 ++++++++++++++++++++--- compiler/gen/tests/gen_primitives.rs | 21 ++++++++++++++++++++- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/compiler/gen/src/llvm/build.rs b/compiler/gen/src/llvm/build.rs index bb69434428..30dcc86ba9 100644 --- a/compiler/gen/src/llvm/build.rs +++ b/compiler/gen/src/llvm/build.rs @@ -692,7 +692,9 @@ pub fn build_exp_call<'a, 'ctx, 'env>( ) } - CallType::ByPointer { name, .. } => { + CallType::ByPointer { + name, full_layout, .. + } => { let sub_expr = load_symbol(scope, name); let mut arg_vals: Vec = @@ -702,10 +704,25 @@ pub fn build_exp_call<'a, 'ctx, 'env>( arg_vals.push(load_symbol(scope, arg)); } - let call = match sub_expr { - BasicValueEnum::PointerValue(ptr) => { + let call = match (full_layout, sub_expr) { + (_, BasicValueEnum::PointerValue(ptr)) => { env.builder.build_call(ptr, arg_vals.as_slice(), "tmp") } + (Layout::Closure(_, _, _), BasicValueEnum::StructValue(closure_struct)) => { + let fpointer = env + .builder + .build_extract_value(closure_struct, 0, "fpointer") + .unwrap() + .into_pointer_value(); + + let closure_data = env + .builder + .build_extract_value(closure_struct, 1, "closure_data") + .unwrap(); + + arg_vals.push(closure_data); + env.builder.build_call(fpointer, arg_vals.as_slice(), "tmp") + } non_ptr => { panic!( "Tried to call by pointer, but encountered a non-pointer: {:?}", diff --git a/compiler/gen/tests/gen_primitives.rs b/compiler/gen/tests/gen_primitives.rs index 59b5760dda..3a247fad7e 100644 --- a/compiler/gen/tests/gen_primitives.rs +++ b/compiler/gen/tests/gen_primitives.rs @@ -1952,7 +1952,7 @@ mod gen_primitives { main = x : Tree F64 x = singleton 3 - when x is + when x is Tree 3.0 _ -> True _ -> False "# @@ -2215,4 +2215,23 @@ mod gen_primitives { i64 ); } + + #[test] + fn build_then_apply_closure() { + assert_evals_to!( + indoc!( + r#" + app "test" provides [ main ] to "./platform" + + main : Str + main = + x = "long string that is malloced" + + (\_ -> x) {} + "# + ), + "long string that is malloced", + &'static str + ); + } } From a08485b0eea54ad60850871511bcfcbfee000b85 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 16 Feb 2021 17:07:33 +0100 Subject: [PATCH 04/18] fix alias error incorrectly reported --- cli/src/build.rs | 41 ++++++++++++++++++++++--------------- compiler/unify/src/unify.rs | 4 +++- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/cli/src/build.rs b/cli/src/build.rs index 681608081c..1c0550025f 100644 --- a/cli/src/build.rs +++ b/cli/src/build.rs @@ -117,22 +117,24 @@ pub fn build_file<'a>( report_timing(buf, "Generate LLVM IR", code_gen_timing.code_gen); report_timing(buf, "Emit .o file", code_gen_timing.emit_o_file); - println!( - "\n\nCompilation finished! Here's how long each module took to compile:\n\n{}", - buf - ); - - println!("\nSuccess! 🎉\n\n\t➡ {}\n", app_o_file.display()); - let compilation_end = compilation_start.elapsed().unwrap(); let size = std::fs::metadata(&app_o_file).unwrap().len(); - println!( - "Finished compilation and code gen in {} ms\n\nProduced a app.o file of size {:?}\n", - compilation_end.as_millis(), - size, - ); + if emit_debug_info { + println!( + "\n\nCompilation finished! Here's how long each module took to compile:\n\n{}", + buf + ); + + println!("\nSuccess! 🎉\n\n\t➡ {}\n", app_o_file.display()); + + println!( + "Finished compilation and code gen in {} ms\n\nProduced a app.o file of size {:?}\n", + compilation_end.as_millis(), + size, + ); + } // Step 2: link the precompiled host and compiled app let mut host_input_path = PathBuf::from(cwd); @@ -144,10 +146,13 @@ pub fn build_file<'a>( let rebuild_host_start = SystemTime::now(); rebuild_host(host_input_path.as_path()); let rebuild_host_end = rebuild_host_start.elapsed().unwrap(); - println!( - "Finished rebuilding the host in {} ms\n", - rebuild_host_end.as_millis() - ); + + if emit_debug_info { + println!( + "Finished rebuilding the host in {} ms\n", + rebuild_host_end.as_millis() + ); + } // TODO try to move as much of this linking as possible to the precompiled // host, to minimize the amount of host-application linking required. @@ -168,7 +173,9 @@ pub fn build_file<'a>( }); let link_end = link_start.elapsed().unwrap(); - println!("Finished linking in {} ms\n", link_end.as_millis()); + if emit_debug_info { + println!("Finished linking in {} ms\n", link_end.as_millis()); + } // Clean up the leftover .o file from the Roc, if possible. // (If cleaning it up fails, that's fine. No need to take action.) diff --git a/compiler/unify/src/unify.rs b/compiler/unify/src/unify.rs index 96935b1e90..8e4689657d 100644 --- a/compiler/unify/src/unify.rs +++ b/compiler/unify/src/unify.rs @@ -190,7 +190,9 @@ fn unify_alias( problems.extend(unify_pool(subs, pool, *l_var, *r_var)); } - problems.extend(merge(subs, &ctx, other_content.clone())); + if problems.is_empty() { + problems.extend(merge(subs, &ctx, other_content.clone())); + } if problems.is_empty() { problems.extend(unify_pool(subs, pool, real_var, *other_real_var)); From 22a9779fe4a090d08550d0d9983d50ac7fd53d25 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 16 Feb 2021 17:22:06 +0100 Subject: [PATCH 05/18] test case for alias diff reporting --- compiler/mono/src/ir.rs | 2 +- compiler/reporting/tests/test_reporting.rs | 273 ++++++++++++--------- 2 files changed, 156 insertions(+), 119 deletions(-) diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index a8e427c079..2c400ef411 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -17,7 +17,7 @@ use roc_types::subs::{Content, FlatType, Subs, Variable}; use std::collections::HashMap; use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder}; -pub const PRETTY_PRINT_IR_SYMBOLS: bool = true; +pub const PRETTY_PRINT_IR_SYMBOLS: bool = false; macro_rules! return_on_layout_error { ($env:expr, $layout_result:expr) => { diff --git a/compiler/reporting/tests/test_reporting.rs b/compiler/reporting/tests/test_reporting.rs index f4f9c0f8b2..0feda26ada 100644 --- a/compiler/reporting/tests/test_reporting.rs +++ b/compiler/reporting/tests/test_reporting.rs @@ -1132,7 +1132,7 @@ mod test_reporting { But the type annotation on `x` says it should be: - Int b + Int a Tip: You can convert between Int and Float using functions like `Num.toFloat` and `Num.round`. @@ -1171,7 +1171,7 @@ mod test_reporting { But the type annotation on `x` says it should be: - Int b + Int a Tip: You can convert between Int and Float using functions like `Num.toFloat` and `Num.round`. @@ -1207,7 +1207,7 @@ mod test_reporting { But the type annotation on `x` says it should be: - Int b + Int a Tip: You can convert between Int and Float using functions like `Num.toFloat` and `Num.round`. @@ -1541,7 +1541,7 @@ mod test_reporting { But the type annotation says it should be: - { x : Int b } + { x : Int a } Tip: You can convert between Int and Float using functions like `Num.toFloat` and `Num.round`. @@ -4041,9 +4041,9 @@ mod test_reporting { indoc!( r#" ── PARSE PROBLEM ─────────────────────────────────────────────────────────────── - + Unexpected token : - + 1│ f :: I64 ^ "# @@ -4061,7 +4061,7 @@ mod test_reporting { indoc!( r#" x = 3 - y = + y = x == 5 Num.add 1 2 @@ -4071,12 +4071,12 @@ mod test_reporting { indoc!( r#" ── TOO MANY ARGS ─────────────────────────────────────────────────────────────── - + The `add` function expects 2 arguments, but it got 4 instead: - + 4│ Num.add 1 2 ^^^^^^^ - + Are there any missing commas? Or missing parentheses? "# ), @@ -4096,9 +4096,9 @@ mod test_reporting { indoc!( r#" ── PARSE PROBLEM ─────────────────────────────────────────────────────────────── - + Unexpected token : - + 2│ 5 ** 3 ^ "# @@ -4117,12 +4117,12 @@ mod test_reporting { indoc!( r#" ── UNFINISHED TAG UNION TYPE ─────────────────────────────────────────────────── - + I just started parsing a tag union type, but I got stuck here: - + 1│ f : [ ^ - + Tag unions look like [ Many I64, None ], so I was expecting to see a tag name next. "# @@ -4135,18 +4135,18 @@ mod test_reporting { report_problem_as( indoc!( r#" - f : [ Yes, + f : [ Yes, "# ), indoc!( r#" ── UNFINISHED TAG UNION TYPE ─────────────────────────────────────────────────── - + I am partway through parsing a tag union type, but I got stuck here: - - 1│ f : [ Yes, + + 1│ f : [ Yes, ^ - + I was expecting to see a closing square bracket before this, so try adding a ] and see if that helps? "# @@ -4159,20 +4159,20 @@ mod test_reporting { report_problem_as( indoc!( r#" - f : [ lowercase ] + f : [ lowercase ] "# ), indoc!( r#" ── WEIRD TAG NAME ────────────────────────────────────────────────────────────── - + I am partway through parsing a tag union type, but I got stuck here: - - 1│ f : [ lowercase ] + + 1│ f : [ lowercase ] ^ - + I was expecting to see a tag name. - + Hint: Tag names start with an uppercase letter, like Err or Green. "# ), @@ -4184,20 +4184,20 @@ mod test_reporting { report_problem_as( indoc!( r#" - f : [ Good, bad ] + f : [ Good, bad ] "# ), indoc!( r#" ── WEIRD TAG NAME ────────────────────────────────────────────────────────────── - + I am partway through parsing a tag union type, but I got stuck here: - - 1│ f : [ Good, bad ] + + 1│ f : [ Good, bad ] ^ - + I was expecting to see a tag name. - + Hint: Tag names start with an uppercase letter, like Err or Green. "# ), @@ -4215,12 +4215,12 @@ mod test_reporting { indoc!( r#" ── UNFINISHED RECORD TYPE ────────────────────────────────────────────────────── - + I just started parsing a record type, but I got stuck here: - + 1│ f : { ^ - + Record types look like { name : String, age : Int }, so I was expecting to see a field name next. "# @@ -4240,15 +4240,15 @@ mod test_reporting { indoc!( r#" ── UNFINISHED RECORD TYPE ────────────────────────────────────────────────────── - + I am partway through parsing a record type, but I got stuck here: - + 1│ f : { ^ - + I was expecting to see a closing curly brace before this, so try adding a } and see if that helps? - + Note: I may be confused by indentation "# ), @@ -4260,18 +4260,18 @@ mod test_reporting { report_problem_as( indoc!( r#" - f : { a: Int, + f : { a: Int, "# ), indoc!( r#" ── UNFINISHED RECORD TYPE ────────────────────────────────────────────────────── - + I am partway through parsing a record type, but I got stuck here: - - 1│ f : { a: Int, + + 1│ f : { a: Int, ^ - + I was expecting to see a closing curly brace before this, so try adding a } and see if that helps? "# @@ -4291,13 +4291,13 @@ mod test_reporting { indoc!( r#" ── NEED MORE INDENTATION ─────────────────────────────────────────────────────── - + I am partway through parsing a record type, but I got stuck here: - + 1│ f : { a: Int 2│ } ^ - + I need this curly brace to be indented more. Try adding more spaces before it! "# @@ -4310,19 +4310,19 @@ mod test_reporting { report_problem_as( indoc!( r#" - f : { if : I64 } + f : { if : I64 } "# ), indoc!( r#" ── UNFINISHED RECORD TYPE ────────────────────────────────────────────────────── - + I just started parsing a record type, but I got stuck on this field name: - - 1│ f : { if : I64 } + + 1│ f : { if : I64 } ^^ - + Looks like you are trying to use `if` as a field name, but that is a reserved word. Try using a different name! "# @@ -4342,12 +4342,12 @@ mod test_reporting { indoc!( r#" ── UNFINISHED RECORD TYPE ────────────────────────────────────────────────────── - + I am partway through parsing a record type, but I got stuck here: - + 1│ f : { foo bar } ^ - + I was expecting to see a colon, question mark, comma or closing curly brace. "# @@ -4363,12 +4363,12 @@ mod test_reporting { indoc!( r#" ── TAB CHARACTER ─────────────────────────────────────────────────────────────── - + I encountered a tab character - + 1│ f : { foo } ^ - + Tab characters are not allowed. "# ), @@ -4383,12 +4383,12 @@ mod test_reporting { indoc!( r#" ── TAB CHARACTER ─────────────────────────────────────────────────────────────── - + I encountered a tab character - + 1│ f : { foo } ^ - + Tab characters are not allowed. "# ), @@ -4407,12 +4407,12 @@ mod test_reporting { indoc!( r#" ── UNFINISHED RECORD TYPE ────────────────────────────────────────────────────── - + I am partway through parsing a record type, but I got stuck here: - - 1│ f : { a: Int, + + 1│ f : { a: Int, ^ - + I was expecting to see a closing curly brace before this, so try adding a } and see if that helps? "# @@ -4431,13 +4431,13 @@ mod test_reporting { indoc!( r#" ── UNFINISHED PARENTHESES ────────────────────────────────────────────────────── - + I am partway through parsing a type in parentheses, but I got stuck here: - + 1│ f : ( I64 ^ - + I was expecting to see a closing parenthesis before this, so try adding a ) and see if that helps? "# @@ -4450,18 +4450,18 @@ mod test_reporting { report_problem_as( indoc!( r#" - f : Foo..Bar + f : Foo..Bar "# ), indoc!( r#" ── DOUBLE DOT ────────────────────────────────────────────────────────────────── - + I encountered two dots in a row: - - 1│ f : Foo..Bar + + 1│ f : Foo..Bar ^ - + Try removing one of them. "# ), @@ -4479,12 +4479,12 @@ mod test_reporting { indoc!( r#" ── TRAILING DOT ──────────────────────────────────────────────────────────────── - + I encountered a dot with nothing after it: - + 1│ f : Foo.Bar. ^ - + Dots are used to refer to a type in a qualified way, like Num.I64 or List.List a. Try adding a type name next. "# @@ -4499,19 +4499,19 @@ mod test_reporting { report_problem_as( indoc!( r#" - f : . + f : . "# ), indoc!( r#" ── UNFINISHED PARENTHESES ────────────────────────────────────────────────────── - + I am partway through parsing a type in parentheses, but I got stuck here: - + 1│ f : ( I64 ^ - + I was expecting to see a closing parenthesis before this, so try adding a ) and see if that helps? "# @@ -4530,12 +4530,12 @@ mod test_reporting { indoc!( r#" ── WEIRD QUALIFIED NAME ──────────────────────────────────────────────────────── - + I encountered a number at the start of a qualified name segment: - + 1│ f : Foo.1 ^ - + All parts of a qualified type name must start with an uppercase letter, like Num.I64 or List.List a. "# @@ -4554,13 +4554,13 @@ mod test_reporting { indoc!( r#" ── WEIRD QUALIFIED NAME ──────────────────────────────────────────────────────── - + I encountered a lowercase letter at the start of a qualified name segment: - + 1│ f : Foo.foo ^ - + All parts of a qualified type name must start with an uppercase letter, like Num.I64 or List.List a. "# @@ -4573,7 +4573,7 @@ mod test_reporting { report_problem_as( indoc!( r#" - f : I64 as + f : I64 as f = 0 f @@ -4582,12 +4582,12 @@ mod test_reporting { indoc!( r#" ── UNFINISHED INLINE ALIAS ───────────────────────────────────────────────────── - + I just started parsing an inline type alias, but I got stuck here: - - 1│ f : I64 as + + 1│ f : I64 as ^ - + Note: I may be confused by indentation "# ), @@ -4599,7 +4599,7 @@ mod test_reporting { report_problem_as( indoc!( r#" - f : I64,,I64 -> I64 + f : I64,,I64 -> I64 f = 0 f @@ -4608,13 +4608,13 @@ mod test_reporting { indoc!( r#" ── DOUBLE COMMA ──────────────────────────────────────────────────────────────── - + I just started parsing a function argument type, but I encounterd two commas in a row: - - 1│ f : I64,,I64 -> I64 + + 1│ f : I64,,I64 -> I64 ^ - + Try removing one of them. "# ), @@ -4626,7 +4626,7 @@ mod test_reporting { report_problem_as( indoc!( r#" - f : I64, I64 + f : I64, I64 f = 0 f @@ -4635,12 +4635,12 @@ mod test_reporting { indoc!( r#" ── UNFINISHED TYPE ───────────────────────────────────────────────────────────── - + I just started parsing a type, but I got stuck here: - - 1│ f : I64, I64 + + 1│ f : I64, I64 ^ - + Note: I may be confused by indentation "# ), @@ -4653,7 +4653,7 @@ mod test_reporting { report_problem_as( indoc!( r#" - f : I64, I64 -> + f : I64, I64 -> f = 0 f @@ -4662,12 +4662,12 @@ mod test_reporting { indoc!( r#" ── UNFINISHED TYPE ───────────────────────────────────────────────────────────── - + I just started parsing a type, but I got stuck here: - - 1│ f : I64, I64 -> + + 1│ f : I64, I64 -> ^ - + Note: I may be confused by indentation "# ), @@ -4680,7 +4680,7 @@ mod test_reporting { report_problem_as( indoc!( r#" - f : [ @Foo Bool, @100 I64 ] + f : [ @Foo Bool, @100 I64 ] f = 0 f @@ -4689,14 +4689,14 @@ mod test_reporting { indoc!( r#" ── WEIRD TAG NAME ────────────────────────────────────────────────────────────── - + I am partway through parsing a tag union type, but I got stuck here: - - 1│ f : [ @Foo Bool, @100 I64 ] + + 1│ f : [ @Foo Bool, @100 I64 ] ^ - + I was expecting to see a private tag name. - + Hint: Private tag names start with an `@` symbol followed by an uppercase letter, like @UID or @SecretKey. "# @@ -4719,22 +4719,59 @@ mod test_reporting { indoc!( r#" ── TYPE MISMATCH ─────────────────────────────────────────────────────────────── - + Something is off with the body of the `myDict` definition: - + 1│ myDict : Dict I64 Str 2│ myDict = Dict.insert Dict.empty "foo" 42 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - + This `insert` call produces: - + Dict Str (Num a) - + But the type annotation on `myDict` says it should be: - + Dict I64 Str "# ), ) } + + #[test] + fn alias_type_diff() { + report_problem_as( + indoc!( + r#" + HSet a : Set a + + foo : Str -> HSet {} + + myDict : HSet Str + myDict = foo "bar" + + myDict + "# + ), + indoc!( + r#" + ── TYPE MISMATCH ─────────────────────────────────────────────────────────────── + + Something is off with the body of the `myDict` definition: + + 5│ myDict : HSet Str + 6│ myDict = foo "bar" + ^^^^^^^^^ + + This `foo` call produces: + + HSet {} + + But the type annotation on `myDict` says it should be: + + HSet Str + "# + ), + ) + } } From 4b20f969def0edb9297050d747d9c52e684213e6 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 16 Feb 2021 21:58:52 +0100 Subject: [PATCH 06/18] never borrow a closure layout --- compiler/mono/src/borrow.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/compiler/mono/src/borrow.rs b/compiler/mono/src/borrow.rs index 1215c50e3e..c13359b720 100644 --- a/compiler/mono/src/borrow.rs +++ b/compiler/mono/src/borrow.rs @@ -6,6 +6,13 @@ use roc_collections::all::{MutMap, MutSet}; use roc_module::low_level::LowLevel; use roc_module::symbol::Symbol; +fn should_borrow_layout(layout: &Layout) -> bool { + match layout { + Layout::Closure(_, _, _) => false, + _ => false, //layout.is_refcounted(), + } +} + pub fn infer_borrow<'a>( arena: &'a Bump, procs: &MutMap<(Symbol, Layout<'a>), Proc<'a>>, @@ -126,7 +133,7 @@ impl<'a> ParamMap<'a> { fn init_borrow_args(arena: &'a Bump, ps: &'a [(Layout<'a>, Symbol)]) -> &'a [Param<'a>] { Vec::from_iter_in( ps.iter().map(|(layout, symbol)| Param { - borrow: layout.is_refcounted(), + borrow: should_borrow_layout(layout), layout: layout.clone(), symbol: *symbol, }), From bc0a6f24efdf71c81fc23599b53beefc447e8d46 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 16 Feb 2021 21:59:08 +0100 Subject: [PATCH 07/18] cover the closure case in RC expansion --- compiler/mono/src/expand_rc.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/mono/src/expand_rc.rs b/compiler/mono/src/expand_rc.rs index c05f232eb7..1e8c7a6a1c 100644 --- a/compiler/mono/src/expand_rc.rs +++ b/compiler/mono/src/expand_rc.rs @@ -227,7 +227,11 @@ fn layout_for_constructor<'a>( debug_assert_eq!(constructor, 0); HasFields(fields) } - _ => unreachable!(), + Closure(_, _, _) => { + // TODO can we somehow get the to the fields reliably? + Unknown + } + other => unreachable!("weird layout {:?}", other), } } From c788965182e72628d496b3327002ebd223d9a184 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 16 Feb 2021 22:00:15 +0100 Subject: [PATCH 08/18] only reference count closures if the captured values are RC'd --- compiler/mono/src/inc_dec.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/mono/src/inc_dec.rs b/compiler/mono/src/inc_dec.rs index a7413a9c60..964be08878 100644 --- a/compiler/mono/src/inc_dec.rs +++ b/compiler/mono/src/inc_dec.rs @@ -602,7 +602,10 @@ impl<'a> Context<'a> { consume: bool, ) -> Self { // can this type be reference-counted at runtime? - let reference = layout.contains_refcounted(); + let reference = match layout { + Layout::Closure(_, closure, _) => closure.layout.contains_refcounted(), + _ => layout.contains_refcounted(), + }; let info = VarInfo { reference, From 3e39255769642c2c8e82a04c1e7e893e954064f4 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 16 Feb 2021 23:13:49 +0100 Subject: [PATCH 09/18] move test --- examples/benchmarks/{ClosureBorrowed.roc => Closure1.roc} | 0 examples/benchmarks/platform/Task.roc | 6 +++--- 2 files changed, 3 insertions(+), 3 deletions(-) rename examples/benchmarks/{ClosureBorrowed.roc => Closure1.roc} (100%) diff --git a/examples/benchmarks/ClosureBorrowed.roc b/examples/benchmarks/Closure1.roc similarity index 100% rename from examples/benchmarks/ClosureBorrowed.roc rename to examples/benchmarks/Closure1.roc diff --git a/examples/benchmarks/platform/Task.roc b/examples/benchmarks/platform/Task.roc index 0a469829e8..28523e54e2 100644 --- a/examples/benchmarks/platform/Task.roc +++ b/examples/benchmarks/platform/Task.roc @@ -24,10 +24,10 @@ after = \effect, transform -> map : Task a err, (a -> b) -> Task b err map = \effect, transform -> - Effect.after effect \result -> + Effect.map effect \result -> when result is - Ok a -> Task.succeed (transform a) - Err err -> Effect.always (Err err) # Task.fail err does not work. WEIRD! + Ok a -> Ok (transform a) + Err err -> Err err putLine : Str -> Task {} * putLine = \line -> Effect.map (Effect.putLine line) (\_ -> Ok {}) From b2e96be7368b99ef87f4802ce15907245444c68a Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 16 Feb 2021 23:20:30 +0100 Subject: [PATCH 10/18] add closure tests --- cli/tests/cli_run.rs | 32 ++++++++++++++++++++++++++++---- examples/benchmarks/Closure1.roc | 2 +- examples/benchmarks/Closure2.roc | 15 +++++++++++++++ examples/benchmarks/Closure3.roc | 15 +++++++++++++++ examples/benchmarks/Closure4.roc | 16 ++++++++++++++++ 5 files changed, 75 insertions(+), 5 deletions(-) create mode 100644 examples/benchmarks/Closure2.roc create mode 100644 examples/benchmarks/Closure3.roc create mode 100644 examples/benchmarks/Closure4.roc diff --git a/cli/tests/cli_run.rs b/cli/tests/cli_run.rs index 37406bc71e..48f682c03b 100644 --- a/cli/tests/cli_run.rs +++ b/cli/tests/cli_run.rs @@ -210,11 +210,35 @@ mod cli_run { } #[test] - #[serial(closure_borrowed)] - fn run_closure_borrowed_not_optimized() { + #[serial(closure1)] + fn closure1() { check_output( - &example_file("benchmarks", "ClosureBorrowed.roc"), - "closure-borrowed", + &example_file("benchmarks", "Closure1.roc"), + "closure1", + &[], + "", + true, + ); + } + + #[test] + #[serial(closure2)] + fn closure2() { + check_output( + &example_file("benchmarks", "Closure2.roc"), + "closure2", + &[], + "", + true, + ); + } + + #[test] + #[serial(closure3)] + fn closure3() { + check_output( + &example_file("benchmarks", "Closure3.roc"), + "closure3", &[], "", true, diff --git a/examples/benchmarks/Closure1.roc b/examples/benchmarks/Closure1.roc index ebe3de2a39..6065effa90 100644 --- a/examples/benchmarks/Closure1.roc +++ b/examples/benchmarks/Closure1.roc @@ -1,4 +1,4 @@ -app "closure-borrowed" +app "closure1" packages { base: "platform" } imports [base.Task] provides [ main ] to base diff --git a/examples/benchmarks/Closure2.roc b/examples/benchmarks/Closure2.roc new file mode 100644 index 0000000000..338735f813 --- /dev/null +++ b/examples/benchmarks/Closure2.roc @@ -0,0 +1,15 @@ +app "closure2" + packages { base: "platform" } + imports [base.Task] + provides [ main ] to base + +# see https://github.com/rtfeldman/roc/issues/985 + +main : Task.Task {} [] +main = + x : Str + x = "a long string such that it's malloced" + + Task.succeed {} + |> Task.map (\_ -> x) + |> Task.map (\_ -> {}) diff --git a/examples/benchmarks/Closure3.roc b/examples/benchmarks/Closure3.roc new file mode 100644 index 0000000000..e890c0579a --- /dev/null +++ b/examples/benchmarks/Closure3.roc @@ -0,0 +1,15 @@ +app "closure3" + packages { base: "platform" } + imports [base.Task] + provides [ main ] to base + +# see https://github.com/rtfeldman/roc/issues/985 + +main : Task.Task {} [] +main = + x : Str + x = "a long string such that it's malloced" + + Task.succeed {} + |> Task.after (\_ -> Task.succeed x |> Task.map (\_ -> {})) + diff --git a/examples/benchmarks/Closure4.roc b/examples/benchmarks/Closure4.roc new file mode 100644 index 0000000000..5945e8897c --- /dev/null +++ b/examples/benchmarks/Closure4.roc @@ -0,0 +1,16 @@ +app "closure4" + packages { base: "platform" } + imports [base.Task] + provides [ main ] to base + +# see https://github.com/rtfeldman/roc/issues/985 + +main : Task.Task {} [] +main = + x : Str + x = "a long string such that it's malloced" + + Task.succeed {} + |> Task.after (\_ -> Task.succeed x) + |> Task.map (\_ -> {}) + From f59e79d7797a7468aee4bba473e79f038edd46e9 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 17 Feb 2021 00:38:14 +0100 Subject: [PATCH 11/18] RC expansion of nested structs --- compiler/mono/src/expand_rc.rs | 55 ++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 13 deletions(-) diff --git a/compiler/mono/src/expand_rc.rs b/compiler/mono/src/expand_rc.rs index 1e8c7a6a1c..4cc335cf35 100644 --- a/compiler/mono/src/expand_rc.rs +++ b/compiler/mono/src/expand_rc.rs @@ -157,6 +157,24 @@ impl<'a, 'i> Env<'a, 'i> { } } + fn try_insert_struct_info(&mut self, symbol: Symbol, layout: &Layout<'a>) { + use Layout::*; + + match layout { + Struct(fields) => { + self.constructor_map.insert(symbol, 0); + self.layout_map.insert(symbol, Layout::Struct(fields)); + } + Closure(arguments, closure_layout, result) => { + let fpointer = Layout::FunctionPointer(arguments, result); + let fields = self.arena.alloc([fpointer, closure_layout.layout.clone()]); + self.constructor_map.insert(symbol, 0); + self.layout_map.insert(symbol, Layout::Struct(fields)); + } + _ => {} + } + } + fn insert_struct_info(&mut self, symbol: Symbol, fields: &'a [Layout<'a>]) { self.constructor_map.insert(symbol, 0); self.layout_map.insert(symbol, Layout::Struct(fields)); @@ -185,7 +203,7 @@ impl<'a, 'i> Env<'a, 'i> { } fn layout_for_constructor<'a>( - _arena: &'a Bump, + arena: &'a Bump, layout: &Layout<'a>, constructor: u64, ) -> ConstructorLayout<&'a [Layout<'a>]> { @@ -227,9 +245,10 @@ fn layout_for_constructor<'a>( debug_assert_eq!(constructor, 0); HasFields(fields) } - Closure(_, _, _) => { - // TODO can we somehow get the to the fields reliably? - Unknown + Closure(arguments, closure_layout, result) => { + let fpointer = Layout::FunctionPointer(arguments, result); + let fields = arena.alloc([fpointer, closure_layout.layout.clone()]); + HasFields(fields) } other => unreachable!("weird layout {:?}", other), } @@ -257,11 +276,11 @@ fn work_for_constructor<'a>( match layout_for_constructor(env.arena, full_layout, constructor) { Unknown => Unknown, IsNull => IsNull, - HasFields(cons_layout) => { + HasFields(constructor_layout) => { // figure out if there is at least one aliased refcounted field. Only then // does it make sense to inline the decrement let at_least_one_aliased = (|| { - for (i, field_layout) in cons_layout.iter().enumerate() { + for (i, field_layout) in constructor_layout.iter().enumerate() { if field_layout.contains_refcounted() && field_aliases.and_then(|map| map.get(&(i as u64))).is_some() { @@ -273,7 +292,7 @@ fn work_for_constructor<'a>( // for each field, if it has refcounted content, check if it has an alias // if so, use the alias, otherwise load the field. - for (i, field_layout) in cons_layout.iter().enumerate() { + for (i, field_layout) in constructor_layout.iter().enumerate() { if field_layout.contains_refcounted() { match field_aliases.and_then(|map| map.get(&(i as u64))) { Some(alias_symbol) => { @@ -287,7 +306,7 @@ fn work_for_constructor<'a>( let expr = Expr::AccessAtIndex { index: i as u64, - field_layouts: cons_layout, + field_layouts: constructor_layout, structure: *symbol, wrapped: Wrapped::MultiTagUnion, }; @@ -354,10 +373,11 @@ pub fn expand_and_cancel_proc<'a>( introduced.push(*symbol); } - Layout::Closure(_, closure_layout, _) => { - if let Layout::Struct(fields) = closure_layout.layout { - env.insert_struct_info(*symbol, fields); - } + Layout::Closure(arguments, closure_layout, result) => { + let fpointer = Layout::FunctionPointer(arguments, result); + let fields = env.arena.alloc([fpointer, closure_layout.layout.clone()]); + env.insert_struct_info(*symbol, fields); + introduced.push(*symbol); } _ => {} } @@ -418,7 +438,10 @@ fn expand_and_cancel<'a>(env: &mut Env<'a, '_>, stmt: &'a Stmt<'a>) -> &'a Stmt< match &expr { Expr::AccessAtIndex { - structure, index, .. + structure, + index, + field_layouts, + .. } => { let entry = env .alias_map @@ -427,8 +450,14 @@ fn expand_and_cancel<'a>(env: &mut Env<'a, '_>, stmt: &'a Stmt<'a>) -> &'a Stmt< entry.insert(*index, symbol); + // if the field is a struct, we know its constructor too! + let field_layout = &field_layouts[*index as usize]; + env.try_insert_struct_info(symbol, field_layout); + new_cont = expand_and_cancel(env, cont); + env.remove_struct_info(symbol); + // make sure to remove the alias, so other branches don't use it by accident env.alias_map .get_mut(structure) From 6d6c7a294a736b96831f741238747eeba3e27e62 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 17 Feb 2021 00:39:34 +0100 Subject: [PATCH 12/18] improved debug printing of layouts --- compiler/mono/src/ir.rs | 73 +++++++++++++---------- compiler/mono/src/layout.rs | 116 ++++++++++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+), 32 deletions(-) diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 2c400ef411..f61e06f935 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -136,8 +136,15 @@ pub enum SelfRecursive { SelfRecursive(JoinPointId), } +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum Parens { + NotNeeded, + InTypeParam, + InFunction, +} + impl<'a> Proc<'a> { - pub fn to_doc<'b, D, A>(&'b self, alloc: &'b D, _parens: bool) -> DocBuilder<'b, D, A> + pub fn to_doc<'b, D, A>(&'b self, alloc: &'b D, _parens: Parens) -> DocBuilder<'b, D, A> where D: DocAllocator<'b, A>, D::Doc: Clone, @@ -148,20 +155,36 @@ impl<'a> Proc<'a> { .iter() .map(|(_, symbol)| symbol_to_doc(alloc, *symbol)); - alloc - .text("procedure ") - .append(symbol_to_doc(alloc, self.name)) - .append(" (") - .append(alloc.intersperse(args_doc, ", ")) - .append("):") - .append(alloc.hardline()) - .append(self.body.to_doc(alloc).indent(4)) + if PRETTY_PRINT_IR_SYMBOLS { + alloc + .text("procedure : ") + .append(symbol_to_doc(alloc, self.name)) + .append(" ") + .append(self.ret_layout.to_doc(alloc, Parens::NotNeeded)) + .append(alloc.hardline()) + .append(alloc.text("procedure = ")) + .append(symbol_to_doc(alloc, self.name)) + .append(" (") + .append(alloc.intersperse(args_doc, ", ")) + .append("):") + .append(alloc.hardline()) + .append(self.body.to_doc(alloc).indent(4)) + } else { + alloc + .text("procedure ") + .append(symbol_to_doc(alloc, self.name)) + .append(" (") + .append(alloc.intersperse(args_doc, ", ")) + .append("):") + .append(alloc.hardline()) + .append(self.body.to_doc(alloc).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) + self.to_doc::<_, ()>(&allocator, Parens::NotNeeded) .1 .render(width, &mut w) .unwrap(); @@ -2460,8 +2483,7 @@ fn specialize_naked_symbol<'a>( match hole { Stmt::Jump(_, _) => todo!("not sure what to do in this case yet"), _ => { - let expr = - call_by_pointer(env, procs, layout_cache, symbol, layout.clone()); + let expr = call_by_pointer(env, procs, symbol, layout.clone()); let new_symbol = env.unique_symbol(); return Stmt::Let( new_symbol, @@ -3549,7 +3571,7 @@ pub fn with_hole<'a>( // TODO should the let have layout Pointer? Stmt::Let( assigned, - call_by_pointer(env, procs, layout_cache, name, layout.clone()), + call_by_pointer(env, procs, name, layout.clone()), layout, hole, ) @@ -3746,13 +3768,7 @@ pub fn with_hole<'a>( } } - let expr = call_by_pointer( - env, - procs, - layout_cache, - name, - function_ptr_layout.clone(), - ); + let expr = call_by_pointer(env, procs, name, function_ptr_layout.clone()); stmt = Stmt::Let( function_pointer, @@ -3778,7 +3794,7 @@ pub fn with_hole<'a>( // TODO should the let have layout Pointer? Stmt::Let( assigned, - call_by_pointer(env, procs, layout_cache, name, layout.clone()), + call_by_pointer(env, procs, name, layout.clone()), layout, hole, ) @@ -5359,7 +5375,7 @@ fn handle_variable_aliasing<'a>( .from_var(env.arena, variable, env.subs) .unwrap(); - let expr = call_by_pointer(env, procs, layout_cache, right, layout.clone()); + let expr = call_by_pointer(env, procs, right, layout.clone()); Stmt::Let(left, expr, layout, env.arena.alloc(result)) } else { substitute_in_exprs(env.arena, &mut result, left, right); @@ -5407,7 +5423,7 @@ fn reuse_function_symbol<'a>( // an imported symbol is always a function pointer: // either it's a function, or a top-level 0-argument thunk - let expr = call_by_pointer(env, procs, layout_cache, original, layout.clone()); + let expr = call_by_pointer(env, procs, original, layout.clone()); return Stmt::Let(symbol, expr, layout, env.arena.alloc(result)); } _ => { @@ -5505,13 +5521,7 @@ fn reuse_function_symbol<'a>( } } - let expr = call_by_pointer( - env, - procs, - layout_cache, - original, - function_ptr_layout.clone(), - ); + let expr = call_by_pointer(env, procs, original, function_ptr_layout.clone()); stmt = Stmt::Let( function_pointer, @@ -5533,7 +5543,7 @@ fn reuse_function_symbol<'a>( Stmt::Let( symbol, - call_by_pointer(env, procs, layout_cache, original, layout.clone()), + call_by_pointer(env, procs, original, layout.clone()), layout, env.arena.alloc(result), ) @@ -5613,7 +5623,6 @@ where fn call_by_pointer<'a>( env: &mut Env<'a, '_>, procs: &mut Procs<'a>, - layout_cache: &mut LayoutCache<'a>, symbol: Symbol, layout: Layout<'a>, ) -> Expr<'a> { diff --git a/compiler/mono/src/layout.rs b/compiler/mono/src/layout.rs index 6cfe7a9b72..d76dd39c24 100644 --- a/compiler/mono/src/layout.rs +++ b/compiler/mono/src/layout.rs @@ -1,3 +1,4 @@ +use crate::ir::Parens; use bumpalo::collections::Vec; use bumpalo::Bump; use roc_collections::all::{default_hasher, MutMap, MutSet}; @@ -6,6 +7,7 @@ use roc_module::symbol::{Interns, Symbol}; use roc_types::subs::{Content, FlatType, Subs, Variable}; use roc_types::types::RecordField; use std::collections::HashMap; +use ven_pretty::{DocAllocator, DocBuilder}; pub const MAX_ENUM_SIZE: usize = (std::mem::size_of::() * 8) as usize; const GENERATE_NULLABLE: bool = true; @@ -63,6 +65,34 @@ pub enum UnionLayout<'a> { }, } +impl<'a> UnionLayout<'a> { + pub fn to_doc<'b, D, A>(&'b self, alloc: &'b D, _parens: Parens) -> DocBuilder<'b, D, A> + where + D: DocAllocator<'b, A>, + D::Doc: Clone, + A: Clone, + { + use UnionLayout::*; + + match self { + NonRecursive(tags) => { + let tags_doc = tags.iter().map(|fields| { + alloc.text("C ").append(alloc.intersperse( + fields.iter().map(|x| x.to_doc(alloc, Parens::InTypeParam)), + ", ", + )) + }); + + alloc + .text("[") + .append(alloc.intersperse(tags_doc, ", ")) + .append(alloc.text("]")) + } + _ => alloc.text("TODO"), + } + } +} + #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct ClosureLayout<'a> { /// the layout that this specific closure captures @@ -654,6 +684,51 @@ impl<'a> Layout<'a> { FunctionPointer(_, _) | Pointer(_) => false, } } + + pub fn to_doc<'b, D, A>(&'b self, alloc: &'b D, parens: Parens) -> DocBuilder<'b, D, A> + where + D: DocAllocator<'b, A>, + D::Doc: Clone, + A: Clone, + { + use Layout::*; + + match self { + Builtin(builtin) => builtin.to_doc(alloc, parens), + PhantomEmptyStruct => alloc.text("{}"), + Struct(fields) => { + let fields_doc = fields.iter().map(|x| x.to_doc(alloc, parens)); + + alloc + .text("{") + .append(alloc.intersperse(fields_doc, ", ")) + .append(alloc.text("}")) + } + Union(union_layout) => union_layout.to_doc(alloc, parens), + RecursivePointer => alloc.text("*self"), + FunctionPointer(args, result) => { + let args_doc = args.iter().map(|x| x.to_doc(alloc, Parens::InFunction)); + + alloc + .intersperse(args_doc, ", ") + .append(alloc.text(" -> ")) + .append(result.to_doc(alloc, Parens::InFunction)) + } + Closure(args, closure_layout, result) => { + let args_doc = args.iter().map(|x| x.to_doc(alloc, Parens::InFunction)); + + let bom = closure_layout.layout.to_doc(alloc, Parens::NotNeeded); + + alloc + .intersperse(args_doc, ", ") + .append(alloc.text(" {| ")) + .append(bom) + .append(" |} -> ") + .append(result.to_doc(alloc, Parens::InFunction)) + } + Pointer(_) => todo!(), + } + } } /// Avoid recomputing Layout from Variable multiple times. @@ -878,6 +953,47 @@ impl<'a> Builtin<'a> { Str | Dict(_, _) | Set(_) => true, } } + + pub fn to_doc<'b, D, A>(&'b self, alloc: &'b D, _parens: Parens) -> DocBuilder<'b, D, A> + where + D: DocAllocator<'b, A>, + D::Doc: Clone, + A: Clone, + { + use Builtin::*; + + match self { + Int128 => alloc.text("Int128"), + Int64 => alloc.text("Int64"), + Int32 => alloc.text("Int32"), + Int16 => alloc.text("Int16"), + Int8 => alloc.text("Int8"), + Int1 => alloc.text("Int1"), + Usize => alloc.text("Usize"), + Float128 => alloc.text("Float128"), + Float64 => alloc.text("Float64"), + Float32 => alloc.text("Float32"), + Float16 => alloc.text("Float16"), + + EmptyStr => alloc.text("EmptyStr"), + EmptyList => alloc.text("EmptyList"), + EmptyDict => alloc.text("EmptyDict"), + EmptySet => alloc.text("EmptySet"), + + Str => alloc.text("Str"), + List(_, layout) => alloc + .text("List ") + .append(layout.to_doc(alloc, Parens::InTypeParam)), + Set(layout) => alloc + .text("Set ") + .append(layout.to_doc(alloc, Parens::InTypeParam)), + Dict(key_layout, value_layout) => alloc + .text("Dict ") + .append(key_layout.to_doc(alloc, Parens::InTypeParam)) + .append(" ") + .append(value_layout.to_doc(alloc, Parens::InTypeParam)), + } + } } fn layout_from_flat_type<'a>( From 42c0de1314b9be30e62d58e4543abced5c608fa6 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 17 Feb 2021 00:40:01 +0100 Subject: [PATCH 13/18] better reporting of valgrind errors --- cli/tests/cli_run.rs | 34 +++++++++++++++++++++++++++++++++- cli/tests/helpers.rs | 12 ++++++------ 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/cli/tests/cli_run.rs b/cli/tests/cli_run.rs index 48f682c03b..0819fa03a0 100644 --- a/cli/tests/cli_run.rs +++ b/cli/tests/cli_run.rs @@ -13,6 +13,7 @@ mod helpers; mod cli_run { use crate::helpers::{ example_file, extract_valgrind_errors, fixture_file, run_cmd, run_roc, run_with_valgrind, + ValgrindError, ValgrindErrorXWhat, }; use serial_test::serial; use std::path::Path; @@ -59,8 +60,39 @@ mod cli_run { panic!("failed to parse the `valgrind` xml output. Error was:\n\n{:?}\n\nvalgrind xml was: \"{}\"\n\nvalgrind stdout was: \"{}\"\n\nvalgrind stderr was: \"{}\"", err, raw_xml, valgrind_out.stdout, valgrind_out.stderr); }); + // #[derive(Debug, Deserialize, Clone)] + // pub struct ValgrindError { + // kind: String, + // #[serde(default)] + // what: Option, + // #[serde(default)] + // xwhat: Option, + // } + // + // #[derive(Debug, Deserialize, Clone)] + // pub struct ValgrindErrorXWhat { + // text: String, + // #[serde(default)] + // leakedbytes: Option, + // #[serde(default)] + // leakedblocks: Option, + // } + if !memory_errors.is_empty() { - panic!("{:?}", memory_errors); + for error in memory_errors { + let ValgrindError { kind, what, xwhat } = error; + println!("Valgrind Error: {}\n", kind); + + if let Some(ValgrindErrorXWhat { + text, + leakedbytes: _, + leakedblocks: _, + }) = xwhat + { + println!(" {}", text); + } + } + panic!("Valgrind reported memory errors"); } } else { let exit_code = match valgrind_out.status.code() { diff --git a/cli/tests/helpers.rs b/cli/tests/helpers.rs index 376158d678..27e8ad1044 100644 --- a/cli/tests/helpers.rs +++ b/cli/tests/helpers.rs @@ -192,20 +192,20 @@ struct ValgrindDummyStruct {} #[derive(Debug, Deserialize, Clone)] pub struct ValgrindError { - kind: String, + pub kind: String, #[serde(default)] - what: Option, + pub what: Option, #[serde(default)] - xwhat: Option, + pub xwhat: Option, } #[derive(Debug, Deserialize, Clone)] pub struct ValgrindErrorXWhat { - text: String, + pub text: String, #[serde(default)] - leakedbytes: Option, + pub leakedbytes: Option, #[serde(default)] - leakedblocks: Option, + pub leakedblocks: Option, } #[allow(dead_code)] From f1d5a9824ac4bfc4262aa7ca33a25be985785d53 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 17 Feb 2021 00:42:09 +0100 Subject: [PATCH 14/18] add fourth closure test --- cli/tests/cli_run.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/cli/tests/cli_run.rs b/cli/tests/cli_run.rs index 0819fa03a0..999adaa12f 100644 --- a/cli/tests/cli_run.rs +++ b/cli/tests/cli_run.rs @@ -266,6 +266,7 @@ mod cli_run { } #[test] + #[ignore] #[serial(closure3)] fn closure3() { check_output( @@ -277,6 +278,18 @@ mod cli_run { ); } + #[test] + #[serial(closure4)] + fn closure4() { + check_output( + &example_file("benchmarks", "Closure4.roc"), + "closure4", + &[], + "", + true, + ); + } + // #[test] // #[serial(effect)] // fn run_effect_unoptimized() { From 80736f93b93ce92decc145f96098b05e4d1177c2 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 17 Feb 2021 00:48:37 +0100 Subject: [PATCH 15/18] fix mono tests --- cli/tests/cli_run.rs | 6 +++- compiler/mono/src/borrow.rs | 2 +- compiler/mono/tests/test_mono.rs | 49 +++++++++++++++++++++----------- 3 files changed, 39 insertions(+), 18 deletions(-) diff --git a/cli/tests/cli_run.rs b/cli/tests/cli_run.rs index 999adaa12f..b84ef1b8b0 100644 --- a/cli/tests/cli_run.rs +++ b/cli/tests/cli_run.rs @@ -80,7 +80,11 @@ mod cli_run { if !memory_errors.is_empty() { for error in memory_errors { - let ValgrindError { kind, what, xwhat } = error; + let ValgrindError { + kind, + what: _, + xwhat, + } = error; println!("Valgrind Error: {}\n", kind); if let Some(ValgrindErrorXWhat { diff --git a/compiler/mono/src/borrow.rs b/compiler/mono/src/borrow.rs index c13359b720..9132dde870 100644 --- a/compiler/mono/src/borrow.rs +++ b/compiler/mono/src/borrow.rs @@ -9,7 +9,7 @@ use roc_module::symbol::Symbol; fn should_borrow_layout(layout: &Layout) -> bool { match layout { Layout::Closure(_, _, _) => false, - _ => false, //layout.is_refcounted(), + _ => layout.is_refcounted(), } } diff --git a/compiler/mono/tests/test_mono.rs b/compiler/mono/tests/test_mono.rs index 91a9768f1e..4e31636ffd 100644 --- a/compiler/mono/tests/test_mono.rs +++ b/compiler/mono/tests/test_mono.rs @@ -650,8 +650,12 @@ mod test_mono { let Test.3 = lowlevel DictSize #Attr.2; ret Test.3; + procedure Test.5 (): + let Test.4 = lowlevel DictEmpty ; + ret Test.4; + procedure Test.0 (): - let Test.2 = FunctionPointer Dict.2; + let Test.2 = FunctionPointer Test.5; let Test.1 = CallByName Dict.6 Test.2; ret Test.1; "# @@ -1774,11 +1778,11 @@ mod test_mono { indoc!( r#" procedure List.4 (#Attr.2, #Attr.3, #Attr.4): - let Test.22 = lowlevel ListLen #Attr.2; - let Test.20 = lowlevel NumLt #Attr.3 Test.22; - if Test.20 then - let Test.21 = lowlevel ListSet #Attr.2 #Attr.3 #Attr.4; - ret Test.21; + let Test.23 = lowlevel ListLen #Attr.2; + let Test.21 = lowlevel NumLt #Attr.3 Test.23; + if Test.21 then + let Test.22 = lowlevel ListSet #Attr.2 #Attr.3 #Attr.4; + ret Test.22; else ret #Attr.2; @@ -1798,17 +1802,24 @@ mod test_mono { ret Test.10; procedure Test.2 (Test.3): - let Test.17 = 0i64; let Test.18 = 0i64; - let Test.16 = CallByName List.4 Test.3 Test.17 Test.18; - ret Test.16; + let Test.19 = 0i64; + let Test.17 = CallByName List.4 Test.3 Test.18 Test.19; + ret Test.17; + + procedure Test.24 (): + let Test.11 = 1i64; + let Test.12 = 2i64; + let Test.13 = 3i64; + let Test.10 = Array [Test.11, Test.12, Test.13]; + ret Test.10; procedure Test.0 (): - let Test.15 = FunctionPointer Test.1; - let Test.14 = CallByName Test.2 Test.15; - let Test.5 = CallByName List.7 Test.14; - dec Test.14; - let Test.8 = FunctionPointer Test.1; + let Test.16 = FunctionPointer Test.24; + let Test.15 = CallByName Test.2 Test.16; + let Test.5 = CallByName List.7 Test.15; + dec Test.15; + let Test.8 = FunctionPointer Test.14; let Test.6 = CallByName List.7 Test.8; dec Test.8; let Test.4 = CallByName Num.24 Test.5 Test.6; @@ -2345,10 +2356,13 @@ mod test_mono { r#" procedure Test.1 (Test.5): let Test.2 = 42i64; - let Test.13 = FunctionPointer Test.3; + let Test.13 = FunctionPointer Test.16; let Test.3 = Struct {Test.13, Test.2}; ret Test.3; + procedure Test.16 (Test.11, #Attr.12): + ret #Attr.12; + procedure Test.3 (Test.11, #Attr.12): ret #Attr.12; @@ -2393,11 +2407,14 @@ mod test_mono { procedure Test.1 (Test.5): let Test.2 = 41i64; - let Test.12 = FunctionPointer Test.3; + let Test.12 = FunctionPointer Test.15; let Test.11 = Struct {Test.12, Test.2}; let Test.10 = Array [Test.11]; ret Test.10; + procedure Test.15 (Test.9, #Attr.12): + ret #Attr.12; + procedure Test.3 (Test.9, #Attr.12): ret #Attr.12; From e6ec85eeba52d81766c2cb792ab11c61d816a36f Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 17 Feb 2021 01:05:03 +0100 Subject: [PATCH 16/18] revert mono tests --- cli/tests/cli_run.rs | 23 +++------------ compiler/mono/src/ir.rs | 15 ++++++++-- compiler/mono/tests/test_mono.rs | 49 +++++++++++--------------------- 3 files changed, 32 insertions(+), 55 deletions(-) diff --git a/cli/tests/cli_run.rs b/cli/tests/cli_run.rs index b84ef1b8b0..c92a52c90f 100644 --- a/cli/tests/cli_run.rs +++ b/cli/tests/cli_run.rs @@ -60,24 +60,6 @@ mod cli_run { panic!("failed to parse the `valgrind` xml output. Error was:\n\n{:?}\n\nvalgrind xml was: \"{}\"\n\nvalgrind stdout was: \"{}\"\n\nvalgrind stderr was: \"{}\"", err, raw_xml, valgrind_out.stdout, valgrind_out.stderr); }); - // #[derive(Debug, Deserialize, Clone)] - // pub struct ValgrindError { - // kind: String, - // #[serde(default)] - // what: Option, - // #[serde(default)] - // xwhat: Option, - // } - // - // #[derive(Debug, Deserialize, Clone)] - // pub struct ValgrindErrorXWhat { - // text: String, - // #[serde(default)] - // leakedbytes: Option, - // #[serde(default)] - // leakedblocks: Option, - // } - if !memory_errors.is_empty() { for error in memory_errors { let ValgrindError { @@ -245,6 +227,7 @@ mod cli_run { ); } + #[ignore] #[test] #[serial(closure1)] fn closure1() { @@ -257,6 +240,7 @@ mod cli_run { ); } + #[ignore] #[test] #[serial(closure2)] fn closure2() { @@ -269,8 +253,8 @@ mod cli_run { ); } - #[test] #[ignore] + #[test] #[serial(closure3)] fn closure3() { check_output( @@ -282,6 +266,7 @@ mod cli_run { ); } + #[ignore] #[test] #[serial(closure4)] fn closure4() { diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index f61e06f935..25b9e79641 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -195,8 +195,11 @@ impl<'a> Proc<'a> { pub fn insert_refcount_operations( arena: &'a Bump, procs: &mut MutMap<(Symbol, Layout<'a>), Proc<'a>>, - passed_by_pointer: &MutMap<(Symbol, Layout<'a>), Symbol>, + _passed_by_pointer: &MutMap<(Symbol, Layout<'a>), Symbol>, ) { + // currently we ignore the passed-by-pointerness + let passed_by_pointer = &Default::default(); + let borrow_params = arena.alloc(crate::borrow::infer_borrow(arena, procs, passed_by_pointer)); @@ -207,6 +210,11 @@ impl<'a> Proc<'a> { let layout = key.1.clone(); procs.insert((*other, layout), proc); + } else { + unreachable!( + "we need a by-pointer version of {:?}, but its by-name version does not exist", + key.0 + ) } } @@ -5621,12 +5629,13 @@ where } fn call_by_pointer<'a>( - env: &mut Env<'a, '_>, + _env: &mut Env<'a, '_>, procs: &mut Procs<'a>, symbol: Symbol, layout: Layout<'a>, ) -> Expr<'a> { - let other = env.unique_symbol(); + // let other = env.unique_symbol(); + let other = symbol; procs .passed_by_pointer diff --git a/compiler/mono/tests/test_mono.rs b/compiler/mono/tests/test_mono.rs index 4e31636ffd..91a9768f1e 100644 --- a/compiler/mono/tests/test_mono.rs +++ b/compiler/mono/tests/test_mono.rs @@ -650,12 +650,8 @@ mod test_mono { let Test.3 = lowlevel DictSize #Attr.2; ret Test.3; - procedure Test.5 (): - let Test.4 = lowlevel DictEmpty ; - ret Test.4; - procedure Test.0 (): - let Test.2 = FunctionPointer Test.5; + let Test.2 = FunctionPointer Dict.2; let Test.1 = CallByName Dict.6 Test.2; ret Test.1; "# @@ -1778,11 +1774,11 @@ mod test_mono { indoc!( r#" procedure List.4 (#Attr.2, #Attr.3, #Attr.4): - let Test.23 = lowlevel ListLen #Attr.2; - let Test.21 = lowlevel NumLt #Attr.3 Test.23; - if Test.21 then - let Test.22 = lowlevel ListSet #Attr.2 #Attr.3 #Attr.4; - ret Test.22; + let Test.22 = lowlevel ListLen #Attr.2; + let Test.20 = lowlevel NumLt #Attr.3 Test.22; + if Test.20 then + let Test.21 = lowlevel ListSet #Attr.2 #Attr.3 #Attr.4; + ret Test.21; else ret #Attr.2; @@ -1802,24 +1798,17 @@ mod test_mono { ret Test.10; procedure Test.2 (Test.3): + let Test.17 = 0i64; let Test.18 = 0i64; - let Test.19 = 0i64; - let Test.17 = CallByName List.4 Test.3 Test.18 Test.19; - ret Test.17; - - procedure Test.24 (): - let Test.11 = 1i64; - let Test.12 = 2i64; - let Test.13 = 3i64; - let Test.10 = Array [Test.11, Test.12, Test.13]; - ret Test.10; + let Test.16 = CallByName List.4 Test.3 Test.17 Test.18; + ret Test.16; procedure Test.0 (): - let Test.16 = FunctionPointer Test.24; - let Test.15 = CallByName Test.2 Test.16; - let Test.5 = CallByName List.7 Test.15; - dec Test.15; - let Test.8 = FunctionPointer Test.14; + let Test.15 = FunctionPointer Test.1; + let Test.14 = CallByName Test.2 Test.15; + let Test.5 = CallByName List.7 Test.14; + dec Test.14; + let Test.8 = FunctionPointer Test.1; let Test.6 = CallByName List.7 Test.8; dec Test.8; let Test.4 = CallByName Num.24 Test.5 Test.6; @@ -2356,13 +2345,10 @@ mod test_mono { r#" procedure Test.1 (Test.5): let Test.2 = 42i64; - let Test.13 = FunctionPointer Test.16; + let Test.13 = FunctionPointer Test.3; let Test.3 = Struct {Test.13, Test.2}; ret Test.3; - procedure Test.16 (Test.11, #Attr.12): - ret #Attr.12; - procedure Test.3 (Test.11, #Attr.12): ret #Attr.12; @@ -2407,14 +2393,11 @@ mod test_mono { procedure Test.1 (Test.5): let Test.2 = 41i64; - let Test.12 = FunctionPointer Test.15; + let Test.12 = FunctionPointer Test.3; let Test.11 = Struct {Test.12, Test.2}; let Test.10 = Array [Test.11]; ret Test.10; - procedure Test.15 (Test.9, #Attr.12): - ret #Attr.12; - procedure Test.3 (Test.9, #Attr.12): ret #Attr.12; From b09f82ff51735e6a2312573ff12012cb5cf11573 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 17 Feb 2021 01:08:24 +0100 Subject: [PATCH 17/18] clippy --- compiler/mono/src/ir.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 25b9e79641..80395a5993 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -356,6 +356,7 @@ impl<'a> Procs<'a> { } } + #[allow(clippy::type_complexity)] pub fn get_specialized_procs_without_rc( self, arena: &'a Bump, From 04fd04dc42f72b7809655244c71dbab494f596dc Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Wed, 17 Feb 2021 23:11:56 -0500 Subject: [PATCH 18/18] Fix some things in roc-for-elm-programmers.md --- roc-for-elm-programmers.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/roc-for-elm-programmers.md b/roc-for-elm-programmers.md index 45a261f3a7..9742d08bf2 100644 --- a/roc-for-elm-programmers.md +++ b/roc-for-elm-programmers.md @@ -955,7 +955,7 @@ Either way, you get `+` being able to work on both integers and floats! Separately, there's also `Int a`, which is a type alias for `Num (Integer a)`, and `Float a`, which is a type alias for `Num (Float a)`. These allow functions -that can work on any integer or any flaot. For example, +that can work on any integer or any float. For example, `Num.bitwiseAnd : Int a, Int a -> Int a`. In Roc, number literals with decimal points are `Float *` values. @@ -984,7 +984,7 @@ These don't exist in Roc. Also [like Python](https://www.python.org/dev/peps/pep-0515/) Roc permits underscores in number literals for readability purposes. Roc also supports hexadecimal (`0x01`), octal (`0o01`), and binary (`0b01`) integer literals; these -literals all have type `Int` instead of `Num *`. +literals all have type `Int *` instead of `Num *`. If you put these into a hypothetical Roc REPL, here's what you'd see: @@ -993,16 +993,16 @@ If you put these into a hypothetical Roc REPL, here's what you'd see: 2048 : Num * > 1 + 2.14 -3.14 : Float +3.14 : Float * > 1.0 + 1 -2.0 : Float +2.0 : Float * > 1.1 + 0x11 - + > 11 + 0x11 -28 : Int +28 : Int * ``` ## Phantom Types