From 07a432581f69012a3bf16678a378969f7701dec3 Mon Sep 17 00:00:00 2001 From: Brian Carroll Date: Sun, 3 Jul 2022 09:47:53 +0100 Subject: [PATCH 01/66] wasm: start implementing Box and Unbox (bugs!) --- crates/compiler/gen_wasm/src/backend.rs | 59 +++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 3 deletions(-) diff --git a/crates/compiler/gen_wasm/src/backend.rs b/crates/compiler/gen_wasm/src/backend.rs index d84741d8de..f0c1bcac88 100644 --- a/crates/compiler/gen_wasm/src/backend.rs +++ b/crates/compiler/gen_wasm/src/backend.rs @@ -953,9 +953,9 @@ impl<'a> WasmBackend<'a> { index, } => self.expr_union_at_index(*structure, *tag_id, union_layout, *index, sym), - Expr::ExprBox { .. } | Expr::ExprUnbox { .. } => { - todo!("Expression `{}`", expr.to_pretty(100)) - } + Expr::ExprBox { symbol: arg_sym } => self.expr_box(sym, *arg_sym, layout, storage), + + Expr::ExprUnbox { symbol: arg_sym } => self.expr_unbox(sym, *arg_sym, storage), Expr::Reuse { tag_layout, @@ -1716,6 +1716,59 @@ impl<'a> WasmBackend<'a> { .copy_value_from_memory(&mut self.code_builder, symbol, from_ptr, from_offset); } + /******************************************************************* + * Box + *******************************************************************/ + + fn expr_box( + &mut self, + ret_sym: Symbol, + arg_sym: Symbol, + layout: &Layout<'a>, + storage: &StoredValue, + ) { + // create a local variable for the heap pointer + let ptr_local_id = match self.storage.ensure_value_has_local( + &mut self.code_builder, + ret_sym, + storage.clone(), + ) { + StoredValue::Local { local_id, .. } => local_id, + _ => internal_error!("A heap pointer will always be an i32"), + }; + + // allocate heap memory and load its data address onto the value stack + let arg_layout = match layout { + Layout::Boxed(arg) => *arg, + _ => internal_error!("ExprBox should always produce a Boxed layout"), + }; + let (size, alignment) = arg_layout.stack_size_and_alignment(TARGET_INFO); + self.allocate_with_refcount(Some(size), alignment, 1); + + // store the pointer value from the value stack into the local variable + self.code_builder.set_local(ptr_local_id); + + // copy the argument to the pointer address + self.storage + .copy_value_to_memory(&mut self.code_builder, ptr_local_id, 0, arg_sym); + } + + fn expr_unbox(&mut self, ret_sym: Symbol, arg_sym: Symbol, storage: &StoredValue) { + // ensure we have a local variable for the argument (an i32 heap pointer) + let ptr_local_id = match self.storage.ensure_value_has_local( + &mut self.code_builder, + arg_sym, + storage.clone(), + ) { + StoredValue::Local { local_id, .. } => local_id, + _ => internal_error!("A heap pointer will always be an i32"), + }; + + // Copy the value + self.storage + .copy_value_from_memory(&mut self.code_builder, ret_sym, ptr_local_id, 0); + } + /******************************************************************* * Refcounting & Heap allocation *******************************************************************/ From 7ea9854e494abeaad96bddcfad4f93de8aab25d9 Mon Sep 17 00:00:00 2001 From: Brian Carroll Date: Sun, 3 Jul 2022 10:22:11 +0100 Subject: [PATCH 02/66] wasm: Refactor and bug-fix copy_value_from_memory --- crates/compiler/gen_wasm/src/backend.rs | 69 +++++++++++-------- crates/compiler/gen_wasm/src/low_level.rs | 6 +- crates/compiler/gen_wasm/src/storage.rs | 24 ++++++- .../compiler/test_gen/src/gen_primitives.rs | 8 +-- 4 files changed, 68 insertions(+), 39 deletions(-) diff --git a/crates/compiler/gen_wasm/src/backend.rs b/crates/compiler/gen_wasm/src/backend.rs index f0c1bcac88..ee30aa1aec 100644 --- a/crates/compiler/gen_wasm/src/backend.rs +++ b/crates/compiler/gen_wasm/src/backend.rs @@ -17,7 +17,7 @@ use roc_std::RocDec; use crate::layout::{CallConv, ReturnMethod, WasmLayout}; use crate::low_level::{call_higher_order_lowlevel, LowLevelCall}; -use crate::storage::{Storage, StoredValue, StoredVarKind}; +use crate::storage::{AddressValue, Storage, StoredValue, StoredVarKind}; use crate::wasm_module::linking::{DataSymbol, WasmObjectSymbol}; use crate::wasm_module::sections::{ ConstExpr, DataMode, DataSegment, Export, Global, GlobalType, Import, ImportDesc, Limits, @@ -928,7 +928,7 @@ impl<'a> WasmBackend<'a> { index, field_layouts, structure, - } => self.expr_struct_at_index(sym, storage, *index, field_layouts, *structure), + } => self.expr_struct_at_index(sym, *index, field_layouts, *structure), Expr::Array { elems, elem_layout } => self.expr_array(sym, storage, elem_layout, elems), @@ -955,7 +955,7 @@ impl<'a> WasmBackend<'a> { Expr::ExprBox { symbol: arg_sym } => self.expr_box(sym, *arg_sym, layout, storage), - Expr::ExprUnbox { symbol: arg_sym } => self.expr_unbox(sym, *arg_sym, storage), + Expr::ExprUnbox { symbol: arg_sym } => self.expr_unbox(sym, *arg_sym), Expr::Reuse { tag_layout, @@ -1353,16 +1353,15 @@ impl<'a> WasmBackend<'a> { fn expr_struct_at_index( &mut self, sym: Symbol, - storage: &StoredValue, index: u64, field_layouts: &'a [Layout<'a>], structure: Symbol, ) { - self.storage - .ensure_value_has_local(&mut self.code_builder, sym, storage.to_owned()); - let (local_id, mut offset) = match self.storage.get(&structure) { + let (from_addr_val, mut offset) = match self.storage.get(&structure) { StoredValue::StackMemory { location, .. } => { - location.local_and_offset(self.storage.stack_frame_pointer) + let (local_id, offset) = + location.local_and_offset(self.storage.stack_frame_pointer); + (AddressValue::NotLoaded(local_id), offset) } StoredValue::Local { @@ -1371,18 +1370,20 @@ impl<'a> WasmBackend<'a> { .. } => { debug_assert!(matches!(value_type, ValueType::I32)); - (*local_id, 0) + (AddressValue::NotLoaded(*local_id), 0) } StoredValue::VirtualMachineStack { .. } => { - internal_error!("ensure_value_has_local didn't work") + self.storage + .load_symbols(&mut self.code_builder, &[structure]); + (AddressValue::Loaded, 0) } }; for field in field_layouts.iter().take(index as usize) { offset += field.stack_size(TARGET_INFO); } self.storage - .copy_value_from_memory(&mut self.code_builder, sym, local_id, offset); + .copy_value_from_memory(&mut self.code_builder, sym, from_addr_val, offset); } /******************************************************************* @@ -1700,20 +1701,22 @@ impl<'a> WasmBackend<'a> { let stores_tag_id_in_pointer = union_layout.stores_tag_id_in_pointer(TARGET_INFO); - let from_ptr = if stores_tag_id_in_pointer { - let ptr = self.storage.create_anonymous_local(ValueType::I32); + let from_addr_val = if stores_tag_id_in_pointer { self.code_builder.get_local(tag_local_id); self.code_builder.i32_const(-4); // 11111111...1100 self.code_builder.i32_and(); - self.code_builder.set_local(ptr); - ptr + AddressValue::Loaded } else { - tag_local_id + AddressValue::NotLoaded(tag_local_id) }; let from_offset = tag_offset + field_offset; - self.storage - .copy_value_from_memory(&mut self.code_builder, symbol, from_ptr, from_offset); + self.storage.copy_value_from_memory( + &mut self.code_builder, + symbol, + from_addr_val, + from_offset, + ); } /******************************************************************* @@ -1753,20 +1756,28 @@ impl<'a> WasmBackend<'a> { .copy_value_to_memory(&mut self.code_builder, ptr_local_id, 0, arg_sym); } - fn expr_unbox(&mut self, ret_sym: Symbol, arg_sym: Symbol, storage: &StoredValue) { - // ensure we have a local variable for the argument (an i32 heap pointer) - let ptr_local_id = match self.storage.ensure_value_has_local( - &mut self.code_builder, - arg_sym, - storage.clone(), - ) { - StoredValue::Local { local_id, .. } => local_id, - _ => internal_error!("A heap pointer will always be an i32"), + fn expr_unbox(&mut self, ret_sym: Symbol, arg_sym: Symbol) { + let (from_addr_val, from_offset) = match self.storage.get(&arg_sym) { + StoredValue::VirtualMachineStack { .. } => { + self.storage + .load_symbols(&mut self.code_builder, &[arg_sym]); + (AddressValue::Loaded, 0) + } + StoredValue::Local { local_id, .. } => (AddressValue::NotLoaded(*local_id), 0), + StoredValue::StackMemory { location, .. } => { + let (local_id, offset) = + location.local_and_offset(self.storage.stack_frame_pointer); + (AddressValue::NotLoaded(local_id), offset) + } }; // Copy the value - self.storage - .copy_value_from_memory(&mut self.code_builder, ret_sym, ptr_local_id, 0); + self.storage.copy_value_from_memory( + &mut self.code_builder, + ret_sym, + from_addr_val, + from_offset, + ); } /******************************************************************* diff --git a/crates/compiler/gen_wasm/src/low_level.rs b/crates/compiler/gen_wasm/src/low_level.rs index 79433707c2..70dfe1a0e0 100644 --- a/crates/compiler/gen_wasm/src/low_level.rs +++ b/crates/compiler/gen_wasm/src/low_level.rs @@ -11,7 +11,7 @@ use roc_mono::low_level::HigherOrder; use crate::backend::{ProcLookupData, ProcSource, WasmBackend}; use crate::layout::{CallConv, StackMemoryFormat, WasmLayout}; -use crate::storage::{StackMemoryLocation, StoredValue}; +use crate::storage::{StackMemoryLocation, StoredValue, AddressValue}; use crate::wasm_module::{Align, LocalId, ValueType}; use crate::TARGET_INFO; @@ -315,14 +315,12 @@ impl<'a> LowLevelCall<'a> { // Target element heap pointer backend.code_builder.i32_add(); // base + index*size - let elem_heap_ptr = backend.storage.create_anonymous_local(ValueType::I32); - backend.code_builder.set_local(elem_heap_ptr); // Copy to stack backend.storage.copy_value_from_memory( &mut backend.code_builder, self.ret_symbol, - elem_heap_ptr, + AddressValue::Loaded, 0, ); diff --git a/crates/compiler/gen_wasm/src/storage.rs b/crates/compiler/gen_wasm/src/storage.rs index ff37d11e84..0c7b03d5da 100644 --- a/crates/compiler/gen_wasm/src/storage.rs +++ b/crates/compiler/gen_wasm/src/storage.rs @@ -76,6 +76,13 @@ impl StoredValue { } } +pub enum AddressValue { + /// The address value has been loaded to the VM stack + Loaded, + /// The address value is in a local variable + NotLoaded(LocalId), +} + /// Helper structure for WasmBackend, to keep track of how values are stored, /// including the VM stack, local variables, and linear memory #[derive(Debug)] @@ -610,7 +617,7 @@ impl<'a> Storage<'a> { &mut self, code_builder: &mut CodeBuilder, to_symbol: Symbol, - from_ptr: LocalId, + from_addr: AddressValue, from_offset: u32, ) -> u32 { let to_storage = self.get(&to_symbol).to_owned(); @@ -625,6 +632,16 @@ impl<'a> Storage<'a> { self.stack_frame_pointer = Some(self.get_next_local_id()); } + let from_ptr = match from_addr { + AddressValue::NotLoaded(ptr) => ptr, + AddressValue::Loaded => { + // The `from` address is on the VM stack but we want it in a local for copying + let tmp_local = self.create_anonymous_local(PTR_TYPE); + code_builder.set_local(tmp_local); + tmp_local + } + }; + let (to_ptr, to_offset) = location.local_and_offset(self.stack_frame_pointer); copy_memory( code_builder, @@ -648,7 +665,10 @@ impl<'a> Storage<'a> { } => { use crate::wasm_module::Align::*; - code_builder.get_local(from_ptr); + if let AddressValue::NotLoaded(from_ptr) = from_addr { + code_builder.get_local(from_ptr); + } + match (value_type, size) { (ValueType::I64, 8) => code_builder.i64_load(Bytes8, from_offset), (ValueType::I32, 4) => code_builder.i32_load(Bytes4, from_offset), diff --git a/crates/compiler/test_gen/src/gen_primitives.rs b/crates/compiler/test_gen/src/gen_primitives.rs index 7e4dc1e8b5..c46f4e56d4 100644 --- a/crates/compiler/test_gen/src/gen_primitives.rs +++ b/crates/compiler/test_gen/src/gen_primitives.rs @@ -3260,7 +3260,7 @@ fn issue_2322() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn box_and_unbox_string() { assert_evals_to!( indoc!( @@ -3278,7 +3278,7 @@ fn box_and_unbox_string() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn box_and_unbox_num() { assert_evals_to!( indoc!( @@ -3292,7 +3292,7 @@ fn box_and_unbox_num() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn box_and_unbox_record() { assert_evals_to!( indoc!( @@ -3306,7 +3306,7 @@ fn box_and_unbox_record() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn box_and_unbox_tag_union() { assert_evals_to!( indoc!( From 817ffba982873d1fe3fa548cb928c2d3112739ec Mon Sep 17 00:00:00 2001 From: Brian Carroll Date: Sun, 3 Jul 2022 10:59:19 +0100 Subject: [PATCH 03/66] formatting --- crates/compiler/gen_wasm/src/low_level.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/compiler/gen_wasm/src/low_level.rs b/crates/compiler/gen_wasm/src/low_level.rs index 70dfe1a0e0..6ef9106620 100644 --- a/crates/compiler/gen_wasm/src/low_level.rs +++ b/crates/compiler/gen_wasm/src/low_level.rs @@ -11,7 +11,7 @@ use roc_mono::low_level::HigherOrder; use crate::backend::{ProcLookupData, ProcSource, WasmBackend}; use crate::layout::{CallConv, StackMemoryFormat, WasmLayout}; -use crate::storage::{StackMemoryLocation, StoredValue, AddressValue}; +use crate::storage::{AddressValue, StackMemoryLocation, StoredValue}; use crate::wasm_module::{Align, LocalId, ValueType}; use crate::TARGET_INFO; From cecb6987e76a01bba6ba5b6ba15e1578e6889c74 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Mon, 27 Jun 2022 12:51:54 -0400 Subject: [PATCH 04/66] Admit duplicate lambdas in lambda sets when their captures don't unify --- crates/compiler/solve/tests/solve_expr.rs | 62 +++++++ crates/compiler/types/src/subs.rs | 4 + .../compiler/types/src/unification_table.rs | 4 + crates/compiler/unify/src/unify.rs | 166 +++++++++--------- 4 files changed, 154 insertions(+), 82 deletions(-) diff --git a/crates/compiler/solve/tests/solve_expr.rs b/crates/compiler/solve/tests/solve_expr.rs index 1a380b0dc5..9b83cc8233 100644 --- a/crates/compiler/solve/tests/solve_expr.rs +++ b/crates/compiler/solve/tests/solve_expr.rs @@ -6953,6 +6953,68 @@ mod solve_expr { "# ), "Str", + ) + } + + #[test] + fn lambda_sets_collide_with_captured_var() { + infer_queries!( + indoc!( + r#" + capture : a -> ({} -> Str) + capture = \val -> + thunk = + \{} -> + when val is + _ -> "" + thunk + + x : [True, False] + + fun = + when x is + True -> capture "" + False -> capture {} + fun + #^^^{-1} + "# + ), + &["fun : {} -[[thunk(5) {}, thunk(5) Str]]-> Str"], + ); + } + + #[test] + fn lambda_sets_collide_with_captured_function() { + infer_queries!( + indoc!( + r#" + Lazy a : {} -> a + + after : Lazy a, (a -> Lazy b) -> Lazy b + after = \effect, map -> + thunk = \{} -> + when map (effect {}) is + b -> b {} + thunk + + f = \_ -> \_ -> "" + g = \{ s1 } -> \_ -> s1 + + x : [True, False] + + fun = + when x is + True -> after (\{} -> "") f + False -> after (\{} -> {s1: "s1"}) g + fun + #^^^{-1} + "# + ), + &[ + "fun : {} -[[thunk(9) (({} -[[15(15)]]-> { s1 : Str })) ({ s1 : Str } -[[g(4)]]-> ({} -[[13(13) Str]]-> Str)), \ + thunk(9) (({} -[[14(14)]]-> Str)) (Str -[[f(3)]]-> ({} -[[11(11)]]-> Str))]]-> Str", + ], + print_only_under_alias = true, ); } } diff --git a/crates/compiler/types/src/subs.rs b/crates/compiler/types/src/subs.rs index f48e11f7a8..eb3e9f4a98 100644 --- a/crates/compiler/types/src/subs.rs +++ b/crates/compiler/types/src/subs.rs @@ -1872,6 +1872,10 @@ impl Subs { self.utable.unioned(left, right) } + pub fn equivalent_without_compacting(&self, left: Variable, right: Variable) -> bool { + self.utable.unioned_without_compacting(left, right) + } + pub fn redundant(&self, var: Variable) -> bool { self.utable.is_redirect(var) } diff --git a/crates/compiler/types/src/unification_table.rs b/crates/compiler/types/src/unification_table.rs index 12e10f4648..cb44cb4f80 100644 --- a/crates/compiler/types/src/unification_table.rs +++ b/crates/compiler/types/src/unification_table.rs @@ -323,6 +323,10 @@ impl UnificationTable { self.root_key(a) == self.root_key(b) } + pub fn unioned_without_compacting(&self, a: Variable, b: Variable) -> bool { + self.root_key_without_compacting(a) == self.root_key_without_compacting(b) + } + // custom very specific helpers #[inline(always)] pub fn get_rank_set_mark(&mut self, key: Variable, mark: Mark) -> Rank { diff --git a/crates/compiler/unify/src/unify.rs b/crates/compiler/unify/src/unify.rs index 19974716af..1f289b983d 100644 --- a/crates/compiler/unify/src/unify.rs +++ b/crates/compiler/unify/src/unify.rs @@ -1000,103 +1000,105 @@ fn unify_lambda_set_help( in_both, } = separate_union_lambdas(subs, solved1, solved2); - let num_shared = in_both.len(); + let mut new_lambdas = vec![]; + for (lambda_name, (vars1, vars2)) in in_both { + let mut captures_unify = vars1.len() == vars2.len(); - let mut joined_lambdas = vec![]; - for (tag_name, (vars1, vars2)) in in_both { - let mut matching_vars = vec![]; + if captures_unify { + for (var1, var2) in (vars1.into_iter()).zip(vars2.into_iter()) { + let (var1, var2) = (subs[var1], subs[var2]); - if vars1.len() != vars2.len() { - continue; // this is a type mismatch; not adding the tag will trigger it below. - } + // Lambda sets are effectively tags under another name, and their usage can also result + // in the arguments of a lambda name being recursive. It very well may happen that + // during unification, a lambda set previously marked as not recursive becomes + // recursive. See the docs of [LambdaSet] for one example, or https://github.com/rtfeldman/roc/pull/2307. + // + // Like with tag unions, if it has, we'll always pass through this branch. So, take + // this opportunity to promote the lambda set to recursive if need be. + maybe_mark_union_recursive(subs, var1); + maybe_mark_union_recursive(subs, var2); - let num_vars = vars1.len(); - for (var1, var2) in (vars1.into_iter()).zip(vars2.into_iter()) { - let (var1, var2) = (subs[var1], subs[var2]); + let snapshot = subs.snapshot(); + let outcome = unify_pool::(subs, pool, var1, var2, ctx.mode); - // Lambda sets are effectively tags under another name, and their usage can also result - // in the arguments of a lambda name being recursive. It very well may happen that - // during unification, a lambda set previously marked as not recursive becomes - // recursive. See the docs of [LambdaSet] for one example, or https://github.com/rtfeldman/roc/pull/2307. - // - // Like with tag unions, if it has, we'll always pass through this branch. So, take - // this opportunity to promote the lambda set to recursive if need be. - maybe_mark_union_recursive(subs, var1); - maybe_mark_union_recursive(subs, var2); - - let outcome = unify_pool::(subs, pool, var1, var2, ctx.mode); - - if outcome.mismatches.is_empty() { - matching_vars.push(var1); + if !outcome.mismatches.is_empty() { + captures_unify = false; + subs.rollback_to(snapshot); + // Continue so the other variables can unify if possible, allowing us to re-use + // shared variables. + } } } - if matching_vars.len() == num_vars { - joined_lambdas.push((tag_name, matching_vars)); + if captures_unify { + debug_assert!((subs.get_subs_slice(vars1).iter()) + .zip(subs.get_subs_slice(vars2).iter()) + .all(|(v1, v2)| subs.equivalent_without_compacting(*v1, *v2))); + + new_lambdas.push((lambda_name, subs.get_subs_slice(vars1).to_vec())); + } else { + debug_assert!((subs.get_subs_slice(vars1).iter()) + .zip(subs.get_subs_slice(vars2).iter()) + .any(|(v1, v2)| !subs.equivalent_without_compacting(*v1, *v2))); + + new_lambdas.push((lambda_name, subs.get_subs_slice(vars1).to_vec())); + new_lambdas.push((lambda_name, subs.get_subs_slice(vars2).to_vec())); } } - if joined_lambdas.len() == num_shared { - let all_lambdas = joined_lambdas; - let all_lambdas = merge_sorted( - all_lambdas, - only_in_1.into_iter().map(|(name, subs_slice)| { - let vec = subs.get_subs_slice(subs_slice).to_vec(); - (name, vec) - }), - ); - let all_lambdas = merge_sorted( - all_lambdas, - only_in_2.into_iter().map(|(name, subs_slice)| { - let vec = subs.get_subs_slice(subs_slice).to_vec(); - (name, vec) - }), - ); + let all_lambdas = new_lambdas; + let all_lambdas = merge_sorted( + all_lambdas, + only_in_1.into_iter().map(|(name, subs_slice)| { + let vec = subs.get_subs_slice(subs_slice).to_vec(); + (name, vec) + }), + ); + let all_lambdas = merge_sorted( + all_lambdas, + only_in_2.into_iter().map(|(name, subs_slice)| { + let vec = subs.get_subs_slice(subs_slice).to_vec(); + (name, vec) + }), + ); - let recursion_var = match (rec1.into_variable(), rec2.into_variable()) { - // Prefer left when it's available. - (Some(rec), _) | (_, Some(rec)) => OptVariable::from(rec), - (None, None) => OptVariable::NONE, - }; + let recursion_var = match (rec1.into_variable(), rec2.into_variable()) { + // Prefer left when it's available. + (Some(rec), _) | (_, Some(rec)) => OptVariable::from(rec), + (None, None) => OptVariable::NONE, + }; - // Combine the unspecialized lambda sets as needed. Note that we don't need to update the - // bookkeeping of variable -> lambda set to be resolved, because if we had v1 -> lset1, and - // now lset1 ~ lset2, then afterward either lset1 still resolves to itself or re-points to - // lset2. In either case the merged unspecialized lambda sets will be there. - let merged_unspecialized = match (uls1.is_empty(), uls2.is_empty()) { - (true, true) => SubsSlice::default(), - (false, true) => uls1, - (true, false) => uls2, - (false, false) => { - let mut all_uls = (subs.get_subs_slice(uls1).iter()) - .chain(subs.get_subs_slice(uls2)) - .map(|&Uls(var, sym, region)| { - // Take the root key to deduplicate - Uls(subs.get_root_key_without_compacting(var), sym, region) - }) - .collect::>(); - all_uls.sort(); - all_uls.dedup(); + // Combine the unspecialized lambda sets as needed. Note that we don't need to update the + // bookkeeping of variable -> lambda set to be resolved, because if we had v1 -> lset1, and + // now lset1 ~ lset2, then afterward either lset1 still resolves to itself or re-points to + // lset2. In either case the merged unspecialized lambda sets will be there. + let merged_unspecialized = match (uls1.is_empty(), uls2.is_empty()) { + (true, true) => SubsSlice::default(), + (false, true) => uls1, + (true, false) => uls2, + (false, false) => { + let mut all_uls = (subs.get_subs_slice(uls1).iter()) + .chain(subs.get_subs_slice(uls2)) + .map(|&Uls(var, sym, region)| { + // Take the root key to deduplicate + Uls(subs.get_root_key_without_compacting(var), sym, region) + }) + .collect::>(); + all_uls.sort(); + all_uls.dedup(); - SubsSlice::extend_new(&mut subs.unspecialized_lambda_sets, all_uls) - } - }; + SubsSlice::extend_new(&mut subs.unspecialized_lambda_sets, all_uls) + } + }; - let new_solved = UnionLabels::insert_into_subs(subs, all_lambdas); - let new_lambda_set = Content::LambdaSet(LambdaSet { - solved: new_solved, - recursion_var, - unspecialized: merged_unspecialized, - }); + let new_solved = UnionLabels::insert_into_subs(subs, all_lambdas); + let new_lambda_set = Content::LambdaSet(LambdaSet { + solved: new_solved, + recursion_var, + unspecialized: merged_unspecialized, + }); - merge(subs, ctx, new_lambda_set) - } else { - mismatch!( - "Problem with lambda sets: there should be {:?} matching lambda, but only found {:?}", - num_shared, - &joined_lambdas - ) - } + merge(subs, ctx, new_lambda_set) } /// Ensures that a non-recursive tag union, when unified with a recursion var to become a recursive From 8fb9ccccfeacd415ae900d44dc5c9f4eb73bff48 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Mon, 27 Jun 2022 13:03:09 -0400 Subject: [PATCH 05/66] Allow captures to be marked as unified without having to be merged It's very possible to unify two variables without their actual variable numbers having been merged in the unification forest. We might want to do that in the future, but it's not necessarily true today. For example two concrete constructors `{}` and `{}` are unified by their contents, but the variables are not necessarily merged afterward. --- crates/compiler/unify/src/unify.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/crates/compiler/unify/src/unify.rs b/crates/compiler/unify/src/unify.rs index 1f289b983d..3929829561 100644 --- a/crates/compiler/unify/src/unify.rs +++ b/crates/compiler/unify/src/unify.rs @@ -1021,7 +1021,9 @@ fn unify_lambda_set_help( let snapshot = subs.snapshot(); let outcome = unify_pool::(subs, pool, var1, var2, ctx.mode); - if !outcome.mismatches.is_empty() { + if outcome.mismatches.is_empty() { + subs.commit_snapshot(snapshot); + } else { captures_unify = false; subs.rollback_to(snapshot); // Continue so the other variables can unify if possible, allowing us to re-use @@ -1031,10 +1033,6 @@ fn unify_lambda_set_help( } if captures_unify { - debug_assert!((subs.get_subs_slice(vars1).iter()) - .zip(subs.get_subs_slice(vars2).iter()) - .all(|(v1, v2)| subs.equivalent_without_compacting(*v1, *v2))); - new_lambdas.push((lambda_name, subs.get_subs_slice(vars1).to_vec())); } else { debug_assert!((subs.get_subs_slice(vars1).iter()) From ca87faa906b60e2d5ed896fa624cb0266d6d051c Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Mon, 27 Jun 2022 13:07:22 -0400 Subject: [PATCH 06/66] Allow union lambdas to have duplicates --- crates/compiler/types/src/subs.rs | 7 +++++-- crates/compiler/unify/src/unify.rs | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/crates/compiler/types/src/subs.rs b/crates/compiler/types/src/subs.rs index eb3e9f4a98..a4b99eba78 100644 --- a/crates/compiler/types/src/subs.rs +++ b/crates/compiler/types/src/subs.rs @@ -2650,11 +2650,14 @@ impl UnionLabels where L: Label + Ord, { - pub fn is_sorted_no_duplicates(&self, subs: &Subs) -> bool { + /// Checks if the union of labels is sorted by label. + /// Duplicates *are* admitted, since this represents a lambda set, in which we may have + /// duplicate lambda captures, if those lambda captures have different representations! + pub fn is_sorted(&self, subs: &Subs) -> bool { let mut iter = self.iter_from_subs(subs).peekable(); while let Some((before, _)) = iter.next() { if let Some((after, _)) = iter.peek() { - if before >= after { + if before > after { return false; } } diff --git a/crates/compiler/unify/src/unify.rs b/crates/compiler/unify/src/unify.rs index 3929829561..fb525f2c8e 100644 --- a/crates/compiler/unify/src/unify.rs +++ b/crates/compiler/unify/src/unify.rs @@ -1506,8 +1506,8 @@ fn separate_union_lambdas( fields1: UnionLambdas, fields2: UnionLambdas, ) -> Separate { - debug_assert!(fields1.is_sorted_no_duplicates(subs)); - debug_assert!(fields2.is_sorted_no_duplicates(subs)); + debug_assert!(fields1.is_sorted(subs)); + debug_assert!(fields2.is_sorted(subs)); let it1 = fields1.iter_all().map(|(s, vars)| (subs[s], subs[vars])); let it2 = fields2.iter_all().map(|(s, vars)| (subs[s], subs[vars])); From 1c40ad6127d4b5fef72ab062710b305da7afc5b9 Mon Sep 17 00:00:00 2001 From: ayazhafiz Date: Mon, 27 Jun 2022 23:21:56 -0400 Subject: [PATCH 07/66] Check if function doesn't need closure arg without associated method --- crates/compiler/mono/src/ir.rs | 117 +++++++++++++++++------------ crates/compiler/mono/src/layout.rs | 11 --- 2 files changed, 71 insertions(+), 57 deletions(-) diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index 91612ac787..b32e000bb7 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -9103,15 +9103,22 @@ fn match_on_lambda_set<'a>( } => { let function_symbol = lambda_set.set[0].0; + let closure_info = match field_layouts { + [] => ClosureInfo::DoesNotCapture, + _ => ClosureInfo::Captures { + lambda_set, + closure_data_symbol, + closure_data_layout: Layout::Struct { + field_layouts, + field_order_hash, + }, + }, + }; + union_lambda_set_branch_help( env, function_symbol, - lambda_set, - closure_data_symbol, - Layout::Struct { - field_layouts, - field_order_hash, - }, + closure_info, argument_symbols, argument_layouts, return_layout, @@ -9183,14 +9190,21 @@ fn union_lambda_set_to_switch<'a>( let mut branches = Vec::with_capacity_in(lambda_set.set.len(), env.arena); - for (i, (function_symbol, _)) in lambda_set.set.iter().enumerate() { + for (i, (function_symbol, capture_layouts)) in lambda_set.set.iter().enumerate() { + let closure_info = match capture_layouts { + [] => ClosureInfo::DoesNotCapture, + _ => ClosureInfo::Captures { + lambda_set, + closure_data_symbol, + closure_data_layout: closure_layout, + }, + }; + let stmt = union_lambda_set_branch( env, - lambda_set, join_point_id, *function_symbol, - closure_data_symbol, - closure_layout, + closure_info, argument_symbols, argument_layouts, return_layout, @@ -9229,11 +9243,9 @@ fn union_lambda_set_to_switch<'a>( #[allow(clippy::too_many_arguments)] fn union_lambda_set_branch<'a>( env: &mut Env<'a, '_>, - lambda_set: LambdaSet<'a>, join_point_id: JoinPointId, function_symbol: Symbol, - closure_data_symbol: Symbol, - closure_data_layout: Layout<'a>, + closure_info: ClosureInfo<'a>, argument_symbols_slice: &'a [Symbol], argument_layouts_slice: &'a [Layout<'a>], return_layout: &'a Layout<'a>, @@ -9245,9 +9257,7 @@ fn union_lambda_set_branch<'a>( union_lambda_set_branch_help( env, function_symbol, - lambda_set, - closure_data_symbol, - closure_data_layout, + closure_info, argument_symbols_slice, argument_layouts_slice, return_layout, @@ -9256,52 +9266,67 @@ fn union_lambda_set_branch<'a>( ) } +enum ClosureInfo<'a> { + Captures { + closure_data_symbol: Symbol, + /// The layout of this closure variant + closure_data_layout: Layout<'a>, + /// The whole lambda set representation this closure is a variant of + lambda_set: LambdaSet<'a>, + }, + DoesNotCapture, +} + #[allow(clippy::too_many_arguments)] fn union_lambda_set_branch_help<'a>( env: &mut Env<'a, '_>, function_symbol: Symbol, - lambda_set: LambdaSet<'a>, - closure_data_symbol: Symbol, - closure_data_layout: Layout<'a>, + closure_info: ClosureInfo<'a>, argument_symbols_slice: &'a [Symbol], argument_layouts_slice: &'a [Layout<'a>], return_layout: &'a Layout<'a>, assigned: Symbol, hole: &'a Stmt<'a>, ) -> Stmt<'a> { - let (argument_layouts, argument_symbols) = match closure_data_layout { - Layout::Struct { - field_layouts: &[], .. - } - | Layout::Builtin(Builtin::Bool) - | Layout::Builtin(Builtin::Int(IntWidth::U8)) => { - (argument_layouts_slice, argument_symbols_slice) - } - _ if lambda_set.member_does_not_need_closure_argument(function_symbol) => { + let (argument_layouts, argument_symbols) = match closure_info { + ClosureInfo::Captures { + lambda_set, + closure_data_symbol, + closure_data_layout, + } => match closure_data_layout { + Layout::Struct { + field_layouts: &[], .. + } + | Layout::Builtin(Builtin::Bool) + | Layout::Builtin(Builtin::Int(IntWidth::U8)) => { + (argument_layouts_slice, argument_symbols_slice) + } + _ => { + // extend layouts with the layout of the closure environment + let mut argument_layouts = + Vec::with_capacity_in(argument_layouts_slice.len() + 1, env.arena); + argument_layouts.extend(argument_layouts_slice); + argument_layouts.push(Layout::LambdaSet(lambda_set)); + + // extend symbols with the symbol of the closure environment + let mut argument_symbols = + Vec::with_capacity_in(argument_symbols_slice.len() + 1, env.arena); + argument_symbols.extend(argument_symbols_slice); + argument_symbols.push(closure_data_symbol); + + ( + argument_layouts.into_bump_slice(), + argument_symbols.into_bump_slice(), + ) + } + }, + ClosureInfo::DoesNotCapture => { // sometimes unification causes a function that does not itself capture anything // to still get a lambda set that does store information. We must not pass a closure // argument in this case (argument_layouts_slice, argument_symbols_slice) } - _ => { - // extend layouts with the layout of the closure environment - let mut argument_layouts = - Vec::with_capacity_in(argument_layouts_slice.len() + 1, env.arena); - argument_layouts.extend(argument_layouts_slice); - argument_layouts.push(Layout::LambdaSet(lambda_set)); - - // extend symbols with the symbol of the closure environment - let mut argument_symbols = - Vec::with_capacity_in(argument_symbols_slice.len() + 1, env.arena); - argument_symbols.extend(argument_symbols_slice); - argument_symbols.push(closure_data_symbol); - - ( - argument_layouts.into_bump_slice(), - argument_symbols.into_bump_slice(), - ) - } }; // build the call diff --git a/crates/compiler/mono/src/layout.rs b/crates/compiler/mono/src/layout.rs index df89ee1049..5870184d48 100644 --- a/crates/compiler/mono/src/layout.rs +++ b/crates/compiler/mono/src/layout.rs @@ -741,17 +741,6 @@ impl<'a> LambdaSet<'a> { } } - pub fn member_does_not_need_closure_argument(&self, function_symbol: Symbol) -> bool { - match self.layout_for_member(function_symbol) { - ClosureRepresentation::Union { - alphabetic_order_fields, - .. - } => alphabetic_order_fields.is_empty(), - ClosureRepresentation::AlphabeticOrderStruct(fields) => fields.is_empty(), - ClosureRepresentation::Other(_) => false, - } - } - pub fn layout_for_member(&self, function_symbol: Symbol) -> ClosureRepresentation<'a> { debug_assert!( self.set.iter().any(|(s, _)| *s == function_symbol), From 51978e08ede93c663983d10e5d99f0af1f0e121e Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Tue, 28 Jun 2022 12:13:53 -0400 Subject: [PATCH 08/66] First pass at supporting multimorphic lambdas in lambda sets --- crates/compiler/alias_analysis/src/lib.rs | 6 +- crates/compiler/gen_dev/src/lib.rs | 4 +- crates/compiler/gen_llvm/src/llvm/build.rs | 4 +- crates/compiler/gen_wasm/src/backend.rs | 2 +- crates/compiler/load_internal/src/file.rs | 43 +- crates/compiler/mono/src/borrow.rs | 8 +- crates/compiler/mono/src/code_gen_help/mod.rs | 4 +- crates/compiler/mono/src/inc_dec.rs | 2 +- crates/compiler/mono/src/ir.rs | 841 +++++++++++++----- crates/compiler/mono/src/layout.rs | 378 ++++++-- crates/compiler/test_mono/src/tests.rs | 25 + crates/repl_cli/src/lib.rs | 11 +- crates/repl_eval/src/eval.rs | 84 +- crates/repl_eval/src/lib.rs | 4 +- 14 files changed, 1061 insertions(+), 355 deletions(-) diff --git a/crates/compiler/alias_analysis/src/lib.rs b/crates/compiler/alias_analysis/src/lib.rs index 3c9c404802..a21b1e6ee3 100644 --- a/crates/compiler/alias_analysis/src/lib.rs +++ b/crates/compiler/alias_analysis/src/lib.rs @@ -23,7 +23,11 @@ pub const STATIC_LIST_NAME: ConstName = ConstName(b"THIS IS A STATIC LIST"); const ENTRY_POINT_NAME: &[u8] = b"mainForHost"; pub fn func_name_bytes(proc: &Proc) -> [u8; SIZE] { - func_name_bytes_help(proc.name, proc.args.iter().map(|x| x.0), &proc.ret_layout) + func_name_bytes_help( + proc.name.call_name(), + proc.args.iter().map(|x| x.0), + &proc.ret_layout, + ) } #[inline(always)] diff --git a/crates/compiler/gen_dev/src/lib.rs b/crates/compiler/gen_dev/src/lib.rs index 73927170e6..4a650b66a7 100644 --- a/crates/compiler/gen_dev/src/lib.rs +++ b/crates/compiler/gen_dev/src/lib.rs @@ -106,8 +106,8 @@ trait Backend<'a> { proc: Proc<'a>, layout_ids: &mut LayoutIds<'a>, ) -> (Vec, Vec, Vec<'a, (Symbol, String)>) { - let layout_id = layout_ids.get(proc.name, &proc.ret_layout); - let proc_name = self.symbol_to_string(proc.name, layout_id); + let layout_id = layout_ids.get(proc.name.call_name(), &proc.ret_layout); + let proc_name = self.symbol_to_string(proc.name.call_name(), layout_id); self.reset(proc_name, proc.is_self_recursive); self.load_args(proc.args, &proc.ret_layout); for (layout, sym) in proc.args { diff --git a/crates/compiler/gen_llvm/src/llvm/build.rs b/crates/compiler/gen_llvm/src/llvm/build.rs index 5a6eaf33f1..46caeb1672 100644 --- a/crates/compiler/gen_llvm/src/llvm/build.rs +++ b/crates/compiler/gen_llvm/src/llvm/build.rs @@ -4185,7 +4185,7 @@ fn build_procedures_help<'a, 'ctx, 'env>( // only have top-level thunks for this proc's module in scope // this retain is not needed for correctness, but will cause less confusion when debugging - let home = proc.name.module_id(); + let home = proc.name.call_name().module_id(); current_scope.retain_top_level_thunks_for_module(home); build_proc( @@ -4559,7 +4559,7 @@ pub fn build_proc<'a, 'ctx, 'env>( } }; - let ident_string = proc.name.as_str(&env.interns); + let ident_string = proc.name.call_name().as_str(&env.interns); let fn_name: String = format!("{}_1", ident_string); build_closure_caller( diff --git a/crates/compiler/gen_wasm/src/backend.rs b/crates/compiler/gen_wasm/src/backend.rs index ee30aa1aec..cb0ea554e1 100644 --- a/crates/compiler/gen_wasm/src/backend.rs +++ b/crates/compiler/gen_wasm/src/backend.rs @@ -380,7 +380,7 @@ impl<'a> WasmBackend<'a> { println!("\ngenerating procedure {:?}\n", proc.name); } - self.append_proc_debug_name(proc.name); + self.append_proc_debug_name(proc.name.call_name()); self.start_proc(proc); diff --git a/crates/compiler/load_internal/src/file.rs b/crates/compiler/load_internal/src/file.rs index eaabd9b14e..e67aa4cfc4 100644 --- a/crates/compiler/load_internal/src/file.rs +++ b/crates/compiler/load_internal/src/file.rs @@ -29,11 +29,12 @@ use roc_module::symbol::{ IdentIds, IdentIdsByModule, Interns, ModuleId, ModuleIds, PQModuleName, PackageModuleIds, PackageQualified, Symbol, }; +use roc_mono::fresh_multimorphic_symbol; use roc_mono::ir::{ CapturedSymbols, EntryPoint, ExternalSpecializations, PartialProc, Proc, ProcLayout, Procs, ProcsBase, UpdateModeIds, }; -use roc_mono::layout::{Layout, LayoutCache, LayoutProblem}; +use roc_mono::layout::{LambdaName, Layout, LayoutCache, LayoutProblem}; use roc_parse::ast::{self, Defs, ExtractSpaces, Spaced, StrLiteral, TypeAnnotation}; use roc_parse::header::{ExposedName, ImportsEntry, PackageEntry, PlatformHeader, To, TypedIdent}; use roc_parse::header::{HeaderFor, ModuleNameEnum, PackageName}; @@ -4545,8 +4546,12 @@ fn build_pending_specializations<'a>( // never gets called by Roc code, it will never // get specialized! if is_host_exposed { - let layout_result = - layout_cache.raw_from_var(mono_env.arena, expr_var, mono_env.subs); + let layout_result = layout_cache.raw_from_var( + mono_env.arena, + expr_var, + mono_env.subs, + fresh_multimorphic_symbol!(mono_env), + ); // cannot specialize when e.g. main's type contains type variables if let Err(e) = layout_result { @@ -4571,7 +4576,7 @@ fn build_pending_specializations<'a>( procs_base.host_specializations.insert_host_exposed( mono_env.subs, - symbol, + LambdaName::only_receiver(symbol), annotation, expr_var, ); @@ -4605,8 +4610,12 @@ fn build_pending_specializations<'a>( // never gets called by Roc code, it will never // get specialized! if is_host_exposed { - let layout_result = - layout_cache.raw_from_var(mono_env.arena, expr_var, mono_env.subs); + let layout_result = layout_cache.raw_from_var( + mono_env.arena, + expr_var, + mono_env.subs, + fresh_multimorphic_symbol!(mono_env), + ); // cannot specialize when e.g. main's type contains type variables if let Err(e) = layout_result { @@ -4631,7 +4640,7 @@ fn build_pending_specializations<'a>( procs_base.host_specializations.insert_host_exposed( mono_env.subs, - symbol, + LambdaName::only_receiver(symbol), annotation, expr_var, ); @@ -4683,8 +4692,12 @@ fn build_pending_specializations<'a>( // never gets called by Roc code, it will never // get specialized! if is_host_exposed { - let layout_result = - layout_cache.raw_from_var(mono_env.arena, expr_var, mono_env.subs); + let layout_result = layout_cache.raw_from_var( + mono_env.arena, + expr_var, + mono_env.subs, + fresh_multimorphic_symbol!(mono_env), + ); // cannot specialize when e.g. main's type contains type variables if let Err(e) = layout_result { @@ -4709,7 +4722,7 @@ fn build_pending_specializations<'a>( procs_base.host_specializations.insert_host_exposed( mono_env.subs, - symbol, + LambdaName::only_receiver(symbol), annotation, expr_var, ); @@ -4743,8 +4756,12 @@ fn build_pending_specializations<'a>( // never gets called by Roc code, it will never // get specialized! if is_host_exposed { - let layout_result = - layout_cache.raw_from_var(mono_env.arena, expr_var, mono_env.subs); + let layout_result = layout_cache.raw_from_var( + mono_env.arena, + expr_var, + mono_env.subs, + fresh_multimorphic_symbol!(mono_env), + ); // cannot specialize when e.g. main's type contains type variables if let Err(e) = layout_result { @@ -4769,7 +4786,7 @@ fn build_pending_specializations<'a>( procs_base.host_specializations.insert_host_exposed( mono_env.subs, - symbol, + LambdaName::only_receiver(symbol), annotation, expr_var, ); diff --git a/crates/compiler/mono/src/borrow.rs b/crates/compiler/mono/src/borrow.rs index d3cc966c05..92f8f39d80 100644 --- a/crates/compiler/mono/src/borrow.rs +++ b/crates/compiler/mono/src/borrow.rs @@ -263,7 +263,7 @@ impl<'a> ParamMap<'a> { self.declarations[index + i] = param; } - self.visit_stmt(arena, proc.name, &proc.body); + self.visit_stmt(arena, proc.name.call_name(), &proc.body); } fn visit_proc_always_owned( @@ -282,7 +282,7 @@ impl<'a> ParamMap<'a> { self.declarations[index + i] = param; } - self.visit_stmt(arena, proc.name, &proc.body); + self.visit_stmt(arena, proc.name.call_name(), &proc.body); } fn visit_stmt(&mut self, arena: &'a Bump, _fnid: Symbol, stmt: &Stmt<'a>) { @@ -852,10 +852,10 @@ impl<'a> BorrowInfState<'a> { let ys = Vec::from_iter_in(proc.args.iter().map(|t| t.1), self.arena).into_bump_slice(); self.update_param_set_symbols(ys); - self.current_proc = proc.name; + self.current_proc = proc.name.call_name(); // ensure that current_proc is in the owned map - self.owned.entry(proc.name).or_default(); + self.owned.entry(proc.name.call_name()).or_default(); self.collect_stmt(param_map, &proc.body); self.update_param_map_declaration(param_map, param_offset, proc.args.len()); diff --git a/crates/compiler/mono/src/code_gen_help/mod.rs b/crates/compiler/mono/src/code_gen_help/mod.rs index dadaafd9f1..02dbbe0c2f 100644 --- a/crates/compiler/mono/src/code_gen_help/mod.rs +++ b/crates/compiler/mono/src/code_gen_help/mod.rs @@ -8,7 +8,7 @@ use crate::ir::{ Call, CallSpecId, CallType, Expr, HostExposedLayouts, JoinPointId, ModifyRc, Proc, ProcLayout, SelfRecursive, Stmt, UpdateModeId, }; -use crate::layout::{Builtin, Layout, UnionLayout}; +use crate::layout::{Builtin, LambdaName, Layout, UnionLayout}; mod equality; mod refcount; @@ -343,7 +343,7 @@ impl<'a> CodeGenHelp<'a> { }; self.specializations[spec_index].proc = Some(Proc { - name: proc_symbol, + name: LambdaName::from_non_multimorphic(proc_symbol), args, body, closure_data_layout: None, diff --git a/crates/compiler/mono/src/inc_dec.rs b/crates/compiler/mono/src/inc_dec.rs index 28e72e006d..f341d6dea6 100644 --- a/crates/compiler/mono/src/inc_dec.rs +++ b/crates/compiler/mono/src/inc_dec.rs @@ -1406,7 +1406,7 @@ fn visit_proc<'a, 'i>( proc: &mut Proc<'a>, layout: ProcLayout<'a>, ) { - let params = match param_map.get_symbol(proc.name, layout) { + let params = match param_map.get_symbol(proc.name.call_name(), layout) { Some(slice) => slice, None => Vec::from_iter_in( proc.args.iter().cloned().map(|(layout, symbol)| Param { diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index b32e000bb7..6eec3a6e5f 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -1,7 +1,7 @@ #![allow(clippy::manual_map)] use crate::layout::{ - Builtin, ClosureRepresentation, LambdaSet, Layout, LayoutCache, LayoutProblem, + Builtin, ClosureRepresentation, LambdaName, LambdaSet, Layout, LayoutCache, LayoutProblem, RawFunctionLayout, TagIdIntType, TagOrClosure, UnionLayout, WrappedVariant, }; use bumpalo::collections::{CollectIn, Vec}; @@ -305,7 +305,7 @@ impl<'a> Default for CapturedSymbols<'a> { #[derive(Clone, Debug, PartialEq)] pub struct Proc<'a> { - pub name: Symbol, + pub name: LambdaName, pub args: &'a [(Layout<'a>, Symbol)], pub body: Stmt<'a>, pub closure_data_layout: Option>, @@ -352,12 +352,12 @@ impl<'a> Proc<'a> { if pretty_print_ir_symbols() { alloc .text("procedure : ") - .append(symbol_to_doc(alloc, self.name)) + .append(symbol_to_doc(alloc, self.name.call_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(symbol_to_doc(alloc, self.name.call_name())) .append(" (") .append(alloc.intersperse(args_doc, ", ")) .append("):") @@ -366,7 +366,7 @@ impl<'a> Proc<'a> { } else { alloc .text("procedure ") - .append(symbol_to_doc(alloc, self.name)) + .append(symbol_to_doc(alloc, self.name.call_name())) .append(" (") .append(alloc.intersperse(args_doc, ", ")) .append("):") @@ -439,7 +439,7 @@ impl<'a> Proc<'a> { let transformed = crate::tail_recursion::make_tail_recursive( env.arena, id, - self.name, + self.name.call_name(), self.body.clone(), args.into_bump_slice(), self.ret_layout, @@ -458,7 +458,8 @@ impl<'a> Proc<'a> { pub struct HostSpecializations { /// Not a bumpalo vec because bumpalo is not thread safe /// Separate array so we can search for membership quickly - symbols: std::vec::Vec, + /// If it's a value and not a lambda, the value is recorded as LambdaName::thunk. + symbol_or_lambdas: std::vec::Vec, storage_subs: StorageSubs, /// For each symbol, what types to specialize it for, points into the storage_subs types_to_specialize: std::vec::Vec, @@ -475,7 +476,7 @@ impl Default for HostSpecializations { impl HostSpecializations { pub fn new() -> Self { Self { - symbols: std::vec::Vec::new(), + symbol_or_lambdas: std::vec::Vec::new(), storage_subs: StorageSubs::new(Subs::default()), types_to_specialize: std::vec::Vec::new(), exposed_aliases: std::vec::Vec::new(), @@ -485,7 +486,7 @@ impl HostSpecializations { pub fn insert_host_exposed( &mut self, env_subs: &mut Subs, - symbol: Symbol, + symbol_or_lambda: LambdaName, opt_annotation: Option, variable: Variable, ) { @@ -497,9 +498,13 @@ impl HostSpecializations { host_exposed_aliases.extend(annotation.introduced_variables.host_exposed_aliases); } - match self.symbols.iter().position(|s| *s == symbol) { + match self + .symbol_or_lambdas + .iter() + .position(|s| *s == symbol_or_lambda) + { None => { - self.symbols.push(symbol); + self.symbol_or_lambdas.push(symbol_or_lambda); self.types_to_specialize.push(variable); self.exposed_aliases.push(host_exposed_aliases); } @@ -518,9 +523,9 @@ impl HostSpecializations { self, ) -> ( StorageSubs, - impl Iterator)>, + impl Iterator)>, ) { - let it1 = self.symbols.into_iter(); + let it1 = self.symbol_or_lambdas.into_iter(); let it2 = self.types_to_specialize.into_iter(); let it3 = self.exposed_aliases.into_iter(); @@ -537,7 +542,8 @@ impl HostSpecializations { pub struct ExternalSpecializations { /// Not a bumpalo vec because bumpalo is not thread safe /// Separate array so we can search for membership quickly - pub symbols: std::vec::Vec, + /// If it's a value and not a lambda, the value is recorded as LambdaName::thunk. + pub symbol_or_lambda: std::vec::Vec, storage_subs: StorageSubs, /// For each symbol, what types to specialize it for, points into the storage_subs types_to_specialize: std::vec::Vec>, @@ -552,18 +558,27 @@ impl Default for ExternalSpecializations { impl ExternalSpecializations { pub fn new() -> Self { Self { - symbols: std::vec::Vec::new(), + symbol_or_lambda: std::vec::Vec::new(), storage_subs: StorageSubs::new(Subs::default()), types_to_specialize: std::vec::Vec::new(), } } - fn insert_external(&mut self, symbol: Symbol, env_subs: &mut Subs, variable: Variable) { + fn insert_external( + &mut self, + symbol_or_lambda: LambdaName, + env_subs: &mut Subs, + variable: Variable, + ) { let variable = self.storage_subs.extend_with_variable(env_subs, variable); - match self.symbols.iter().position(|s| *s == symbol) { + match self + .symbol_or_lambda + .iter() + .position(|s| *s == symbol_or_lambda) + { None => { - self.symbols.push(symbol); + self.symbol_or_lambda.push(symbol_or_lambda); self.types_to_specialize.push(vec![variable]); } Some(index) => { @@ -577,11 +592,11 @@ impl ExternalSpecializations { self, ) -> ( StorageSubs, - impl Iterator)>, + impl Iterator)>, ) { ( self.storage_subs, - self.symbols + self.symbol_or_lambda .into_iter() .zip(self.types_to_specialize.into_iter()), ) @@ -591,7 +606,8 @@ impl ExternalSpecializations { #[derive(Clone, Debug)] pub struct Suspended<'a> { pub store: StorageSubs, - pub symbols: Vec<'a, Symbol>, + /// LambdaName::thunk if it's a value + pub symbol_or_lambdas: Vec<'a, LambdaName>, pub layouts: Vec<'a, ProcLayout<'a>>, pub variables: Vec<'a, Variable>, } @@ -600,7 +616,7 @@ impl<'a> Suspended<'a> { fn new_in(arena: &'a Bump) -> Self { Self { store: StorageSubs::new(Subs::new_from_varstore(Default::default())), - symbols: Vec::new_in(arena), + symbol_or_lambdas: Vec::new_in(arena), layouts: Vec::new_in(arena), variables: Vec::new_in(arena), } @@ -609,13 +625,13 @@ impl<'a> Suspended<'a> { fn specialization( &mut self, subs: &mut Subs, - symbol: Symbol, + symbol_or_lambda: LambdaName, proc_layout: ProcLayout<'a>, variable: Variable, ) { // de-duplicate - for (i, s) in self.symbols.iter().enumerate() { - if *s == symbol { + for (i, s) in self.symbol_or_lambdas.iter().enumerate() { + if *s == symbol_or_lambda { let existing = &self.layouts[i]; if &proc_layout == existing { // symbol + layout combo exists @@ -624,7 +640,7 @@ impl<'a> Suspended<'a> { } } - self.symbols.push(symbol); + self.symbol_or_lambdas.push(symbol_or_lambda); self.layouts.push(proc_layout); let variable = self.store.extend_with_variable(subs, variable); @@ -670,7 +686,9 @@ impl<'a> Specialized<'a> { None } else { match in_progress { - InProgressProc::InProgress => panic!("Function is not done specializing"), + InProgressProc::InProgress => { + panic!("Function {:?} ({:?}) is not done specializing", s, l) + } InProgressProc::Done(proc) => Some((s, l, proc)), } } @@ -688,6 +706,7 @@ impl<'a> Specialized<'a> { } fn mark_in_progress(&mut self, symbol: Symbol, layout: ProcLayout<'a>) { + // dbg!((symbol, layout)); for (i, s) in self.symbols.iter().enumerate() { if *s == symbol && self.proc_layouts[i] == layout { match &self.procedures[i] { @@ -708,6 +727,7 @@ impl<'a> Specialized<'a> { } fn remove_specialized(&mut self, symbol: Symbol, layout: &ProcLayout<'a>) -> bool { + // dbg!((symbol, layout)); let mut index = None; for (i, s) in self.symbols.iter().enumerate() { @@ -726,6 +746,7 @@ impl<'a> Specialized<'a> { } fn insert_specialized(&mut self, symbol: Symbol, layout: ProcLayout<'a>, proc: Proc<'a>) { + // dbg!((symbol, layout)); for (i, s) in self.symbols.iter().enumerate() { if *s == symbol && self.proc_layouts[i] == layout { match &self.procedures[i] { @@ -793,6 +814,16 @@ struct SymbolSpecializations<'a>( VecMap, (Variable, Symbol)>>, ); +#[macro_export] +macro_rules! fresh_multimorphic_symbol { + ($env:expr) => { + &mut || { + let ident_id = $env.ident_ids.gen_unique(); + Symbol::new($env.home, ident_id) + } + }; +} + impl<'a> SymbolSpecializations<'a> { /// Gets a specialization for a symbol, or creates a new one. #[inline(always)] @@ -806,7 +837,12 @@ impl<'a> SymbolSpecializations<'a> { let arena = env.arena; let subs: &Subs = env.subs; - let layout = match layout_cache.from_var(arena, specialization_var, subs) { + let layout = match layout_cache.from_var( + arena, + specialization_var, + subs, + fresh_multimorphic_symbol!(env), + ) { Ok(layout) => layout, // This can happen when the def symbol has a type error. In such cases just use the // def symbol, which is erroring. @@ -818,7 +854,12 @@ impl<'a> SymbolSpecializations<'a> { Content::Structure(FlatType::Func(..)) ); let function_mark = if is_closure { - let fn_layout = match layout_cache.raw_from_var(arena, specialization_var, subs) { + let fn_layout = match layout_cache.raw_from_var( + arena, + specialization_var, + subs, + fresh_multimorphic_symbol!(env), + ) { Ok(layout) => layout, // This can happen when the def symbol has a type error. In such cases just use the // def symbol, which is erroring. @@ -991,7 +1032,7 @@ impl<'a> Procs<'a> { fn insert_anonymous( &mut self, env: &mut Env<'a, '_>, - symbol: Symbol, + name: LambdaName, annotation: Variable, loc_args: std::vec::Vec<(Variable, AnnotatedMark, Loc)>, loc_body: Loc, @@ -1000,7 +1041,12 @@ impl<'a> Procs<'a> { layout_cache: &mut LayoutCache<'a>, ) -> Result, RuntimeError> { let raw_layout = layout_cache - .raw_from_var(env.arena, annotation, env.subs) + .raw_from_var( + env.arena, + annotation, + env.subs, + fresh_multimorphic_symbol!(env), + ) .unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err)); let top_level = ProcLayout::from_raw(env.arena, raw_layout); @@ -1008,7 +1054,7 @@ impl<'a> Procs<'a> { // anonymous functions cannot reference themselves, therefore cannot be tail-recursive // EXCEPT when the closure conversion makes it tail-recursive. let is_self_recursive = match top_level.arguments.last() { - Some(Layout::LambdaSet(lambda_set)) => lambda_set.contains(symbol), + Some(Layout::LambdaSet(lambda_set)) => lambda_set.contains(name.call_name()), _ => false, }; @@ -1018,22 +1064,24 @@ impl<'a> Procs<'a> { // by the surrounding context, so we can add pending specializations // for them immediately. - let already_specialized = self.specialized.is_specialized(symbol, &top_level); + let already_specialized = self + .specialized + .is_specialized(name.call_name(), &top_level); let layout = top_level; // if we've already specialized this one, no further work is needed. if !already_specialized { - if self.is_module_thunk(symbol) { + if self.is_module_thunk(name.source_name()) { debug_assert!(layout.arguments.is_empty()); } match &mut self.pending_specializations { PendingSpecializations::Finding(suspended) => { // register the pending specialization, so this gets code genned later - suspended.specialization(env.subs, symbol, layout, annotation); + suspended.specialization(env.subs, name, layout, annotation); - match self.partial_procs.symbol_to_id(symbol) { + match self.partial_procs.symbol_to_id(name.source_name()) { Some(occupied) => { let existing = self.partial_procs.get_id(occupied); // if we're adding the same partial proc twice, they must be the actual same! @@ -1059,7 +1107,7 @@ impl<'a> Procs<'a> { is_self_recursive, }; - self.partial_procs.insert(symbol, partial_proc); + self.partial_procs.insert(name.call_name(), partial_proc); } } } @@ -1067,12 +1115,12 @@ impl<'a> Procs<'a> { // Mark this proc as in-progress, so if we're dealing with // mutually recursive functions, we don't loop forever. // (We had a bug around this before this system existed!) - self.specialized.mark_in_progress(symbol, layout); + self.specialized.mark_in_progress(name.call_name(), layout); let outside_layout = layout; let partial_proc_id = if let Some(partial_proc_id) = - self.partial_procs.symbol_to_id(symbol) + self.partial_procs.symbol_to_id(name.source_name()) { let existing = self.partial_procs.get_id(partial_proc_id); // if we're adding the same partial proc twice, they must be the actual same! @@ -1097,13 +1145,13 @@ impl<'a> Procs<'a> { is_self_recursive, }; - self.partial_procs.insert(symbol, partial_proc) + self.partial_procs.insert(name.call_name(), partial_proc) }; match specialize_variable( env, self, - symbol, + name, layout_cache, annotation, &[], @@ -1118,11 +1166,15 @@ impl<'a> Procs<'a> { proc.name ); - if self.is_module_thunk(proc.name) { + if self.is_module_thunk(proc.name.source_name()) { debug_assert!(top_level.arguments.is_empty()); } - self.specialized.insert_specialized(symbol, top_level, proc); + self.specialized.insert_specialized( + name.call_name(), + top_level, + proc, + ); } Err(error) => { panic!("TODO generate a RuntimeError message for {:?}", error); @@ -1142,23 +1194,23 @@ impl<'a> Procs<'a> { &mut self, env: &mut Env<'a, '_>, fn_var: Variable, - name: Symbol, + name: LambdaName, layout: ProcLayout<'a>, layout_cache: &mut LayoutCache<'a>, ) { // If we've already specialized this one, no further work is needed. - if self.specialized.is_specialized(name, &layout) { + if self.specialized.is_specialized(name.call_name(), &layout) { return; } // If this is an imported symbol, let its home module make this specialization - if env.is_imported_symbol(name) { + if env.is_imported_symbol(name.source_name()) { add_needed_external(self, env, fn_var, name); return; } // register the pending specialization, so this gets code genned later - if self.module_thunks.contains(&name) { + if self.module_thunks.contains(&name.source_name()) { debug_assert!(layout.arguments.is_empty()); } @@ -1171,7 +1223,7 @@ impl<'a> Procs<'a> { PendingSpecializations::Making => { let symbol = name; - let partial_proc_id = match self.partial_procs.symbol_to_id(symbol) { + let partial_proc_id = match self.partial_procs.symbol_to_id(symbol.source_name()) { Some(p) => p, None => panic!("no partial_proc for {:?} in module {:?}", symbol, env.home), }; @@ -1179,7 +1231,8 @@ impl<'a> Procs<'a> { // Mark this proc as in-progress, so if we're dealing with // mutually recursive functions, we don't loop forever. // (We had a bug around this before this system existed!) - self.specialized.mark_in_progress(symbol, layout); + self.specialized + .mark_in_progress(symbol.call_name(), layout); // See https://github.com/rtfeldman/roc/issues/1600 // @@ -1217,9 +1270,13 @@ impl<'a> Procs<'a> { // NOTE: some function are specialized to have a closure, but don't actually // need any closure argument. Here is where we correct this sort of thing, // by trusting the layout of the Proc, not of what we specialize for - self.specialized.remove_specialized(symbol, &layout); self.specialized - .insert_specialized(symbol, proper_layout, proc); + .remove_specialized(symbol.call_name(), &layout); + self.specialized.insert_specialized( + symbol.call_name(), + proper_layout, + proc, + ); } Err(error) => { panic!("TODO generate a RuntimeError message for {:?}", error); @@ -2566,28 +2623,33 @@ fn specialize_suspended<'a>( ) { let offset_variable = StorageSubs::merge_into(suspended.store, env.subs); - for (i, (symbol, var)) in suspended - .symbols + for (i, (symbol_or_lambda, var)) in suspended + .symbol_or_lambdas .iter() .zip(suspended.variables.iter()) .enumerate() { - let name = *symbol; + let name = *symbol_or_lambda; let outside_layout = suspended.layouts[i]; let var = offset_variable(*var); // TODO define our own Entry for Specialized? - let partial_proc = if procs.specialized.is_specialized(name, &outside_layout) { + let partial_proc = if procs + .specialized + .is_specialized(name.call_name(), &outside_layout) + { // already specialized, just continue continue; } else { - match procs.partial_procs.symbol_to_id(name) { + match procs.partial_procs.symbol_to_id(name.source_name()) { Some(v) => { // Mark this proc as in-progress, so if we're dealing with // mutually recursive functions, we don't loop forever. // (We had a bug around this before this system existed!) - procs.specialized.mark_in_progress(name, outside_layout); + procs + .specialized + .mark_in_progress(name.call_name(), outside_layout); v } @@ -2604,7 +2666,7 @@ fn specialize_suspended<'a>( // TODO thiscode is duplicated elsewhere let top_level = ProcLayout::from_raw(env.arena, layout); - if procs.is_module_thunk(proc.name) { + if procs.is_module_thunk(proc.name.source_name()) { debug_assert!( top_level.arguments.is_empty(), "{:?} from {:?}", @@ -2614,16 +2676,20 @@ fn specialize_suspended<'a>( } debug_assert_eq!(outside_layout, top_level, " in {:?}", name); - procs.specialized.insert_specialized(name, top_level, proc); + procs + .specialized + .insert_specialized(name.call_name(), top_level, proc); } Err(SpecializeFailure { attempted_layout, .. }) => { - let proc = generate_runtime_error_function(env, name, attempted_layout); + let proc = generate_runtime_error_function(env, name.call_name(), attempted_layout); let top_level = ProcLayout::from_raw(env.arena, attempted_layout); - procs.specialized.insert_specialized(name, top_level, proc); + procs + .specialized + .insert_specialized(name.call_name(), top_level, proc); } } } @@ -2720,11 +2786,11 @@ fn specialize_external_help<'a>( env: &mut Env<'a, '_>, procs: &mut Procs<'a>, layout_cache: &mut LayoutCache<'a>, - name: Symbol, + name: LambdaName, variable: Variable, host_exposed_aliases: &[(Symbol, Variable)], ) { - let partial_proc_id = match procs.partial_procs.symbol_to_id(name) { + let partial_proc_id = match procs.partial_procs.symbol_to_id(name.source_name()) { Some(v) => v, None => { panic!("Cannot find a partial proc for {:?}", name); @@ -2745,18 +2811,22 @@ fn specialize_external_help<'a>( Ok((proc, layout)) => { let top_level = ProcLayout::from_raw(env.arena, layout); - if procs.is_module_thunk(name) { + if procs.is_module_thunk(name.source_name()) { debug_assert!(top_level.arguments.is_empty()); } - procs.specialized.insert_specialized(name, top_level, proc); + procs + .specialized + .insert_specialized(name.call_name(), top_level, proc); } Err(SpecializeFailure { attempted_layout }) => { - let proc = generate_runtime_error_function(env, name, attempted_layout); + let proc = generate_runtime_error_function(env, name.call_name(), attempted_layout); let top_level = ProcLayout::from_raw(env.arena, attempted_layout); - procs.specialized.insert_specialized(name, top_level, proc); + procs + .specialized + .insert_specialized(name.call_name(), top_level, proc); } } } @@ -2798,7 +2868,7 @@ fn generate_runtime_error_function<'a>( }; Proc { - name, + name: LambdaName::from_non_multimorphic(name), args, body: runtime_error, closure_data_layout: None, @@ -2812,7 +2882,7 @@ fn generate_runtime_error_function<'a>( fn specialize_external<'a>( env: &mut Env<'a, '_>, procs: &mut Procs<'a>, - proc_name: Symbol, + lambda_name: LambdaName, layout_cache: &mut LayoutCache<'a>, fn_var: Variable, host_exposed_variables: &[(Symbol, Variable)], @@ -2844,7 +2914,7 @@ fn specialize_external<'a>( }; let specialized = - build_specialized_proc_from_var(env, layout_cache, proc_name, pattern_symbols, fn_var)?; + build_specialized_proc_from_var(env, layout_cache, lambda_name, pattern_symbols, fn_var)?; // determine the layout of aliases/rigids exposed to the host let host_exposed_layouts = if host_exposed_variables.is_empty() { @@ -2854,7 +2924,12 @@ fn specialize_external<'a>( for (symbol, variable) in host_exposed_variables { let layout = layout_cache - .raw_from_var(env.arena, *variable, env.subs) + .raw_from_var( + env.arena, + *variable, + env.subs, + fresh_multimorphic_symbol!(env), + ) .unwrap(); let name = env.unique_symbol(); @@ -2900,7 +2975,7 @@ fn specialize_external<'a>( ); let proc = Proc { - name, + name: LambdaName::from_non_multimorphic(name), args: proc_arguments.into_bump_slice(), body, closure_data_layout: None, @@ -2965,10 +3040,12 @@ fn specialize_external<'a>( }; // I'm not sure how to handle the closure case, does it ever occur? - debug_assert!(matches!(captured_symbols, CapturedSymbols::None)); + debug_assert!( + matches!(captured_symbols, CapturedSymbols::None) && !lambda_name.is_multimorphic() + ); let proc = Proc { - name: proc_name, + name: lambda_name, args: &[], body: specialized_body, closure_data_layout: Some(closure_data_layout), @@ -3000,7 +3077,7 @@ fn specialize_external<'a>( .unwrap_or(symbol) }; - match closure_layout.layout_for_member(proc_name) { + match closure_layout.layout_for_member_with_lambda_name(lambda_name) { ClosureRepresentation::Union { alphabetic_order_fields: field_layouts, union_layout, @@ -3065,7 +3142,7 @@ fn specialize_external<'a>( captured.len(), field_layouts.len(), "{:?} captures {:?} but has layout {:?}", - proc_name, + lambda_name, &captured, &field_layouts ); @@ -3142,7 +3219,7 @@ fn specialize_external<'a>( }; let proc = Proc { - name: proc_name, + name: lambda_name, args: proc_args.into_bump_slice(), body: specialized_body, closure_data_layout, @@ -3176,18 +3253,18 @@ enum SpecializedLayout<'a> { fn build_specialized_proc_from_var<'a>( env: &mut Env<'a, '_>, layout_cache: &mut LayoutCache<'a>, - proc_name: Symbol, + lambda_name: LambdaName, pattern_symbols: &[Symbol], fn_var: Variable, ) -> Result, LayoutProblem> { - match layout_cache.raw_from_var(env.arena, fn_var, env.subs)? { + match layout_cache.raw_from_var(env.arena, fn_var, env.subs, fresh_multimorphic_symbol!(env))? { RawFunctionLayout::Function(pattern_layouts, closure_layout, ret_layout) => { let mut pattern_layouts_vec = Vec::with_capacity_in(pattern_layouts.len(), env.arena); pattern_layouts_vec.extend_from_slice(pattern_layouts); build_specialized_proc( env.arena, - proc_name, + lambda_name, pattern_symbols, pattern_layouts_vec, Some(closure_layout), @@ -3198,7 +3275,7 @@ fn build_specialized_proc_from_var<'a>( // a top-level constant 0-argument thunk build_specialized_proc( env.arena, - proc_name, + lambda_name, pattern_symbols, Vec::new_in(env.arena), None, @@ -3211,7 +3288,7 @@ fn build_specialized_proc_from_var<'a>( #[allow(clippy::type_complexity)] fn build_specialized_proc<'a>( arena: &'a Bump, - proc_name: Symbol, + lambda_name: LambdaName, pattern_symbols: &[Symbol], pattern_layouts: Vec<'a, Layout<'a>>, lambda_set: Option>, @@ -3245,6 +3322,7 @@ fn build_specialized_proc<'a>( // // then + let proc_name = lambda_name.call_name(); match lambda_set { Some(lambda_set) if pattern_symbols.last() == Some(&Symbol::ARG_CLOSURE) => { // here we define the lifted (now top-level) f function. Its final argument is `Symbol::ARG_CLOSURE`, @@ -3359,7 +3437,7 @@ type SpecializeSuccess<'a> = (Proc<'a>, RawFunctionLayout<'a>); fn specialize_variable<'a>( env: &mut Env<'a, '_>, procs: &mut Procs<'a>, - proc_name: Symbol, + proc_name: LambdaName, layout_cache: &mut LayoutCache<'a>, fn_var: Variable, host_exposed_aliases: &[(Symbol, Variable)], @@ -3379,7 +3457,7 @@ fn specialize_variable<'a>( fn specialize_variable_help<'a, F>( env: &mut Env<'a, '_>, procs: &mut Procs<'a>, - proc_name: Symbol, + proc_name: LambdaName, layout_cache: &mut LayoutCache<'a>, fn_var_thunk: F, host_exposed_variables: &[(Symbol, Variable)], @@ -3398,10 +3476,10 @@ where // for debugging only let raw = layout_cache - .raw_from_var(env.arena, fn_var, env.subs) + .raw_from_var(env.arena, fn_var, env.subs, fresh_multimorphic_symbol!(env)) .unwrap_or_else(|err| panic!("TODO handle invalid function {:?}", err)); - let raw = if procs.is_module_thunk(proc_name) { + let raw = if procs.is_module_thunk(proc_name.source_name()) { match raw { RawFunctionLayout::Function(_, lambda_set, _) => { RawFunctionLayout::ZeroArgumentThunk(Layout::LambdaSet(lambda_set)) @@ -3508,7 +3586,7 @@ fn specialize_naked_symbol<'a>( env, procs, fn_var, - symbol, + LambdaName::thunk(symbol), std::vec::Vec::new(), layout_cache, assigned, @@ -3517,7 +3595,12 @@ fn specialize_naked_symbol<'a>( return result; } else if env.is_imported_symbol(symbol) { - match layout_cache.from_var(env.arena, variable, env.subs) { + match layout_cache.from_var( + env.arena, + variable, + env.subs, + fresh_multimorphic_symbol!(env), + ) { Err(e) => panic!("invalid layout {:?}", e), Ok(_) => { // this is a 0-arity thunk @@ -3525,7 +3608,7 @@ fn specialize_naked_symbol<'a>( env, procs, variable, - symbol, + LambdaName::thunk(symbol), std::vec::Vec::new(), layout_cache, assigned, @@ -3559,7 +3642,8 @@ fn specialize_naked_symbol<'a>( opt_fn_var, symbol, result, - original, + // The function symbol is the only receiver, so not multimorphic + LambdaName::from_non_multimorphic(original), ) } @@ -3899,6 +3983,7 @@ pub fn with_hole<'a>( record_var, env.subs, env.target_info, + fresh_multimorphic_symbol!(env), ) { Ok(fields) => fields, Err(_) => return Stmt::RuntimeError("Can't create record with improper layout"), @@ -3910,7 +3995,7 @@ pub fn with_hole<'a>( #[allow(clippy::enum_variant_names)] enum Field { // TODO: rename this since it can handle unspecialized expressions now too - Function(Symbol, Variable), + FunctionOrUnspecialized(Symbol, Variable), ValueSymbol, Field(roc_can::expr::Field), } @@ -3925,7 +4010,7 @@ pub fn with_hole<'a>( | LocalFunction(symbol) | UnspecializedExpr(symbol) => { field_symbols.push(symbol); - can_fields.push(Field::Function(symbol, variable)); + can_fields.push(Field::FunctionOrUnspecialized(symbol, variable)); } Value(symbol) => { let reusable = procs.symbol_specializations.get_or_insert( @@ -3952,7 +4037,12 @@ pub fn with_hole<'a>( // creating a record from the var will unpack it if it's just a single field. let layout = layout_cache - .from_var(env.arena, record_var, env.subs) + .from_var( + env.arena, + record_var, + env.subs, + fresh_multimorphic_symbol!(env), + ) .unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err)); let field_symbols = field_symbols.into_bump_slice(); @@ -3971,7 +4061,7 @@ pub fn with_hole<'a>( Field::ValueSymbol => { // this symbol is already defined; nothing to do } - Field::Function(symbol, variable) => { + Field::FunctionOrUnspecialized(symbol, variable) => { stmt = specialize_symbol( env, procs, @@ -3979,7 +4069,9 @@ pub fn with_hole<'a>( Some(variable), symbol, stmt, - symbol, + // If it's a known function (and not an expression) there can only be + // one receiver, so not multimorphic + LambdaName::from_non_multimorphic(symbol), ); } Field::Field(field) => { @@ -4010,8 +4102,18 @@ pub fn with_hole<'a>( final_else, } => { match ( - layout_cache.from_var(env.arena, branch_var, env.subs), - layout_cache.from_var(env.arena, cond_var, env.subs), + layout_cache.from_var( + env.arena, + branch_var, + env.subs, + fresh_multimorphic_symbol!(env), + ), + layout_cache.from_var( + env.arena, + cond_var, + env.subs, + fresh_multimorphic_symbol!(env), + ), ) { (Ok(ret_layout), Ok(cond_layout)) => { // if the hole is a return, then we don't need to merge the two @@ -4110,7 +4212,12 @@ pub fn with_hole<'a>( } let layout = layout_cache - .from_var(env.arena, branch_var, env.subs) + .from_var( + env.arena, + branch_var, + env.subs, + fresh_multimorphic_symbol!(env), + ) .unwrap_or_else(|err| { panic!("TODO turn fn_var into a RuntimeError {:?}", err) }); @@ -4177,7 +4284,12 @@ pub fn with_hole<'a>( ); let layout = layout_cache - .from_var(env.arena, expr_var, env.subs) + .from_var( + env.arena, + expr_var, + env.subs, + fresh_multimorphic_symbol!(env), + ) .unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err)); let param = Param { @@ -4200,7 +4312,12 @@ pub fn with_hole<'a>( .. } if loc_elems.is_empty() => { // because an empty list has an unknown element type, it is handled differently - let opt_elem_layout = layout_cache.from_var(env.arena, elem_var, env.subs); + let opt_elem_layout = layout_cache.from_var( + env.arena, + elem_var, + env.subs, + fresh_multimorphic_symbol!(env), + ); match opt_elem_layout { Ok(elem_layout) => { @@ -4254,7 +4371,12 @@ pub fn with_hole<'a>( let arg_symbols = arg_symbols.into_bump_slice(); let elem_layout = layout_cache - .from_var(env.arena, elem_var, env.subs) + .from_var( + env.arena, + elem_var, + env.subs, + fresh_multimorphic_symbol!(env), + ) .unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err)); let expr = Expr::Array { @@ -4290,6 +4412,7 @@ pub fn with_hole<'a>( record_var, env.subs, env.target_info, + fresh_multimorphic_symbol!(env), ) { Ok(fields) => fields, Err(_) => return Stmt::RuntimeError("Can't access record with improper layout"), @@ -4340,7 +4463,12 @@ pub fn with_hole<'a>( }; let layout = layout_cache - .from_var(env.arena, field_var, env.subs) + .from_var( + env.arena, + field_var, + env.subs, + fresh_multimorphic_symbol!(env), + ) .unwrap_or_else(|err| { panic!("TODO turn fn_var into a RuntimeError {:?}", err) }); @@ -4376,7 +4504,8 @@ pub fn with_hole<'a>( match procs.insert_anonymous( env, - name, + // Accessors never capture so they can't be multimorphic + LambdaName::from_non_multimorphic(name), function_type, arguments, *loc_body, @@ -4387,12 +4516,30 @@ pub fn with_hole<'a>( Ok(_) => { let raw_layout = return_on_layout_error!( env, - layout_cache.raw_from_var(env.arena, function_type, env.subs) + layout_cache.raw_from_var( + env.arena, + function_type, + env.subs, + fresh_multimorphic_symbol!(env), + ) ); match raw_layout { RawFunctionLayout::Function(_, lambda_set, _) => { - construct_closure_data(env, lambda_set, name, &[], assigned, hole) + let lambda_name = + find_lambda_name(env, layout_cache, lambda_set, name, &[]); + debug_assert!( + !lambda_name.is_multimorphic(), + "no captures, but somehow this lambda is multimorphic" + ); + construct_closure_data( + env, + lambda_set, + lambda_name, + &[], + assigned, + hole, + ) } RawFunctionLayout::ZeroArgumentThunk(_) => unreachable!(), } @@ -4426,6 +4573,7 @@ pub fn with_hole<'a>( record_var, env.subs, env.target_info, + fresh_multimorphic_symbol!(env), ) { Ok(fields) => fields, Err(_) => return Stmt::RuntimeError("Can't update record with improper layout"), @@ -4470,7 +4618,12 @@ pub fn with_hole<'a>( let symbols = symbols.into_bump_slice(); let record_layout = layout_cache - .from_var(env.arena, record_var, env.subs) + .from_var( + env.arena, + record_var, + env.subs, + fresh_multimorphic_symbol!(env), + ) .unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err)); let field_layouts = match &record_layout { @@ -4557,7 +4710,10 @@ pub fn with_hole<'a>( Some(record_var), specialized_structure_sym, stmt, - structure, + // This is only hit if somehow this field is an alias + // to an ability member, but ability members can't capture, so + // it must be necessary non-multimorphic + LambdaName::from_non_multimorphic(structure), ); } } @@ -4578,7 +4734,12 @@ pub fn with_hole<'a>( }) => { let loc_body = *boxed_body; - let raw = layout_cache.raw_from_var(env.arena, function_type, env.subs); + let raw = layout_cache.raw_from_var( + env.arena, + function_type, + env.subs, + fresh_multimorphic_symbol!(env), + ); match return_on_layout_error!(env, raw) { RawFunctionLayout::ZeroArgumentThunk(_) => { @@ -4589,9 +4750,20 @@ pub fn with_hole<'a>( captured_symbols.sort(); let captured_symbols = captured_symbols.into_bump_slice(); + let symbols = + Vec::from_iter_in(captured_symbols.iter(), env.arena).into_bump_slice(); + + let lambda_name = find_lambda_name( + env, + layout_cache, + lambda_set, + name, + symbols.iter().copied(), + ); + let inserted = procs.insert_anonymous( env, - name, + lambda_name, function_type, arguments, loc_body, @@ -4613,13 +4785,10 @@ pub fn with_hole<'a>( // define the closure data - let symbols = - Vec::from_iter_in(captured_symbols.iter(), env.arena).into_bump_slice(); - construct_closure_data( env, lambda_set, - name, + lambda_name, symbols.iter().copied(), assigned, hole, @@ -4649,7 +4818,9 @@ pub fn with_hole<'a>( env, procs, fn_var, - proc_name, + // THEORY: calls to a known name can never be multimorphic, because they + // only have one receiver + LambdaName::from_non_multimorphic(proc_name), loc_args, layout_cache, assigned, @@ -4664,7 +4835,8 @@ pub fn with_hole<'a>( env, procs, fn_var, - specialization_proc_name, + // Ability members never capture, can't be multimorphic + LambdaName::from_non_multimorphic(specialization_proc_name), loc_args, layout_cache, assigned, @@ -4704,7 +4876,12 @@ pub fn with_hole<'a>( let full_layout = return_on_layout_error!( env, - layout_cache.raw_from_var(env.arena, fn_var, env.subs) + layout_cache.raw_from_var( + env.arena, + fn_var, + env.subs, + fresh_multimorphic_symbol!(env), + ) ); // if the function expression (loc_expr) is already a symbol, @@ -4718,7 +4895,7 @@ pub fn with_hole<'a>( Imported(thunk_name) => { debug_assert!(procs.is_imported_module_thunk(thunk_name)); - add_needed_external(procs, env, fn_var, thunk_name); + add_needed_external(procs, env, fn_var, LambdaName::thunk(thunk_name)); let function_symbol = env.unique_symbol(); @@ -4795,7 +4972,10 @@ pub fn with_hole<'a>( ); let resolved_proc = match resolved_proc { - Resolved::Specialization(symbol) => symbol, + Resolved::Specialization(symbol) => { + // Ability members never capture, they cannot be multimorphic + LambdaName::from_non_multimorphic(symbol) + } Resolved::NeedsGenerated => { todo_abilities!("Generate impls for structural types") } @@ -4882,8 +5062,15 @@ pub fn with_hole<'a>( let arg_symbols = arg_symbols.into_bump_slice(); // layout of the return type - let layout = - return_on_layout_error!(env, layout_cache.from_var(env.arena, ret_var, env.subs)); + let layout = return_on_layout_error!( + env, + layout_cache.from_var( + env.arena, + ret_var, + env.subs, + fresh_multimorphic_symbol!(env), + ) + ); let call = self::Call { call_type: CallType::Foreign { @@ -4918,8 +5105,15 @@ pub fn with_hole<'a>( let arg_symbols = arg_symbols.into_bump_slice(); // layout of the return type - let layout = - return_on_layout_error!(env, layout_cache.from_var(env.arena, ret_var, env.subs)); + let layout = return_on_layout_error!( + env, + layout_cache.from_var( + env.arena, + ret_var, + env.subs, + fresh_multimorphic_symbol!(env), + ) + ); macro_rules! match_on_closure_argument { ( $ho:ident, [$($x:ident),* $(,)?]) => {{ @@ -4929,7 +5123,7 @@ pub fn with_hole<'a>( let closure_data_layout = return_on_layout_error!( env, - layout_cache.raw_from_var(env.arena, closure_data_var, env.subs) + layout_cache.raw_from_var(env.arena, closure_data_var, env.subs,fresh_multimorphic_symbol!(env),) ); let top_level = ProcLayout::from_raw(env.arena, closure_data_layout); @@ -5163,12 +5357,32 @@ fn late_resolve_ability_specialization<'a>( } } +fn find_lambda_name<'a, I>( + env: &mut Env<'a, '_>, + layout_cache: &mut LayoutCache<'a>, + lambda_set: LambdaSet<'a>, + function_name: Symbol, + captures: I, +) -> LambdaName +where + I: IntoIterator, +{ + let this_function_captures_layouts = captures + .into_iter() + .map(|(_, var)| { + layout_cache + .from_var(env.arena, *var, env.subs, fresh_multimorphic_symbol!(env)) + .expect("layout problem for capture") + }) + .collect_in::>(env.arena); + lambda_set.find_lambda_name(function_name, &this_function_captures_layouts) +} + #[allow(clippy::too_many_arguments)] fn construct_closure_data<'a, I>( env: &mut Env<'a, '_>, - // procs: &mut Procs<'a>, lambda_set: LambdaSet<'a>, - name: Symbol, + name: LambdaName, symbols: I, assigned: Symbol, hole: &'a Stmt<'a>, @@ -5180,7 +5394,7 @@ where let lambda_set_layout = Layout::LambdaSet(lambda_set); let symbols = symbols.into_iter(); - let result = match lambda_set.layout_for_member(name) { + let result = match lambda_set.layout_for_member_with_lambda_name(name) { ClosureRepresentation::Union { tag_id, alphabetic_order_fields: field_layouts, @@ -5252,6 +5466,10 @@ where debug_assert_eq!(symbols.len(), 0); debug_assert_eq!(lambda_set.set.len(), 2); + debug_assert!( + !name.is_multimorphic(), + "This lambda set has no lambdas that capture, but somehow the name is multimorphic" + ); let tag_id = name != lambda_set.set[0].0; let expr = Expr::Literal(Literal::Bool(tag_id)); @@ -5261,7 +5479,12 @@ where debug_assert_eq!(symbols.len(), 0); debug_assert!(lambda_set.set.len() > 2); + debug_assert!( + !name.is_multimorphic(), + "This lambda set has no lambdas that capture, but somehow the name is multimorphic" + ); let tag_id = lambda_set.set.iter().position(|(s, _)| *s == name).unwrap() as u8; + let expr = Expr::Literal(Literal::Byte(tag_id)); Stmt::Let(assigned, expr, lambda_set_layout, hole) @@ -5285,8 +5508,13 @@ fn convert_tag_union<'a>( arena: &'a Bump, ) -> Stmt<'a> { use crate::layout::UnionVariant::*; - let res_variant = - crate::layout::union_sorted_tags(env.arena, variant_var, env.subs, env.target_info); + let res_variant = crate::layout::union_sorted_tags( + env.arena, + variant_var, + env.subs, + env.target_info, + fresh_multimorphic_symbol!(env), + ); let variant = match res_variant { Ok(cached) => cached, Err(LayoutProblem::UnresolvedTypeVar(_)) => { @@ -5345,7 +5573,12 @@ fn convert_tag_union<'a>( // Layout will unpack this unwrapped tack if it only has one (non-zero-sized) field let layout = layout_cache - .from_var(env.arena, variant_var, env.subs) + .from_var( + env.arena, + variant_var, + env.subs, + fresh_multimorphic_symbol!(env), + ) .unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err)); // even though this was originally a Tag, we treat it as a Struct from now on @@ -5373,7 +5606,12 @@ fn convert_tag_union<'a>( // version is not the same as the minimal version. let union_layout = match return_on_layout_error!( env, - layout_cache.from_var(env.arena, variant_var, env.subs) + layout_cache.from_var( + env.arena, + variant_var, + env.subs, + fresh_multimorphic_symbol!(env), + ) ) { Layout::Union(ul) => ul, _ => unreachable!(), @@ -5552,9 +5790,12 @@ fn tag_union_to_function<'a>( ext_var, }); + // Lambda does not capture anything, can't be multimorphic + let lambda_name = LambdaName::from_non_multimorphic(proc_symbol); + let inserted = procs.insert_anonymous( env, - proc_symbol, + lambda_name, whole_var, loc_pattern_args, loc_body, @@ -5568,12 +5809,23 @@ fn tag_union_to_function<'a>( // only need to construct closure data let raw_layout = return_on_layout_error!( env, - layout_cache.raw_from_var(env.arena, whole_var, env.subs) + layout_cache.raw_from_var( + env.arena, + whole_var, + env.subs, + fresh_multimorphic_symbol!(env), + ) ); match raw_layout { RawFunctionLayout::Function(_, lambda_set, _) => { - construct_closure_data(env, lambda_set, proc_symbol, &[], assigned, hole) + let lambda_name = + find_lambda_name(env, layout_cache, lambda_set, proc_symbol, &[]); + debug_assert!( + !lambda_name.is_multimorphic(), + "no captures, but somehow this lambda is multimorphic" + ); + construct_closure_data(env, lambda_set, lambda_name, &[], assigned, hole) } RawFunctionLayout::ZeroArgumentThunk(_) => unreachable!(), } @@ -5606,7 +5858,12 @@ fn sorted_field_symbols<'a>( for (var, mut arg) in args.drain(..) { // Layout will unpack this unwrapped tag if it only has one (non-zero-sized) field - let layout = match layout_cache.from_var(env.arena, var, env.subs) { + let layout = match layout_cache.from_var( + env.arena, + var, + env.subs, + fresh_multimorphic_symbol!(env), + ) { Ok(cached) => cached, Err(LayoutProblem::UnresolvedTypeVar(_)) => { // this argument has type `forall a. a`, which is isomorphic to @@ -5704,7 +5961,13 @@ fn register_capturing_closure<'a>( let captured_symbols = match *env.subs.get_content_without_compacting(function_type) { Content::Structure(FlatType::Func(_, closure_var, _)) => { - match LambdaSet::from_var(env.arena, env.subs, closure_var, env.target_info) { + match LambdaSet::from_var( + env.arena, + env.subs, + closure_var, + env.target_info, + fresh_multimorphic_symbol!(env), + ) { Ok(lambda_set) => { if let Layout::Struct { field_layouts: &[], .. @@ -5735,7 +5998,12 @@ fn register_capturing_closure<'a>( captured_symbols.is_empty(), "{:?} with layout {:?} {:?} {:?}", &captured_symbols, - layout_cache.raw_from_var(env.arena, function_type, env.subs), + layout_cache.raw_from_var( + env.arena, + function_type, + env.subs, + fresh_multimorphic_symbol!(env), + ), env.subs, (function_type, closure_type), ); @@ -5814,10 +6082,20 @@ pub fn from_can<'a>( final_else, } => { let ret_layout = layout_cache - .from_var(env.arena, branch_var, env.subs) + .from_var( + env.arena, + branch_var, + env.subs, + fresh_multimorphic_symbol!(env), + ) .expect("invalid ret_layout"); let cond_layout = layout_cache - .from_var(env.arena, cond_var, env.subs) + .from_var( + env.arena, + cond_var, + env.subs, + fresh_multimorphic_symbol!(env), + ) .expect("invalid cond_layout"); let mut stmt = from_can(env, branch_var, final_else.value, procs, layout_cache); @@ -5861,7 +6139,12 @@ pub fn from_can<'a>( let mut layouts = Vec::with_capacity_in(lookups_in_cond.len(), env.arena); for (_, var) in lookups_in_cond { - let res_layout = layout_cache.from_var(env.arena, var, env.subs); + let res_layout = layout_cache.from_var( + env.arena, + var, + env.subs, + fresh_multimorphic_symbol!(env), + ); let layout = return_on_layout_error!(env, res_layout); layouts.push(layout); } @@ -6028,11 +6311,25 @@ fn from_can_when<'a>( } let opt_branches = to_opt_branches(env, procs, branches, exhaustive_mark, layout_cache); - let cond_layout = - return_on_layout_error!(env, layout_cache.from_var(env.arena, cond_var, env.subs)); + let cond_layout = return_on_layout_error!( + env, + layout_cache.from_var( + env.arena, + cond_var, + env.subs, + fresh_multimorphic_symbol!(env), + ) + ); - let ret_layout = - return_on_layout_error!(env, layout_cache.from_var(env.arena, expr_var, env.subs)); + let ret_layout = return_on_layout_error!( + env, + layout_cache.from_var( + env.arena, + expr_var, + env.subs, + fresh_multimorphic_symbol!(env), + ) + ); let arena = env.arena; let it = opt_branches @@ -7023,9 +7320,19 @@ where // specialized, and wrap the original in a function pointer. let mut result = result; for (_, (variable, left)) in needed_specializations_of_left { - add_needed_external(procs, env, variable, right); + add_needed_external( + procs, + env, + variable, + LambdaName::from_non_multimorphic(right), + ); - let res_layout = layout_cache.from_var(env.arena, variable, env.subs); + let res_layout = layout_cache.from_var( + env.arena, + variable, + env.subs, + fresh_multimorphic_symbol!(env), + ); let layout = return_on_layout_error!(env, res_layout); result = force_thunk(env, right, layout, left, env.arena.alloc(result)); @@ -7034,7 +7341,12 @@ where } else if env.is_imported_symbol(right) { // if this is an imported symbol, then we must make sure it is // specialized, and wrap the original in a function pointer. - add_needed_external(procs, env, variable, right); + add_needed_external( + procs, + env, + variable, + LambdaName::from_non_multimorphic(right), + ); // then we must construct its closure; since imported symbols have no closure, we use the empty struct let_empty_struct(left, env.arena.alloc(result)) @@ -7109,18 +7421,23 @@ fn specialize_symbol<'a>( arg_var: Option, symbol: Symbol, result: Stmt<'a>, - original: Symbol, + original: LambdaName, ) -> Stmt<'a> { - match procs.get_partial_proc(original) { + match procs.get_partial_proc(original.source_name()) { None => { match arg_var { - Some(arg_var) if env.is_imported_symbol(original) => { - let raw = match layout_cache.raw_from_var(env.arena, arg_var, env.subs) { + Some(arg_var) if env.is_imported_symbol(original.source_name()) => { + let raw = match layout_cache.raw_from_var( + env.arena, + arg_var, + env.subs, + fresh_multimorphic_symbol!(env), + ) { Ok(v) => v, Err(e) => return_on_layout_error_help!(env, e), }; - if procs.is_imported_module_thunk(original) { + if procs.is_imported_module_thunk(original.source_name()) { let layout = match raw { RawFunctionLayout::ZeroArgumentThunk(layout) => layout, RawFunctionLayout::Function(_, lambda_set, _) => { @@ -7139,7 +7456,13 @@ fn specialize_symbol<'a>( layout_cache, ); - force_thunk(env, original, layout, symbol, env.arena.alloc(result)) + force_thunk( + env, + original.call_name(), + layout, + symbol, + env.arena.alloc(result), + ) } else { let top_level = ProcLayout::from_raw(env.arena, raw); procs.insert_passed_by_name( @@ -7157,7 +7480,7 @@ fn specialize_symbol<'a>( _ => { // danger: a foreign symbol may not be specialized! debug_assert!( - !env.is_imported_symbol(original), + !env.is_imported_symbol(original.source_name()), "symbol {:?} while processing module {:?}", original, (env.home, &arg_var), @@ -7174,7 +7497,12 @@ fn specialize_symbol<'a>( // to it in the IR. let res_layout = return_on_layout_error!( env, - layout_cache.raw_from_var(env.arena, arg_var, env.subs) + layout_cache.raw_from_var( + env.arena, + arg_var, + env.subs, + fresh_multimorphic_symbol!(env), + ) ); // we have three kinds of functions really. Plain functions, closures by capture, @@ -7216,7 +7544,7 @@ fn specialize_symbol<'a>( closure_data, env.arena.alloc(result), ) - } else if procs.is_module_thunk(original) { + } else if procs.is_module_thunk(original.source_name()) { // this is a 0-argument thunk // TODO suspicious @@ -7232,7 +7560,13 @@ fn specialize_symbol<'a>( layout_cache, ); - force_thunk(env, original, layout, symbol, env.arena.alloc(result)) + force_thunk( + env, + original.call_name(), + layout, + symbol, + env.arena.alloc(result), + ) } else { procs.insert_passed_by_name( env, @@ -7244,6 +7578,11 @@ fn specialize_symbol<'a>( // even though this function may not itself capture, // unification may still cause it to have an extra argument + debug_assert!( + !original.is_multimorphic(), + "no captures, but somehow this symbol is multimorphic" + ); + construct_closure_data( env, lambda_set, @@ -7259,7 +7598,13 @@ fn specialize_symbol<'a>( let top_level = ProcLayout::new(env.arena, &[], ret_layout); procs.insert_passed_by_name(env, arg_var, original, top_level, layout_cache); - force_thunk(env, original, ret_layout, symbol, env.arena.alloc(result)) + force_thunk( + env, + original.call_name(), + ret_layout, + symbol, + env.arena.alloc(result), + ) } } } @@ -7286,7 +7631,8 @@ fn assign_to_symbol<'a>( Some(arg_var), symbol, result, - original, + // The function symbol is the only receiver + LambdaName::only_receiver(original), ) } Value(_symbol) => result, @@ -7323,12 +7669,15 @@ fn add_needed_external<'a>( procs: &mut Procs<'a>, env: &mut Env<'a, '_>, fn_var: Variable, - name: Symbol, + name: LambdaName, ) { // call of a function that is not in this module use hashbrown::hash_map::Entry::{Occupied, Vacant}; - let existing = match procs.externals_we_need.entry(name.module_id()) { + let existing = match procs + .externals_we_need + .entry(name.source_name().module_id()) + { Vacant(entry) => entry.insert(ExternalSpecializations::new()), Occupied(entry) => entry.into_mut(), }; @@ -7383,14 +7732,14 @@ fn call_by_name<'a>( env: &mut Env<'a, '_>, procs: &mut Procs<'a>, fn_var: Variable, - proc_name: Symbol, + proc_name: LambdaName, loc_args: std::vec::Vec<(Variable, Loc)>, layout_cache: &mut LayoutCache<'a>, assigned: Symbol, hole: &'a Stmt<'a>, ) -> Stmt<'a> { // Register a pending_specialization for this function - match layout_cache.raw_from_var(env.arena, fn_var, env.subs) { + match layout_cache.raw_from_var(env.arena, fn_var, env.subs, fresh_multimorphic_symbol!(env)) { Err(LayoutProblem::UnresolvedTypeVar(var)) => { let msg = format!( "Hit an unresolved type variable {:?} when creating a layout for {:?} (var {:?})", @@ -7408,13 +7757,13 @@ fn call_by_name<'a>( evaluate_arguments_then_runtime_error(env, procs, layout_cache, msg, loc_args) } Ok(RawFunctionLayout::Function(arg_layouts, lambda_set, ret_layout)) => { - if procs.is_module_thunk(proc_name) { + if procs.is_module_thunk(proc_name.source_name()) { if loc_args.is_empty() { call_by_name_module_thunk( env, procs, fn_var, - proc_name, + proc_name.call_name(), env.arena.alloc(Layout::LambdaSet(lambda_set)), layout_cache, assigned, @@ -7464,7 +7813,7 @@ fn call_by_name<'a>( env, procs, fn_var, - proc_name, + proc_name.call_name(), env.arena.alloc(Layout::LambdaSet(lambda_set)), layout_cache, closure_data_symbol, @@ -7491,21 +7840,21 @@ fn call_by_name<'a>( } } Ok(RawFunctionLayout::ZeroArgumentThunk(ret_layout)) => { - if procs.is_module_thunk(proc_name) { + if procs.is_module_thunk(proc_name.source_name()) { // here we turn a call to a module thunk into forcing of that thunk call_by_name_module_thunk( env, procs, fn_var, - proc_name, + proc_name.call_name(), env.arena.alloc(ret_layout), layout_cache, assigned, hole, ) - } else if env.is_imported_symbol(proc_name) { - add_needed_external(procs, env, fn_var, proc_name); - force_thunk(env, proc_name, ret_layout, assigned, hole) + } else if env.is_imported_symbol(proc_name.source_name()) { + add_needed_external(procs, env, fn_var, LambdaName::thunk(proc_name.call_name())); + force_thunk(env, proc_name.call_name(), ret_layout, assigned, hole) } else { panic!("most likely we're trying to call something that is not a function"); } @@ -7518,7 +7867,7 @@ fn call_by_name_help<'a>( env: &mut Env<'a, '_>, procs: &mut Procs<'a>, fn_var: Variable, - proc_name: Symbol, + proc_name: LambdaName, loc_args: std::vec::Vec<(Variable, Loc)>, lambda_set: LambdaSet<'a>, argument_layouts: &'a [Layout<'a>], @@ -7547,7 +7896,7 @@ fn call_by_name_help<'a>( // the variables of the given arguments let mut pattern_vars = Vec::with_capacity_in(loc_args.len(), arena); for (var, _) in &loc_args { - match layout_cache.from_var(env.arena, *var, env.subs) { + match layout_cache.from_var(env.arena, *var, env.subs, fresh_multimorphic_symbol!(env)) { Ok(_) => { pattern_vars.push(*var); } @@ -7562,7 +7911,7 @@ fn call_by_name_help<'a>( // If we've already specialized this one, no further work is needed. if procs .specialized - .is_specialized(proc_name, &top_level_layout) + .is_specialized(proc_name.call_name(), &top_level_layout) { debug_assert_eq!( argument_layouts.len(), @@ -7575,7 +7924,7 @@ fn call_by_name_help<'a>( let call = self::Call { call_type: CallType::ByName { - name: proc_name, + name: proc_name.call_name(), ret_layout, arg_layouts: argument_layouts, specialization_id: env.next_call_specialization_id(), @@ -7587,15 +7936,15 @@ fn call_by_name_help<'a>( let iter = loc_args.into_iter().rev().zip(field_symbols.iter().rev()); assign_to_symbols(env, procs, layout_cache, iter, result) - } else if env.is_imported_symbol(proc_name) { + } else if env.is_imported_symbol(proc_name.source_name()) { add_needed_external(procs, env, original_fn_var, proc_name); - debug_assert_ne!(proc_name.module_id(), ModuleId::ATTR); + debug_assert_ne!(proc_name.call_name().module_id(), ModuleId::ATTR); - if procs.is_imported_module_thunk(proc_name) { + if procs.is_imported_module_thunk(proc_name.call_name()) { force_thunk( env, - proc_name, + proc_name.call_name(), Layout::LambdaSet(lambda_set), assigned, hole, @@ -7605,6 +7954,10 @@ fn call_by_name_help<'a>( // imported symbols cannot capture anything let captured = &[]; + debug_assert!( + !proc_name.is_multimorphic(), + "no captures, but somehow this lambda is multimorphic" + ); construct_closure_data(env, lambda_set, proc_name, captured, assigned, hole) } else { @@ -7619,7 +7972,7 @@ fn call_by_name_help<'a>( let call = self::Call { call_type: CallType::ByName { - name: proc_name, + name: proc_name.call_name(), ret_layout, arg_layouts: argument_layouts, specialization_id: env.next_call_specialization_id(), @@ -7644,13 +7997,13 @@ fn call_by_name_help<'a>( // the same specialization independently), we work through the // queue of pending specializations to complete each specialization // exactly once. - if procs.is_module_thunk(proc_name) { + if procs.is_module_thunk(proc_name.source_name()) { debug_assert!(top_level_layout.arguments.is_empty()); } match &mut procs.pending_specializations { PendingSpecializations::Finding(suspended) => { - debug_assert!(!env.is_imported_symbol(proc_name)); + debug_assert!(!env.is_imported_symbol(proc_name.source_name())); // register the pending specialization, so this gets code genned later suspended.specialization(env.subs, proc_name, top_level_layout, fn_var); @@ -7662,10 +8015,10 @@ fn call_by_name_help<'a>( proc_name, ); - let has_closure = argument_layouts.len() != top_level_layout.arguments.len(); + let has_captures = argument_layouts.len() != top_level_layout.arguments.len(); let closure_argument = env.unique_symbol(); - if has_closure { + if has_captures { field_symbols.push(closure_argument); } @@ -7673,7 +8026,7 @@ fn call_by_name_help<'a>( let call = self::Call { call_type: CallType::ByName { - name: proc_name, + name: proc_name.call_name(), ret_layout, arg_layouts: top_level_layout.arguments, specialization_id: env.next_call_specialization_id(), @@ -7689,8 +8042,11 @@ fn call_by_name_help<'a>( let iter = loc_args.into_iter().zip(field_symbols.iter()).rev(); let result = assign_to_symbols(env, procs, layout_cache, iter, result); - if has_closure { - let partial_proc = procs.partial_procs.get_symbol(proc_name).unwrap(); + if has_captures { + let partial_proc = procs + .partial_procs + .get_symbol(proc_name.source_name()) + .unwrap(); let captured = match partial_proc.captured_symbols { CapturedSymbols::None => &[], @@ -7710,7 +8066,7 @@ fn call_by_name_help<'a>( } } PendingSpecializations::Making => { - let opt_partial_proc = procs.partial_procs.symbol_to_id(proc_name); + let opt_partial_proc = procs.partial_procs.symbol_to_id(proc_name.source_name()); let field_symbols = field_symbols.into_bump_slice(); @@ -7721,7 +8077,7 @@ fn call_by_name_help<'a>( // (We had a bug around this before this system existed!) procs .specialized - .mark_in_progress(proc_name, top_level_layout); + .mark_in_progress(proc_name.call_name(), top_level_layout); match specialize_variable( env, @@ -7737,7 +8093,6 @@ fn call_by_name_help<'a>( call_specialized_proc( env, procs, - proc_name, proc, lambda_set, layout, @@ -7751,14 +8106,13 @@ fn call_by_name_help<'a>( Err(SpecializeFailure { attempted_layout }) => { let proc = generate_runtime_error_function( env, - proc_name, + proc_name.call_name(), attempted_layout, ); call_specialized_proc( env, procs, - proc_name, proc, lambda_set, attempted_layout, @@ -7824,7 +8178,12 @@ fn call_by_name_module_thunk<'a>( debug_assert!(!env.is_imported_symbol(proc_name)); // register the pending specialization, so this gets code genned later - suspended.specialization(env.subs, proc_name, top_level_layout, fn_var); + suspended.specialization( + env.subs, + LambdaName::thunk(proc_name), + top_level_layout, + fn_var, + ); force_thunk(env, proc_name, inner_layout, assigned, hole) } @@ -7843,7 +8202,7 @@ fn call_by_name_module_thunk<'a>( match specialize_variable( env, procs, - proc_name, + LambdaName::thunk(proc_name), layout_cache, fn_var, &[], @@ -7905,7 +8264,6 @@ fn call_by_name_module_thunk<'a>( fn call_specialized_proc<'a>( env: &mut Env<'a, '_>, procs: &mut Procs<'a>, - proc_name: Symbol, proc: Proc<'a>, lambda_set: LambdaSet<'a>, layout: RawFunctionLayout<'a>, @@ -7917,9 +8275,10 @@ fn call_specialized_proc<'a>( ) -> Stmt<'a> { let function_layout = ProcLayout::from_raw(env.arena, layout); + let proc_name = proc.name; procs .specialized - .insert_specialized(proc_name, function_layout, proc); + .insert_specialized(proc_name.call_name(), function_layout, proc); if field_symbols.is_empty() { debug_assert!(loc_args.is_empty()); @@ -7936,7 +8295,7 @@ fn call_specialized_proc<'a>( // when the body is a closure, the function will return the closure environment let call = self::Call { call_type: CallType::ByName { - name: proc_name, + name: proc_name.call_name(), ret_layout: env.arena.alloc(function_layout.result), arg_layouts: function_layout.arguments, specialization_id: env.next_call_specialization_id(), @@ -7957,7 +8316,7 @@ fn call_specialized_proc<'a>( match procs .partial_procs - .get_symbol(proc_name) + .get_symbol(proc_name.call_name()) .map(|pp| &pp.captured_symbols) { Some(&CapturedSymbols::Captured(captured_symbols)) => { @@ -8009,7 +8368,7 @@ fn call_specialized_proc<'a>( let call = self::Call { call_type: CallType::ByName { - name: proc_name, + name: proc_name.call_name(), ret_layout: env.arena.alloc(function_layout.result), arg_layouts: function_layout.arguments, specialization_id: env.next_call_specialization_id(), @@ -8204,9 +8563,14 @@ fn from_can_pattern_help<'a>( use crate::layout::UnionVariant::*; use roc_exhaustive::Union; - let res_variant = - crate::layout::union_sorted_tags(env.arena, *whole_var, env.subs, env.target_info) - .map_err(Into::into); + let res_variant = crate::layout::union_sorted_tags( + env.arena, + *whole_var, + env.subs, + env.target_info, + fresh_multimorphic_symbol!(env), + ) + .map_err(Into::into); let variant = match res_variant { Ok(cached) => cached, @@ -8289,12 +8653,12 @@ fn from_can_pattern_help<'a>( arguments.sort_by(|arg1, arg2| { let size1 = layout_cache - .from_var(env.arena, arg1.0, env.subs) + .from_var(env.arena, arg1.0, env.subs, fresh_multimorphic_symbol!(env)) .map(|x| x.alignment_bytes(env.target_info)) .unwrap_or(0); let size2 = layout_cache - .from_var(env.arena, arg2.0, env.subs) + .from_var(env.arena, arg2.0, env.subs, fresh_multimorphic_symbol!(env)) .map(|x| x.alignment_bytes(env.target_info)) .unwrap_or(0); @@ -8329,10 +8693,22 @@ fn from_can_pattern_help<'a>( let mut temp = arguments.clone(); temp.sort_by(|arg1, arg2| { - let layout1 = - layout_cache.from_var(env.arena, arg1.0, env.subs).unwrap(); - let layout2 = - layout_cache.from_var(env.arena, arg2.0, env.subs).unwrap(); + let layout1 = layout_cache + .from_var( + env.arena, + arg1.0, + env.subs, + fresh_multimorphic_symbol!(env), + ) + .unwrap(); + let layout2 = layout_cache + .from_var( + env.arena, + arg2.0, + env.subs, + fresh_multimorphic_symbol!(env), + ) + .unwrap(); let size1 = layout1.alignment_bytes(env.target_info); let size2 = layout2.alignment_bytes(env.target_info); @@ -8347,7 +8723,12 @@ fn from_can_pattern_help<'a>( // from `layouts` would unroll recursive tag unions, and that leads to // problems down the line because we hash layouts and an unrolled // version is not the same as the minimal version. - let layout = match layout_cache.from_var(env.arena, *whole_var, env.subs) { + let layout = match layout_cache.from_var( + env.arena, + *whole_var, + env.subs, + fresh_multimorphic_symbol!(env), + ) { Ok(Layout::Union(ul)) => ul, _ => unreachable!(), }; @@ -8638,7 +9019,12 @@ fn from_can_pattern_help<'a>( } => { let (arg_var, loc_arg_pattern) = &(**argument); let arg_layout = layout_cache - .from_var(env.arena, *arg_var, env.subs) + .from_var( + env.arena, + *arg_var, + env.subs, + fresh_multimorphic_symbol!(env), + ) .unwrap(); let mono_arg_pattern = from_can_pattern_help( env, @@ -8659,9 +9045,14 @@ fn from_can_pattern_help<'a>( .. } => { // sorted fields based on the type - let sorted_fields = - crate::layout::sort_record_fields(env.arena, *whole_var, env.subs, env.target_info) - .map_err(RuntimeError::from)?; + let sorted_fields = crate::layout::sort_record_fields( + env.arena, + *whole_var, + env.subs, + env.target_info, + fresh_multimorphic_symbol!(env), + ) + .map_err(RuntimeError::from)?; // sorted fields based on the destruct let mut mono_destructs = Vec::with_capacity_in(destructs.len(), env.arena); @@ -8929,7 +9320,7 @@ where let call_spec_id = env.next_call_specialization_id(); let update_mode = env.next_update_mode_id(); let call = to_lowlevel_call(( - *function_symbol, + function_symbol.call_name(), closure_data_symbol, lambda_set.is_represented(), call_spec_id, @@ -8988,7 +9379,7 @@ where #[allow(clippy::too_many_arguments)] fn lowlevel_union_lambda_set_to_switch<'a, ToLowLevelCall>( env: &mut Env<'a, '_>, - lambda_set: &'a [(Symbol, &'a [Layout<'a>])], + lambda_set: &'a [(LambdaName, &'a [Layout<'a>])], closure_tag_id_symbol: Symbol, closure_tag_id_layout: Layout<'a>, closure_data_symbol: Symbol, @@ -9007,7 +9398,7 @@ where let mut branches = Vec::with_capacity_in(lambda_set.len(), env.arena); - for (i, (function_symbol, _)) in lambda_set.iter().enumerate() { + for (i, (lambda_name, _)) in lambda_set.iter().enumerate() { let assigned = env.unique_symbol(); let hole = Stmt::Jump(join_point_id, env.arena.alloc([assigned])); @@ -9015,7 +9406,7 @@ where let call_spec_id = env.next_call_specialization_id(); let update_mode = env.next_update_mode_id(); let call = to_lowlevel_call(( - *function_symbol, + lambda_name.call_name(), closure_data_symbol, closure_env_layout, call_spec_id, @@ -9244,7 +9635,7 @@ fn union_lambda_set_to_switch<'a>( fn union_lambda_set_branch<'a>( env: &mut Env<'a, '_>, join_point_id: JoinPointId, - function_symbol: Symbol, + lambda_name: LambdaName, closure_info: ClosureInfo<'a>, argument_symbols_slice: &'a [Symbol], argument_layouts_slice: &'a [Layout<'a>], @@ -9256,7 +9647,7 @@ fn union_lambda_set_branch<'a>( union_lambda_set_branch_help( env, - function_symbol, + lambda_name, closure_info, argument_symbols_slice, argument_layouts_slice, @@ -9280,7 +9671,7 @@ enum ClosureInfo<'a> { #[allow(clippy::too_many_arguments)] fn union_lambda_set_branch_help<'a>( env: &mut Env<'a, '_>, - function_symbol: Symbol, + lambda_name: LambdaName, closure_info: ClosureInfo<'a>, argument_symbols_slice: &'a [Symbol], argument_layouts_slice: &'a [Layout<'a>], @@ -9332,7 +9723,7 @@ fn union_lambda_set_branch_help<'a>( // build the call let call = self::Call { call_type: CallType::ByName { - name: function_symbol, + name: lambda_name.call_name(), ret_layout: return_layout, arg_layouts: argument_layouts, specialization_id: env.next_call_specialization_id(), @@ -9346,7 +9737,7 @@ fn union_lambda_set_branch_help<'a>( #[allow(clippy::too_many_arguments)] fn enum_lambda_set_to_switch<'a>( env: &mut Env<'a, '_>, - lambda_set: &'a [(Symbol, &'a [Layout<'a>])], + lambda_set: &'a [(LambdaName, &'a [Layout<'a>])], closure_tag_id_symbol: Symbol, closure_tag_id_layout: Layout<'a>, closure_data_symbol: Symbol, @@ -9410,7 +9801,7 @@ fn enum_lambda_set_to_switch<'a>( fn enum_lambda_set_branch<'a>( env: &mut Env<'a, '_>, join_point_id: JoinPointId, - function_symbol: Symbol, + lambda_name: LambdaName, closure_data_symbol: Symbol, closure_data_layout: Layout<'a>, argument_symbols_slice: &'a [Symbol], @@ -9453,7 +9844,7 @@ fn enum_lambda_set_branch<'a>( let call = self::Call { call_type: CallType::ByName { - name: function_symbol, + name: lambda_name.call_name(), ret_layout: return_layout, arg_layouts: argument_layouts, specialization_id: env.next_call_specialization_id(), @@ -9466,7 +9857,7 @@ fn enum_lambda_set_branch<'a>( #[allow(clippy::too_many_arguments)] fn lowlevel_enum_lambda_set_to_switch<'a, ToLowLevelCall>( env: &mut Env<'a, '_>, - lambda_set: &'a [(Symbol, &'a [Layout<'a>])], + lambda_set: &'a [(LambdaName, &'a [Layout<'a>])], closure_tag_id_symbol: Symbol, closure_tag_id_layout: Layout<'a>, closure_data_symbol: Symbol, @@ -9493,7 +9884,7 @@ where let call_spec_id = env.next_call_specialization_id(); let update_mode = env.next_update_mode_id(); let call = to_lowlevel_call(( - *function_symbol, + function_symbol.call_name(), closure_data_symbol, closure_env_layout, call_spec_id, diff --git a/crates/compiler/mono/src/layout.rs b/crates/compiler/mono/src/layout.rs index 5870184d48..63d9433829 100644 --- a/crates/compiler/mono/src/layout.rs +++ b/crates/compiler/mono/src/layout.rs @@ -67,8 +67,8 @@ impl<'a> RawFunctionLayout<'a> { matches!(self, RawFunctionLayout::ZeroArgumentThunk(_)) } - fn new_help<'b>( - env: &mut Env<'a, 'b>, + fn new_help<'b, F: FreshMultimorphicSymbol>( + env: &mut Env<'a, 'b, F>, var: Variable, content: Content, ) -> Result { @@ -153,8 +153,8 @@ impl<'a> RawFunctionLayout<'a> { } } - fn layout_from_lambda_set( - _env: &mut Env<'a, '_>, + fn layout_from_lambda_set( + _env: &mut Env<'a, '_, F>, _lset: subs::LambdaSet, ) -> Result { unreachable!() @@ -162,8 +162,8 @@ impl<'a> RawFunctionLayout<'a> { // Self::layout_from_flat_type(env, lset.as_tag_union()) } - fn layout_from_flat_type( - env: &mut Env<'a, '_>, + fn layout_from_flat_type( + env: &mut Env<'a, '_, F>, flat_type: FlatType, ) -> Result { use roc_types::subs::FlatType::*; @@ -184,8 +184,13 @@ impl<'a> RawFunctionLayout<'a> { let fn_args = fn_args.into_bump_slice(); let ret = arena.alloc(ret); - let lambda_set = - LambdaSet::from_var(env.arena, env.subs, closure_var, env.target_info)?; + let lambda_set = LambdaSet::from_var( + env.arena, + env.subs, + closure_var, + env.target_info, + env.fresh_multimorphic_symbol, + )?; Ok(Self::Function(fn_args, lambda_set, ret)) } @@ -216,7 +221,10 @@ impl<'a> RawFunctionLayout<'a> { /// Returns Err(()) if given an error, or Ok(Layout) if given a non-erroneous Structure. /// Panics if given a FlexVar or RigidVar, since those should have been /// monomorphized away already! - fn from_var(env: &mut Env<'a, '_>, var: Variable) -> Result { + fn from_var( + env: &mut Env<'a, '_, F>, + var: Variable, + ) -> Result { if env.is_seen(var) { unreachable!("The initial variable of a signature cannot be seen already") } else { @@ -672,13 +680,16 @@ impl std::fmt::Debug for SetElement<'_> { impl std::fmt::Debug for LambdaSet<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { struct Helper<'a> { - set: &'a [(Symbol, &'a [Layout<'a>])], + set: &'a [(LambdaName, &'a [Layout<'a>])], } impl std::fmt::Debug for Helper<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let entries = self.set.iter().map(|x| SetElement { - symbol: x.0, + symbol: match (x.0).0 { + LambdaNameInner::Name(name) => name, + LambdaNameInner::Multimorphic { alias, .. } => alias, + }, layout: x.1, }); @@ -693,10 +704,79 @@ impl std::fmt::Debug for LambdaSet<'_> { } } +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +enum LambdaNameInner { + /// Standard lambda name assigned during canonicalize/constrain + Name(Symbol), + /// Sometimes we can end up with lambdas of the same name and different captures in the same + /// lambda set, like [[Thunk U8, Thunk Str]]. See also https://github.com/rtfeldman/roc/issues/3336. + /// We call such lambdas "multi-morphic". + /// + /// The current compilation scheme in such cases is to assign an alias name for subsequent such + /// lambda names, and then code-gen those lambda variants under a different `Proc`. In our + /// example, the lambda set would be transformed to something like + /// [[Thunk U8, Multimorphic(Thunk, ThunkAliasStr) Str]] which tells us to specialize the + /// second variant using the proc `Thunk` but under the name `ThunkAliasStr`, with that + /// particular closure layout. + /// + /// Currently we do no de-duplication of alias names. This does make compilation faster, but + /// also we should expect redundant multimorphic aliases to be somewhat rare, as that means a + /// non-unitary lambda set is the same in multiple areas of a program. + Multimorphic { + /// The lambda we came from, e.g. `Thunk` in the example + source: Symbol, + /// The lambda we become, e.g. `ThunkAliasStr` in the example + alias: Symbol, + }, +} + +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +pub struct LambdaName(LambdaNameInner); + +impl LambdaName { + #[inline(always)] + pub fn source_name(&self) -> Symbol { + match self.0 { + LambdaNameInner::Name(name) => name, + LambdaNameInner::Multimorphic { source, .. } => source, + } + } + + #[inline(always)] + pub fn call_name(&self) -> Symbol { + match self.0 { + LambdaNameInner::Name(name) => name, + LambdaNameInner::Multimorphic { alias, .. } => alias, + } + } + + #[inline(always)] + pub fn is_multimorphic(&self) -> bool { + matches!(self.0, LambdaNameInner::Multimorphic { .. }) + } + + #[inline(always)] + pub fn from_non_multimorphic(name: Symbol) -> Self { + Self(LambdaNameInner::Name(name)) + } + + #[inline(always)] + pub fn thunk(name: Symbol) -> Self { + Self(LambdaNameInner::Name(name)) + } + + // When the function name is known, so there can only be one possible receiver, in such cases + // the lambda cannot be multimorphic. + #[inline(always)] + pub fn only_receiver(name: Symbol) -> Self { + Self(LambdaNameInner::Name(name)) + } +} + #[derive(Clone, Copy, PartialEq, Eq, Hash)] pub struct LambdaSet<'a> { /// collection of function names and their closure arguments - pub set: &'a [(Symbol, &'a [Layout<'a>])], + pub set: &'a [(LambdaName, &'a [Layout<'a>])], /// how the closure will be represented at runtime representation: &'a Layout<'a>, } @@ -726,8 +806,13 @@ impl<'a> LambdaSet<'a> { } /// Does the lambda set contain the given symbol? + /// NOTE: for multimorphic variants, this checks the alias name; the source name will always be + /// the name of the first multimorphic variant. pub fn contains(&self, symbol: Symbol) -> bool { - self.set.iter().any(|(s, _)| *s == symbol) + self.set.iter().any(|(s, _)| match s.0 { + LambdaNameInner::Name(name) => name == symbol, + LambdaNameInner::Multimorphic { alias, .. } => alias == symbol, + }) } pub fn is_represented(&self) -> Option> { @@ -741,12 +826,92 @@ impl<'a> LambdaSet<'a> { } } - pub fn layout_for_member(&self, function_symbol: Symbol) -> ClosureRepresentation<'a> { + pub fn layout_for_member_with_lambda_name( + &self, + lambda_name: LambdaName, + ) -> ClosureRepresentation<'a> { debug_assert!( - self.set.iter().any(|(s, _)| *s == function_symbol), - "function symbol not in set" + self.set.iter().any(|(s, _)| *s == lambda_name), + "lambda not in set" ); + let comparator = + |other_name: LambdaName, _other_captures_layouts: &[Layout]| other_name == lambda_name; + + self.layout_for_member(comparator) + } + + pub fn find_lambda_name( + &self, + function_symbol: Symbol, + captures_layouts: &[Layout], + ) -> LambdaName { + debug_assert!(self.contains(function_symbol), "function symbol not in set"); + + let comparator = |other_name: LambdaName, other_captures_layouts: &[Layout]| { + let other_name = match other_name.0 { + LambdaNameInner::Name(name) => name, + // Take the source, since we'll want to pick out the multimorphic name if it + // matches + LambdaNameInner::Multimorphic { source, .. } => source, + }; + other_name == function_symbol + && captures_layouts.iter().eq(other_captures_layouts.iter()) + }; + + let (name, _) = self + .set + .iter() + .find(|(name, layouts)| comparator(*name, layouts)) + .expect("no lambda set found"); + + *name + } + + // Layout for a single member of the lambda set, when you are constructing a proc, and already + // know the multimorphic name (if any). + // pub fn layout_for_member_constructing_proc( + // &self, + // lambda_name: LambdaName, + // ) -> ClosureRepresentation<'a> { + // debug_assert!( + // self.set.iter().any(|(s, _)| *s == lambda_name), + // "lambda not in set" + // ); + + // let comparator = + // |other_name: LambdaName, _other_captures_layouts: &[Layout]| other_name == lambda_name; + + // self.layout_for_member(comparator) + // } + + // Layout for a single member of the lambda set, when you are constructing a closure + // representation, and maybe need to pick out a multimorphic variant. + // pub fn layout_for_member_constructing_closure_data( + // &self, + // function_symbol: Symbol, + // captures_layouts: &[Layout], + // ) -> ClosureRepresentation<'a> { + // debug_assert!(self.contains(function_symbol), "function symbol not in set"); + + // let comparator = |other_name: LambdaName, other_captures_layouts: &[Layout]| { + // let other_name = match other_name.0 { + // LambdaNameInner::Name(name) => name, + // // Take the source, since we'll want to pick out the multimorphic name if it + // // matches + // LambdaNameInner::Multimorphic { source, .. } => source, + // }; + // other_name == function_symbol + // && captures_layouts.iter().eq(other_captures_layouts.iter()) + // }; + + // self.layout_for_member(comparator) + // } + + fn layout_for_member(&self, comparator: F) -> ClosureRepresentation<'a> + where + F: Fn(LambdaName, &[Layout]) -> bool, + { match self.representation { Layout::Union(union) => { // here we rely on the fact that a union in a closure would be stored in a one-element record. @@ -755,17 +920,22 @@ impl<'a> LambdaSet<'a> { UnionLayout::NonRecursive(_) => { // get the fields from the set, where they are sorted in alphabetic order // (and not yet sorted by their alignment) - let (index, (_, fields)) = self + let (index, (name, fields)) = self .set .iter() .enumerate() - .find(|(_, (s, _))| *s == function_symbol) + .find(|(_, (s, layouts))| comparator(*s, layouts)) .unwrap(); + let closure_name = match name.0 { + LambdaNameInner::Name(name) => name, + LambdaNameInner::Multimorphic { alias, .. } => alias, + }; + ClosureRepresentation::Union { tag_id: index as TagIdIntType, alphabetic_order_fields: fields, - closure_name: function_symbol, + closure_name, union_layout: *union, } } @@ -782,12 +952,14 @@ impl<'a> LambdaSet<'a> { } } Layout::Struct { .. } => { + debug_assert_eq!(self.set.len(), 1); + // get the fields from the set, where they are sorted in alphabetic order // (and not yet sorted by their alignment) let (_, fields) = self .set .iter() - .find(|(s, _)| *s == function_symbol) + .find(|(s, layouts)| comparator(*s, layouts)) .unwrap(); ClosureRepresentation::AlphabeticOrderStruct(fields) @@ -829,38 +1001,67 @@ impl<'a> LambdaSet<'a> { } } - pub fn from_var( + pub fn from_var( arena: &'a Bump, subs: &Subs, closure_var: Variable, target_info: TargetInfo, - ) -> Result { + fresh_multimorphic_symbol: &mut F, + ) -> Result + where + F: FreshMultimorphicSymbol, + { match roc_types::pretty_print::resolve_lambda_set(subs, closure_var) { ResolvedLambdaSet::Set(mut lambdas) => { // sort the tags; make sure ordering stays intact! - lambdas.sort(); + lambdas.sort_by_key(|(sym, _)| *sym); - let mut set = Vec::with_capacity_in(lambdas.len(), arena); - - let mut env = Env { - arena, - subs, - seen: Vec::new_in(arena), - target_info, - }; + let mut set: Vec<(LambdaName, &[Layout])> = + Vec::with_capacity_in(lambdas.len(), arena); + let mut last_function_symbol = None; for (function_symbol, variables) in lambdas.iter() { let mut arguments = Vec::with_capacity_in(variables.len(), arena); + let mut env = Env { + arena, + subs, + seen: Vec::new_in(arena), + target_info, + fresh_multimorphic_symbol, + }; + for var in variables { arguments.push(Layout::from_var(&mut env, *var)?); } - set.push((*function_symbol, arguments.into_bump_slice())); + let lambda_name = match last_function_symbol { + None => LambdaNameInner::Name(*function_symbol), + Some(last_function_symbol) => { + if function_symbol != last_function_symbol { + LambdaNameInner::Name(*function_symbol) + } else { + LambdaNameInner::Multimorphic { + source: *function_symbol, + alias: (*fresh_multimorphic_symbol)(), + } + } + } + }; + let lambda_name = LambdaName(lambda_name); + + set.push((lambda_name, arguments.into_bump_slice())); + + last_function_symbol = Some(function_symbol); } - let representation = - arena.alloc(Self::make_representation(arena, subs, lambdas, target_info)); + let representation = arena.alloc(Self::make_representation( + arena, + subs, + lambdas, + target_info, + fresh_multimorphic_symbol, + )); Ok(LambdaSet { set: set.into_bump_slice(), @@ -878,14 +1079,22 @@ impl<'a> LambdaSet<'a> { } } - fn make_representation( + fn make_representation( arena: &'a Bump, subs: &Subs, tags: std::vec::Vec<(Symbol, std::vec::Vec)>, target_info: TargetInfo, + fresh_multimorphic_symbol: &mut F, ) -> Layout<'a> { // otherwise, this is a closure with a payload - let variant = union_sorted_tags_help(arena, tags, None, subs, target_info); + let variant = union_sorted_tags_help( + arena, + tags, + None, + subs, + target_info, + fresh_multimorphic_symbol, + ); use UnionVariant::*; match variant { @@ -952,14 +1161,21 @@ pub enum Builtin<'a> { List(&'a Layout<'a>), } -pub struct Env<'a, 'b> { +pub struct Env<'a, 'b, F> +where + F: FreshMultimorphicSymbol, +{ target_info: TargetInfo, arena: &'a Bump, seen: Vec<'a, Variable>, subs: &'b Subs, + fresh_multimorphic_symbol: &'b mut F, } -impl<'a, 'b> Env<'a, 'b> { +impl<'a, 'b, F> Env<'a, 'b, F> +where + F: FreshMultimorphicSymbol, +{ fn is_seen(&self, var: Variable) -> bool { let var = self.subs.get_root_key_without_compacting(var); @@ -1009,8 +1225,8 @@ impl<'a> Layout<'a> { field_order_hash: FieldOrderHash::ZERO_FIELD_HASH, }; - fn new_help<'b>( - env: &mut Env<'a, 'b>, + fn new_help<'b, F: FreshMultimorphicSymbol>( + env: &mut Env<'a, 'b, F>, var: Variable, content: Content, ) -> Result { @@ -1068,7 +1284,10 @@ impl<'a> Layout<'a> { /// Returns Err(()) if given an error, or Ok(Layout) if given a non-erroneous Structure. /// Panics if given a FlexVar or RigidVar, since those should have been /// monomorphized away already! - fn from_var(env: &mut Env<'a, '_>, var: Variable) -> Result { + fn from_var( + env: &mut Env<'a, '_, F>, + var: Variable, + ) -> Result { if env.is_seen(var) { Ok(Layout::RecursivePointer) } else { @@ -1392,12 +1611,16 @@ impl<'a> LayoutCache<'a> { } } - pub fn from_var( + pub fn from_var( &mut self, arena: &'a Bump, var: Variable, subs: &Subs, - ) -> Result, LayoutProblem> { + fresh_multimorphic_symbol: &mut F, + ) -> Result, LayoutProblem> + where + F: FreshMultimorphicSymbol, + { // Store things according to the root Variable, to avoid duplicate work. let var = subs.get_root_key_without_compacting(var); @@ -1406,16 +1629,18 @@ impl<'a> LayoutCache<'a> { subs, seen: Vec::new_in(arena), target_info: self.target_info, + fresh_multimorphic_symbol, }; Layout::from_var(&mut env, var) } - pub fn raw_from_var( + pub fn raw_from_var( &mut self, arena: &'a Bump, var: Variable, subs: &Subs, + fresh_multimorphic_symbol: &mut F, ) -> Result, LayoutProblem> { // Store things according to the root Variable, to avoid duplicate work. let var = subs.get_root_key_without_compacting(var); @@ -1425,6 +1650,7 @@ impl<'a> LayoutCache<'a> { subs, seen: Vec::new_in(arena), target_info: self.target_info, + fresh_multimorphic_symbol, }; RawFunctionLayout::from_var(&mut env, var) } @@ -1679,8 +1905,8 @@ impl<'a> Builtin<'a> { } } -fn layout_from_lambda_set<'a>( - env: &mut Env<'a, '_>, +fn layout_from_lambda_set<'a, F: FreshMultimorphicSymbol>( + env: &mut Env<'a, '_, F>, lset: subs::LambdaSet, ) -> Result, LayoutProblem> { // Lambda set is just a tag union from the layout's perspective. @@ -1709,8 +1935,8 @@ fn layout_from_lambda_set<'a>( } } -fn layout_from_flat_type<'a>( - env: &mut Env<'a, '_>, +fn layout_from_flat_type<'a, F: FreshMultimorphicSymbol>( + env: &mut Env<'a, '_, F>, flat_type: FlatType, ) -> Result, LayoutProblem> { use roc_types::subs::FlatType::*; @@ -1818,8 +2044,13 @@ fn layout_from_flat_type<'a>( } } Func(_, closure_var, _) => { - let lambda_set = - LambdaSet::from_var(env.arena, env.subs, closure_var, env.target_info)?; + let lambda_set = LambdaSet::from_var( + env.arena, + env.subs, + closure_var, + env.target_info, + env.fresh_multimorphic_symbol, + )?; Ok(Layout::LambdaSet(lambda_set)) } @@ -1898,17 +2129,19 @@ fn layout_from_flat_type<'a>( pub type SortedField<'a> = (Lowercase, Variable, Result, Layout<'a>>); -pub fn sort_record_fields<'a>( +pub fn sort_record_fields<'a, F: FreshMultimorphicSymbol>( arena: &'a Bump, var: Variable, subs: &Subs, target_info: TargetInfo, + fresh_multimorphic_symbol: &mut F, ) -> Result>, LayoutProblem> { let mut env = Env { arena, subs, seen: Vec::new_in(arena), target_info, + fresh_multimorphic_symbol, }; let (it, _) = match gather_fields_unsorted_iter(subs, RecordFields::empty(), var) { @@ -1923,8 +2156,8 @@ pub fn sort_record_fields<'a>( sort_record_fields_help(&mut env, it) } -fn sort_record_fields_help<'a>( - env: &mut Env<'a, '_>, +fn sort_record_fields_help<'a, F: FreshMultimorphicSymbol>( + env: &mut Env<'a, '_, F>, fields_map: impl Iterator)>, ) -> Result>, LayoutProblem> { let target_info = env.target_info; @@ -2110,11 +2343,12 @@ impl<'a> WrappedVariant<'a> { } } -pub fn union_sorted_tags<'a>( +pub fn union_sorted_tags<'a, F: FreshMultimorphicSymbol>( arena: &'a Bump, var: Variable, subs: &Subs, target_info: TargetInfo, + fresh_multimorphic_symbol: &mut F, ) -> Result, LayoutProblem> { let var = if let Content::RecursionVar { structure, .. } = subs.get_content_without_compacting(var) { @@ -2135,7 +2369,7 @@ pub fn union_sorted_tags<'a>( | Err((_, Content::FlexVar(_) | Content::RigidVar(_))) | Err((_, Content::RecursionVar { .. })) => { let opt_rec_var = get_recursion_var(subs, var); - union_sorted_tags_help(arena, tags_vec, opt_rec_var, subs, target_info) + union_sorted_tags_help(arena, tags_vec, opt_rec_var, subs, target_info, fresh_multimorphic_symbol) } Err((_, Content::Error)) => return Err(LayoutProblem::Erroneous), Err(other) => panic!("invalid content in tag union variable: {:?}", other), @@ -2164,8 +2398,8 @@ fn is_recursive_tag_union(layout: &Layout) -> bool { ) } -fn union_sorted_tags_help_new<'a, L>( - env: &mut Env<'a, '_>, +fn union_sorted_tags_help_new<'a, L, F: FreshMultimorphicSymbol>( + env: &mut Env<'a, '_, F>, tags_list: &[(&'_ L, &[Variable])], opt_rec_var: Option, ) -> UnionVariant<'a> @@ -2355,12 +2589,13 @@ where } } -pub fn union_sorted_tags_help<'a, L>( +pub fn union_sorted_tags_help<'a, L, F: FreshMultimorphicSymbol>( arena: &'a Bump, mut tags_vec: std::vec::Vec<(L, std::vec::Vec)>, opt_rec_var: Option, subs: &Subs, target_info: TargetInfo, + fresh_multimorphic_symbol: &mut F, ) -> UnionVariant<'a> where L: Into + Ord + Clone, @@ -2373,6 +2608,7 @@ where subs, seen: Vec::new_in(arena), target_info, + fresh_multimorphic_symbol, }; match tags_vec.len() { @@ -2561,8 +2797,8 @@ where } } -fn layout_from_newtype<'a, L: Label>( - env: &mut Env<'a, '_>, +fn layout_from_newtype<'a, L: Label, F: FreshMultimorphicSymbol>( + env: &mut Env<'a, '_, F>, tags: &UnsortedUnionLabels, ) -> Layout<'a> { debug_assert!(tags.is_newtype_wrapper(env.subs)); @@ -2585,7 +2821,10 @@ fn layout_from_newtype<'a, L: Label>( } } -fn layout_from_union<'a, L>(env: &mut Env<'a, '_>, tags: &UnsortedUnionLabels) -> Layout<'a> +fn layout_from_union<'a, L, F: FreshMultimorphicSymbol>( + env: &mut Env<'a, '_, F>, + tags: &UnsortedUnionLabels, +) -> Layout<'a> where L: Label + Ord + Into, { @@ -2661,8 +2900,8 @@ where } } -fn layout_from_recursive_union<'a, L>( - env: &mut Env<'a, '_>, +fn layout_from_recursive_union<'a, L, F: FreshMultimorphicSymbol>( + env: &mut Env<'a, '_, F>, rec_var: Variable, tags: &UnsortedUnionLabels, ) -> Result, LayoutProblem> @@ -2843,8 +3082,8 @@ fn layout_from_num_content<'a>( } } -fn dict_layout_from_key_value<'a>( - env: &mut Env<'a, '_>, +fn dict_layout_from_key_value<'a, F: FreshMultimorphicSymbol>( + env: &mut Env<'a, '_, F>, key_var: Variable, value_var: Variable, ) -> Result, LayoutProblem> { @@ -2876,8 +3115,11 @@ fn dict_layout_from_key_value<'a>( ))) } -pub fn list_layout_from_elem<'a>( - env: &mut Env<'a, '_>, +pub trait FreshMultimorphicSymbol: FnMut() -> Symbol {} +impl FreshMultimorphicSymbol for T where T: FnMut() -> Symbol {} + +pub fn list_layout_from_elem<'a, F: FreshMultimorphicSymbol>( + env: &mut Env<'a, '_, F>, element_var: Variable, ) -> Result, LayoutProblem> { let is_variable = |content| matches!(content, &Content::FlexVar(_) | &Content::RigidVar(_)); @@ -3032,7 +3274,7 @@ mod test { #[test] fn width_and_alignment_union_empty_struct() { let lambda_set = LambdaSet { - set: &[(Symbol::LIST_MAP, &[])], + set: &[(LambdaName::from_non_multimorphic(Symbol::LIST_MAP), &[])], representation: &Layout::UNIT, }; diff --git a/crates/compiler/test_mono/src/tests.rs b/crates/compiler/test_mono/src/tests.rs index 17b7bf51f3..10be2c684e 100644 --- a/crates/compiler/test_mono/src/tests.rs +++ b/crates/compiler/test_mono/src/tests.rs @@ -1525,3 +1525,28 @@ fn tail_call_with_different_layout() { "# ) } + +#[mono_test] +#[ignore] +fn lambda_sets_collide_with_captured_var() { + indoc!( + r#" + capture : a -> ({} -> Str) + capture = \val -> + thunk = + \{} -> + when val is + _ -> "" + thunk + + x : [True, False] + + fun = + when x is + True -> capture 1u8 + False -> capture 1u64 + + fun {} + "# + ) +} diff --git a/crates/repl_cli/src/lib.rs b/crates/repl_cli/src/lib.rs index cadc017eab..4b8dcb8011 100644 --- a/crates/repl_cli/src/lib.rs +++ b/crates/repl_cli/src/lib.rs @@ -130,9 +130,9 @@ impl<'a> ReplApp<'a> for CliApp { /// Run user code that returns a type with a `Builtin` layout /// Size of the return value is statically determined from its Rust type - fn call_function(&self, main_fn_name: &str, transform: F) -> Expr<'a> + fn call_function(&self, main_fn_name: &str, mut transform: F) -> Expr<'a> where - F: Fn(&'a Self::Memory, Return) -> Expr<'a>, + F: FnMut(&'a Self::Memory, Return) -> Expr<'a>, Self::Memory: 'a, { run_jit_function!(self.lib, main_fn_name, Return, |v| transform(&CliMemory, v)) @@ -143,10 +143,10 @@ impl<'a> ReplApp<'a> for CliApp { &self, main_fn_name: &str, ret_bytes: usize, - transform: F, + mut transform: F, ) -> T where - F: Fn(&'a Self::Memory, usize) -> T, + F: FnMut(&'a Self::Memory, usize) -> T, Self::Memory: 'a, { run_jit_function_dynamic_type!(self.lib, main_fn_name, ret_bytes, |v| transform( @@ -305,6 +305,7 @@ fn gen_and_eval_llvm<'a>( let app = CliApp { lib }; + let mut env = env; let res_answer = jit_to_ast( &arena, &app, @@ -312,6 +313,8 @@ fn gen_and_eval_llvm<'a>( main_fn_layout, content, &subs, + home, + env.interns.all_ident_ids.get_mut(&home).unwrap(), target_info, ); diff --git a/crates/repl_eval/src/eval.rs b/crates/repl_eval/src/eval.rs index 84b666e7e0..97d1f89e66 100644 --- a/crates/repl_eval/src/eval.rs +++ b/crates/repl_eval/src/eval.rs @@ -1,12 +1,13 @@ use bumpalo::collections::Vec; use bumpalo::Bump; +use roc_mono::fresh_multimorphic_symbol; use std::cmp::{max_by_key, min_by_key}; use roc_builtins::bitcode::{FloatWidth, IntWidth}; use roc_collections::all::MutMap; use roc_module::called_via::CalledVia; use roc_module::ident::TagName; -use roc_module::symbol::Symbol; +use roc_module::symbol::{IdentIds, ModuleId, Symbol}; use roc_mono::ir::ProcLayout; use roc_mono::layout::{ union_sorted_tags_help, Builtin, Layout, LayoutCache, UnionLayout, UnionVariant, WrappedVariant, @@ -19,10 +20,12 @@ use roc_types::subs::{Content, FlatType, GetSubsSlice, RecordFields, Subs, Union use crate::{ReplApp, ReplAppMemory}; -struct Env<'a, 'env> { +struct Env<'a> { + home: ModuleId, arena: &'a Bump, - subs: &'env Subs, + subs: &'a Subs, target_info: TargetInfo, + ident_ids: &'a mut IdentIds, } pub enum ToAstProblem { @@ -45,12 +48,16 @@ pub fn jit_to_ast<'a, A: ReplApp<'a>>( layout: ProcLayout<'a>, content: &'a Content, subs: &'a Subs, + module_id: ModuleId, + ident_ids: &'a mut IdentIds, target_info: TargetInfo, ) -> Result, ToAstProblem> { - let env = Env { + let mut env = Env { arena, subs, target_info, + home: module_id, + ident_ids, }; match layout { @@ -59,7 +66,7 @@ pub fn jit_to_ast<'a, A: ReplApp<'a>>( result, } => { // this is a thunk - jit_to_ast_help(&env, app, main_fn_name, &result, content) + jit_to_ast_help(&mut env, app, main_fn_name, &result, content) } _ => Err(ToAstProblem::FunctionLayout), } @@ -86,7 +93,7 @@ enum NewtypeKind<'a> { /// /// Returns (new type containers, optional alias content, real content). fn unroll_newtypes_and_aliases<'a>( - env: &Env<'a, 'a>, + env: &Env<'a>, mut content: &'a Content, ) -> (Vec<'a, NewtypeKind<'a>>, Option<&'a Content>, &'a Content) { let mut newtype_containers = Vec::with_capacity_in(1, env.arena); @@ -134,7 +141,7 @@ fn unroll_newtypes_and_aliases<'a>( } fn apply_newtypes<'a>( - env: &Env<'a, '_>, + env: &Env<'a>, newtype_containers: Vec<'a, NewtypeKind<'a>>, mut expr: Expr<'a>, ) -> Expr<'a> { @@ -161,7 +168,7 @@ fn apply_newtypes<'a>( expr } -fn unroll_recursion_var<'a>(env: &Env<'a, 'a>, mut content: &'a Content) -> &'a Content { +fn unroll_recursion_var<'a>(env: &Env<'a>, mut content: &'a Content) -> &'a Content { while let Content::RecursionVar { structure, .. } = content { content = env.subs.get_content_without_compacting(*structure); } @@ -169,7 +176,7 @@ fn unroll_recursion_var<'a>(env: &Env<'a, 'a>, mut content: &'a Content) -> &'a } fn get_tags_vars_and_variant<'a>( - env: &Env<'a, '_>, + env: &mut Env<'a>, tags: &UnionTags, opt_rec_var: Option, ) -> (MutMap>, UnionVariant<'a>) { @@ -180,14 +187,20 @@ fn get_tags_vars_and_variant<'a>( let vars_of_tag: MutMap<_, _> = tags_vec.iter().cloned().collect(); - let union_variant = - union_sorted_tags_help(env.arena, tags_vec, opt_rec_var, env.subs, env.target_info); + let union_variant = union_sorted_tags_help( + env.arena, + tags_vec, + opt_rec_var, + env.subs, + env.target_info, + fresh_multimorphic_symbol!(env), + ); (vars_of_tag, union_variant) } fn expr_of_tag<'a, M: ReplAppMemory>( - env: &Env<'a, 'a>, + env: &mut Env<'a>, mem: &'a M, data_addr: usize, tag_name: &TagName, @@ -211,7 +224,7 @@ fn expr_of_tag<'a, M: ReplAppMemory>( /// Gets the tag ID of a union variant, assuming that the tag ID is stored alongside (after) the /// tag data. The caller is expected to check that the tag ID is indeed stored this way. fn tag_id_from_data<'a, M: ReplAppMemory>( - env: &Env<'a, 'a>, + env: &Env<'a>, mem: &M, union_layout: UnionLayout, data_addr: usize, @@ -239,7 +252,7 @@ fn tag_id_from_data<'a, M: ReplAppMemory>( /// - the tag ID /// - the address of the data of the union variant, unmasked if the pointer held the tag ID fn tag_id_from_recursive_ptr<'a, M: ReplAppMemory>( - env: &Env<'a, 'a>, + env: &Env<'a>, mem: &M, union_layout: UnionLayout, rec_addr: usize, @@ -264,7 +277,7 @@ const OPAQUE_FUNCTION: Expr = Expr::Var { }; fn jit_to_ast_help<'a, A: ReplApp<'a>>( - env: &Env<'a, 'a>, + env: &mut Env<'a>, app: &'a A, main_fn_name: &str, layout: &Layout<'a>, @@ -344,6 +357,11 @@ fn jit_to_ast_help<'a, A: ReplApp<'a>>( todo!("add support for rendering builtin {:?} to the REPL", other) } Layout::Struct { field_layouts, .. } => { + let fields = [Layout::u64(), *layout]; + let layout = Layout::struct_no_name_order(&fields); + + let result_stack_size = layout.stack_size(env.target_info); + let struct_addr_to_ast = |mem: &'a A::Memory, addr: usize| match raw_content { Content::Structure(FlatType::Record(fields, _)) => { Ok(struct_to_ast(env, mem, addr, *fields)) @@ -389,11 +407,6 @@ fn jit_to_ast_help<'a, A: ReplApp<'a>>( } }; - let fields = [Layout::u64(), *layout]; - let layout = Layout::struct_no_name_order(&fields); - - let result_stack_size = layout.stack_size(env.target_info); - app.call_function_dynamic_size( main_fn_name, result_stack_size as usize, @@ -462,7 +475,7 @@ fn jit_to_ast_help<'a, A: ReplApp<'a>>( result.map(|e| apply_newtypes(env, newtype_containers, e)) } -fn tag_name_to_expr<'a>(env: &Env<'a, '_>, tag_name: &TagName) -> Expr<'a> { +fn tag_name_to_expr<'a>(env: &Env<'a>, tag_name: &TagName) -> Expr<'a> { Expr::Tag(env.arena.alloc_str(&tag_name.as_ident_str())) } @@ -475,7 +488,7 @@ enum WhenRecursive<'a> { } fn addr_to_ast<'a, M: ReplAppMemory>( - env: &Env<'a, 'a>, + env: &mut Env<'a>, mem: &'a M, addr: usize, layout: &Layout<'a>, @@ -770,7 +783,7 @@ fn addr_to_ast<'a, M: ReplAppMemory>( } fn list_to_ast<'a, M: ReplAppMemory>( - env: &Env<'a, 'a>, + env: &mut Env<'a>, mem: &'a M, addr: usize, len: usize, @@ -822,7 +835,7 @@ fn list_to_ast<'a, M: ReplAppMemory>( } fn single_tag_union_to_ast<'a, M: ReplAppMemory>( - env: &Env<'a, 'a>, + env: &mut Env<'a>, mem: &'a M, addr: usize, field_layouts: &'a [Layout<'a>], @@ -849,7 +862,7 @@ fn single_tag_union_to_ast<'a, M: ReplAppMemory>( } fn sequence_of_expr<'a, I, M: ReplAppMemory>( - env: &Env<'a, 'a>, + env: &mut Env<'a>, mem: &'a M, addr: usize, sequence: I, @@ -881,7 +894,7 @@ where } fn struct_to_ast<'a, M: ReplAppMemory>( - env: &Env<'a, 'a>, + env: &mut Env<'a>, mem: &'a M, addr: usize, record_fields: RecordFields, @@ -900,7 +913,12 @@ fn struct_to_ast<'a, M: ReplAppMemory>( let inner_content = env.subs.get_content_without_compacting(field.into_inner()); let field_layout = layout_cache - .from_var(arena, field.into_inner(), env.subs) + .from_var( + arena, + field.into_inner(), + env.subs, + fresh_multimorphic_symbol!(env), + ) .unwrap(); let inner_layouts = arena.alloc([field_layout]); @@ -939,7 +957,12 @@ fn struct_to_ast<'a, M: ReplAppMemory>( for (label, field) in record_fields.sorted_iterator(subs, Variable::EMPTY_RECORD) { let content = subs.get_content_without_compacting(field.into_inner()); let field_layout = layout_cache - .from_var(arena, field.into_inner(), env.subs) + .from_var( + arena, + field.into_inner(), + env.subs, + fresh_multimorphic_symbol!(env), + ) .unwrap(); let loc_expr = &*arena.alloc(Loc { @@ -1006,7 +1029,7 @@ fn unpack_two_element_tag_union( } fn bool_to_ast<'a, M: ReplAppMemory>( - env: &Env<'a, '_>, + env: &Env<'a>, mem: &M, value: bool, content: &Content, @@ -1081,7 +1104,7 @@ fn bool_to_ast<'a, M: ReplAppMemory>( } fn byte_to_ast<'a, M: ReplAppMemory>( - env: &Env<'a, '_>, + env: &mut Env<'a>, mem: &M, value: u8, content: &Content, @@ -1139,6 +1162,7 @@ fn byte_to_ast<'a, M: ReplAppMemory>( None, env.subs, env.target_info, + fresh_multimorphic_symbol!(env), ); match union_variant { diff --git a/crates/repl_eval/src/lib.rs b/crates/repl_eval/src/lib.rs index 898777d4e4..ac84dcac3c 100644 --- a/crates/repl_eval/src/lib.rs +++ b/crates/repl_eval/src/lib.rs @@ -12,7 +12,7 @@ pub trait ReplApp<'a> { /// The `transform` callback takes the app's memory and the returned value fn call_function(&self, main_fn_name: &str, transform: F) -> Expr<'a> where - F: Fn(&'a Self::Memory, Return) -> Expr<'a>, + F: FnMut(&'a Self::Memory, Return) -> Expr<'a>, Self::Memory: 'a; /// Run user code that returns a struct or union, whose size is provided as an argument @@ -24,7 +24,7 @@ pub trait ReplApp<'a> { transform: F, ) -> T where - F: Fn(&'a Self::Memory, usize) -> T, + F: FnMut(&'a Self::Memory, usize) -> T, Self::Memory: 'a; } From 5f8b509cb353330c9cef7905f70c05963f83a199 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Tue, 28 Jun 2022 12:20:51 -0400 Subject: [PATCH 09/66] Fixup compile errors --- crates/bindgen/src/load.rs | 4 ++-- crates/bindgen/src/types.rs | 46 +++++++++++++++++++++++++++++------- crates/repl_wasm/src/repl.rs | 9 +++---- 3 files changed, 45 insertions(+), 14 deletions(-) diff --git a/crates/bindgen/src/load.rs b/crates/bindgen/src/load.rs index ab50290097..da61882300 100644 --- a/crates/bindgen/src/load.rs +++ b/crates/bindgen/src/load.rs @@ -23,7 +23,7 @@ pub fn load_types( mut type_problems, mut declarations_by_id, mut solved, - interns, + mut interns, .. } = roc_load::load_and_typecheck( arena, @@ -74,7 +74,7 @@ pub fn load_types( let types_and_targets = Architecture::iter() .map(|arch| { let target_info = arch.into(); - let mut env = Env::new(arena, subs, &interns, target_info); + let mut env = Env::new(home, arena, subs, &mut interns, target_info); (env.vars_to_types(variables.clone()), target_info) }) diff --git a/crates/bindgen/src/types.rs b/crates/bindgen/src/types.rs index d2c34976ea..1270f9fbd1 100644 --- a/crates/bindgen/src/types.rs +++ b/crates/bindgen/src/types.rs @@ -6,7 +6,7 @@ use roc_builtins::bitcode::{ IntWidth::{self, *}, }; use roc_collections::VecMap; -use roc_module::symbol::{Interns, Symbol}; +use roc_module::symbol::{Interns, ModuleId, Symbol}; use roc_mono::layout::{ cmp_fields, ext_var_is_empty_tag_union, round_up_to_alignment, Builtin, Layout, LayoutCache, UnionLayout, @@ -410,7 +410,8 @@ pub struct Env<'a> { arena: &'a Bump, subs: &'a Subs, layout_cache: LayoutCache<'a>, - interns: &'a Interns, + home: ModuleId, + interns: &'a mut Interns, struct_names: Structs, enum_names: Enums, pending_recursive_types: VecMap>, @@ -418,11 +419,32 @@ pub struct Env<'a> { target: TargetInfo, } +macro_rules! fresh_multimorphic_symbol { + ($env:expr) => { + &mut || { + let ident_id = $env + .interns + .all_ident_ids + .get_mut(&$env.home) + .unwrap() + .gen_unique(); + Symbol::new($env.home, ident_id) + } + }; +} + impl<'a> Env<'a> { - pub fn new(arena: &'a Bump, subs: &'a Subs, interns: &'a Interns, target: TargetInfo) -> Self { + pub fn new( + home: ModuleId, + arena: &'a Bump, + subs: &'a Subs, + interns: &'a mut Interns, + target: TargetInfo, + ) -> Self { Env { arena, subs, + home, interns, struct_names: Default::default(), enum_names: Default::default(), @@ -451,7 +473,7 @@ impl<'a> Env<'a> { fn add_type(&mut self, var: Variable, types: &mut Types) -> TypeId { let layout = self .layout_cache - .from_var(self.arena, var, self.subs) + .from_var(self.arena, var, self.subs, fresh_multimorphic_symbol!(self)) .expect("Something weird ended up in the content"); add_type_help(self, layout, var, None, types) @@ -587,7 +609,7 @@ fn add_type_help<'a>( let type_id = types.add(RocType::RecursivePointer(TypeId::PENDING), layout); let structure_layout = env .layout_cache - .from_var(env.arena, *structure, subs) + .from_var(env.arena, *structure, subs, fresh_multimorphic_symbol!(env)) .unwrap(); env.pending_recursive_types @@ -695,7 +717,7 @@ where label, field_var, env.layout_cache - .from_var(env.arena, field_var, subs) + .from_var(env.arena, field_var, subs, fresh_multimorphic_symbol!(env)) .unwrap(), )); } @@ -765,7 +787,12 @@ fn add_tag_union<'a>( let payload_var = payload_vars.get(0).unwrap(); let payload_layout = env .layout_cache - .from_var(env.arena, *payload_var, env.subs) + .from_var( + env.arena, + *payload_var, + env.subs, + fresh_multimorphic_symbol!(env), + ) .expect("Something weird ended up in the content"); let payload_id = add_type_help(env, payload_layout, *payload_var, None, types); @@ -914,7 +941,10 @@ fn struct_fields_needed>(env: &mut Env<'_>, var let arena = env.arena; vars.into_iter().fold(0, |count, var| { - let layout = env.layout_cache.from_var(arena, var, subs).unwrap(); + let layout = env + .layout_cache + .from_var(arena, var, subs, fresh_multimorphic_symbol!(env)) + .unwrap(); if layout.is_dropped_because_empty() { count diff --git a/crates/repl_wasm/src/repl.rs b/crates/repl_wasm/src/repl.rs index 52164d496b..15e6e487e9 100644 --- a/crates/repl_wasm/src/repl.rs +++ b/crates/repl_wasm/src/repl.rs @@ -108,9 +108,9 @@ impl<'a> ReplApp<'a> for WasmReplApp<'a> { /// Size of the return value is statically determined from its Rust type /// The `transform` callback takes the app's memory and the returned value /// _main_fn_name is always the same and we don't use it here - fn call_function(&self, _main_fn_name: &str, transform: F) -> Expr<'a> + fn call_function(&self, _main_fn_name: &str, mut transform: F) -> Expr<'a> where - F: Fn(&'a Self::Memory, Return) -> Expr<'a>, + F: FnMut(&'a Self::Memory, Return) -> Expr<'a>, Self::Memory: 'a, { let app_final_memory_size: usize = js_run_app(); @@ -138,10 +138,10 @@ impl<'a> ReplApp<'a> for WasmReplApp<'a> { &self, _main_fn_name: &str, _ret_bytes: usize, - transform: F, + mut transform: F, ) -> T where - F: Fn(&'a Self::Memory, usize) -> T, + F: FnMut(&'a Self::Memory, usize) -> T, Self::Memory: 'a, { let app_final_memory_size: usize = js_run_app(); @@ -251,6 +251,7 @@ pub async fn entrypoint_from_js(src: String) -> Result { main_fn_layout, content, &subs, + module_id, interns.all_ident_ids.get_mut(&module_id).unwrap(), target_info, ); From 88618c098d5e353bf0ac29fcf749723178521a62 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Tue, 28 Jun 2022 16:45:46 -0400 Subject: [PATCH 10/66] Unify lambda sets with left/right closure capture differences --- crates/compiler/solve/tests/solve_expr.rs | 8 +- crates/compiler/unify/src/unify.rs | 218 +++++++++++++++------- 2 files changed, 159 insertions(+), 67 deletions(-) diff --git a/crates/compiler/solve/tests/solve_expr.rs b/crates/compiler/solve/tests/solve_expr.rs index 9b83cc8233..a0200c0dd9 100644 --- a/crates/compiler/solve/tests/solve_expr.rs +++ b/crates/compiler/solve/tests/solve_expr.rs @@ -6974,12 +6974,18 @@ mod solve_expr { fun = when x is True -> capture "" + # ^^^^^^^ False -> capture {} + # ^^^^^^^ fun #^^^{-1} "# ), - &["fun : {} -[[thunk(5) {}, thunk(5) Str]]-> Str"], + &[ + "capture : Str -[[capture(1)]]-> ({} -[[thunk(5) {}, thunk(5) Str]]-> Str)", + "capture : {} -[[capture(1)]]-> ({} -[[thunk(5) {}, thunk(5) Str]]-> Str)", + "fun : {} -[[thunk(5) {}, thunk(5) Str]]-> Str", + ] ); } diff --git a/crates/compiler/unify/src/unify.rs b/crates/compiler/unify/src/unify.rs index fb525f2c8e..58c0651208 100644 --- a/crates/compiler/unify/src/unify.rs +++ b/crates/compiler/unify/src/unify.rs @@ -966,6 +966,129 @@ fn extract_specialization_lambda_set( outcome } +#[derive(Debug)] +struct Sides { + left: Vec<(Symbol, VariableSubsSlice)>, + right: Vec<(Symbol, VariableSubsSlice)>, +} + +impl Default for Sides { + fn default() -> Self { + Self { + left: Vec::with_capacity(1), + right: Vec::with_capacity(1), + } + } +} + +fn separate_union_lambdas( + subs: &mut Subs, + pool: &mut Pool, + fields1: UnionLambdas, + fields2: UnionLambdas, +) -> ( + Vec<(Symbol, VariableSubsSlice)>, + Vec<(Symbol, VariableSubsSlice)>, + Vec<(Symbol, VariableSubsSlice)>, +) { + debug_assert!(fields1.is_sorted(subs)); + debug_assert!(fields2.is_sorted(subs)); + + // lambda names -> (the captures for that lambda on the left side, the captures for that lambda on the right side) + // e.g. [[F1 U8], [F1 U64], [F2 a]] ~ [[F1 Str], [F2 Str]] becomes + // F1 -> { left: [ [U8], [U64] ], right: [ [Str] ] } + // F2 -> { left: [ [a] ], right: [ [Str] ] } + let mut buckets: VecMap = VecMap::with_capacity(fields1.len() + fields2.len()); + + for (sym, vars) in fields1.iter_all() { + let bucket = buckets.get_or_insert(subs[sym], Sides::default); + bucket.left.push((subs[sym], subs[vars])); + } + for (sym, vars) in fields2.iter_all() { + let bucket = buckets.get_or_insert(subs[sym], Sides::default); + bucket.right.push((subs[sym], subs[vars])); + } + + let mut only_in_left = Vec::with_capacity(fields1.len()); + let mut only_in_right = Vec::with_capacity(fields2.len()); + let mut joined = Vec::with_capacity(fields1.len() + fields2.len()); + for (lambda_name, Sides { left, mut right }) in buckets { + match (left.as_slice(), right.as_slice()) { + (&[], &[]) => internal_error!("somehow both are empty but there's an entry?"), + (&[], _) => only_in_right.extend(right), + (_, &[]) => only_in_left.extend(left), + (_, _) => { + 'next_left: for (_, left_slice) in left { + // Does the current slice on the left unify with a slice on the right? + // + // If yes, we unify then and the unified result to `joined`. + // + // Otherwise if no such slice on the right is found, then the slice on the `left` has no slice, + // either on the left or right, it unifies with (since the left was constructed + // inductively via the same procedure). + // + // At the end each slice in the left and right has been explored, so + // - `joined` contains all the slices that can unify + // - left contains unique captures slices that will unify with no other slice + // - right contains unique captures slices that will unify with no other slice + // + // Note also if a slice l on the left and a slice r on the right unify, there + // is no other r' != r on the right such that l ~ r', and respectively there is + // no other l' != l on the left such that l' ~ r. Otherwise, it must be that l ~ l' + // (resp. r ~ r'), but then l = l' (resp. r = r'), and they would have become the same + // slice in a previous call to `separate_union_lambdas`. + 'try_next_right: for (right_index, (_, right_slice)) in right.iter().enumerate() + { + if left_slice.len() != right_slice.len() { + continue 'try_next_right; + } + + let snapshot = subs.snapshot(); + for (var1, var2) in (left_slice.into_iter()).zip(right_slice.into_iter()) { + let (var1, var2) = (subs[var1], subs[var2]); + + // Lambda sets are effectively tags under another name, and their usage can also result + // in the arguments of a lambda name being recursive. It very well may happen that + // during unification, a lambda set previously marked as not recursive becomes + // recursive. See the docs of [LambdaSet] for one example, or https://github.com/rtfeldman/roc/pull/2307. + // + // Like with tag unions, if it has, we'll always pass through this branch. So, take + // this opportunity to promote the lambda set to recursive if need be. + maybe_mark_union_recursive(subs, var1); + maybe_mark_union_recursive(subs, var2); + + let outcome = + unify_pool::(subs, pool, var1, var2, Mode::EQ); + + if !outcome.mismatches.is_empty() { + subs.rollback_to(snapshot); + continue 'try_next_right; + } + } + + // All the variables unified, so we can join the left + right. + // The variables are unified in left and right slice, so just reuse the left slice. + joined.push((lambda_name, left_slice)); + // Remove the right slice, it unifies with the left so this is its unique + // unification. + right.swap_remove(right_index); + continue 'next_left; + } + + // No slice on the right unified with the left, so the slice on the left is on + // its own. + only_in_left.push((lambda_name, left_slice)); + } + + // Possible that there are items left over in the right, they are on their own. + only_in_right.extend(right); + } + } + } + + (only_in_left, only_in_right, joined) +} + fn unify_lambda_set_help( subs: &mut Subs, pool: &mut Pool, @@ -994,65 +1117,20 @@ fn unify_lambda_set_help( "Recursion var is present, but it doesn't have a recursive content!" ); - let Separate { - only_in_1, - only_in_2, - in_both, - } = separate_union_lambdas(subs, solved1, solved2); + let (only_in_1, only_in_2, in_both) = separate_union_lambdas(subs, pool, solved1, solved2); - let mut new_lambdas = vec![]; - for (lambda_name, (vars1, vars2)) in in_both { - let mut captures_unify = vars1.len() == vars2.len(); + let all_lambdas = in_both + .into_iter() + .map(|(name, slice)| (name, subs.get_subs_slice(slice).to_vec())); - if captures_unify { - for (var1, var2) in (vars1.into_iter()).zip(vars2.into_iter()) { - let (var1, var2) = (subs[var1], subs[var2]); - - // Lambda sets are effectively tags under another name, and their usage can also result - // in the arguments of a lambda name being recursive. It very well may happen that - // during unification, a lambda set previously marked as not recursive becomes - // recursive. See the docs of [LambdaSet] for one example, or https://github.com/rtfeldman/roc/pull/2307. - // - // Like with tag unions, if it has, we'll always pass through this branch. So, take - // this opportunity to promote the lambda set to recursive if need be. - maybe_mark_union_recursive(subs, var1); - maybe_mark_union_recursive(subs, var2); - - let snapshot = subs.snapshot(); - let outcome = unify_pool::(subs, pool, var1, var2, ctx.mode); - - if outcome.mismatches.is_empty() { - subs.commit_snapshot(snapshot); - } else { - captures_unify = false; - subs.rollback_to(snapshot); - // Continue so the other variables can unify if possible, allowing us to re-use - // shared variables. - } - } - } - - if captures_unify { - new_lambdas.push((lambda_name, subs.get_subs_slice(vars1).to_vec())); - } else { - debug_assert!((subs.get_subs_slice(vars1).iter()) - .zip(subs.get_subs_slice(vars2).iter()) - .any(|(v1, v2)| !subs.equivalent_without_compacting(*v1, *v2))); - - new_lambdas.push((lambda_name, subs.get_subs_slice(vars1).to_vec())); - new_lambdas.push((lambda_name, subs.get_subs_slice(vars2).to_vec())); - } - } - - let all_lambdas = new_lambdas; - let all_lambdas = merge_sorted( + let all_lambdas = merge_sorted_preserving_duplicates( all_lambdas, only_in_1.into_iter().map(|(name, subs_slice)| { let vec = subs.get_subs_slice(subs_slice).to_vec(); (name, vec) }), ); - let all_lambdas = merge_sorted( + let all_lambdas = merge_sorted_preserving_duplicates( all_lambdas, only_in_2.into_iter().map(|(name, subs_slice)| { let vec = subs.get_subs_slice(subs_slice).to_vec(); @@ -1396,7 +1474,7 @@ struct Separate { in_both: Vec<(K, (V, V))>, } -fn merge_sorted(input1: I1, input2: I2) -> Vec<(K, V)> +fn merge_sorted_help(input1: I1, input2: I2, preserve_duplicates: bool) -> Vec<(K, V)> where K: Ord, I1: IntoIterator, @@ -1426,8 +1504,11 @@ where } Some(Ordering::Equal) => { let (k, v) = it1.next().unwrap(); - let (_, _) = it2.next().unwrap(); + let (k2, v2) = it2.next().unwrap(); result.push((k, v)); + if preserve_duplicates { + result.push((k2, v2)); + } } Some(Ordering::Greater) => { result.push(it2.next().unwrap()); @@ -1439,6 +1520,24 @@ where result } +fn merge_sorted(input1: I1, input2: I2) -> Vec<(K, V)> +where + K: Ord, + I1: IntoIterator, + I2: IntoIterator, +{ + merge_sorted_help(input1, input2, false) +} + +fn merge_sorted_preserving_duplicates(input1: I1, input2: I2) -> Vec<(K, V)> +where + K: Ord, + I1: IntoIterator, + I2: IntoIterator, +{ + merge_sorted_help(input1, input2, true) +} + fn separate(input1: I1, input2: I2) -> Separate where K: Ord, @@ -1501,19 +1600,6 @@ fn separate_union_tags( (separate(it1, it2), new_ext1, new_ext2) } -fn separate_union_lambdas( - subs: &Subs, - fields1: UnionLambdas, - fields2: UnionLambdas, -) -> Separate { - debug_assert!(fields1.is_sorted(subs)); - debug_assert!(fields2.is_sorted(subs)); - let it1 = fields1.iter_all().map(|(s, vars)| (subs[s], subs[vars])); - let it2 = fields2.iter_all().map(|(s, vars)| (subs[s], subs[vars])); - - separate(it1, it2) -} - #[derive(Debug, Copy, Clone)] enum Rec { None, From 8be230695bd373d66b39ed6129487da15512d59e Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Tue, 28 Jun 2022 17:13:15 -0400 Subject: [PATCH 11/66] Get multimorphic lambda captures working --- crates/compiler/collections/src/vec_map.rs | 4 + crates/compiler/load_internal/src/file.rs | 26 +- crates/compiler/module/src/symbol.rs | 25 +- crates/compiler/mono/src/borrow.rs | 2 +- crates/compiler/mono/src/ir.rs | 277 +++-------- crates/compiler/mono/src/layout.rs | 445 ++++++++++++------ .../multimorphic_lambda_set_capture.txt | 49 ++ crates/compiler/test_mono/src/tests.rs | 16 +- crates/compiler/types/src/pretty_print.rs | 37 +- 9 files changed, 458 insertions(+), 423 deletions(-) create mode 100644 crates/compiler/test_mono/generated/multimorphic_lambda_set_capture.txt diff --git a/crates/compiler/collections/src/vec_map.rs b/crates/compiler/collections/src/vec_map.rs index 827f2ad293..7b117ee842 100644 --- a/crates/compiler/collections/src/vec_map.rs +++ b/crates/compiler/collections/src/vec_map.rs @@ -97,6 +97,10 @@ impl VecMap { self.keys.iter().zip(self.values.iter()) } + pub fn iter_mut(&mut self) -> impl ExactSizeIterator { + self.keys.iter().zip(self.values.iter_mut()) + } + pub fn keys(&self) -> impl ExactSizeIterator { self.keys.iter() } diff --git a/crates/compiler/load_internal/src/file.rs b/crates/compiler/load_internal/src/file.rs index e67aa4cfc4..328fd7542f 100644 --- a/crates/compiler/load_internal/src/file.rs +++ b/crates/compiler/load_internal/src/file.rs @@ -29,12 +29,11 @@ use roc_module::symbol::{ IdentIds, IdentIdsByModule, Interns, ModuleId, ModuleIds, PQModuleName, PackageModuleIds, PackageQualified, Symbol, }; -use roc_mono::fresh_multimorphic_symbol; use roc_mono::ir::{ CapturedSymbols, EntryPoint, ExternalSpecializations, PartialProc, Proc, ProcLayout, Procs, ProcsBase, UpdateModeIds, }; -use roc_mono::layout::{LambdaName, Layout, LayoutCache, LayoutProblem}; +use roc_mono::layout::{LambdaName, Layout, LayoutCache, LayoutProblem, MultimorphicNames}; use roc_parse::ast::{self, Defs, ExtractSpaces, Spaced, StrLiteral, TypeAnnotation}; use roc_parse::header::{ExposedName, ImportsEntry, PackageEntry, PlatformHeader, To, TypedIdent}; use roc_parse::header::{HeaderFor, ModuleNameEnum, PackageName}; @@ -429,6 +428,7 @@ fn start_phase<'a>( exposed_to_host: state.exposed_to_host.clone(), abilities_store, derived_symbols, + multimorphic_names: state.multimorphic_names.clone(), } } Phase::MakeSpecializations => { @@ -498,6 +498,7 @@ fn start_phase<'a>( module_timing, world_abilities: state.world_abilities.clone_ref(), derived_symbols, + multimorphic_names: state.multimorphic_names.clone(), } } } @@ -827,6 +828,8 @@ struct State<'a> { pub render: RenderTarget, + pub multimorphic_names: MultimorphicNames, + /// All abilities across all modules. pub world_abilities: WorldAbilities, @@ -877,6 +880,7 @@ impl<'a> State<'a> { layout_caches: std::vec::Vec::with_capacity(number_of_workers), cached_subs: Arc::new(Mutex::new(cached_subs)), render, + multimorphic_names: MultimorphicNames::default(), make_specializations_pass: MakeSpecializationsPass::Pass(1), world_abilities: Default::default(), } @@ -1001,6 +1005,7 @@ enum BuildTask<'a> { exposed_to_host: ExposedToHost, abilities_store: AbilitiesStore, derived_symbols: GlobalDerivedSymbols, + multimorphic_names: MultimorphicNames, }, MakeSpecializations { module_id: ModuleId, @@ -1012,6 +1017,7 @@ enum BuildTask<'a> { module_timing: ModuleTiming, world_abilities: WorldAbilities, derived_symbols: GlobalDerivedSymbols, + multimorphic_names: MultimorphicNames, }, } @@ -4413,6 +4419,7 @@ fn make_specializations<'a>( target_info: TargetInfo, world_abilities: WorldAbilities, derived_symbols: GlobalDerivedSymbols, + mut multimorphic_names: MultimorphicNames, ) -> Msg<'a> { let make_specializations_start = SystemTime::now(); let mut update_mode_ids = UpdateModeIds::new(); @@ -4428,6 +4435,7 @@ fn make_specializations<'a>( call_specialization_counter: 1, abilities: AbilitiesView::World(world_abilities), derived_symbols: &derived_symbols, + multimorphic_names: &mut multimorphic_names, }; let mut procs = Procs::new_in(arena); @@ -4491,6 +4499,7 @@ fn build_pending_specializations<'a>( exposed_to_host: ExposedToHost, // TODO remove abilities_store: AbilitiesStore, derived_symbols: GlobalDerivedSymbols, + mut multimorphic_names: MultimorphicNames, ) -> Msg<'a> { let find_specializations_start = SystemTime::now(); @@ -4520,6 +4529,7 @@ fn build_pending_specializations<'a>( // do we need a global view. abilities: AbilitiesView::Module(&abilities_store), derived_symbols: &derived_symbols, + multimorphic_names: &mut multimorphic_names, }; // Add modules' decls to Procs @@ -4550,7 +4560,7 @@ fn build_pending_specializations<'a>( mono_env.arena, expr_var, mono_env.subs, - fresh_multimorphic_symbol!(mono_env), + mono_env.multimorphic_names, ); // cannot specialize when e.g. main's type contains type variables @@ -4614,7 +4624,7 @@ fn build_pending_specializations<'a>( mono_env.arena, expr_var, mono_env.subs, - fresh_multimorphic_symbol!(mono_env), + mono_env.multimorphic_names, ); // cannot specialize when e.g. main's type contains type variables @@ -4696,7 +4706,7 @@ fn build_pending_specializations<'a>( mono_env.arena, expr_var, mono_env.subs, - fresh_multimorphic_symbol!(mono_env), + mono_env.multimorphic_names, ); // cannot specialize when e.g. main's type contains type variables @@ -4760,7 +4770,7 @@ fn build_pending_specializations<'a>( mono_env.arena, expr_var, mono_env.subs, - fresh_multimorphic_symbol!(mono_env), + mono_env.multimorphic_names, ); // cannot specialize when e.g. main's type contains type variables @@ -4914,6 +4924,7 @@ fn run_task<'a>( exposed_to_host, abilities_store, derived_symbols, + multimorphic_names, } => Ok(build_pending_specializations( arena, solved_subs, @@ -4927,6 +4938,7 @@ fn run_task<'a>( exposed_to_host, abilities_store, derived_symbols, + multimorphic_names, )), MakeSpecializations { module_id, @@ -4938,6 +4950,7 @@ fn run_task<'a>( module_timing, world_abilities, derived_symbols, + multimorphic_names, } => Ok(make_specializations( arena, module_id, @@ -4950,6 +4963,7 @@ fn run_task<'a>( target_info, world_abilities, derived_symbols, + multimorphic_names, )), }?; diff --git a/crates/compiler/module/src/symbol.rs b/crates/compiler/module/src/symbol.rs index 4e2614d286..dd3c534252 100644 --- a/crates/compiler/module/src/symbol.rs +++ b/crates/compiler/module/src/symbol.rs @@ -998,7 +998,10 @@ define_builtins! { // Fake module for storing derived function symbols 1 DERIVED: "#Derived" => { } - 2 NUM: "Num" => { + // Fake module for storing fresh multimorphic function symbol names + 2 MULTIMORPHIC: "#Multimorphic" => { + } + 3 NUM: "Num" => { 0 NUM_NUM: "Num" // the Num.Num type alias 1 NUM_I128: "I128" // the Num.I128 type alias 2 NUM_U128: "U128" // the Num.U128 type alias @@ -1141,7 +1144,7 @@ define_builtins! { 139 NUM_MAX_F64: "maxF64" 140 NUM_MIN_F64: "minF64" } - 3 BOOL: "Bool" => { + 4 BOOL: "Bool" => { 0 BOOL_BOOL: "Bool" // the Bool.Bool type alias 1 BOOL_FALSE: "False" imported // Bool.Bool = [False, True] // NB: not strictly needed; used for finding tag names in error suggestions @@ -1154,7 +1157,7 @@ define_builtins! { 7 BOOL_EQ: "isEq" 8 BOOL_NEQ: "isNotEq" } - 4 STR: "Str" => { + 5 STR: "Str" => { 0 STR_STR: "Str" imported // the Str.Str type alias 1 STR_IS_EMPTY: "isEmpty" 2 STR_APPEND: "#append" // unused @@ -1191,7 +1194,7 @@ define_builtins! { 33 STR_TO_I8: "toI8" 34 STR_TO_SCALARS: "toScalars" } - 5 LIST: "List" => { + 6 LIST: "List" => { 0 LIST_LIST: "List" imported // the List.List type alias 1 LIST_IS_EMPTY: "isEmpty" 2 LIST_GET: "get" @@ -1257,7 +1260,7 @@ define_builtins! { 62 LIST_WITH_CAPACITY: "withCapacity" 63 LIST_ITERATE: "iterate" } - 6 RESULT: "Result" => { + 7 RESULT: "Result" => { 0 RESULT_RESULT: "Result" // the Result.Result type alias 1 RESULT_OK: "Ok" imported // Result.Result a e = [Ok a, Err e] // NB: not strictly needed; used for finding tag names in error suggestions @@ -1270,7 +1273,7 @@ define_builtins! { 7 RESULT_IS_OK: "isOk" 8 RESULT_IS_ERR: "isErr" } - 7 DICT: "Dict" => { + 8 DICT: "Dict" => { 0 DICT_DICT: "Dict" imported // the Dict.Dict type alias 1 DICT_EMPTY: "empty" 2 DICT_SINGLE: "single" @@ -1289,7 +1292,7 @@ define_builtins! { 13 DICT_INTERSECTION: "intersection" 14 DICT_DIFFERENCE: "difference" } - 8 SET: "Set" => { + 9 SET: "Set" => { 0 SET_SET: "Set" imported // the Set.Set type alias 1 SET_EMPTY: "empty" 2 SET_SINGLE: "single" @@ -1306,12 +1309,12 @@ define_builtins! { 13 SET_CONTAINS: "contains" 14 SET_TO_DICT: "toDict" } - 9 BOX: "Box" => { + 10 BOX: "Box" => { 0 BOX_BOX_TYPE: "Box" imported // the Box.Box opaque type 1 BOX_BOX_FUNCTION: "box" // Box.box 2 BOX_UNBOX: "unbox" } - 10 ENCODE: "Encode" => { + 11 ENCODE: "Encode" => { 0 ENCODE_ENCODER: "Encoder" 1 ENCODE_ENCODING: "Encoding" 2 ENCODE_TO_ENCODER: "toEncoder" @@ -1339,9 +1342,9 @@ define_builtins! { 24 ENCODE_APPEND: "append" 25 ENCODE_TO_BYTES: "toBytes" } - 11 JSON: "Json" => { + 12 JSON: "Json" => { 0 JSON_JSON: "Json" } - num_modules: 12 // Keep this count up to date by hand! (TODO: see the mut_map! macro for how we could determine this count correctly in the macro) + num_modules: 13 // Keep this count up to date by hand! (TODO: see the mut_map! macro for how we could determine this count correctly in the macro) } diff --git a/crates/compiler/mono/src/borrow.rs b/crates/compiler/mono/src/borrow.rs index 92f8f39d80..293341c95a 100644 --- a/crates/compiler/mono/src/borrow.rs +++ b/crates/compiler/mono/src/borrow.rs @@ -163,7 +163,7 @@ impl<'a> DeclarationToIndex<'a> { } } unreachable!( - "symbol/layout {:?} {:?} combo must be in DeclarationToIndex", + "symbol/layout {:?} {:#?} combo must be in DeclarationToIndex", needle_symbol, needle_layout ) } diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index 6eec3a6e5f..3eba5975ed 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -2,7 +2,7 @@ use crate::layout::{ Builtin, ClosureRepresentation, LambdaName, LambdaSet, Layout, LayoutCache, LayoutProblem, - RawFunctionLayout, TagIdIntType, TagOrClosure, UnionLayout, WrappedVariant, + MultimorphicNames, RawFunctionLayout, TagIdIntType, TagOrClosure, UnionLayout, WrappedVariant, }; use bumpalo::collections::{CollectIn, Vec}; use bumpalo::Bump; @@ -706,7 +706,6 @@ impl<'a> Specialized<'a> { } fn mark_in_progress(&mut self, symbol: Symbol, layout: ProcLayout<'a>) { - // dbg!((symbol, layout)); for (i, s) in self.symbols.iter().enumerate() { if *s == symbol && self.proc_layouts[i] == layout { match &self.procedures[i] { @@ -727,7 +726,6 @@ impl<'a> Specialized<'a> { } fn remove_specialized(&mut self, symbol: Symbol, layout: &ProcLayout<'a>) -> bool { - // dbg!((symbol, layout)); let mut index = None; for (i, s) in self.symbols.iter().enumerate() { @@ -746,7 +744,6 @@ impl<'a> Specialized<'a> { } fn insert_specialized(&mut self, symbol: Symbol, layout: ProcLayout<'a>, proc: Proc<'a>) { - // dbg!((symbol, layout)); for (i, s) in self.symbols.iter().enumerate() { if *s == symbol && self.proc_layouts[i] == layout { match &self.procedures[i] { @@ -814,16 +811,6 @@ struct SymbolSpecializations<'a>( VecMap, (Variable, Symbol)>>, ); -#[macro_export] -macro_rules! fresh_multimorphic_symbol { - ($env:expr) => { - &mut || { - let ident_id = $env.ident_ids.gen_unique(); - Symbol::new($env.home, ident_id) - } - }; -} - impl<'a> SymbolSpecializations<'a> { /// Gets a specialization for a symbol, or creates a new one. #[inline(always)] @@ -837,17 +824,13 @@ impl<'a> SymbolSpecializations<'a> { let arena = env.arena; let subs: &Subs = env.subs; - let layout = match layout_cache.from_var( - arena, - specialization_var, - subs, - fresh_multimorphic_symbol!(env), - ) { - Ok(layout) => layout, - // This can happen when the def symbol has a type error. In such cases just use the - // def symbol, which is erroring. - Err(_) => return symbol, - }; + let layout = + match layout_cache.from_var(arena, specialization_var, subs, env.multimorphic_names) { + Ok(layout) => layout, + // This can happen when the def symbol has a type error. In such cases just use the + // def symbol, which is erroring. + Err(_) => return symbol, + }; let is_closure = matches!( subs.get_content_without_compacting(specialization_var), @@ -858,7 +841,7 @@ impl<'a> SymbolSpecializations<'a> { arena, specialization_var, subs, - fresh_multimorphic_symbol!(env), + env.multimorphic_names, ) { Ok(layout) => layout, // This can happen when the def symbol has a type error. In such cases just use the @@ -1041,12 +1024,7 @@ impl<'a> Procs<'a> { layout_cache: &mut LayoutCache<'a>, ) -> Result, RuntimeError> { let raw_layout = layout_cache - .raw_from_var( - env.arena, - annotation, - env.subs, - fresh_multimorphic_symbol!(env), - ) + .raw_from_var(env.arena, annotation, env.subs, env.multimorphic_names) .unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err)); let top_level = ProcLayout::from_raw(env.arena, raw_layout); @@ -1327,6 +1305,7 @@ pub struct Env<'a, 'i> { pub call_specialization_counter: u32, pub abilities: AbilitiesView<'i>, pub derived_symbols: &'i GlobalDerivedSymbols, + pub multimorphic_names: &'i mut MultimorphicNames, } impl<'a, 'i> Env<'a, 'i> { @@ -2924,12 +2903,7 @@ fn specialize_external<'a>( for (symbol, variable) in host_exposed_variables { let layout = layout_cache - .raw_from_var( - env.arena, - *variable, - env.subs, - fresh_multimorphic_symbol!(env), - ) + .raw_from_var(env.arena, *variable, env.subs, env.multimorphic_names) .unwrap(); let name = env.unique_symbol(); @@ -3257,7 +3231,7 @@ fn build_specialized_proc_from_var<'a>( pattern_symbols: &[Symbol], fn_var: Variable, ) -> Result, LayoutProblem> { - match layout_cache.raw_from_var(env.arena, fn_var, env.subs, fresh_multimorphic_symbol!(env))? { + match layout_cache.raw_from_var(env.arena, fn_var, env.subs, env.multimorphic_names)? { RawFunctionLayout::Function(pattern_layouts, closure_layout, ret_layout) => { let mut pattern_layouts_vec = Vec::with_capacity_in(pattern_layouts.len(), env.arena); pattern_layouts_vec.extend_from_slice(pattern_layouts); @@ -3476,7 +3450,7 @@ where // for debugging only let raw = layout_cache - .raw_from_var(env.arena, fn_var, env.subs, fresh_multimorphic_symbol!(env)) + .raw_from_var(env.arena, fn_var, env.subs, env.multimorphic_names) .unwrap_or_else(|err| panic!("TODO handle invalid function {:?}", err)); let raw = if procs.is_module_thunk(proc_name.source_name()) { @@ -3595,12 +3569,7 @@ fn specialize_naked_symbol<'a>( return result; } else if env.is_imported_symbol(symbol) { - match layout_cache.from_var( - env.arena, - variable, - env.subs, - fresh_multimorphic_symbol!(env), - ) { + match layout_cache.from_var(env.arena, variable, env.subs, env.multimorphic_names) { Err(e) => panic!("invalid layout {:?}", e), Ok(_) => { // this is a 0-arity thunk @@ -3983,7 +3952,7 @@ pub fn with_hole<'a>( record_var, env.subs, env.target_info, - fresh_multimorphic_symbol!(env), + env.multimorphic_names, ) { Ok(fields) => fields, Err(_) => return Stmt::RuntimeError("Can't create record with improper layout"), @@ -4037,12 +4006,7 @@ pub fn with_hole<'a>( // creating a record from the var will unpack it if it's just a single field. let layout = layout_cache - .from_var( - env.arena, - record_var, - env.subs, - fresh_multimorphic_symbol!(env), - ) + .from_var(env.arena, record_var, env.subs, env.multimorphic_names) .unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err)); let field_symbols = field_symbols.into_bump_slice(); @@ -4102,18 +4066,8 @@ pub fn with_hole<'a>( final_else, } => { match ( - layout_cache.from_var( - env.arena, - branch_var, - env.subs, - fresh_multimorphic_symbol!(env), - ), - layout_cache.from_var( - env.arena, - cond_var, - env.subs, - fresh_multimorphic_symbol!(env), - ), + layout_cache.from_var(env.arena, branch_var, env.subs, env.multimorphic_names), + layout_cache.from_var(env.arena, cond_var, env.subs, env.multimorphic_names), ) { (Ok(ret_layout), Ok(cond_layout)) => { // if the hole is a return, then we don't need to merge the two @@ -4212,12 +4166,7 @@ pub fn with_hole<'a>( } let layout = layout_cache - .from_var( - env.arena, - branch_var, - env.subs, - fresh_multimorphic_symbol!(env), - ) + .from_var(env.arena, branch_var, env.subs, env.multimorphic_names) .unwrap_or_else(|err| { panic!("TODO turn fn_var into a RuntimeError {:?}", err) }); @@ -4284,12 +4233,7 @@ pub fn with_hole<'a>( ); let layout = layout_cache - .from_var( - env.arena, - expr_var, - env.subs, - fresh_multimorphic_symbol!(env), - ) + .from_var(env.arena, expr_var, env.subs, env.multimorphic_names) .unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err)); let param = Param { @@ -4312,12 +4256,8 @@ pub fn with_hole<'a>( .. } if loc_elems.is_empty() => { // because an empty list has an unknown element type, it is handled differently - let opt_elem_layout = layout_cache.from_var( - env.arena, - elem_var, - env.subs, - fresh_multimorphic_symbol!(env), - ); + let opt_elem_layout = + layout_cache.from_var(env.arena, elem_var, env.subs, env.multimorphic_names); match opt_elem_layout { Ok(elem_layout) => { @@ -4371,12 +4311,7 @@ pub fn with_hole<'a>( let arg_symbols = arg_symbols.into_bump_slice(); let elem_layout = layout_cache - .from_var( - env.arena, - elem_var, - env.subs, - fresh_multimorphic_symbol!(env), - ) + .from_var(env.arena, elem_var, env.subs, env.multimorphic_names) .unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err)); let expr = Expr::Array { @@ -4412,7 +4347,7 @@ pub fn with_hole<'a>( record_var, env.subs, env.target_info, - fresh_multimorphic_symbol!(env), + env.multimorphic_names, ) { Ok(fields) => fields, Err(_) => return Stmt::RuntimeError("Can't access record with improper layout"), @@ -4463,12 +4398,7 @@ pub fn with_hole<'a>( }; let layout = layout_cache - .from_var( - env.arena, - field_var, - env.subs, - fresh_multimorphic_symbol!(env), - ) + .from_var(env.arena, field_var, env.subs, env.multimorphic_names) .unwrap_or_else(|err| { panic!("TODO turn fn_var into a RuntimeError {:?}", err) }); @@ -4520,7 +4450,7 @@ pub fn with_hole<'a>( env.arena, function_type, env.subs, - fresh_multimorphic_symbol!(env), + env.multimorphic_names, ) ); @@ -4573,7 +4503,7 @@ pub fn with_hole<'a>( record_var, env.subs, env.target_info, - fresh_multimorphic_symbol!(env), + env.multimorphic_names, ) { Ok(fields) => fields, Err(_) => return Stmt::RuntimeError("Can't update record with improper layout"), @@ -4618,12 +4548,7 @@ pub fn with_hole<'a>( let symbols = symbols.into_bump_slice(); let record_layout = layout_cache - .from_var( - env.arena, - record_var, - env.subs, - fresh_multimorphic_symbol!(env), - ) + .from_var(env.arena, record_var, env.subs, env.multimorphic_names) .unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err)); let field_layouts = match &record_layout { @@ -4738,7 +4663,7 @@ pub fn with_hole<'a>( env.arena, function_type, env.subs, - fresh_multimorphic_symbol!(env), + env.multimorphic_names, ); match return_on_layout_error!(env, raw) { @@ -4880,7 +4805,7 @@ pub fn with_hole<'a>( env.arena, fn_var, env.subs, - fresh_multimorphic_symbol!(env), + env.multimorphic_names, ) ); @@ -5064,12 +4989,7 @@ pub fn with_hole<'a>( // layout of the return type let layout = return_on_layout_error!( env, - layout_cache.from_var( - env.arena, - ret_var, - env.subs, - fresh_multimorphic_symbol!(env), - ) + layout_cache.from_var(env.arena, ret_var, env.subs, env.multimorphic_names,) ); let call = self::Call { @@ -5107,12 +5027,7 @@ pub fn with_hole<'a>( // layout of the return type let layout = return_on_layout_error!( env, - layout_cache.from_var( - env.arena, - ret_var, - env.subs, - fresh_multimorphic_symbol!(env), - ) + layout_cache.from_var(env.arena, ret_var, env.subs, env.multimorphic_names,) ); macro_rules! match_on_closure_argument { @@ -5123,7 +5038,7 @@ pub fn with_hole<'a>( let closure_data_layout = return_on_layout_error!( env, - layout_cache.raw_from_var(env.arena, closure_data_var, env.subs,fresh_multimorphic_symbol!(env),) + layout_cache.raw_from_var(env.arena, closure_data_var, env.subs,env.multimorphic_names,) ); let top_level = ProcLayout::from_raw(env.arena, closure_data_layout); @@ -5371,7 +5286,7 @@ where .into_iter() .map(|(_, var)| { layout_cache - .from_var(env.arena, *var, env.subs, fresh_multimorphic_symbol!(env)) + .from_var(env.arena, *var, env.subs, env.multimorphic_names) .expect("layout problem for capture") }) .collect_in::>(env.arena); @@ -5513,7 +5428,7 @@ fn convert_tag_union<'a>( variant_var, env.subs, env.target_info, - fresh_multimorphic_symbol!(env), + env.multimorphic_names, ); let variant = match res_variant { Ok(cached) => cached, @@ -5573,12 +5488,7 @@ fn convert_tag_union<'a>( // Layout will unpack this unwrapped tack if it only has one (non-zero-sized) field let layout = layout_cache - .from_var( - env.arena, - variant_var, - env.subs, - fresh_multimorphic_symbol!(env), - ) + .from_var(env.arena, variant_var, env.subs, env.multimorphic_names) .unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err)); // even though this was originally a Tag, we treat it as a Struct from now on @@ -5606,12 +5516,7 @@ fn convert_tag_union<'a>( // version is not the same as the minimal version. let union_layout = match return_on_layout_error!( env, - layout_cache.from_var( - env.arena, - variant_var, - env.subs, - fresh_multimorphic_symbol!(env), - ) + layout_cache.from_var(env.arena, variant_var, env.subs, env.multimorphic_names,) ) { Layout::Union(ul) => ul, _ => unreachable!(), @@ -5809,12 +5714,7 @@ fn tag_union_to_function<'a>( // only need to construct closure data let raw_layout = return_on_layout_error!( env, - layout_cache.raw_from_var( - env.arena, - whole_var, - env.subs, - fresh_multimorphic_symbol!(env), - ) + layout_cache.raw_from_var(env.arena, whole_var, env.subs, env.multimorphic_names,) ); match raw_layout { @@ -5858,12 +5758,7 @@ fn sorted_field_symbols<'a>( for (var, mut arg) in args.drain(..) { // Layout will unpack this unwrapped tag if it only has one (non-zero-sized) field - let layout = match layout_cache.from_var( - env.arena, - var, - env.subs, - fresh_multimorphic_symbol!(env), - ) { + let layout = match layout_cache.from_var(env.arena, var, env.subs, env.multimorphic_names) { Ok(cached) => cached, Err(LayoutProblem::UnresolvedTypeVar(_)) => { // this argument has type `forall a. a`, which is isomorphic to @@ -5966,7 +5861,7 @@ fn register_capturing_closure<'a>( env.subs, closure_var, env.target_info, - fresh_multimorphic_symbol!(env), + env.multimorphic_names, ) { Ok(lambda_set) => { if let Layout::Struct { @@ -6002,7 +5897,7 @@ fn register_capturing_closure<'a>( env.arena, function_type, env.subs, - fresh_multimorphic_symbol!(env), + env.multimorphic_names, ), env.subs, (function_type, closure_type), @@ -6082,20 +5977,10 @@ pub fn from_can<'a>( final_else, } => { let ret_layout = layout_cache - .from_var( - env.arena, - branch_var, - env.subs, - fresh_multimorphic_symbol!(env), - ) + .from_var(env.arena, branch_var, env.subs, env.multimorphic_names) .expect("invalid ret_layout"); let cond_layout = layout_cache - .from_var( - env.arena, - cond_var, - env.subs, - fresh_multimorphic_symbol!(env), - ) + .from_var(env.arena, cond_var, env.subs, env.multimorphic_names) .expect("invalid cond_layout"); let mut stmt = from_can(env, branch_var, final_else.value, procs, layout_cache); @@ -6139,12 +6024,8 @@ pub fn from_can<'a>( let mut layouts = Vec::with_capacity_in(lookups_in_cond.len(), env.arena); for (_, var) in lookups_in_cond { - let res_layout = layout_cache.from_var( - env.arena, - var, - env.subs, - fresh_multimorphic_symbol!(env), - ); + let res_layout = + layout_cache.from_var(env.arena, var, env.subs, env.multimorphic_names); let layout = return_on_layout_error!(env, res_layout); layouts.push(layout); } @@ -6313,22 +6194,12 @@ fn from_can_when<'a>( let cond_layout = return_on_layout_error!( env, - layout_cache.from_var( - env.arena, - cond_var, - env.subs, - fresh_multimorphic_symbol!(env), - ) + layout_cache.from_var(env.arena, cond_var, env.subs, env.multimorphic_names,) ); let ret_layout = return_on_layout_error!( env, - layout_cache.from_var( - env.arena, - expr_var, - env.subs, - fresh_multimorphic_symbol!(env), - ) + layout_cache.from_var(env.arena, expr_var, env.subs, env.multimorphic_names,) ); let arena = env.arena; @@ -7327,12 +7198,8 @@ where LambdaName::from_non_multimorphic(right), ); - let res_layout = layout_cache.from_var( - env.arena, - variable, - env.subs, - fresh_multimorphic_symbol!(env), - ); + let res_layout = + layout_cache.from_var(env.arena, variable, env.subs, env.multimorphic_names); let layout = return_on_layout_error!(env, res_layout); result = force_thunk(env, right, layout, left, env.arena.alloc(result)); @@ -7431,7 +7298,7 @@ fn specialize_symbol<'a>( env.arena, arg_var, env.subs, - fresh_multimorphic_symbol!(env), + env.multimorphic_names, ) { Ok(v) => v, Err(e) => return_on_layout_error_help!(env, e), @@ -7497,12 +7364,7 @@ fn specialize_symbol<'a>( // to it in the IR. let res_layout = return_on_layout_error!( env, - layout_cache.raw_from_var( - env.arena, - arg_var, - env.subs, - fresh_multimorphic_symbol!(env), - ) + layout_cache.raw_from_var(env.arena, arg_var, env.subs, env.multimorphic_names,) ); // we have three kinds of functions really. Plain functions, closures by capture, @@ -7739,7 +7601,7 @@ fn call_by_name<'a>( hole: &'a Stmt<'a>, ) -> Stmt<'a> { // Register a pending_specialization for this function - match layout_cache.raw_from_var(env.arena, fn_var, env.subs, fresh_multimorphic_symbol!(env)) { + match layout_cache.raw_from_var(env.arena, fn_var, env.subs, env.multimorphic_names) { Err(LayoutProblem::UnresolvedTypeVar(var)) => { let msg = format!( "Hit an unresolved type variable {:?} when creating a layout for {:?} (var {:?})", @@ -7896,7 +7758,7 @@ fn call_by_name_help<'a>( // the variables of the given arguments let mut pattern_vars = Vec::with_capacity_in(loc_args.len(), arena); for (var, _) in &loc_args { - match layout_cache.from_var(env.arena, *var, env.subs, fresh_multimorphic_symbol!(env)) { + match layout_cache.from_var(env.arena, *var, env.subs, env.multimorphic_names) { Ok(_) => { pattern_vars.push(*var); } @@ -8568,7 +8430,7 @@ fn from_can_pattern_help<'a>( *whole_var, env.subs, env.target_info, - fresh_multimorphic_symbol!(env), + env.multimorphic_names, ) .map_err(Into::into); @@ -8653,12 +8515,12 @@ fn from_can_pattern_help<'a>( arguments.sort_by(|arg1, arg2| { let size1 = layout_cache - .from_var(env.arena, arg1.0, env.subs, fresh_multimorphic_symbol!(env)) + .from_var(env.arena, arg1.0, env.subs, env.multimorphic_names) .map(|x| x.alignment_bytes(env.target_info)) .unwrap_or(0); let size2 = layout_cache - .from_var(env.arena, arg2.0, env.subs, fresh_multimorphic_symbol!(env)) + .from_var(env.arena, arg2.0, env.subs, env.multimorphic_names) .map(|x| x.alignment_bytes(env.target_info)) .unwrap_or(0); @@ -8694,20 +8556,10 @@ fn from_can_pattern_help<'a>( temp.sort_by(|arg1, arg2| { let layout1 = layout_cache - .from_var( - env.arena, - arg1.0, - env.subs, - fresh_multimorphic_symbol!(env), - ) + .from_var(env.arena, arg1.0, env.subs, env.multimorphic_names) .unwrap(); let layout2 = layout_cache - .from_var( - env.arena, - arg2.0, - env.subs, - fresh_multimorphic_symbol!(env), - ) + .from_var(env.arena, arg2.0, env.subs, env.multimorphic_names) .unwrap(); let size1 = layout1.alignment_bytes(env.target_info); @@ -8727,7 +8579,7 @@ fn from_can_pattern_help<'a>( env.arena, *whole_var, env.subs, - fresh_multimorphic_symbol!(env), + env.multimorphic_names, ) { Ok(Layout::Union(ul)) => ul, _ => unreachable!(), @@ -9019,12 +8871,7 @@ fn from_can_pattern_help<'a>( } => { let (arg_var, loc_arg_pattern) = &(**argument); let arg_layout = layout_cache - .from_var( - env.arena, - *arg_var, - env.subs, - fresh_multimorphic_symbol!(env), - ) + .from_var(env.arena, *arg_var, env.subs, env.multimorphic_names) .unwrap(); let mono_arg_pattern = from_can_pattern_help( env, @@ -9050,7 +8897,7 @@ fn from_can_pattern_help<'a>( *whole_var, env.subs, env.target_info, - fresh_multimorphic_symbol!(env), + env.multimorphic_names, ) .map_err(RuntimeError::from)?; diff --git a/crates/compiler/mono/src/layout.rs b/crates/compiler/mono/src/layout.rs index 63d9433829..6b32423df6 100644 --- a/crates/compiler/mono/src/layout.rs +++ b/crates/compiler/mono/src/layout.rs @@ -1,14 +1,14 @@ use crate::ir::Parens; -use bumpalo::collections::Vec; +use bumpalo::collections::{CollectIn, Vec}; use bumpalo::Bump; use roc_builtins::bitcode::{FloatWidth, IntWidth}; use roc_collections::all::{default_hasher, MutMap}; +use roc_collections::VecMap; use roc_error_macros::{internal_error, todo_abilities}; use roc_module::ident::{Lowercase, TagName}; -use roc_module::symbol::{Interns, Symbol}; +use roc_module::symbol::{IdentIds, Interns, ModuleId, Symbol}; use roc_problem::can::RuntimeError; use roc_target::{PtrWidth, TargetInfo}; -use roc_types::pretty_print::ResolvedLambdaSet; use roc_types::subs::{ self, Content, FlatType, Label, RecordFields, Subs, UnionTags, UnsortedUnionLabels, Variable, }; @@ -17,6 +17,7 @@ use std::cmp::Ordering; use std::collections::hash_map::{DefaultHasher, Entry}; use std::collections::HashMap; use std::hash::{Hash, Hasher}; +use std::sync::{Arc, Mutex}; use ven_pretty::{DocAllocator, DocBuilder}; // if your changes cause this number to go down, great! @@ -67,8 +68,8 @@ impl<'a> RawFunctionLayout<'a> { matches!(self, RawFunctionLayout::ZeroArgumentThunk(_)) } - fn new_help<'b, F: FreshMultimorphicSymbol>( - env: &mut Env<'a, 'b, F>, + fn new_help<'b>( + env: &mut Env<'a, 'b>, var: Variable, content: Content, ) -> Result { @@ -153,8 +154,8 @@ impl<'a> RawFunctionLayout<'a> { } } - fn layout_from_lambda_set( - _env: &mut Env<'a, '_, F>, + fn layout_from_lambda_set( + _env: &mut Env<'a, '_>, _lset: subs::LambdaSet, ) -> Result { unreachable!() @@ -162,8 +163,8 @@ impl<'a> RawFunctionLayout<'a> { // Self::layout_from_flat_type(env, lset.as_tag_union()) } - fn layout_from_flat_type( - env: &mut Env<'a, '_, F>, + fn layout_from_flat_type( + env: &mut Env<'a, '_>, flat_type: FlatType, ) -> Result { use roc_types::subs::FlatType::*; @@ -189,7 +190,7 @@ impl<'a> RawFunctionLayout<'a> { env.subs, closure_var, env.target_info, - env.fresh_multimorphic_symbol, + env.multimorphic_names, )?; Ok(Self::Function(fn_args, lambda_set, ret)) @@ -221,10 +222,7 @@ impl<'a> RawFunctionLayout<'a> { /// Returns Err(()) if given an error, or Ok(Layout) if given a non-erroneous Structure. /// Panics if given a FlexVar or RigidVar, since those should have been /// monomorphized away already! - fn from_var( - env: &mut Env<'a, '_, F>, - var: Variable, - ) -> Result { + fn from_var(env: &mut Env<'a, '_>, var: Variable) -> Result { if env.is_seen(var) { unreachable!("The initial variable of a signature cannot be seen already") } else { @@ -704,6 +702,158 @@ impl std::fmt::Debug for LambdaSet<'_> { } } +#[derive(Default, Debug)] +struct MultimorphicNamesTable { + /// (source symbol, captures layouts) -> multimorphic alias + /// + /// SAFETY: actually, the `Layout` is alive only as long as the `arena` is alive. We take care + /// to promote new layouts to the owned arena. Since we are using a bump-allocating arena, the + /// references will never be invalidated until the arena is dropped, which happens when this + /// struct is dropped. + /// Also, the `Layout`s we owned are never exposed back via the public API. + inner: VecMap<(Symbol, &'static [Layout<'static>]), Symbol>, + arena: Bump, + ident_ids: IdentIds, +} + +impl MultimorphicNamesTable { + fn get<'b>(&self, name: Symbol, captures_layouts: &'b [Layout<'b>]) -> Option { + self.inner.get(&(name, captures_layouts)).copied() + } + + fn insert<'b>(&mut self, name: Symbol, captures_layouts: &'b [Layout<'b>]) -> Symbol { + debug_assert!(!self.inner.contains_key(&(name, captures_layouts))); + + let new_ident = self.ident_ids.gen_unique(); + let new_symbol = Symbol::new(ModuleId::MULTIMORPHIC, new_ident); + + let captures_layouts = self.promote_layout_slice(captures_layouts); + + self.inner.insert((name, captures_layouts), new_symbol); + + new_symbol + } + + fn alloc_st(&self, v: T) -> &'static T { + unsafe { std::mem::transmute::<_, &'static Bump>(&self.arena) }.alloc(v) + } + + fn promote_layout<'b>(&self, layout: Layout<'b>) -> Layout<'static> { + match layout { + Layout::Builtin(builtin) => Layout::Builtin(self.promote_builtin(builtin)), + Layout::Struct { + field_order_hash, + field_layouts, + } => Layout::Struct { + field_order_hash, + field_layouts: self.promote_layout_slice(field_layouts), + }, + Layout::Boxed(layout) => Layout::Boxed(self.alloc_st(self.promote_layout(*layout))), + Layout::Union(union_layout) => Layout::Union(self.promote_union_layout(union_layout)), + Layout::LambdaSet(lambda_set) => Layout::LambdaSet(self.promote_lambda_set(lambda_set)), + Layout::RecursivePointer => Layout::RecursivePointer, + } + } + + fn promote_layout_slice<'b>(&self, layouts: &'b [Layout<'b>]) -> &'static [Layout<'static>] { + layouts + .iter() + .map(|layout| self.promote_layout(*layout)) + .collect_in::>(unsafe { std::mem::transmute(&self.arena) }) + .into_bump_slice() + } + + fn promote_layout_slice_slices<'b>( + &self, + layout_slices: &'b [&'b [Layout<'b>]], + ) -> &'static [&'static [Layout<'static>]] { + layout_slices + .iter() + .map(|slice| self.promote_layout_slice(slice)) + .collect_in::>(unsafe { std::mem::transmute(&self.arena) }) + .into_bump_slice() + } + + fn promote_builtin(&self, builtin: Builtin) -> Builtin<'static> { + match builtin { + Builtin::Int(w) => Builtin::Int(w), + Builtin::Float(w) => Builtin::Float(w), + Builtin::Bool => Builtin::Bool, + Builtin::Decimal => Builtin::Decimal, + Builtin::Str => Builtin::Str, + Builtin::Dict(k, v) => Builtin::Dict( + self.alloc_st(self.promote_layout(*k)), + self.alloc_st(self.promote_layout(*v)), + ), + Builtin::Set(k) => Builtin::Set(self.alloc_st(self.promote_layout(*k))), + Builtin::List(l) => Builtin::Set(self.alloc_st(self.promote_layout(*l))), + } + } + + fn promote_union_layout(&self, union_layout: UnionLayout) -> UnionLayout<'static> { + match union_layout { + UnionLayout::NonRecursive(slices) => { + UnionLayout::NonRecursive(self.promote_layout_slice_slices(slices)) + } + UnionLayout::Recursive(slices) => { + UnionLayout::Recursive(self.promote_layout_slice_slices(slices)) + } + UnionLayout::NonNullableUnwrapped(slice) => { + UnionLayout::NonNullableUnwrapped(self.promote_layout_slice(slice)) + } + UnionLayout::NullableWrapped { + nullable_id, + other_tags, + } => UnionLayout::NullableWrapped { + nullable_id, + other_tags: self.promote_layout_slice_slices(other_tags), + }, + UnionLayout::NullableUnwrapped { + nullable_id, + other_fields, + } => UnionLayout::NullableUnwrapped { + nullable_id, + other_fields: self.promote_layout_slice(other_fields), + }, + } + } + + fn promote_lambda_set(&self, lambda_set: LambdaSet) -> LambdaSet<'static> { + let LambdaSet { + set, + representation, + } = lambda_set; + let set = set + .iter() + .map(|(name, slice)| (*name, self.promote_layout_slice(slice))) + .collect_in::>(unsafe { std::mem::transmute(&self.arena) }) + .into_bump_slice(); + LambdaSet { + set, + representation: self.alloc_st(self.promote_layout(*representation)), + } + } +} + +#[derive(Default, Debug)] +pub struct MultimorphicNames(Arc>); + +impl Clone for MultimorphicNames { + fn clone(&self) -> Self { + Self(Arc::clone(&self.0)) + } +} + +impl MultimorphicNames { + fn get<'b>(&self, name: Symbol, captures_layouts: &'b [Layout<'b>]) -> Option { + self.0.lock().unwrap().get(name, captures_layouts) + } + + fn insert<'b>(&mut self, name: Symbol, captures_layouts: &'b [Layout<'b>]) -> Symbol { + self.0.lock().unwrap().insert(name, captures_layouts) + } +} + #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] enum LambdaNameInner { /// Standard lambda name assigned during canonicalize/constrain @@ -806,8 +956,7 @@ impl<'a> LambdaSet<'a> { } /// Does the lambda set contain the given symbol? - /// NOTE: for multimorphic variants, this checks the alias name; the source name will always be - /// the name of the first multimorphic variant. + /// NOTE: for multimorphic variants, this checks the alias name. pub fn contains(&self, symbol: Symbol) -> bool { self.set.iter().any(|(s, _)| match s.0 { LambdaNameInner::Name(name) => name == symbol, @@ -841,12 +990,23 @@ impl<'a> LambdaSet<'a> { self.layout_for_member(comparator) } + fn contains_source(&self, symbol: Symbol) -> bool { + self.set.iter().any(|(s, _)| match s.0 { + LambdaNameInner::Name(name) => name == symbol, + LambdaNameInner::Multimorphic { source, .. } => source == symbol, + }) + } + + /// Finds an alias name for a possible-multimorphic lambda variant in the lambda set. pub fn find_lambda_name( &self, function_symbol: Symbol, captures_layouts: &[Layout], ) -> LambdaName { - debug_assert!(self.contains(function_symbol), "function symbol not in set"); + debug_assert!( + self.contains_source(function_symbol), + "function symbol not in set" + ); let comparator = |other_name: LambdaName, other_captures_layouts: &[Layout]| { let other_name = match other_name.0 { @@ -868,46 +1028,6 @@ impl<'a> LambdaSet<'a> { *name } - // Layout for a single member of the lambda set, when you are constructing a proc, and already - // know the multimorphic name (if any). - // pub fn layout_for_member_constructing_proc( - // &self, - // lambda_name: LambdaName, - // ) -> ClosureRepresentation<'a> { - // debug_assert!( - // self.set.iter().any(|(s, _)| *s == lambda_name), - // "lambda not in set" - // ); - - // let comparator = - // |other_name: LambdaName, _other_captures_layouts: &[Layout]| other_name == lambda_name; - - // self.layout_for_member(comparator) - // } - - // Layout for a single member of the lambda set, when you are constructing a closure - // representation, and maybe need to pick out a multimorphic variant. - // pub fn layout_for_member_constructing_closure_data( - // &self, - // function_symbol: Symbol, - // captures_layouts: &[Layout], - // ) -> ClosureRepresentation<'a> { - // debug_assert!(self.contains(function_symbol), "function symbol not in set"); - - // let comparator = |other_name: LambdaName, other_captures_layouts: &[Layout]| { - // let other_name = match other_name.0 { - // LambdaNameInner::Name(name) => name, - // // Take the source, since we'll want to pick out the multimorphic name if it - // // matches - // LambdaNameInner::Multimorphic { source, .. } => source, - // }; - // other_name == function_symbol - // && captures_layouts.iter().eq(other_captures_layouts.iter()) - // }; - - // self.layout_for_member(comparator) - // } - fn layout_for_member(&self, comparator: F) -> ClosureRepresentation<'a> where F: Fn(LambdaName, &[Layout]) -> bool, @@ -1001,26 +1121,28 @@ impl<'a> LambdaSet<'a> { } } - pub fn from_var( + pub fn from_var( arena: &'a Bump, subs: &Subs, closure_var: Variable, target_info: TargetInfo, - fresh_multimorphic_symbol: &mut F, - ) -> Result - where - F: FreshMultimorphicSymbol, - { - match roc_types::pretty_print::resolve_lambda_set(subs, closure_var) { + multimorphic_names: &mut MultimorphicNames, + ) -> Result { + match resolve_lambda_set(subs, closure_var) { ResolvedLambdaSet::Set(mut lambdas) => { // sort the tags; make sure ordering stays intact! lambdas.sort_by_key(|(sym, _)| *sym); let mut set: Vec<(LambdaName, &[Layout])> = Vec::with_capacity_in(lambdas.len(), arena); + let mut set_for_making_repr: std::vec::Vec<(Symbol, std::vec::Vec)> = + std::vec::Vec::with_capacity(lambdas.len()); let mut last_function_symbol = None; - for (function_symbol, variables) in lambdas.iter() { + let mut lambdas_it = lambdas.iter().peekable(); + + let mut has_multimorphic = false; + while let Some((function_symbol, variables)) = lambdas_it.next() { let mut arguments = Vec::with_capacity_in(variables.len(), arena); let mut env = Env { @@ -1028,39 +1150,59 @@ impl<'a> LambdaSet<'a> { subs, seen: Vec::new_in(arena), target_info, - fresh_multimorphic_symbol, + multimorphic_names, }; for var in variables { arguments.push(Layout::from_var(&mut env, *var)?); } - let lambda_name = match last_function_symbol { - None => LambdaNameInner::Name(*function_symbol), - Some(last_function_symbol) => { - if function_symbol != last_function_symbol { - LambdaNameInner::Name(*function_symbol) - } else { - LambdaNameInner::Multimorphic { - source: *function_symbol, - alias: (*fresh_multimorphic_symbol)(), - } - } + let arguments = arguments.into_bump_slice(); + + let is_multimorphic = match (last_function_symbol, lambdas_it.peek()) { + (None, None) => false, + (Some(sym), None) | (None, Some((sym, _))) => function_symbol == sym, + (Some(sym1), Some((sym2, _))) => { + function_symbol == sym1 || function_symbol == sym2 } }; + + let lambda_name = if is_multimorphic { + let alias = match multimorphic_names.get(*function_symbol, arguments) { + Some(alias) => alias, + None => multimorphic_names.insert(*function_symbol, arguments), + }; + + has_multimorphic = true; + + LambdaNameInner::Multimorphic { + source: *function_symbol, + alias, + } + } else { + LambdaNameInner::Name(*function_symbol) + }; let lambda_name = LambdaName(lambda_name); - set.push((lambda_name, arguments.into_bump_slice())); + set.push((lambda_name, arguments)); + set_for_making_repr.push((lambda_name.call_name(), variables.to_vec())); last_function_symbol = Some(function_symbol); } + if has_multimorphic { + // Must re-sort the set in case we added multimorphic lambdas since they may under + // another name + set.sort_by_key(|(name, _)| name.call_name()); + set_for_making_repr.sort_by_key(|(name, _)| *name); + } + let representation = arena.alloc(Self::make_representation( arena, subs, - lambdas, + set_for_making_repr, target_info, - fresh_multimorphic_symbol, + multimorphic_names, )); Ok(LambdaSet { @@ -1079,22 +1221,16 @@ impl<'a> LambdaSet<'a> { } } - fn make_representation( + fn make_representation( arena: &'a Bump, subs: &Subs, tags: std::vec::Vec<(Symbol, std::vec::Vec)>, target_info: TargetInfo, - fresh_multimorphic_symbol: &mut F, + multimorphic_names: &mut MultimorphicNames, ) -> Layout<'a> { // otherwise, this is a closure with a payload - let variant = union_sorted_tags_help( - arena, - tags, - None, - subs, - target_info, - fresh_multimorphic_symbol, - ); + let variant = + union_sorted_tags_help(arena, tags, None, subs, target_info, multimorphic_names); use UnionVariant::*; match variant { @@ -1149,6 +1285,40 @@ impl<'a> LambdaSet<'a> { } } +enum ResolvedLambdaSet { + Set(std::vec::Vec<(Symbol, std::vec::Vec)>), + /// TODO: figure out if this can happen in a correct program, or is the result of a bug in our + /// compiler. See https://github.com/rtfeldman/roc/issues/3163. + Unbound, +} + +fn resolve_lambda_set(subs: &Subs, mut var: Variable) -> ResolvedLambdaSet { + let mut set = vec![]; + loop { + match subs.get_content_without_compacting(var) { + Content::LambdaSet(subs::LambdaSet { + solved, + recursion_var: _, + unspecialized, + }) => { + debug_assert!( + unspecialized.is_empty(), + "unspecialized lambda sets left over during resolution: {:?}", + roc_types::subs::SubsFmtContent(subs.get_content_without_compacting(var), subs), + ); + roc_types::pretty_print::push_union(subs, solved, &mut set); + return ResolvedLambdaSet::Set(set); + } + Content::RecursionVar { structure, .. } => { + var = *structure; + } + Content::FlexVar(_) => return ResolvedLambdaSet::Unbound, + + c => internal_error!("called with a non-lambda set {:?}", c), + } + } +} + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum Builtin<'a> { Int(IntWidth), @@ -1161,21 +1331,15 @@ pub enum Builtin<'a> { List(&'a Layout<'a>), } -pub struct Env<'a, 'b, F> -where - F: FreshMultimorphicSymbol, -{ +pub struct Env<'a, 'b> { target_info: TargetInfo, arena: &'a Bump, seen: Vec<'a, Variable>, subs: &'b Subs, - fresh_multimorphic_symbol: &'b mut F, + multimorphic_names: &'b mut MultimorphicNames, } -impl<'a, 'b, F> Env<'a, 'b, F> -where - F: FreshMultimorphicSymbol, -{ +impl<'a, 'b> Env<'a, 'b> { fn is_seen(&self, var: Variable) -> bool { let var = self.subs.get_root_key_without_compacting(var); @@ -1225,8 +1389,8 @@ impl<'a> Layout<'a> { field_order_hash: FieldOrderHash::ZERO_FIELD_HASH, }; - fn new_help<'b, F: FreshMultimorphicSymbol>( - env: &mut Env<'a, 'b, F>, + fn new_help<'b>( + env: &mut Env<'a, 'b>, var: Variable, content: Content, ) -> Result { @@ -1284,10 +1448,7 @@ impl<'a> Layout<'a> { /// Returns Err(()) if given an error, or Ok(Layout) if given a non-erroneous Structure. /// Panics if given a FlexVar or RigidVar, since those should have been /// monomorphized away already! - fn from_var( - env: &mut Env<'a, '_, F>, - var: Variable, - ) -> Result { + fn from_var(env: &mut Env<'a, '_>, var: Variable) -> Result { if env.is_seen(var) { Ok(Layout::RecursivePointer) } else { @@ -1611,16 +1772,13 @@ impl<'a> LayoutCache<'a> { } } - pub fn from_var( + pub fn from_var( &mut self, arena: &'a Bump, var: Variable, subs: &Subs, - fresh_multimorphic_symbol: &mut F, - ) -> Result, LayoutProblem> - where - F: FreshMultimorphicSymbol, - { + multimorphic_names: &mut MultimorphicNames, + ) -> Result, LayoutProblem> { // Store things according to the root Variable, to avoid duplicate work. let var = subs.get_root_key_without_compacting(var); @@ -1629,18 +1787,18 @@ impl<'a> LayoutCache<'a> { subs, seen: Vec::new_in(arena), target_info: self.target_info, - fresh_multimorphic_symbol, + multimorphic_names, }; Layout::from_var(&mut env, var) } - pub fn raw_from_var( + pub fn raw_from_var( &mut self, arena: &'a Bump, var: Variable, subs: &Subs, - fresh_multimorphic_symbol: &mut F, + multimorphic_names: &mut MultimorphicNames, ) -> Result, LayoutProblem> { // Store things according to the root Variable, to avoid duplicate work. let var = subs.get_root_key_without_compacting(var); @@ -1650,7 +1808,7 @@ impl<'a> LayoutCache<'a> { subs, seen: Vec::new_in(arena), target_info: self.target_info, - fresh_multimorphic_symbol, + multimorphic_names, }; RawFunctionLayout::from_var(&mut env, var) } @@ -1905,8 +2063,8 @@ impl<'a> Builtin<'a> { } } -fn layout_from_lambda_set<'a, F: FreshMultimorphicSymbol>( - env: &mut Env<'a, '_, F>, +fn layout_from_lambda_set<'a>( + env: &mut Env<'a, '_>, lset: subs::LambdaSet, ) -> Result, LayoutProblem> { // Lambda set is just a tag union from the layout's perspective. @@ -1935,8 +2093,8 @@ fn layout_from_lambda_set<'a, F: FreshMultimorphicSymbol>( } } -fn layout_from_flat_type<'a, F: FreshMultimorphicSymbol>( - env: &mut Env<'a, '_, F>, +fn layout_from_flat_type<'a>( + env: &mut Env<'a, '_>, flat_type: FlatType, ) -> Result, LayoutProblem> { use roc_types::subs::FlatType::*; @@ -2049,7 +2207,7 @@ fn layout_from_flat_type<'a, F: FreshMultimorphicSymbol>( env.subs, closure_var, env.target_info, - env.fresh_multimorphic_symbol, + env.multimorphic_names, )?; Ok(Layout::LambdaSet(lambda_set)) @@ -2129,19 +2287,19 @@ fn layout_from_flat_type<'a, F: FreshMultimorphicSymbol>( pub type SortedField<'a> = (Lowercase, Variable, Result, Layout<'a>>); -pub fn sort_record_fields<'a, F: FreshMultimorphicSymbol>( +pub fn sort_record_fields<'a>( arena: &'a Bump, var: Variable, subs: &Subs, target_info: TargetInfo, - fresh_multimorphic_symbol: &mut F, + multimorphic_names: &mut MultimorphicNames, ) -> Result>, LayoutProblem> { let mut env = Env { arena, subs, seen: Vec::new_in(arena), target_info, - fresh_multimorphic_symbol, + multimorphic_names, }; let (it, _) = match gather_fields_unsorted_iter(subs, RecordFields::empty(), var) { @@ -2156,8 +2314,8 @@ pub fn sort_record_fields<'a, F: FreshMultimorphicSymbol>( sort_record_fields_help(&mut env, it) } -fn sort_record_fields_help<'a, F: FreshMultimorphicSymbol>( - env: &mut Env<'a, '_, F>, +fn sort_record_fields_help<'a>( + env: &mut Env<'a, '_>, fields_map: impl Iterator)>, ) -> Result>, LayoutProblem> { let target_info = env.target_info; @@ -2343,12 +2501,12 @@ impl<'a> WrappedVariant<'a> { } } -pub fn union_sorted_tags<'a, F: FreshMultimorphicSymbol>( +pub fn union_sorted_tags<'a>( arena: &'a Bump, var: Variable, subs: &Subs, target_info: TargetInfo, - fresh_multimorphic_symbol: &mut F, + multimorphic_names: &mut MultimorphicNames, ) -> Result, LayoutProblem> { let var = if let Content::RecursionVar { structure, .. } = subs.get_content_without_compacting(var) { @@ -2369,7 +2527,7 @@ pub fn union_sorted_tags<'a, F: FreshMultimorphicSymbol>( | Err((_, Content::FlexVar(_) | Content::RigidVar(_))) | Err((_, Content::RecursionVar { .. })) => { let opt_rec_var = get_recursion_var(subs, var); - union_sorted_tags_help(arena, tags_vec, opt_rec_var, subs, target_info, fresh_multimorphic_symbol) + union_sorted_tags_help(arena, tags_vec, opt_rec_var, subs, target_info, multimorphic_names) } Err((_, Content::Error)) => return Err(LayoutProblem::Erroneous), Err(other) => panic!("invalid content in tag union variable: {:?}", other), @@ -2398,8 +2556,8 @@ fn is_recursive_tag_union(layout: &Layout) -> bool { ) } -fn union_sorted_tags_help_new<'a, L, F: FreshMultimorphicSymbol>( - env: &mut Env<'a, '_, F>, +fn union_sorted_tags_help_new<'a, L>( + env: &mut Env<'a, '_>, tags_list: &[(&'_ L, &[Variable])], opt_rec_var: Option, ) -> UnionVariant<'a> @@ -2589,13 +2747,13 @@ where } } -pub fn union_sorted_tags_help<'a, L, F: FreshMultimorphicSymbol>( +pub fn union_sorted_tags_help<'a, L>( arena: &'a Bump, mut tags_vec: std::vec::Vec<(L, std::vec::Vec)>, opt_rec_var: Option, subs: &Subs, target_info: TargetInfo, - fresh_multimorphic_symbol: &mut F, + multimorphic_names: &mut MultimorphicNames, ) -> UnionVariant<'a> where L: Into + Ord + Clone, @@ -2608,7 +2766,7 @@ where subs, seen: Vec::new_in(arena), target_info, - fresh_multimorphic_symbol, + multimorphic_names, }; match tags_vec.len() { @@ -2797,8 +2955,8 @@ where } } -fn layout_from_newtype<'a, L: Label, F: FreshMultimorphicSymbol>( - env: &mut Env<'a, '_, F>, +fn layout_from_newtype<'a, L: Label>( + env: &mut Env<'a, '_>, tags: &UnsortedUnionLabels, ) -> Layout<'a> { debug_assert!(tags.is_newtype_wrapper(env.subs)); @@ -2821,10 +2979,7 @@ fn layout_from_newtype<'a, L: Label, F: FreshMultimorphicSymbol>( } } -fn layout_from_union<'a, L, F: FreshMultimorphicSymbol>( - env: &mut Env<'a, '_, F>, - tags: &UnsortedUnionLabels, -) -> Layout<'a> +fn layout_from_union<'a, L>(env: &mut Env<'a, '_>, tags: &UnsortedUnionLabels) -> Layout<'a> where L: Label + Ord + Into, { @@ -2900,8 +3055,8 @@ where } } -fn layout_from_recursive_union<'a, L, F: FreshMultimorphicSymbol>( - env: &mut Env<'a, '_, F>, +fn layout_from_recursive_union<'a, L>( + env: &mut Env<'a, '_>, rec_var: Variable, tags: &UnsortedUnionLabels, ) -> Result, LayoutProblem> @@ -3082,8 +3237,8 @@ fn layout_from_num_content<'a>( } } -fn dict_layout_from_key_value<'a, F: FreshMultimorphicSymbol>( - env: &mut Env<'a, '_, F>, +fn dict_layout_from_key_value<'a>( + env: &mut Env<'a, '_>, key_var: Variable, value_var: Variable, ) -> Result, LayoutProblem> { @@ -3118,8 +3273,8 @@ fn dict_layout_from_key_value<'a, F: FreshMultimorphicSymbol>( pub trait FreshMultimorphicSymbol: FnMut() -> Symbol {} impl FreshMultimorphicSymbol for T where T: FnMut() -> Symbol {} -pub fn list_layout_from_elem<'a, F: FreshMultimorphicSymbol>( - env: &mut Env<'a, '_, F>, +pub fn list_layout_from_elem<'a>( + env: &mut Env<'a, '_>, element_var: Variable, ) -> Result, LayoutProblem> { let is_variable = |content| matches!(content, &Content::FlexVar(_) | &Content::RigidVar(_)); diff --git a/crates/compiler/test_mono/generated/multimorphic_lambda_set_capture.txt b/crates/compiler/test_mono/generated/multimorphic_lambda_set_capture.txt new file mode 100644 index 0000000000..ba2cb0e203 --- /dev/null +++ b/crates/compiler/test_mono/generated/multimorphic_lambda_set_capture.txt @@ -0,0 +1,49 @@ +procedure #Multimorphic.0 (Test.23, #Attr.12): + let Test.4 : Str = UnionAtIndex (Id 0) (Index 0) #Attr.12; + inc Test.4; + dec #Attr.12; + let Test.25 : Str = ""; + ret Test.25; + +procedure #Multimorphic.1 (Test.17, #Attr.12): + let Test.4 : {} = UnionAtIndex (Id 1) (Index 0) #Attr.12; + dec #Attr.12; + let Test.19 : Str = ""; + ret Test.19; + +procedure Test.1 (Test.4): + let Test.16 : [C Str, C {}] = ClosureTag(#Multimorphic.1) Test.4; + ret Test.16; + +procedure Test.1 (Test.4): + let Test.22 : [C Str, C {}] = ClosureTag(#Multimorphic.0) Test.4; + ret Test.22; + +procedure Test.0 (): + let Test.2 : Int1 = true; + joinpoint Test.13 Test.3: + let Test.8 : {} = Struct {}; + let Test.9 : U8 = GetTagId Test.3; + joinpoint Test.10 Test.7: + ret Test.7; + in + switch Test.9: + case 0: + let Test.11 : Str = CallByName #Multimorphic.0 Test.8 Test.3; + jump Test.10 Test.11; + + default: + let Test.12 : Str = CallByName #Multimorphic.1 Test.8 Test.3; + jump Test.10 Test.12; + + in + let Test.26 : Int1 = true; + let Test.27 : Int1 = lowlevel Eq Test.26 Test.2; + if Test.27 then + let Test.15 : {} = Struct {}; + let Test.14 : [C Str, C {}] = CallByName Test.1 Test.15; + jump Test.13 Test.14; + else + let Test.21 : Str = ""; + let Test.20 : [C Str, C {}] = CallByName Test.1 Test.21; + jump Test.13 Test.20; diff --git a/crates/compiler/test_mono/src/tests.rs b/crates/compiler/test_mono/src/tests.rs index 10be2c684e..62a0ba261f 100644 --- a/crates/compiler/test_mono/src/tests.rs +++ b/crates/compiler/test_mono/src/tests.rs @@ -1527,24 +1527,22 @@ fn tail_call_with_different_layout() { } #[mono_test] -#[ignore] -fn lambda_sets_collide_with_captured_var() { +fn multimorphic_lambda_set_capture() { indoc!( r#" capture : a -> ({} -> Str) capture = \val -> - thunk = - \{} -> - when val is - _ -> "" - thunk + \{} -> + when val is + _ -> "" x : [True, False] + x = True fun = when x is - True -> capture 1u8 - False -> capture 1u64 + True -> capture {} + False -> capture "" fun {} "# diff --git a/crates/compiler/types/src/pretty_print.rs b/crates/compiler/types/src/pretty_print.rs index 0203793009..0837e5f20e 100644 --- a/crates/compiler/types/src/pretty_print.rs +++ b/crates/compiler/types/src/pretty_print.rs @@ -4,7 +4,6 @@ use crate::subs::{ }; use crate::types::{name_type_var, RecordField, Uls}; use roc_collections::all::MutMap; -use roc_error_macros::internal_error; use roc_module::ident::{Lowercase, TagName}; use roc_module::symbol::{Interns, ModuleId, Symbol}; @@ -1152,7 +1151,7 @@ fn write_flat_type<'a>( } } -fn push_union<'a, L: Label>( +pub fn push_union<'a, L: Label>( subs: &'a Subs, tags: &UnionLabels, fields: &mut Vec<(L, Vec)>, @@ -1196,40 +1195,6 @@ pub fn chase_ext_tag_union<'a>( } } -pub enum ResolvedLambdaSet { - Set(Vec<(Symbol, Vec)>), - /// TODO: figure out if this can happen in a correct program, or is the result of a bug in our - /// compiler. See https://github.com/rtfeldman/roc/issues/3163. - Unbound, -} - -pub fn resolve_lambda_set(subs: &Subs, mut var: Variable) -> ResolvedLambdaSet { - let mut set = vec![]; - loop { - match subs.get_content_without_compacting(var) { - Content::LambdaSet(subs::LambdaSet { - solved, - recursion_var: _, - unspecialized, - }) => { - debug_assert!( - unspecialized.is_empty(), - "unspecialized lambda sets left over during resolution: {:?}", - crate::subs::SubsFmtContent(subs.get_content_without_compacting(var), subs), - ); - push_union(subs, solved, &mut set); - return ResolvedLambdaSet::Set(set); - } - Content::RecursionVar { structure, .. } => { - var = *structure; - } - Content::FlexVar(_) => return ResolvedLambdaSet::Unbound, - - c => internal_error!("called with a non-lambda set {:?}", c), - } - } -} - fn write_apply<'a>( env: &Env, ctx: &mut Context<'a>, From ada4b0ea43e9228e27557e811f51e699199df948 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Tue, 28 Jun 2022 17:26:42 -0400 Subject: [PATCH 12/66] Add test gen for multimorphic capture --- crates/compiler/load_internal/src/file.rs | 13 ++++++++- crates/compiler/mono/src/layout.rs | 10 +++++++ .../compiler/test_gen/src/gen_primitives.rs | 27 +++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/crates/compiler/load_internal/src/file.rs b/crates/compiler/load_internal/src/file.rs index 328fd7542f..a62c69adc0 100644 --- a/crates/compiler/load_internal/src/file.rs +++ b/crates/compiler/load_internal/src/file.rs @@ -2599,9 +2599,20 @@ fn finish_specialization( .into_inner() .into_module_ids(); + let mut all_ident_ids = state.constrained_ident_ids; + let multimorphic_idents = state + .multimorphic_names + .try_unwrap_names() + .expect("There were still outstanding Arc references to multimorphic_names"); + let old_idents = all_ident_ids.insert(ModuleId::MULTIMORPHIC, multimorphic_idents); + debug_assert!( + old_idents.is_none() || old_idents.unwrap().is_empty(), + "duplicate multimorphic idents" + ); + let interns = Interns { module_ids, - all_ident_ids: state.constrained_ident_ids, + all_ident_ids, }; let State { diff --git a/crates/compiler/mono/src/layout.rs b/crates/compiler/mono/src/layout.rs index 6b32423df6..204ed4127b 100644 --- a/crates/compiler/mono/src/layout.rs +++ b/crates/compiler/mono/src/layout.rs @@ -852,6 +852,16 @@ impl MultimorphicNames { fn insert<'b>(&mut self, name: Symbol, captures_layouts: &'b [Layout<'b>]) -> Symbol { self.0.lock().unwrap().insert(name, captures_layouts) } + + /// Assumes there is only one clone still alive. + /// If there is more than one clone alive, `self` is returned. + pub fn try_unwrap_names(self) -> Result { + let mutex = Arc::try_unwrap(self.0).map_err(Self)?; + let table = mutex + .into_inner() + .expect("how can there be another lock if we consumed the only ref?"); + Ok(table.ident_ids) + } } #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] diff --git a/crates/compiler/test_gen/src/gen_primitives.rs b/crates/compiler/test_gen/src/gen_primitives.rs index c46f4e56d4..a3c28f8edf 100644 --- a/crates/compiler/test_gen/src/gen_primitives.rs +++ b/crates/compiler/test_gen/src/gen_primitives.rs @@ -3498,3 +3498,30 @@ fn polymorphic_lambda_captures_polymorphic_value() { u64 ) } + +#[test] +#[cfg(any(feature = "gen-llvm"))] +fn multimorphic_lambda_set_u64_vs_u8_capture() { + assert_evals_to!( + indoc!( + r#" + capture : _ -> ({} -> Str) + capture = \val -> + \{} -> + Num.toStr val + + x : [True, False] + x = True + + fun = + when x is + True -> capture 123u64 + False -> capture 18u8 + + fun {} + "# + ), + RocStr::from("123"), + RocStr + ) +} From d63eb23664954f960555c85fe58a32655cdba6e6 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Tue, 28 Jun 2022 17:35:23 -0400 Subject: [PATCH 13/66] Fix compile errors --- Cargo.lock | 1 + crates/bindgen/src/load.rs | 11 +++++++++- crates/bindgen/src/types.rs | 39 +++++++++--------------------------- crates/repl_cli/src/lib.rs | 7 ++++--- crates/repl_eval/src/eval.rs | 33 +++++++++--------------------- crates/repl_wasm/Cargo.toml | 1 + crates/repl_wasm/src/repl.rs | 4 +++- 7 files changed, 39 insertions(+), 57 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eb989d06e2..d5e0279fa4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3963,6 +3963,7 @@ dependencies = [ "roc_collections", "roc_gen_wasm", "roc_load", + "roc_mono", "roc_parse", "roc_repl_eval", "roc_reporting", diff --git a/crates/bindgen/src/load.rs b/crates/bindgen/src/load.rs index da61882300..e7c04ebed0 100644 --- a/crates/bindgen/src/load.rs +++ b/crates/bindgen/src/load.rs @@ -1,6 +1,7 @@ use crate::types::{Env, Types}; use bumpalo::Bump; use roc_load::{LoadedModule, Threading}; +use roc_mono::layout::MultimorphicNames; use roc_reporting::report::RenderTarget; use roc_target::{Architecture, TargetInfo}; use std::io; @@ -36,6 +37,8 @@ pub fn load_types( ) .expect("Problem loading platform module"); + let mut multimorphic_names = MultimorphicNames::default(); + let decls = declarations_by_id.remove(&home).unwrap(); let subs = solved.inner_mut(); @@ -74,7 +77,13 @@ pub fn load_types( let types_and_targets = Architecture::iter() .map(|arch| { let target_info = arch.into(); - let mut env = Env::new(home, arena, subs, &mut interns, target_info); + let mut env = Env::new( + arena, + subs, + &mut interns, + target_info, + &mut multimorphic_names, + ); (env.vars_to_types(variables.clone()), target_info) }) diff --git a/crates/bindgen/src/types.rs b/crates/bindgen/src/types.rs index 1270f9fbd1..003d2ab639 100644 --- a/crates/bindgen/src/types.rs +++ b/crates/bindgen/src/types.rs @@ -6,10 +6,10 @@ use roc_builtins::bitcode::{ IntWidth::{self, *}, }; use roc_collections::VecMap; -use roc_module::symbol::{Interns, ModuleId, Symbol}; +use roc_module::symbol::{Interns, Symbol}; use roc_mono::layout::{ cmp_fields, ext_var_is_empty_tag_union, round_up_to_alignment, Builtin, Layout, LayoutCache, - UnionLayout, + MultimorphicNames, UnionLayout, }; use roc_target::TargetInfo; use roc_types::{ @@ -410,41 +410,26 @@ pub struct Env<'a> { arena: &'a Bump, subs: &'a Subs, layout_cache: LayoutCache<'a>, - home: ModuleId, interns: &'a mut Interns, struct_names: Structs, enum_names: Enums, pending_recursive_types: VecMap>, known_recursive_types: VecMap, TypeId>, target: TargetInfo, -} - -macro_rules! fresh_multimorphic_symbol { - ($env:expr) => { - &mut || { - let ident_id = $env - .interns - .all_ident_ids - .get_mut(&$env.home) - .unwrap() - .gen_unique(); - Symbol::new($env.home, ident_id) - } - }; + multimorphic_names: &'a mut MultimorphicNames, } impl<'a> Env<'a> { pub fn new( - home: ModuleId, arena: &'a Bump, subs: &'a Subs, interns: &'a mut Interns, target: TargetInfo, + multimorphic_names: &'a mut MultimorphicNames, ) -> Self { Env { arena, subs, - home, interns, struct_names: Default::default(), enum_names: Default::default(), @@ -452,6 +437,7 @@ impl<'a> Env<'a> { known_recursive_types: Default::default(), layout_cache: LayoutCache::new(target), target, + multimorphic_names, } } @@ -473,7 +459,7 @@ impl<'a> Env<'a> { fn add_type(&mut self, var: Variable, types: &mut Types) -> TypeId { let layout = self .layout_cache - .from_var(self.arena, var, self.subs, fresh_multimorphic_symbol!(self)) + .from_var(self.arena, var, self.subs, self.multimorphic_names) .expect("Something weird ended up in the content"); add_type_help(self, layout, var, None, types) @@ -609,7 +595,7 @@ fn add_type_help<'a>( let type_id = types.add(RocType::RecursivePointer(TypeId::PENDING), layout); let structure_layout = env .layout_cache - .from_var(env.arena, *structure, subs, fresh_multimorphic_symbol!(env)) + .from_var(env.arena, *structure, subs, env.multimorphic_names) .unwrap(); env.pending_recursive_types @@ -717,7 +703,7 @@ where label, field_var, env.layout_cache - .from_var(env.arena, field_var, subs, fresh_multimorphic_symbol!(env)) + .from_var(env.arena, field_var, subs, env.multimorphic_names) .unwrap(), )); } @@ -787,12 +773,7 @@ fn add_tag_union<'a>( let payload_var = payload_vars.get(0).unwrap(); let payload_layout = env .layout_cache - .from_var( - env.arena, - *payload_var, - env.subs, - fresh_multimorphic_symbol!(env), - ) + .from_var(env.arena, *payload_var, env.subs, env.multimorphic_names) .expect("Something weird ended up in the content"); let payload_id = add_type_help(env, payload_layout, *payload_var, None, types); @@ -943,7 +924,7 @@ fn struct_fields_needed>(env: &mut Env<'_>, var vars.into_iter().fold(0, |count, var| { let layout = env .layout_cache - .from_var(arena, var, subs, fresh_multimorphic_symbol!(env)) + .from_var(arena, var, subs, env.multimorphic_names) .unwrap(); if layout.is_dropped_because_empty() { diff --git a/crates/repl_cli/src/lib.rs b/crates/repl_cli/src/lib.rs index 4b8dcb8011..0c8e588503 100644 --- a/crates/repl_cli/src/lib.rs +++ b/crates/repl_cli/src/lib.rs @@ -2,6 +2,7 @@ use bumpalo::Bump; use const_format::concatcp; use inkwell::context::Context; use libloading::Library; +use roc_mono::layout::MultimorphicNames; use rustyline::highlight::{Highlighter, PromptInfo}; use rustyline::validate::{self, ValidationContext, ValidationResult, Validator}; use rustyline_derive::{Completer, Helper, Hinter}; @@ -305,7 +306,8 @@ fn gen_and_eval_llvm<'a>( let app = CliApp { lib }; - let mut env = env; + let mut multimorphic_names = MultimorphicNames::default(); + let res_answer = jit_to_ast( &arena, &app, @@ -313,8 +315,7 @@ fn gen_and_eval_llvm<'a>( main_fn_layout, content, &subs, - home, - env.interns.all_ident_ids.get_mut(&home).unwrap(), + &mut multimorphic_names, target_info, ); diff --git a/crates/repl_eval/src/eval.rs b/crates/repl_eval/src/eval.rs index 97d1f89e66..8d323920d1 100644 --- a/crates/repl_eval/src/eval.rs +++ b/crates/repl_eval/src/eval.rs @@ -1,16 +1,16 @@ use bumpalo::collections::Vec; use bumpalo::Bump; -use roc_mono::fresh_multimorphic_symbol; use std::cmp::{max_by_key, min_by_key}; use roc_builtins::bitcode::{FloatWidth, IntWidth}; use roc_collections::all::MutMap; use roc_module::called_via::CalledVia; use roc_module::ident::TagName; -use roc_module::symbol::{IdentIds, ModuleId, Symbol}; +use roc_module::symbol::Symbol; use roc_mono::ir::ProcLayout; use roc_mono::layout::{ - union_sorted_tags_help, Builtin, Layout, LayoutCache, UnionLayout, UnionVariant, WrappedVariant, + union_sorted_tags_help, Builtin, Layout, LayoutCache, MultimorphicNames, UnionLayout, + UnionVariant, WrappedVariant, }; use roc_parse::ast::{AssignedField, Collection, Expr, StrLiteral}; use roc_region::all::{Loc, Region}; @@ -21,11 +21,10 @@ use roc_types::subs::{Content, FlatType, GetSubsSlice, RecordFields, Subs, Union use crate::{ReplApp, ReplAppMemory}; struct Env<'a> { - home: ModuleId, arena: &'a Bump, subs: &'a Subs, target_info: TargetInfo, - ident_ids: &'a mut IdentIds, + multimorphic_names: &'a mut MultimorphicNames, } pub enum ToAstProblem { @@ -48,16 +47,14 @@ pub fn jit_to_ast<'a, A: ReplApp<'a>>( layout: ProcLayout<'a>, content: &'a Content, subs: &'a Subs, - module_id: ModuleId, - ident_ids: &'a mut IdentIds, + multimorphic_names: &'a mut MultimorphicNames, target_info: TargetInfo, ) -> Result, ToAstProblem> { let mut env = Env { arena, subs, target_info, - home: module_id, - ident_ids, + multimorphic_names, }; match layout { @@ -193,7 +190,7 @@ fn get_tags_vars_and_variant<'a>( opt_rec_var, env.subs, env.target_info, - fresh_multimorphic_symbol!(env), + env.multimorphic_names, ); (vars_of_tag, union_variant) @@ -913,12 +910,7 @@ fn struct_to_ast<'a, M: ReplAppMemory>( let inner_content = env.subs.get_content_without_compacting(field.into_inner()); let field_layout = layout_cache - .from_var( - arena, - field.into_inner(), - env.subs, - fresh_multimorphic_symbol!(env), - ) + .from_var(arena, field.into_inner(), env.subs, env.multimorphic_names) .unwrap(); let inner_layouts = arena.alloc([field_layout]); @@ -957,12 +949,7 @@ fn struct_to_ast<'a, M: ReplAppMemory>( for (label, field) in record_fields.sorted_iterator(subs, Variable::EMPTY_RECORD) { let content = subs.get_content_without_compacting(field.into_inner()); let field_layout = layout_cache - .from_var( - arena, - field.into_inner(), - env.subs, - fresh_multimorphic_symbol!(env), - ) + .from_var(arena, field.into_inner(), env.subs, env.multimorphic_names) .unwrap(); let loc_expr = &*arena.alloc(Loc { @@ -1162,7 +1149,7 @@ fn byte_to_ast<'a, M: ReplAppMemory>( None, env.subs, env.target_info, - fresh_multimorphic_symbol!(env), + env.multimorphic_names, ); match union_variant { diff --git a/crates/repl_wasm/Cargo.toml b/crates/repl_wasm/Cargo.toml index 4a859465e9..dbea9fb8f7 100644 --- a/crates/repl_wasm/Cargo.toml +++ b/crates/repl_wasm/Cargo.toml @@ -26,6 +26,7 @@ roc_repl_eval = {path = "../repl_eval"} roc_reporting = {path = "../reporting"} roc_target = {path = "../compiler/roc_target"} roc_types = {path = "../compiler/types"} +roc_mono = {path = "../compiler/mono"} [features] wasmer = ["futures"] diff --git a/crates/repl_wasm/src/repl.rs b/crates/repl_wasm/src/repl.rs index 15e6e487e9..4674cdda6a 100644 --- a/crates/repl_wasm/src/repl.rs +++ b/crates/repl_wasm/src/repl.rs @@ -1,4 +1,5 @@ use bumpalo::{collections::vec::Vec, Bump}; +use roc_mono::layout::MultimorphicNames; use std::mem::size_of; use roc_collections::all::MutSet; @@ -241,6 +242,7 @@ pub async fn entrypoint_from_js(src: String) -> Result { .map_err(|js| format!("{:?}", js))?; let app = WasmReplApp { arena }; + let mut multimorphic_names = MultimorphicNames::default(); // Run the app and transform the result value to an AST `Expr` // Restore type constructor names, and other user-facing info that was erased during compilation. @@ -251,7 +253,7 @@ pub async fn entrypoint_from_js(src: String) -> Result { main_fn_layout, content, &subs, - module_id, interns.all_ident_ids.get_mut(&module_id).unwrap(), + &mut multimorphic_names, target_info, ); From eb400590cca23e9fbca9730c021171876e920718 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Tue, 28 Jun 2022 17:38:12 -0400 Subject: [PATCH 14/66] Clippy --- crates/compiler/unify/src/unify.rs | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/crates/compiler/unify/src/unify.rs b/crates/compiler/unify/src/unify.rs index 58c0651208..419ede2897 100644 --- a/crates/compiler/unify/src/unify.rs +++ b/crates/compiler/unify/src/unify.rs @@ -981,16 +981,18 @@ impl Default for Sides { } } +struct SeparatedUnionLambdas { + only_in_left: Vec<(Symbol, VariableSubsSlice)>, + only_in_right: Vec<(Symbol, VariableSubsSlice)>, + joined: Vec<(Symbol, VariableSubsSlice)>, +} + fn separate_union_lambdas( subs: &mut Subs, pool: &mut Pool, fields1: UnionLambdas, fields2: UnionLambdas, -) -> ( - Vec<(Symbol, VariableSubsSlice)>, - Vec<(Symbol, VariableSubsSlice)>, - Vec<(Symbol, VariableSubsSlice)>, -) { +) -> SeparatedUnionLambdas { debug_assert!(fields1.is_sorted(subs)); debug_assert!(fields2.is_sorted(subs)); @@ -1086,7 +1088,11 @@ fn separate_union_lambdas( } } - (only_in_left, only_in_right, joined) + SeparatedUnionLambdas { + only_in_left, + only_in_right, + joined, + } } fn unify_lambda_set_help( @@ -1117,22 +1123,26 @@ fn unify_lambda_set_help( "Recursion var is present, but it doesn't have a recursive content!" ); - let (only_in_1, only_in_2, in_both) = separate_union_lambdas(subs, pool, solved1, solved2); + let SeparatedUnionLambdas { + only_in_left, + only_in_right, + joined, + } = separate_union_lambdas(subs, pool, solved1, solved2); - let all_lambdas = in_both + let all_lambdas = joined .into_iter() .map(|(name, slice)| (name, subs.get_subs_slice(slice).to_vec())); let all_lambdas = merge_sorted_preserving_duplicates( all_lambdas, - only_in_1.into_iter().map(|(name, subs_slice)| { + only_in_left.into_iter().map(|(name, subs_slice)| { let vec = subs.get_subs_slice(subs_slice).to_vec(); (name, vec) }), ); let all_lambdas = merge_sorted_preserving_duplicates( all_lambdas, - only_in_2.into_iter().map(|(name, subs_slice)| { + only_in_right.into_iter().map(|(name, subs_slice)| { let vec = subs.get_subs_slice(subs_slice).to_vec(); (name, vec) }), From 9714376b64b97901d467d0ad746ee60ad33fb1c9 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Tue, 28 Jun 2022 17:51:40 -0400 Subject: [PATCH 15/66] Use one method for mutex --- crates/compiler/mono/src/layout.rs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/crates/compiler/mono/src/layout.rs b/crates/compiler/mono/src/layout.rs index 204ed4127b..7fb3915273 100644 --- a/crates/compiler/mono/src/layout.rs +++ b/crates/compiler/mono/src/layout.rs @@ -845,12 +845,12 @@ impl Clone for MultimorphicNames { } impl MultimorphicNames { - fn get<'b>(&self, name: Symbol, captures_layouts: &'b [Layout<'b>]) -> Option { - self.0.lock().unwrap().get(name, captures_layouts) - } - - fn insert<'b>(&mut self, name: Symbol, captures_layouts: &'b [Layout<'b>]) -> Symbol { - self.0.lock().unwrap().insert(name, captures_layouts) + fn get_or_insert<'b>(&self, name: Symbol, captures_layouts: &'b [Layout<'b>]) -> Symbol { + let mut table = self.0.lock().unwrap(); + match table.get(name, captures_layouts) { + Some(symbol) => symbol, + None => table.insert(name, captures_layouts), + } } /// Assumes there is only one clone still alive. @@ -1178,10 +1178,7 @@ impl<'a> LambdaSet<'a> { }; let lambda_name = if is_multimorphic { - let alias = match multimorphic_names.get(*function_symbol, arguments) { - Some(alias) => alias, - None => multimorphic_names.insert(*function_symbol, arguments), - }; + let alias = multimorphic_names.get_or_insert(*function_symbol, arguments); has_multimorphic = true; From 1903ce4db9967c7659a9d3684d0c379e6c759d72 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Tue, 28 Jun 2022 17:55:08 -0400 Subject: [PATCH 16/66] Remove LambdaName::from_non_multimorphic --- crates/compiler/mono/src/code_gen_help/mod.rs | 2 +- crates/compiler/mono/src/ir.rs | 45 ++++++------------- crates/compiler/mono/src/layout.rs | 7 +-- 3 files changed, 16 insertions(+), 38 deletions(-) diff --git a/crates/compiler/mono/src/code_gen_help/mod.rs b/crates/compiler/mono/src/code_gen_help/mod.rs index 02dbbe0c2f..1888bbd809 100644 --- a/crates/compiler/mono/src/code_gen_help/mod.rs +++ b/crates/compiler/mono/src/code_gen_help/mod.rs @@ -343,7 +343,7 @@ impl<'a> CodeGenHelp<'a> { }; self.specializations[spec_index].proc = Some(Proc { - name: LambdaName::from_non_multimorphic(proc_symbol), + name: LambdaName::only_receiver(proc_symbol), args, body, closure_data_layout: None, diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index 3eba5975ed..cc6b817ba8 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -2847,7 +2847,7 @@ fn generate_runtime_error_function<'a>( }; Proc { - name: LambdaName::from_non_multimorphic(name), + name: LambdaName::only_receiver(name), args, body: runtime_error, closure_data_layout: None, @@ -2949,7 +2949,7 @@ fn specialize_external<'a>( ); let proc = Proc { - name: LambdaName::from_non_multimorphic(name), + name: LambdaName::only_receiver(name), args: proc_arguments.into_bump_slice(), body, closure_data_layout: None, @@ -3611,8 +3611,7 @@ fn specialize_naked_symbol<'a>( opt_fn_var, symbol, result, - // The function symbol is the only receiver, so not multimorphic - LambdaName::from_non_multimorphic(original), + LambdaName::only_receiver(original), ) } @@ -4033,9 +4032,7 @@ pub fn with_hole<'a>( Some(variable), symbol, stmt, - // If it's a known function (and not an expression) there can only be - // one receiver, so not multimorphic - LambdaName::from_non_multimorphic(symbol), + LambdaName::only_receiver(symbol), ); } Field::Field(field) => { @@ -4434,8 +4431,7 @@ pub fn with_hole<'a>( match procs.insert_anonymous( env, - // Accessors never capture so they can't be multimorphic - LambdaName::from_non_multimorphic(name), + LambdaName::only_receiver(name), function_type, arguments, *loc_body, @@ -4636,9 +4632,9 @@ pub fn with_hole<'a>( specialized_structure_sym, stmt, // This is only hit if somehow this field is an alias - // to an ability member, but ability members can't capture, so - // it must be necessary non-multimorphic - LambdaName::from_non_multimorphic(structure), + // to an ability member, and ability members specialize to + // exactly one receiver + LambdaName::only_receiver(structure), ); } } @@ -4743,9 +4739,7 @@ pub fn with_hole<'a>( env, procs, fn_var, - // THEORY: calls to a known name can never be multimorphic, because they - // only have one receiver - LambdaName::from_non_multimorphic(proc_name), + LambdaName::only_receiver(proc_name), loc_args, layout_cache, assigned, @@ -4760,8 +4754,7 @@ pub fn with_hole<'a>( env, procs, fn_var, - // Ability members never capture, can't be multimorphic - LambdaName::from_non_multimorphic(specialization_proc_name), + LambdaName::only_receiver(specialization_proc_name), loc_args, layout_cache, assigned, @@ -4899,7 +4892,7 @@ pub fn with_hole<'a>( let resolved_proc = match resolved_proc { Resolved::Specialization(symbol) => { // Ability members never capture, they cannot be multimorphic - LambdaName::from_non_multimorphic(symbol) + LambdaName::only_receiver(symbol) } Resolved::NeedsGenerated => { todo_abilities!("Generate impls for structural types") @@ -5696,7 +5689,7 @@ fn tag_union_to_function<'a>( }); // Lambda does not capture anything, can't be multimorphic - let lambda_name = LambdaName::from_non_multimorphic(proc_symbol); + let lambda_name = LambdaName::only_receiver(proc_symbol); let inserted = procs.insert_anonymous( env, @@ -7191,12 +7184,7 @@ where // specialized, and wrap the original in a function pointer. let mut result = result; for (_, (variable, left)) in needed_specializations_of_left { - add_needed_external( - procs, - env, - variable, - LambdaName::from_non_multimorphic(right), - ); + add_needed_external(procs, env, variable, LambdaName::thunk(right)); let res_layout = layout_cache.from_var(env.arena, variable, env.subs, env.multimorphic_names); @@ -7208,12 +7196,7 @@ where } else if env.is_imported_symbol(right) { // if this is an imported symbol, then we must make sure it is // specialized, and wrap the original in a function pointer. - add_needed_external( - procs, - env, - variable, - LambdaName::from_non_multimorphic(right), - ); + add_needed_external(procs, env, variable, LambdaName::only_receiver(right)); // then we must construct its closure; since imported symbols have no closure, we use the empty struct let_empty_struct(left, env.arena.alloc(result)) diff --git a/crates/compiler/mono/src/layout.rs b/crates/compiler/mono/src/layout.rs index 7fb3915273..26368cd642 100644 --- a/crates/compiler/mono/src/layout.rs +++ b/crates/compiler/mono/src/layout.rs @@ -915,11 +915,6 @@ impl LambdaName { matches!(self.0, LambdaNameInner::Multimorphic { .. }) } - #[inline(always)] - pub fn from_non_multimorphic(name: Symbol) -> Self { - Self(LambdaNameInner::Name(name)) - } - #[inline(always)] pub fn thunk(name: Symbol) -> Self { Self(LambdaNameInner::Name(name)) @@ -3436,7 +3431,7 @@ mod test { #[test] fn width_and_alignment_union_empty_struct() { let lambda_set = LambdaSet { - set: &[(LambdaName::from_non_multimorphic(Symbol::LIST_MAP), &[])], + set: &[(LambdaName::only_receiver(Symbol::LIST_MAP), &[])], representation: &Layout::UNIT, }; From 806e2f50966b83a14c883bdfefe51832a50bbe43 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Tue, 28 Jun 2022 18:14:09 -0400 Subject: [PATCH 17/66] Make sure to properly preserve lambda set ordering --- crates/compiler/test_mono/src/tests.rs | 52 +++++++++++++++++++++++ crates/compiler/types/src/subs.rs | 17 ++++++-- crates/compiler/unify/src/unify.rs | 59 +++++++++++++++++++++----- 3 files changed, 115 insertions(+), 13 deletions(-) diff --git a/crates/compiler/test_mono/src/tests.rs b/crates/compiler/test_mono/src/tests.rs index 62a0ba261f..3cd2eb1848 100644 --- a/crates/compiler/test_mono/src/tests.rs +++ b/crates/compiler/test_mono/src/tests.rs @@ -1548,3 +1548,55 @@ fn multimorphic_lambda_set_capture() { "# ) } + +#[mono_test] +fn multimorphic_lambdas_with_other_lambda_capture() { + indoc!( + r#" + capture : a -> ({} -> Str) + capture = \val -> + \{} -> + when val is + _ -> "" + + capture2 = \val -> \{} -> "\(val)" + + x : [A, B, C] + x = A + + fun = + when x is + A -> capture {} + B -> capture2 "foo" + C -> capture 1u64 + + fun {} + "# + ) +} + +#[mono_test] +fn multimorphic_lambdas_with_non_capturing_function() { + indoc!( + r#" + capture : a -> ({} -> Str) + capture = \val -> + \{} -> + when val is + _ -> "" + + triv = \{} -> "" + + x : [A, B, C] + x = A + + fun = + when x is + A -> capture {} + B -> triv + C -> capture 1u64 + + fun {} + "# + ) +} diff --git a/crates/compiler/types/src/subs.rs b/crates/compiler/types/src/subs.rs index a4b99eba78..6707a96399 100644 --- a/crates/compiler/types/src/subs.rs +++ b/crates/compiler/types/src/subs.rs @@ -2650,10 +2650,21 @@ impl UnionLabels where L: Label + Ord, { - /// Checks if the union of labels is sorted by label. - /// Duplicates *are* admitted, since this represents a lambda set, in which we may have - /// duplicate lambda captures, if those lambda captures have different representations! + /// Checks if the union of labels is sorted by label, without duplicates. pub fn is_sorted(&self, subs: &Subs) -> bool { + let mut iter = self.iter_from_subs(subs).peekable(); + while let Some((before, _)) = iter.next() { + if let Some((after, _)) = iter.peek() { + if before >= after { + return false; + } + } + } + true + } + + /// Checks if the union of labels is sorted by label, without duplicates. + pub fn is_sorted_allow_duplicates(&self, subs: &Subs) -> bool { let mut iter = self.iter_from_subs(subs).peekable(); while let Some((before, _)) = iter.next() { if let Some((after, _)) = iter.peek() { diff --git a/crates/compiler/unify/src/unify.rs b/crates/compiler/unify/src/unify.rs index 419ede2897..2fa46947aa 100644 --- a/crates/compiler/unify/src/unify.rs +++ b/crates/compiler/unify/src/unify.rs @@ -993,8 +993,16 @@ fn separate_union_lambdas( fields1: UnionLambdas, fields2: UnionLambdas, ) -> SeparatedUnionLambdas { - debug_assert!(fields1.is_sorted(subs)); - debug_assert!(fields2.is_sorted(subs)); + debug_assert!( + fields1.is_sorted_allow_duplicates(subs), + "not sorted: {:?}", + fields1.iter_from_subs(subs).collect::>() + ); + debug_assert!( + fields2.is_sorted_allow_duplicates(subs), + "not sorted: {:?}", + fields2.iter_from_subs(subs).collect::>() + ); // lambda names -> (the captures for that lambda on the left side, the captures for that lambda on the right side) // e.g. [[F1 U8], [F1 U64], [F2 a]] ~ [[F1 Str], [F2 Str]] becomes @@ -1002,13 +1010,43 @@ fn separate_union_lambdas( // F2 -> { left: [ [a] ], right: [ [Str] ] } let mut buckets: VecMap = VecMap::with_capacity(fields1.len() + fields2.len()); - for (sym, vars) in fields1.iter_all() { - let bucket = buckets.get_or_insert(subs[sym], Sides::default); - bucket.left.push((subs[sym], subs[vars])); - } - for (sym, vars) in fields2.iter_all() { - let bucket = buckets.get_or_insert(subs[sym], Sides::default); - bucket.right.push((subs[sym], subs[vars])); + let (mut fields_left, mut fields_right) = ( + fields1.iter_all().into_iter().peekable(), + fields2.iter_all().into_iter().peekable(), + ); + + loop { + use std::cmp::Ordering; + + let ord = match (fields_left.peek(), fields_right.peek()) { + (Some((l, _)), Some((r, _))) => Some((subs[*l]).cmp(&subs[*r])), + (Some(_), None) => Some(Ordering::Less), + (None, Some(_)) => Some(Ordering::Greater), + (None, None) => None, + }; + + match ord { + Some(Ordering::Less) => { + let (sym, vars) = fields_left.next().unwrap(); + let bucket = buckets.get_or_insert(subs[sym], Sides::default); + bucket.left.push((subs[sym], subs[vars])); + } + Some(Ordering::Greater) => { + let (sym, vars) = fields_right.next().unwrap(); + let bucket = buckets.get_or_insert(subs[sym], Sides::default); + bucket.right.push((subs[sym], subs[vars])); + } + Some(Ordering::Equal) => { + let (sym, left_vars) = fields_left.next().unwrap(); + let (_sym, right_vars) = fields_right.next().unwrap(); + debug_assert_eq!(subs[sym], subs[_sym]); + + let bucket = buckets.get_or_insert(subs[sym], Sides::default); + bucket.left.push((subs[sym], subs[left_vars])); + bucket.right.push((subs[sym], subs[right_vars])); + } + None => break, + } } let mut only_in_left = Vec::with_capacity(fields1.len()); @@ -1073,7 +1111,8 @@ fn separate_union_lambdas( joined.push((lambda_name, left_slice)); // Remove the right slice, it unifies with the left so this is its unique // unification. - right.swap_remove(right_index); + // Remove in-place so that the order is preserved. + right.remove(right_index); continue 'next_left; } From b69d538ea0fa6b2d3e4a3b4814ec9d2685eae8e3 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Tue, 28 Jun 2022 18:14:31 -0400 Subject: [PATCH 18/66] Add mono test cases --- ...ic_lambdas_with_non_capturing_function.txt | 59 ++++++++++++++++ ...phic_lambdas_with_other_lambda_capture.txt | 68 +++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 crates/compiler/test_mono/generated/multimorphic_lambdas_with_non_capturing_function.txt create mode 100644 crates/compiler/test_mono/generated/multimorphic_lambdas_with_other_lambda_capture.txt diff --git a/crates/compiler/test_mono/generated/multimorphic_lambdas_with_non_capturing_function.txt b/crates/compiler/test_mono/generated/multimorphic_lambdas_with_non_capturing_function.txt new file mode 100644 index 0000000000..3212379ff4 --- /dev/null +++ b/crates/compiler/test_mono/generated/multimorphic_lambdas_with_non_capturing_function.txt @@ -0,0 +1,59 @@ +procedure #Multimorphic.0 (Test.28, #Attr.12): + let Test.5 : U64 = UnionAtIndex (Id 0) (Index 0) #Attr.12; + let Test.30 : Str = ""; + ret Test.30; + +procedure #Multimorphic.1 (Test.20, #Attr.12): + let Test.5 : {} = UnionAtIndex (Id 1) (Index 0) #Attr.12; + let Test.22 : Str = ""; + ret Test.22; + +procedure Test.1 (Test.5): + let Test.19 : [C U64, C {}, C ] = ClosureTag(#Multimorphic.1) Test.5; + ret Test.19; + +procedure Test.1 (Test.5): + let Test.27 : [C U64, C {}, C ] = ClosureTag(#Multimorphic.0) Test.5; + ret Test.27; + +procedure Test.2 (Test.8): + let Test.24 : Str = ""; + ret Test.24; + +procedure Test.0 (): + let Test.3 : U8 = 0u8; + joinpoint Test.16 Test.4: + let Test.10 : {} = Struct {}; + let Test.11 : U8 = GetTagId Test.4; + joinpoint Test.12 Test.9: + ret Test.9; + in + switch Test.11: + case 0: + let Test.13 : Str = CallByName #Multimorphic.0 Test.10 Test.4; + jump Test.12 Test.13; + + case 1: + let Test.14 : Str = CallByName #Multimorphic.1 Test.10 Test.4; + jump Test.12 Test.14; + + default: + let Test.15 : Str = CallByName Test.2 Test.10; + jump Test.12 Test.15; + + in + switch Test.3: + case 0: + let Test.18 : {} = Struct {}; + let Test.17 : [C U64, C {}, C ] = CallByName Test.1 Test.18; + jump Test.16 Test.17; + + case 1: + let Test.2 : [C U64, C {}, C ] = ClosureTag(Test.2) ; + jump Test.16 Test.2; + + default: + let Test.26 : U64 = 1i64; + let Test.25 : [C U64, C {}, C ] = CallByName Test.1 Test.26; + jump Test.16 Test.25; + diff --git a/crates/compiler/test_mono/generated/multimorphic_lambdas_with_other_lambda_capture.txt b/crates/compiler/test_mono/generated/multimorphic_lambdas_with_other_lambda_capture.txt new file mode 100644 index 0000000000..4477e9c8d4 --- /dev/null +++ b/crates/compiler/test_mono/generated/multimorphic_lambdas_with_other_lambda_capture.txt @@ -0,0 +1,68 @@ +procedure #Multimorphic.0 (Test.33, #Attr.12): + let Test.5 : U64 = UnionAtIndex (Id 0) (Index 0) #Attr.12; + dec #Attr.12; + let Test.35 : Str = ""; + ret Test.35; + +procedure #Multimorphic.1 (Test.21, #Attr.12): + let Test.5 : {} = UnionAtIndex (Id 1) (Index 0) #Attr.12; + dec #Attr.12; + let Test.23 : Str = ""; + ret Test.23; + +procedure Test.1 (Test.5): + let Test.20 : [C U64, C {}, C Str] = ClosureTag(#Multimorphic.1) Test.5; + ret Test.20; + +procedure Test.1 (Test.5): + let Test.32 : [C U64, C {}, C Str] = ClosureTag(#Multimorphic.0) Test.5; + ret Test.32; + +procedure Test.2 (Test.7): + let Test.26 : [C U64, C {}, C Str] = ClosureTag(Test.8) Test.7; + ret Test.26; + +procedure Test.8 (Test.27, #Attr.12): + let Test.7 : Str = UnionAtIndex (Id 2) (Index 0) #Attr.12; + inc Test.7; + dec #Attr.12; + ret Test.7; + +procedure Test.0 (): + let Test.3 : U8 = 0u8; + joinpoint Test.17 Test.4: + let Test.11 : {} = Struct {}; + let Test.12 : U8 = GetTagId Test.4; + joinpoint Test.13 Test.10: + ret Test.10; + in + switch Test.12: + case 0: + let Test.14 : Str = CallByName #Multimorphic.0 Test.11 Test.4; + jump Test.13 Test.14; + + case 1: + let Test.15 : Str = CallByName #Multimorphic.1 Test.11 Test.4; + jump Test.13 Test.15; + + default: + let Test.16 : Str = CallByName Test.8 Test.11 Test.4; + jump Test.13 Test.16; + + in + switch Test.3: + case 0: + let Test.19 : {} = Struct {}; + let Test.18 : [C U64, C {}, C Str] = CallByName Test.1 Test.19; + jump Test.17 Test.18; + + case 1: + let Test.25 : Str = "foo"; + let Test.24 : [C U64, C {}, C Str] = CallByName Test.2 Test.25; + jump Test.17 Test.24; + + default: + let Test.31 : U64 = 1i64; + let Test.30 : [C U64, C {}, C Str] = CallByName Test.1 Test.31; + jump Test.17 Test.30; + From 28c1cf46a3b090ff70d4ae92368bc30870187171 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Tue, 28 Jun 2022 18:28:15 -0400 Subject: [PATCH 19/66] Multimorphic lambdas capture another lambda --- crates/compiler/mono/src/ir.rs | 117 +++++++++--------- crates/compiler/mono/src/layout.rs | 4 +- crates/compiler/solve/tests/solve_expr.rs | 3 +- ...bdas_have_captured_function_in_closure.txt | 85 +++++++++++++ crates/compiler/test_mono/src/tests.rs | 29 +++++ 5 files changed, 175 insertions(+), 63 deletions(-) create mode 100644 crates/compiler/test_mono/generated/multimorphic_lambdas_have_captured_function_in_closure.txt diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index cc6b817ba8..ea76d72427 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -3611,7 +3611,7 @@ fn specialize_naked_symbol<'a>( opt_fn_var, symbol, result, - LambdaName::only_receiver(original), + original, ) } @@ -4032,7 +4032,7 @@ pub fn with_hole<'a>( Some(variable), symbol, stmt, - LambdaName::only_receiver(symbol), + symbol, ); } Field::Field(field) => { @@ -4631,10 +4631,7 @@ pub fn with_hole<'a>( Some(record_var), specialized_structure_sym, stmt, - // This is only hit if somehow this field is an alias - // to an ability member, and ability members specialize to - // exactly one receiver - LambdaName::only_receiver(structure), + structure, ); } } @@ -7271,12 +7268,12 @@ fn specialize_symbol<'a>( arg_var: Option, symbol: Symbol, result: Stmt<'a>, - original: LambdaName, + original: Symbol, ) -> Stmt<'a> { - match procs.get_partial_proc(original.source_name()) { + match procs.get_partial_proc(original) { None => { match arg_var { - Some(arg_var) if env.is_imported_symbol(original.source_name()) => { + Some(arg_var) if env.is_imported_symbol(original) => { let raw = match layout_cache.raw_from_var( env.arena, arg_var, @@ -7287,7 +7284,7 @@ fn specialize_symbol<'a>( Err(e) => return_on_layout_error_help!(env, e), }; - if procs.is_imported_module_thunk(original.source_name()) { + if procs.is_imported_module_thunk(original) { let layout = match raw { RawFunctionLayout::ZeroArgumentThunk(layout) => layout, RawFunctionLayout::Function(_, lambda_set, _) => { @@ -7301,24 +7298,20 @@ fn specialize_symbol<'a>( procs.insert_passed_by_name( env, arg_var, - original, + LambdaName::thunk(original), top_level, layout_cache, ); - force_thunk( - env, - original.call_name(), - layout, - symbol, - env.arena.alloc(result), - ) + force_thunk(env, original, layout, symbol, env.arena.alloc(result)) } else { let top_level = ProcLayout::from_raw(env.arena, raw); procs.insert_passed_by_name( env, arg_var, - original, + // Imported symbol, so it must have exactly one receiver (since + // top-levels can't capture) + LambdaName::only_receiver(original), top_level, layout_cache, ); @@ -7330,7 +7323,7 @@ fn specialize_symbol<'a>( _ => { // danger: a foreign symbol may not be specialized! debug_assert!( - !env.is_imported_symbol(original.source_name()), + !env.is_imported_symbol(original), "symbol {:?} while processing module {:?}", original, (env.home, &arg_var), @@ -7362,17 +7355,6 @@ fn specialize_symbol<'a>( let function_ptr_layout = ProcLayout::from_raw(env.arena, res_layout); if captures { - // this is a closure by capture, meaning it itself captures local variables. - procs.insert_passed_by_name( - env, - arg_var, - original, - function_ptr_layout, - layout_cache, - ); - - let closure_data = symbol; - let symbols = match captured { CapturedSymbols::Captured(captured_symbols) => { Vec::from_iter_in(captured_symbols.iter(), env.arena) @@ -7381,15 +7363,34 @@ fn specialize_symbol<'a>( CapturedSymbols::None => unreachable!(), }; + let lambda_name = find_lambda_name( + env, + layout_cache, + lambda_set, + original, + symbols.iter().copied(), + ); + + // this is a closure by capture, meaning it itself captures local variables. + procs.insert_passed_by_name( + env, + arg_var, + lambda_name, + function_ptr_layout, + layout_cache, + ); + + let closure_data = symbol; + construct_closure_data( env, lambda_set, - original, + lambda_name, symbols.iter().copied(), closure_data, env.arena.alloc(result), ) - } else if procs.is_module_thunk(original.source_name()) { + } else if procs.is_module_thunk(original) { // this is a 0-argument thunk // TODO suspicious @@ -7400,38 +7401,35 @@ fn specialize_symbol<'a>( procs.insert_passed_by_name( env, arg_var, - original, + LambdaName::thunk(original), top_level, layout_cache, ); - force_thunk( - env, - original.call_name(), - layout, - symbol, - env.arena.alloc(result), - ) + force_thunk(env, original, layout, symbol, env.arena.alloc(result)) } else { + // even though this function may not itself capture, + // unification may still cause it to have an extra argument + let lambda_name = + find_lambda_name(env, layout_cache, lambda_set, original, &[]); + + debug_assert!( + !lambda_name.is_multimorphic(), + "no captures, but somehow this symbol is multimorphic" + ); + procs.insert_passed_by_name( env, arg_var, - original, + lambda_name, function_ptr_layout, layout_cache, ); - // even though this function may not itself capture, - // unification may still cause it to have an extra argument - debug_assert!( - !original.is_multimorphic(), - "no captures, but somehow this symbol is multimorphic" - ); - construct_closure_data( env, lambda_set, - original, + lambda_name, &[], symbol, env.arena.alloc(result), @@ -7441,15 +7439,15 @@ fn specialize_symbol<'a>( RawFunctionLayout::ZeroArgumentThunk(ret_layout) => { // this is a 0-argument thunk let top_level = ProcLayout::new(env.arena, &[], ret_layout); - procs.insert_passed_by_name(env, arg_var, original, top_level, layout_cache); - - force_thunk( + procs.insert_passed_by_name( env, - original.call_name(), - ret_layout, - symbol, - env.arena.alloc(result), - ) + arg_var, + LambdaName::thunk(original), + top_level, + layout_cache, + ); + + force_thunk(env, original, ret_layout, symbol, env.arena.alloc(result)) } } } @@ -7476,8 +7474,7 @@ fn assign_to_symbol<'a>( Some(arg_var), symbol, result, - // The function symbol is the only receiver - LambdaName::only_receiver(original), + original, ) } Value(_symbol) => result, diff --git a/crates/compiler/mono/src/layout.rs b/crates/compiler/mono/src/layout.rs index 26368cd642..5841dfd167 100644 --- a/crates/compiler/mono/src/layout.rs +++ b/crates/compiler/mono/src/layout.rs @@ -986,7 +986,9 @@ impl<'a> LambdaSet<'a> { ) -> ClosureRepresentation<'a> { debug_assert!( self.set.iter().any(|(s, _)| *s == lambda_name), - "lambda not in set" + "lambda {:?} not in set {:#?}", + lambda_name, + self.set, ); let comparator = diff --git a/crates/compiler/solve/tests/solve_expr.rs b/crates/compiler/solve/tests/solve_expr.rs index a0200c0dd9..9b3337e3bb 100644 --- a/crates/compiler/solve/tests/solve_expr.rs +++ b/crates/compiler/solve/tests/solve_expr.rs @@ -6998,10 +6998,9 @@ mod solve_expr { after : Lazy a, (a -> Lazy b) -> Lazy b after = \effect, map -> - thunk = \{} -> + \{} -> when map (effect {}) is b -> b {} - thunk f = \_ -> \_ -> "" g = \{ s1 } -> \_ -> s1 diff --git a/crates/compiler/test_mono/generated/multimorphic_lambdas_have_captured_function_in_closure.txt b/crates/compiler/test_mono/generated/multimorphic_lambdas_have_captured_function_in_closure.txt new file mode 100644 index 0000000000..644770023b --- /dev/null +++ b/crates/compiler/test_mono/generated/multimorphic_lambdas_have_captured_function_in_closure.txt @@ -0,0 +1,85 @@ +procedure #Multimorphic.0 (Test.29, #Attr.12): + let Test.8 : {} = UnionAtIndex (Id 0) (Index 1) #Attr.12; + let Test.7 : {} = UnionAtIndex (Id 0) (Index 0) #Attr.12; + let Test.49 : {} = Struct {}; + let Test.48 : Str = CallByName Test.16 Test.49; + let Test.45 : {Str} = CallByName Test.4 Test.48; + let Test.47 : {} = Struct {}; + let Test.46 : Str = CallByName Test.13 Test.47 Test.45; + ret Test.46; + +procedure #Multimorphic.1 (Test.29, #Attr.12): + let Test.8 : {} = UnionAtIndex (Id 1) (Index 1) #Attr.12; + let Test.7 : {} = UnionAtIndex (Id 1) (Index 0) #Attr.12; + let Test.35 : {} = Struct {}; + let Test.34 : Str = CallByName Test.15 Test.35; + let Test.31 : {} = CallByName Test.3 Test.34; + dec Test.34; + let Test.33 : {} = Struct {}; + let Test.32 : Str = CallByName Test.11 Test.33; + ret Test.32; + +procedure Test.11 (Test.37): + let Test.38 : Str = ""; + ret Test.38; + +procedure Test.13 (Test.51, #Attr.12): + let Test.12 : Str = StructAtIndex 0 #Attr.12; + inc Test.12; + dec #Attr.12; + ret Test.12; + +procedure Test.15 (Test.39): + let Test.40 : Str = ""; + ret Test.40; + +procedure Test.16 (Test.54): + let Test.56 : Str = "s1"; + ret Test.56; + +procedure Test.2 (Test.7, Test.8): + let Test.9 : [C {} {}, C {} {}] = ClosureTag(#Multimorphic.0) Test.7 Test.8; + ret Test.9; + +procedure Test.2 (Test.7, Test.8): + let Test.9 : [C {} {}, C {} {}] = ClosureTag(#Multimorphic.1) Test.7 Test.8; + ret Test.9; + +procedure Test.3 (Test.17): + let Test.36 : {} = Struct {}; + ret Test.36; + +procedure Test.4 (Test.18): + let Test.50 : {Str} = Struct {Test.18}; + ret Test.50; + +procedure Test.0 (): + let Test.5 : Int1 = true; + joinpoint Test.25 Test.6: + let Test.20 : {} = Struct {}; + let Test.21 : U8 = GetTagId Test.6; + joinpoint Test.22 Test.19: + ret Test.19; + in + switch Test.21: + case 0: + let Test.23 : Str = CallByName #Multimorphic.0 Test.20 Test.6; + jump Test.22 Test.23; + + default: + let Test.24 : Str = CallByName #Multimorphic.1 Test.20 Test.6; + jump Test.22 Test.24; + + in + let Test.57 : Int1 = true; + let Test.58 : Int1 = lowlevel Eq Test.57 Test.5; + if Test.58 then + let Test.27 : {} = Struct {}; + let Test.28 : {} = Struct {}; + let Test.26 : [C {} {}, C {} {}] = CallByName Test.2 Test.27 Test.28; + jump Test.25 Test.26; + else + let Test.42 : {} = Struct {}; + let Test.43 : {} = Struct {}; + let Test.41 : [C {} {}, C {} {}] = CallByName Test.2 Test.42 Test.43; + jump Test.25 Test.41; diff --git a/crates/compiler/test_mono/src/tests.rs b/crates/compiler/test_mono/src/tests.rs index 3cd2eb1848..b189a0e3a4 100644 --- a/crates/compiler/test_mono/src/tests.rs +++ b/crates/compiler/test_mono/src/tests.rs @@ -1600,3 +1600,32 @@ fn multimorphic_lambdas_with_non_capturing_function() { "# ) } + +#[mono_test] +fn multimorphic_lambdas_have_captured_function_in_closure() { + indoc!( + r#" + Lazy a : {} -> a + + after : Lazy a, (a -> Lazy b) -> Lazy b + after = \effect, map -> + thunk = \{} -> + when map (effect {}) is + b -> b {} + thunk + + f = \_ -> \_ -> "" + g = \{ s1 } -> \_ -> s1 + + x : [True, False] + x = True + + fun = + when x is + True -> after (\{} -> "") f + False -> after (\{} -> {s1: "s1"}) g + + fun {} + "# + ) +} From fe318488e275617d5367fac8819b653ceb56d4db Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Tue, 28 Jun 2022 18:45:23 -0400 Subject: [PATCH 20/66] Add more multimorphic gen tests --- .../compiler/test_gen/src/gen_primitives.rs | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/crates/compiler/test_gen/src/gen_primitives.rs b/crates/compiler/test_gen/src/gen_primitives.rs index a3c28f8edf..9be5b1ca43 100644 --- a/crates/compiler/test_gen/src/gen_primitives.rs +++ b/crates/compiler/test_gen/src/gen_primitives.rs @@ -3525,3 +3525,103 @@ fn multimorphic_lambda_set_u64_vs_u8_capture() { RocStr ) } + +#[test] +#[cfg(any(feature = "gen-llvm"))] +fn multimorphic_lambdas_with_other_lambda_capture() { + assert_evals_to!( + indoc!( + r#" + capture : _ -> ({} -> Str) + capture = \val -> + \{} -> + Num.toStr val + + capture2 = \val -> \{} -> val + + f = \x -> + g = + when x is + A -> capture 11u8 + B -> capture2 "lisa" + C -> capture 187128u64 + g {} + + {a: f A, b: f B, c: f C} + "# + ), + ( + RocStr::from("11"), + RocStr::from("lisa"), + RocStr::from("187128") + ), + (RocStr, RocStr, RocStr) + ) +} + +#[test] +#[cfg(any(feature = "gen-llvm"))] +fn multimorphic_lambdas_with_non_capturing_function() { + assert_evals_to!( + indoc!( + r#" + capture : _ -> ({} -> Str) + capture = \val -> + \{} -> + Num.toStr val + + triv = \{} -> "triv" + + f = \x -> + g = + when x is + A -> capture 11u8 + B -> triv + C -> capture 187128u64 + g {} + + {a: f A, b: f B, c: f C} + "# + ), + ( + RocStr::from("11"), + RocStr::from("triv"), + RocStr::from("187128") + ), + (RocStr, RocStr, RocStr) + ) +} + +#[test] +#[cfg(any(feature = "gen-llvm"))] +fn multimorphic_lambdas_have_captured_function_in_closure() { + assert_evals_to!( + indoc!( + r#" + Lazy a : {} -> a + + after : Lazy a, (a -> Lazy b) -> Lazy b + after = \effect, map -> + thunk = \{} -> + when map (effect {}) is + b -> b {} + thunk + + f = \_ -> \_ -> "" + g = \{ s1 } -> \_ -> s1 + + x : [True, False] + x = False + + fun = + when x is + True -> after (\{} -> "") f + False -> after (\{} -> {s1: "s1"}) g + + fun {} + "# + ), + RocStr::from("s1"), + RocStr + ) +} From 98b310a604ade2e86b4962ac0e44faf918139bea Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Tue, 28 Jun 2022 18:46:28 -0400 Subject: [PATCH 21/66] Fix wasm test gen --- crates/compiler/test_gen/src/wasm_linking.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/compiler/test_gen/src/wasm_linking.rs b/crates/compiler/test_gen/src/wasm_linking.rs index e911bb3c7a..0a2869880f 100644 --- a/crates/compiler/test_gen/src/wasm_linking.rs +++ b/crates/compiler/test_gen/src/wasm_linking.rs @@ -18,7 +18,7 @@ use roc_mono::ir::{ Call, CallType, Expr, HostExposedLayouts, Literal, Proc, ProcLayout, SelfRecursive, Stmt, UpdateModeId, }; -use roc_mono::layout::{Builtin, Layout}; +use roc_mono::layout::{Builtin, LambdaName, Layout}; const LINKING_TEST_HOST_WASM: &str = "build/wasm_linking_test_host.wasm"; const LINKING_TEST_HOST_NATIVE: &str = "build/wasm_linking_test_host"; @@ -110,7 +110,7 @@ fn build_app_mono<'a>( ); let proc = Proc { - name: app_proc, + name: LambdaName::only_receiver(app_proc), args: &[], body, closure_data_layout: None, From ffa2ba1043931804ae4fc5c545645c625315b0be Mon Sep 17 00:00:00 2001 From: ayazhafiz Date: Tue, 28 Jun 2022 21:54:51 -0400 Subject: [PATCH 22/66] Fix solve tes --- crates/compiler/derive_key/Cargo.toml | 2 +- crates/compiler/solve/tests/solve_expr.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/compiler/derive_key/Cargo.toml b/crates/compiler/derive_key/Cargo.toml index 53551245e0..36f154b059 100644 --- a/crates/compiler/derive_key/Cargo.toml +++ b/crates/compiler/derive_key/Cargo.toml @@ -14,4 +14,4 @@ roc_types = { path = "../types" } [features] default = [] -debug-derived-symbols = [] +debug-derived-symbols = ["roc_module/debug-symbols"] diff --git a/crates/compiler/solve/tests/solve_expr.rs b/crates/compiler/solve/tests/solve_expr.rs index 9b3337e3bb..a0200c0dd9 100644 --- a/crates/compiler/solve/tests/solve_expr.rs +++ b/crates/compiler/solve/tests/solve_expr.rs @@ -6998,9 +6998,10 @@ mod solve_expr { after : Lazy a, (a -> Lazy b) -> Lazy b after = \effect, map -> - \{} -> + thunk = \{} -> when map (effect {}) is b -> b {} + thunk f = \_ -> \_ -> "" g = \{ s1 } -> \_ -> s1 From 1ed2e1a8e98a31f4fab49b70fbf23cbbd8e3c024 Mon Sep 17 00:00:00 2001 From: ayazhafiz Date: Tue, 28 Jun 2022 22:16:48 -0400 Subject: [PATCH 23/66] Improve test --- .../compiler/test_gen/src/gen_primitives.rs | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/crates/compiler/test_gen/src/gen_primitives.rs b/crates/compiler/test_gen/src/gen_primitives.rs index 9be5b1ca43..458990b60c 100644 --- a/crates/compiler/test_gen/src/gen_primitives.rs +++ b/crates/compiler/test_gen/src/gen_primitives.rs @@ -3607,21 +3607,20 @@ fn multimorphic_lambdas_have_captured_function_in_closure() { b -> b {} thunk - f = \_ -> \_ -> "" + f = \_ -> \_ -> "fun f" g = \{ s1 } -> \_ -> s1 - x : [True, False] - x = False + fun = \x -> + h = + when x is + True -> after (\{} -> "") f + False -> after (\{} -> {s1: "s1"}) g + h {} - fun = - when x is - True -> after (\{} -> "") f - False -> after (\{} -> {s1: "s1"}) g - - fun {} + {a: fun False, b: fun True} "# ), - RocStr::from("s1"), - RocStr + (RocStr::from("s1"), RocStr::from("fun f")), + (RocStr, RocStr) ) } From 019ebd93f7ca0bba5468262f9db6008ee4f4020c Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Fri, 1 Jul 2022 15:52:36 -0400 Subject: [PATCH 24/66] Put the capture niche in the lambda name instead of generating new names --- crates/compiler/alias_analysis/src/lib.rs | 49 +- crates/compiler/builtins/src/bitcode.rs | 4 +- crates/compiler/can/src/def.rs | 36 +- crates/compiler/gen_dev/src/lib.rs | 20 +- crates/compiler/gen_llvm/src/llvm/build.rs | 43 +- crates/compiler/gen_wasm/src/backend.rs | 5 +- crates/compiler/gen_wasm/src/low_level.rs | 4 +- crates/compiler/load_internal/src/file.rs | 9 +- crates/compiler/mono/src/borrow.rs | 32 +- crates/compiler/mono/src/code_gen_help/mod.rs | 8 +- crates/compiler/mono/src/inc_dec.rs | 10 +- crates/compiler/mono/src/ir.rs | 468 ++++++++++-------- crates/compiler/mono/src/layout.rs | 211 ++++---- crates/compiler/mono/src/tail_recursion.rs | 8 +- .../multimorphic_lambda_set_capture.txt | 43 +- ...bdas_have_captured_function_in_closure.txt | 50 +- ...ic_lambdas_with_non_capturing_function.txt | 36 +- ...phic_lambdas_with_other_lambda_capture.txt | 32 +- crates/compiler/test_mono/src/tests.rs | 9 +- crates/repl_eval/src/eval.rs | 1 + 20 files changed, 592 insertions(+), 486 deletions(-) diff --git a/crates/compiler/alias_analysis/src/lib.rs b/crates/compiler/alias_analysis/src/lib.rs index a21b1e6ee3..5ad45686f9 100644 --- a/crates/compiler/alias_analysis/src/lib.rs +++ b/crates/compiler/alias_analysis/src/lib.rs @@ -23,11 +23,13 @@ pub const STATIC_LIST_NAME: ConstName = ConstName(b"THIS IS A STATIC LIST"); const ENTRY_POINT_NAME: &[u8] = b"mainForHost"; pub fn func_name_bytes(proc: &Proc) -> [u8; SIZE] { - func_name_bytes_help( - proc.name.call_name(), + let bytes = func_name_bytes_help( + proc.name.name(), proc.args.iter().map(|x| x.0), + proc.name.captures_niche.iter().copied(), &proc.ret_layout, - ) + ); + bytes } #[inline(always)] @@ -65,13 +67,15 @@ impl TagUnionId { } } -pub fn func_name_bytes_help<'a, I>( +pub fn func_name_bytes_help<'a, I1, I2>( symbol: Symbol, - argument_layouts: I, + argument_layouts: I2, + captures_niche: I1, return_layout: &Layout<'a>, ) -> [u8; SIZE] where - I: IntoIterator>, + I1: IntoIterator>, + I2: IntoIterator>, { let mut name_bytes = [0u8; SIZE]; @@ -86,6 +90,10 @@ where layout.hash(&mut hasher); } + for capture_layout in captures_niche { + capture_layout.hash(&mut hasher); + } + return_layout.hash(&mut hasher); hasher.finish() @@ -177,13 +185,17 @@ where match layout { RawFunctionLayout::Function(_, _, _) => { let it = top_level.arguments.iter().copied(); - let bytes = func_name_bytes_help(*symbol, it, &top_level.result); + let bytes = func_name_bytes_help(*symbol, it, [], &top_level.result); host_exposed_functions.push((bytes, top_level.arguments)); } RawFunctionLayout::ZeroArgumentThunk(_) => { - let bytes = - func_name_bytes_help(*symbol, [Layout::UNIT], &top_level.result); + let bytes = func_name_bytes_help( + *symbol, + [Layout::UNIT], + [], + &top_level.result, + ); host_exposed_functions.push((bytes, top_level.arguments)); } @@ -211,6 +223,7 @@ where let roc_main_bytes = func_name_bytes_help( entry_point.symbol, entry_point.layout.arguments.iter().copied(), + std::iter::empty(), &entry_point.layout.result, ); let roc_main = FuncName(&roc_main_bytes); @@ -635,7 +648,7 @@ fn call_spec( match &call.call_type { ByName { - name: symbol, + name, ret_layout, arg_layouts, specialization_id, @@ -644,8 +657,9 @@ fn call_spec( let spec_var = CalleeSpecVar(&array); let arg_value_id = build_tuple_value(builder, env, block, call.arguments)?; - let it = arg_layouts.iter().copied(); - let bytes = func_name_bytes_help(*symbol, it, ret_layout); + let args_it = arg_layouts.iter().copied(); + let captures_it = name.captures_niche.iter().copied(); + let bytes = func_name_bytes_help(name.name(), args_it, captures_it, ret_layout); let name = FuncName(&bytes); let module = MOD_APP; builder.add_call(block, spec_var, module, name, arg_value_id) @@ -688,9 +702,14 @@ fn call_spec( let mode = update_mode.to_bytes(); let update_mode_var = UpdateModeVar(&mode); - let it = passed_function.argument_layouts.iter().copied(); - let bytes = - func_name_bytes_help(passed_function.name, it, &passed_function.return_layout); + let args_it = passed_function.argument_layouts.iter().copied(); + let captures_it = passed_function.name.captures_niche.iter().copied(); + let bytes = func_name_bytes_help( + passed_function.name.name(), + args_it, + captures_it, + &passed_function.return_layout, + ); let name = FuncName(&bytes); let module = MOD_APP; diff --git a/crates/compiler/builtins/src/bitcode.rs b/crates/compiler/builtins/src/bitcode.rs index ca7db04afb..24a07a3f76 100644 --- a/crates/compiler/builtins/src/bitcode.rs +++ b/crates/compiler/builtins/src/bitcode.rs @@ -29,7 +29,7 @@ pub enum DecWidth { } #[repr(u8)] -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] pub enum FloatWidth { F32, F64, @@ -76,7 +76,7 @@ impl FloatWidth { } #[repr(u8)] -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] pub enum IntWidth { U8 = 0, U16 = 1, diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index 9a077e0e40..adaf215ae4 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -7,6 +7,7 @@ use crate::annotation::make_apply_symbol; use crate::annotation::IntroducedVariables; use crate::annotation::OwnedNamedOrAble; use crate::env::Env; +use crate::expr::AccessorData; use crate::expr::AnnotatedMark; use crate::expr::ClosureData; use crate::expr::Declarations; @@ -1554,7 +1555,10 @@ fn canonicalize_pending_value_def<'a>( region: loc_ann.region, } } else { - let symbol = scope.gen_unique_symbol(); + let symbol = match &loc_can_pattern.value { + Pattern::Identifier(symbol) => *symbol, + _ => scope.gen_unique_symbol(), + }; // generate a fake pattern for each argument. this makes signatures // that are functions only crash when they are applied. @@ -1725,6 +1729,36 @@ fn canonicalize_pending_body<'a>( (loc_can_expr, def_references) } + // Turn f = .foo into f = \rcd -[f]-> rcd.foo + // ( + // Pattern::Identifier(defined_symbol) + // | Pattern::AbilityMemberSpecialization { + // ident: defined_symbol, + // .. + // }, + // ast::Expr::AccessorFunction(field), + // ) => { + // let (loc_can_expr, can_output) = ( + // Loc::at( + // loc_expr.region, + // Accessor(AccessorData { + // name: scope.gen_unique_symbol(), + // function_var: var_store.fresh(), + // record_var: var_store.fresh(), + // ext_var: var_store.fresh(), + // closure_var: var_store.fresh(), + // field_var: var_store.fresh(), + // field: (*field).into(), + // }), + // ), + // Output::default(), + // ); + // let def_references = DefReferences::Value(can_output.references.clone()); + // output.union(can_output); + + // (loc_can_expr, def_references) + // } + _ => { let (loc_can_expr, can_output) = canonicalize_expr(env, var_store, scope, loc_expr.region, &loc_expr.value); diff --git a/crates/compiler/gen_dev/src/lib.rs b/crates/compiler/gen_dev/src/lib.rs index 4a650b66a7..960278588e 100644 --- a/crates/compiler/gen_dev/src/lib.rs +++ b/crates/compiler/gen_dev/src/lib.rs @@ -106,8 +106,8 @@ trait Backend<'a> { proc: Proc<'a>, layout_ids: &mut LayoutIds<'a>, ) -> (Vec, Vec, Vec<'a, (Symbol, String)>) { - let layout_id = layout_ids.get(proc.name.call_name(), &proc.ret_layout); - let proc_name = self.symbol_to_string(proc.name.call_name(), layout_id); + let layout_id = layout_ids.get(proc.name.name(), &proc.ret_layout); + let proc_name = self.symbol_to_string(proc.name.name(), layout_id); self.reset(proc_name, proc.is_self_recursive); self.load_args(proc.args, &proc.ret_layout); for (layout, sym) in proc.args { @@ -263,7 +263,7 @@ trait Backend<'a> { .. } => { if let LowLevelWrapperType::CanBeReplacedBy(lowlevel) = - LowLevelWrapperType::from_symbol(*func_sym) + LowLevelWrapperType::from_symbol(func_sym.name()) { self.build_run_low_level( sym, @@ -272,14 +272,20 @@ trait Backend<'a> { arg_layouts, ret_layout, ) - } else if self.defined_in_app_module(*func_sym) { - let layout_id = LayoutIds::default().get(*func_sym, layout); - let fn_name = self.symbol_to_string(*func_sym, layout_id); + } else if self.defined_in_app_module(func_sym.name()) { + let layout_id = LayoutIds::default().get(func_sym.name(), layout); + let fn_name = self.symbol_to_string(func_sym.name(), layout_id); // Now that the arguments are needed, load them if they are literals. self.load_literal_symbols(arguments); self.build_fn_call(sym, fn_name, arguments, arg_layouts, ret_layout) } else { - self.build_builtin(sym, *func_sym, arguments, arg_layouts, ret_layout) + self.build_builtin( + sym, + func_sym.name(), + arguments, + arg_layouts, + ret_layout, + ) } } diff --git a/crates/compiler/gen_llvm/src/llvm/build.rs b/crates/compiler/gen_llvm/src/llvm/build.rs index 46caeb1672..683cd41504 100644 --- a/crates/compiler/gen_llvm/src/llvm/build.rs +++ b/crates/compiler/gen_llvm/src/llvm/build.rs @@ -62,7 +62,9 @@ use roc_mono::ir::{ BranchInfo, CallType, EntryPoint, HigherOrderLowLevel, JoinPointId, ListLiteralElement, ModifyRc, OptLevel, ProcLayout, }; -use roc_mono::layout::{Builtin, LambdaSet, Layout, LayoutIds, TagIdIntType, UnionLayout}; +use roc_mono::layout::{ + Builtin, LambdaName, LambdaSet, Layout, LayoutIds, TagIdIntType, UnionLayout, +}; use roc_std::RocDec; use roc_target::{PtrWidth, TargetInfo}; use std::convert::{TryFrom, TryInto}; @@ -715,7 +717,7 @@ fn promote_to_main_function<'a, 'ctx, 'env>( top_level: ProcLayout<'a>, ) -> (&'static str, FunctionValue<'ctx>) { let it = top_level.arguments.iter().copied(); - let bytes = roc_alias_analysis::func_name_bytes_help(symbol, it, &top_level.result); + let bytes = roc_alias_analysis::func_name_bytes_help(symbol, it, [], &top_level.result); let func_name = FuncName(&bytes); let func_solutions = mod_solutions.func_solutions(func_name).unwrap(); @@ -727,7 +729,7 @@ fn promote_to_main_function<'a, 'ctx, 'env>( ); // NOTE fake layout; it is only used for debug prints - let roc_main_fn = function_value_by_func_spec(env, *func_spec, symbol, &[], &Layout::UNIT); + let roc_main_fn = function_value_by_func_spec(env, *func_spec, symbol, &[], &[], &Layout::UNIT); let main_fn_name = "$Test.main"; @@ -3296,6 +3298,7 @@ fn expose_function_to_host<'a, 'ctx, 'env>( symbol: Symbol, roc_function: FunctionValue<'ctx>, arguments: &'a [Layout<'a>], + captures_niche: &'a [Layout<'a>], return_layout: Layout<'a>, layout_ids: &mut LayoutIds<'a>, ) { @@ -3304,6 +3307,7 @@ fn expose_function_to_host<'a, 'ctx, 'env>( let proc_layout = ProcLayout { arguments, result: return_layout, + captures_niche, }; let c_function_name: String = layout_ids @@ -4185,7 +4189,7 @@ fn build_procedures_help<'a, 'ctx, 'env>( // only have top-level thunks for this proc's module in scope // this retain is not needed for correctness, but will cause less confusion when debugging - let home = proc.name.call_name().module_id(); + let home = proc.name.name().module_id(); current_scope.retain_top_level_thunks_for_module(home); build_proc( @@ -4301,6 +4305,7 @@ fn build_proc_header<'a, 'ctx, 'env>( symbol, fn_val, arguments.into_bump_slice(), + proc.name.captures_niche, proc.ret_layout, layout_ids, ); @@ -4530,8 +4535,12 @@ pub fn build_proc<'a, 'ctx, 'env>( // * roc__mainForHost_1_Update_result_size() -> i64 let it = top_level.arguments.iter().copied(); - let bytes = - roc_alias_analysis::func_name_bytes_help(symbol, it, &top_level.result); + let bytes = roc_alias_analysis::func_name_bytes_help( + symbol, + it, + [], + &top_level.result, + ); let func_name = FuncName(&bytes); let func_solutions = mod_solutions.func_solutions(func_name).unwrap(); @@ -4548,6 +4557,7 @@ pub fn build_proc<'a, 'ctx, 'env>( *func_spec, symbol, top_level.arguments, + &[], &top_level.result, ) } @@ -4559,7 +4569,7 @@ pub fn build_proc<'a, 'ctx, 'env>( } }; - let ident_string = proc.name.call_name().as_str(&env.interns); + let ident_string = proc.name.name().as_str(&env.interns); let fn_name: String = format!("{}_1", ident_string); build_closure_caller( @@ -4624,17 +4634,19 @@ fn function_value_by_func_spec<'a, 'ctx, 'env>( func_spec: FuncSpec, symbol: Symbol, arguments: &[Layout<'a>], + captures_niche: &[Layout<'a>], result: &Layout<'a>, ) -> FunctionValue<'ctx> { let fn_name = func_spec_name(env.arena, &env.interns, symbol, func_spec); let fn_name = fn_name.as_str(); - function_value_by_name_help(env, arguments, result, symbol, fn_name) + function_value_by_name_help(env, arguments, captures_niche, result, symbol, fn_name) } fn function_value_by_name_help<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, arguments: &[Layout<'a>], + _captures_niche: &[Layout<'a>], result: &Layout<'a>, symbol: Symbol, fn_name: &str, @@ -4675,12 +4687,18 @@ fn roc_call_with_args<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, argument_layouts: &[Layout<'a>], result_layout: &Layout<'a>, - symbol: Symbol, + name: LambdaName<'a>, func_spec: FuncSpec, arguments: &[BasicValueEnum<'ctx>], ) -> BasicValueEnum<'ctx> { - let fn_val = - function_value_by_func_spec(env, func_spec, symbol, argument_layouts, result_layout); + let fn_val = function_value_by_func_spec( + env, + func_spec, + name.name(), + argument_layouts, + name.captures_niche, + result_layout, + ); call_roc_function(env, fn_val, result_layout, arguments) } @@ -4869,8 +4887,9 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>( let function = function_value_by_func_spec( env, func_spec, - function_name, + function_name.name(), argument_layouts, + function_name.captures_niche, return_layout, ); diff --git a/crates/compiler/gen_wasm/src/backend.rs b/crates/compiler/gen_wasm/src/backend.rs index cb0ea554e1..b931234f6a 100644 --- a/crates/compiler/gen_wasm/src/backend.rs +++ b/crates/compiler/gen_wasm/src/backend.rs @@ -380,7 +380,7 @@ impl<'a> WasmBackend<'a> { println!("\ngenerating procedure {:?}\n", proc.name); } - self.append_proc_debug_name(proc.name.call_name()); + self.append_proc_debug_name(proc.name.name()); self.start_proc(proc); @@ -1125,9 +1125,10 @@ impl<'a> WasmBackend<'a> { let proc_layout = ProcLayout { arguments: arg_layouts, result: **result, + captures_niche: func_sym.captures_niche, }; self.expr_call_by_name( - *func_sym, + func_sym.name(), &proc_layout, arguments, ret_sym, diff --git a/crates/compiler/gen_wasm/src/low_level.rs b/crates/compiler/gen_wasm/src/low_level.rs index 762c110e3e..0386723340 100644 --- a/crates/compiler/gen_wasm/src/low_level.rs +++ b/crates/compiler/gen_wasm/src/low_level.rs @@ -1951,12 +1951,13 @@ pub fn call_higher_order_lowlevel<'a>( let passed_proc_layout = ProcLayout { arguments: argument_layouts, result: *result_layout, + captures_niche: fn_name.captures_niche, }; let passed_proc_index = backend .proc_lookup .iter() .position(|ProcLookupData { name, layout, .. }| { - name == fn_name && layout == &passed_proc_layout + *name == fn_name.name() && layout == &passed_proc_layout }) .unwrap(); ProcSource::HigherOrderWrapper(passed_proc_index) @@ -1984,6 +1985,7 @@ pub fn call_higher_order_lowlevel<'a>( ProcLayout { arguments: wrapper_arg_layouts.into_bump_slice(), result: Layout::UNIT, + captures_niche: &[], } }; diff --git a/crates/compiler/load_internal/src/file.rs b/crates/compiler/load_internal/src/file.rs index a62c69adc0..3e6b2ef5b2 100644 --- a/crates/compiler/load_internal/src/file.rs +++ b/crates/compiler/load_internal/src/file.rs @@ -132,7 +132,7 @@ struct ModuleCache<'a> { typechecked: MutMap>, found_specializations: MutMap>, late_specializations: MutMap>, - external_specializations_requested: MutMap>, + external_specializations_requested: MutMap>>, /// Various information imports: MutMap>, @@ -717,7 +717,7 @@ enum Msg<'a> { module_id: ModuleId, ident_ids: IdentIds, layout_cache: LayoutCache<'a>, - external_specializations_requested: BumpMap, + external_specializations_requested: BumpMap>, procs_base: ProcsBase<'a>, procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>, update_mode_ids: UpdateModeIds, @@ -1013,7 +1013,7 @@ enum BuildTask<'a> { subs: Subs, procs_base: ProcsBase<'a>, layout_cache: LayoutCache<'a>, - specializations_we_must_make: Vec, + specializations_we_must_make: Vec>, module_timing: ModuleTiming, world_abilities: WorldAbilities, derived_symbols: GlobalDerivedSymbols, @@ -2678,6 +2678,7 @@ fn finish_specialization( layout: roc_mono::ir::ProcLayout { arguments: &[], result: Layout::struct_no_name_order(&[]), + captures_niche: &[], }, symbol, } @@ -4425,7 +4426,7 @@ fn make_specializations<'a>( mut subs: Subs, procs_base: ProcsBase<'a>, mut layout_cache: LayoutCache<'a>, - specializations_we_must_make: Vec, + specializations_we_must_make: Vec>, mut module_timing: ModuleTiming, target_info: TargetInfo, world_abilities: WorldAbilities, diff --git a/crates/compiler/mono/src/borrow.rs b/crates/compiler/mono/src/borrow.rs index 293341c95a..1d819b3d7e 100644 --- a/crates/compiler/mono/src/borrow.rs +++ b/crates/compiler/mono/src/borrow.rs @@ -263,7 +263,7 @@ impl<'a> ParamMap<'a> { self.declarations[index + i] = param; } - self.visit_stmt(arena, proc.name.call_name(), &proc.body); + self.visit_stmt(arena, proc.name.name(), &proc.body); } fn visit_proc_always_owned( @@ -282,7 +282,7 @@ impl<'a> ParamMap<'a> { self.declarations[index + i] = param; } - self.visit_stmt(arena, proc.name.call_name(), &proc.body); + self.visit_stmt(arena, proc.name.name(), &proc.body); } fn visit_stmt(&mut self, arena: &'a Bump, _fnid: Symbol, stmt: &Stmt<'a>) { @@ -501,11 +501,12 @@ impl<'a> BorrowInfState<'a> { arg_layouts, .. } => { - let top_level = ProcLayout::new(self.arena, arg_layouts, **ret_layout); + let top_level = + ProcLayout::new(self.arena, arg_layouts, name.captures_niche, **ret_layout); // get the borrow signature of the applied function let ps = param_map - .get_symbol(*name, top_level) + .get_symbol(name.name(), top_level) .expect("function is defined"); // the return value will be owned @@ -544,12 +545,14 @@ impl<'a> BorrowInfState<'a> { let closure_layout = ProcLayout { arguments: passed_function.argument_layouts, result: passed_function.return_layout, + captures_niche: passed_function.name.captures_niche, }; - let function_ps = match param_map.get_symbol(passed_function.name, closure_layout) { - Some(function_ps) => function_ps, - None => unreachable!(), - }; + let function_ps = + match param_map.get_symbol(passed_function.name.name(), closure_layout) { + Some(function_ps) => function_ps, + None => unreachable!(), + }; match op { ListMap { xs } => { @@ -743,12 +746,13 @@ impl<'a> BorrowInfState<'a> { Stmt::Ret(z), ) = (v, b) { - let top_level = ProcLayout::new(self.arena, arg_layouts, **ret_layout); + let top_level = + ProcLayout::new(self.arena, arg_layouts, g.captures_niche, **ret_layout); - if self.current_proc == *g && x == *z { + if self.current_proc == g.name() && x == *z { // anonymous functions (for which the ps may not be known) // can never be tail-recursive, so this is fine - if let Some(ps) = param_map.get_symbol(*g, top_level) { + if let Some(ps) = param_map.get_symbol(g.name(), top_level) { self.own_params_using_args(ys, ps) } } @@ -852,10 +856,10 @@ impl<'a> BorrowInfState<'a> { let ys = Vec::from_iter_in(proc.args.iter().map(|t| t.1), self.arena).into_bump_slice(); self.update_param_set_symbols(ys); - self.current_proc = proc.name.call_name(); + self.current_proc = proc.name.name(); // ensure that current_proc is in the owned map - self.owned.entry(proc.name.call_name()).or_default(); + self.owned.entry(proc.name.name()).or_default(); self.collect_stmt(param_map, &proc.body); self.update_param_map_declaration(param_map, param_offset, proc.args.len()); @@ -975,7 +979,7 @@ fn call_info_call<'a>(call: &crate::ir::Call<'a>, info: &mut CallInfo<'a>) { match call.call_type { ByName { name, .. } => { - info.keys.push(name); + info.keys.push(name.name()); } Foreign { .. } => {} LowLevel { .. } => {} diff --git a/crates/compiler/mono/src/code_gen_help/mod.rs b/crates/compiler/mono/src/code_gen_help/mod.rs index 1888bbd809..a02b04cf3f 100644 --- a/crates/compiler/mono/src/code_gen_help/mod.rs +++ b/crates/compiler/mono/src/code_gen_help/mod.rs @@ -170,7 +170,7 @@ impl<'a> CodeGenHelp<'a> { let arg_layouts = self.arena.alloc([layout]); let expr = Expr::Call(Call { call_type: CallType::ByName { - name: proc_name, + name: LambdaName::only_receiver(proc_name), ret_layout, arg_layouts, specialization_id: CallSpecId::BACKEND_DUMMY, @@ -262,7 +262,7 @@ impl<'a> CodeGenHelp<'a> { Some(Expr::Call(Call { call_type: CallType::ByName { - name: proc_name, + name: LambdaName::only_receiver(proc_name), ret_layout, arg_layouts, specialization_id: CallSpecId::BACKEND_DUMMY, @@ -375,19 +375,23 @@ impl<'a> CodeGenHelp<'a> { HelperOp::Inc => ProcLayout { arguments: self.arena.alloc([*layout, self.layout_isize]), result: LAYOUT_UNIT, + captures_niche: &[], }, HelperOp::Dec => ProcLayout { arguments: self.arena.alloc([*layout]), result: LAYOUT_UNIT, + captures_niche: &[], }, HelperOp::Reset => ProcLayout { arguments: self.arena.alloc([*layout]), result: *layout, + captures_niche: &[], }, HelperOp::DecRef(_) => unreachable!("No generated Proc for DecRef"), HelperOp::Eq => ProcLayout { arguments: self.arena.alloc([*layout, *layout]), result: LAYOUT_BOOL, + captures_niche: &[], }, }; diff --git a/crates/compiler/mono/src/inc_dec.rs b/crates/compiler/mono/src/inc_dec.rs index f341d6dea6..bdcd0765ec 100644 --- a/crates/compiler/mono/src/inc_dec.rs +++ b/crates/compiler/mono/src/inc_dec.rs @@ -564,12 +564,13 @@ impl<'a> Context<'a> { arg_layouts, .. } => { - let top_level = ProcLayout::new(self.arena, arg_layouts, **ret_layout); + let top_level = + ProcLayout::new(self.arena, arg_layouts, name.captures_niche, **ret_layout); // get the borrow signature let ps = self .param_map - .get_symbol(*name, top_level) + .get_symbol(name.name(), top_level) .expect("function is defined"); let v = Expr::Call(crate::ir::Call { @@ -614,11 +615,12 @@ impl<'a> Context<'a> { let function_layout = ProcLayout { arguments: passed_function.argument_layouts, result: passed_function.return_layout, + captures_niche: passed_function.name.captures_niche, }; let function_ps = match self .param_map - .get_symbol(passed_function.name, function_layout) + .get_symbol(passed_function.name.name(), function_layout) { Some(function_ps) => function_ps, None => unreachable!(), @@ -1406,7 +1408,7 @@ fn visit_proc<'a, 'i>( proc: &mut Proc<'a>, layout: ProcLayout<'a>, ) { - let params = match param_map.get_symbol(proc.name.call_name(), layout) { + let params = match param_map.get_symbol(proc.name.name(), layout) { Some(slice) => slice, None => Vec::from_iter_in( proc.args.iter().cloned().map(|(layout, symbol)| Param { diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index ea76d72427..be52b1a912 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -62,9 +62,9 @@ roc_error_macros::assert_sizeof_wasm!(CallType, 28); roc_error_macros::assert_sizeof_non_wasm!(Literal, 3 * 8); roc_error_macros::assert_sizeof_non_wasm!(Expr, 10 * 8); roc_error_macros::assert_sizeof_non_wasm!(Stmt, 19 * 8); -roc_error_macros::assert_sizeof_non_wasm!(ProcLayout, 6 * 8); -roc_error_macros::assert_sizeof_non_wasm!(Call, 7 * 8); -roc_error_macros::assert_sizeof_non_wasm!(CallType, 5 * 8); +roc_error_macros::assert_sizeof_non_wasm!(ProcLayout, 8 * 8); +roc_error_macros::assert_sizeof_non_wasm!(Call, 9 * 8); +roc_error_macros::assert_sizeof_non_wasm!(CallType, 7 * 8); macro_rules! return_on_layout_error { ($env:expr, $layout_result:expr) => { @@ -305,7 +305,7 @@ impl<'a> Default for CapturedSymbols<'a> { #[derive(Clone, Debug, PartialEq)] pub struct Proc<'a> { - pub name: LambdaName, + pub name: LambdaName<'a>, pub args: &'a [(Layout<'a>, Symbol)], pub body: Stmt<'a>, pub closure_data_layout: Option>, @@ -352,12 +352,12 @@ impl<'a> Proc<'a> { if pretty_print_ir_symbols() { alloc .text("procedure : ") - .append(symbol_to_doc(alloc, self.name.call_name())) + .append(symbol_to_doc(alloc, self.name.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.call_name())) + .append(symbol_to_doc(alloc, self.name.name())) .append(" (") .append(alloc.intersperse(args_doc, ", ")) .append("):") @@ -366,7 +366,7 @@ impl<'a> Proc<'a> { } else { alloc .text("procedure ") - .append(symbol_to_doc(alloc, self.name.call_name())) + .append(symbol_to_doc(alloc, self.name.name())) .append(" (") .append(alloc.intersperse(args_doc, ", ")) .append("):") @@ -439,7 +439,7 @@ impl<'a> Proc<'a> { let transformed = crate::tail_recursion::make_tail_recursive( env.arena, id, - self.name.call_name(), + self.name, self.body.clone(), args.into_bump_slice(), self.ret_layout, @@ -455,11 +455,11 @@ impl<'a> Proc<'a> { /// A host-exposed function must be specialized; it's a seed for subsequent specializations #[derive(Clone, Debug)] -pub struct HostSpecializations { +pub struct HostSpecializations<'a> { /// Not a bumpalo vec because bumpalo is not thread safe /// Separate array so we can search for membership quickly /// If it's a value and not a lambda, the value is recorded as LambdaName::thunk. - symbol_or_lambdas: std::vec::Vec, + symbol_or_lambdas: std::vec::Vec>, storage_subs: StorageSubs, /// For each symbol, what types to specialize it for, points into the storage_subs types_to_specialize: std::vec::Vec, @@ -467,13 +467,13 @@ pub struct HostSpecializations { exposed_aliases: std::vec::Vec>, } -impl Default for HostSpecializations { +impl Default for HostSpecializations<'_> { fn default() -> Self { Self::new() } } -impl HostSpecializations { +impl<'a> HostSpecializations<'a> { pub fn new() -> Self { Self { symbol_or_lambdas: std::vec::Vec::new(), @@ -486,7 +486,7 @@ impl HostSpecializations { pub fn insert_host_exposed( &mut self, env_subs: &mut Subs, - symbol_or_lambda: LambdaName, + symbol_or_lambda: LambdaName<'a>, opt_annotation: Option, variable: Variable, ) { @@ -523,7 +523,7 @@ impl HostSpecializations { self, ) -> ( StorageSubs, - impl Iterator)>, + impl Iterator, Variable, std::vec::Vec<(Symbol, Variable)>)>, ) { let it1 = self.symbol_or_lambdas.into_iter(); @@ -539,23 +539,23 @@ impl HostSpecializations { /// Specializations of this module's symbols that other modules need #[derive(Clone, Debug)] -pub struct ExternalSpecializations { +pub struct ExternalSpecializations<'a> { /// Not a bumpalo vec because bumpalo is not thread safe /// Separate array so we can search for membership quickly /// If it's a value and not a lambda, the value is recorded as LambdaName::thunk. - pub symbol_or_lambda: std::vec::Vec, + pub symbol_or_lambda: std::vec::Vec>, storage_subs: StorageSubs, /// For each symbol, what types to specialize it for, points into the storage_subs types_to_specialize: std::vec::Vec>, } -impl Default for ExternalSpecializations { +impl Default for ExternalSpecializations<'_> { fn default() -> Self { Self::new() } } -impl ExternalSpecializations { +impl<'a> ExternalSpecializations<'a> { pub fn new() -> Self { Self { symbol_or_lambda: std::vec::Vec::new(), @@ -566,7 +566,7 @@ impl ExternalSpecializations { fn insert_external( &mut self, - symbol_or_lambda: LambdaName, + symbol_or_lambda: LambdaName<'a>, env_subs: &mut Subs, variable: Variable, ) { @@ -592,7 +592,7 @@ impl ExternalSpecializations { self, ) -> ( StorageSubs, - impl Iterator)>, + impl Iterator, std::vec::Vec)>, ) { ( self.storage_subs, @@ -607,7 +607,7 @@ impl ExternalSpecializations { pub struct Suspended<'a> { pub store: StorageSubs, /// LambdaName::thunk if it's a value - pub symbol_or_lambdas: Vec<'a, LambdaName>, + pub symbol_or_lambdas: Vec<'a, LambdaName<'a>>, pub layouts: Vec<'a, ProcLayout<'a>>, pub variables: Vec<'a, Variable>, } @@ -625,7 +625,7 @@ impl<'a> Suspended<'a> { fn specialization( &mut self, subs: &mut Subs, - symbol_or_lambda: LambdaName, + symbol_or_lambda: LambdaName<'a>, proc_layout: ProcLayout<'a>, variable: Variable, ) { @@ -931,7 +931,7 @@ pub struct ProcsBase<'a> { pub partial_procs: BumpMap>, pub module_thunks: &'a [Symbol], /// A host-exposed function must be specialized; it's a seed for subsequent specializations - pub host_specializations: HostSpecializations, + pub host_specializations: HostSpecializations<'a>, pub runtime_errors: BumpMap, pub imported_module_thunks: &'a [Symbol], } @@ -945,7 +945,7 @@ pub struct Procs<'a> { pending_specializations: PendingSpecializations<'a>, specialized: Specialized<'a>, pub runtime_errors: BumpMap, - pub externals_we_need: BumpMap, + pub externals_we_need: BumpMap>, symbol_specializations: SymbolSpecializations<'a>, } @@ -1015,7 +1015,7 @@ impl<'a> Procs<'a> { fn insert_anonymous( &mut self, env: &mut Env<'a, '_>, - name: LambdaName, + name: LambdaName<'a>, annotation: Variable, loc_args: std::vec::Vec<(Variable, AnnotatedMark, Loc)>, loc_body: Loc, @@ -1027,12 +1027,12 @@ impl<'a> Procs<'a> { .raw_from_var(env.arena, annotation, env.subs, env.multimorphic_names) .unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err)); - let top_level = ProcLayout::from_raw(env.arena, raw_layout); + let top_level = ProcLayout::from_raw(env.arena, raw_layout, name.captures_niche); // anonymous functions cannot reference themselves, therefore cannot be tail-recursive // EXCEPT when the closure conversion makes it tail-recursive. let is_self_recursive = match top_level.arguments.last() { - Some(Layout::LambdaSet(lambda_set)) => lambda_set.contains(name.call_name()), + Some(Layout::LambdaSet(lambda_set)) => lambda_set.contains(name.name()), _ => false, }; @@ -1042,15 +1042,13 @@ impl<'a> Procs<'a> { // by the surrounding context, so we can add pending specializations // for them immediately. - let already_specialized = self - .specialized - .is_specialized(name.call_name(), &top_level); + let already_specialized = self.specialized.is_specialized(name.name(), &top_level); let layout = top_level; // if we've already specialized this one, no further work is needed. if !already_specialized { - if self.is_module_thunk(name.source_name()) { + if self.is_module_thunk(name.name()) { debug_assert!(layout.arguments.is_empty()); } @@ -1059,7 +1057,7 @@ impl<'a> Procs<'a> { // register the pending specialization, so this gets code genned later suspended.specialization(env.subs, name, layout, annotation); - match self.partial_procs.symbol_to_id(name.source_name()) { + match self.partial_procs.symbol_to_id(name.name()) { Some(occupied) => { let existing = self.partial_procs.get_id(occupied); // if we're adding the same partial proc twice, they must be the actual same! @@ -1085,7 +1083,7 @@ impl<'a> Procs<'a> { is_self_recursive, }; - self.partial_procs.insert(name.call_name(), partial_proc); + self.partial_procs.insert(name.name(), partial_proc); } } } @@ -1093,12 +1091,12 @@ impl<'a> Procs<'a> { // Mark this proc as in-progress, so if we're dealing with // mutually recursive functions, we don't loop forever. // (We had a bug around this before this system existed!) - self.specialized.mark_in_progress(name.call_name(), layout); + self.specialized.mark_in_progress(name.name(), layout); let outside_layout = layout; let partial_proc_id = if let Some(partial_proc_id) = - self.partial_procs.symbol_to_id(name.source_name()) + self.partial_procs.symbol_to_id(name.name()) { let existing = self.partial_procs.get_id(partial_proc_id); // if we're adding the same partial proc twice, they must be the actual same! @@ -1123,7 +1121,7 @@ impl<'a> Procs<'a> { is_self_recursive, }; - self.partial_procs.insert(name.call_name(), partial_proc) + self.partial_procs.insert(name.name(), partial_proc) }; match specialize_variable( @@ -1136,7 +1134,11 @@ impl<'a> Procs<'a> { partial_proc_id, ) { Ok((proc, layout)) => { - let top_level = ProcLayout::from_raw(env.arena, layout); + let top_level = ProcLayout::from_raw( + env.arena, + layout, + proc.name.captures_niche, + ); debug_assert_eq!( outside_layout, top_level, @@ -1144,12 +1146,12 @@ impl<'a> Procs<'a> { proc.name ); - if self.is_module_thunk(proc.name.source_name()) { + if self.is_module_thunk(proc.name.name()) { debug_assert!(top_level.arguments.is_empty()); } self.specialized.insert_specialized( - name.call_name(), + name.name(), top_level, proc, ); @@ -1172,23 +1174,23 @@ impl<'a> Procs<'a> { &mut self, env: &mut Env<'a, '_>, fn_var: Variable, - name: LambdaName, + name: LambdaName<'a>, layout: ProcLayout<'a>, layout_cache: &mut LayoutCache<'a>, ) { // If we've already specialized this one, no further work is needed. - if self.specialized.is_specialized(name.call_name(), &layout) { + if self.specialized.is_specialized(name.name(), &layout) { return; } // If this is an imported symbol, let its home module make this specialization - if env.is_imported_symbol(name.source_name()) { + if env.is_imported_symbol(name.name()) { add_needed_external(self, env, fn_var, name); return; } // register the pending specialization, so this gets code genned later - if self.module_thunks.contains(&name.source_name()) { + if self.module_thunks.contains(&name.name()) { debug_assert!(layout.arguments.is_empty()); } @@ -1201,7 +1203,7 @@ impl<'a> Procs<'a> { PendingSpecializations::Making => { let symbol = name; - let partial_proc_id = match self.partial_procs.symbol_to_id(symbol.source_name()) { + let partial_proc_id = match self.partial_procs.symbol_to_id(symbol.name()) { Some(p) => p, None => panic!("no partial_proc for {:?} in module {:?}", symbol, env.home), }; @@ -1209,8 +1211,7 @@ impl<'a> Procs<'a> { // Mark this proc as in-progress, so if we're dealing with // mutually recursive functions, we don't loop forever. // (We had a bug around this before this system existed!) - self.specialized - .mark_in_progress(symbol.call_name(), layout); + self.specialized.mark_in_progress(symbol.name(), layout); // See https://github.com/rtfeldman/roc/issues/1600 // @@ -1243,18 +1244,15 @@ impl<'a> Procs<'a> { let proper_layout = ProcLayout { arguments, result: proc.ret_layout, + captures_niche: proc.name.captures_niche, }; // NOTE: some function are specialized to have a closure, but don't actually // need any closure argument. Here is where we correct this sort of thing, // by trusting the layout of the Proc, not of what we specialize for + self.specialized.remove_specialized(symbol.name(), &layout); self.specialized - .remove_specialized(symbol.call_name(), &layout); - self.specialized.insert_specialized( - symbol.call_name(), - proper_layout, - proc, - ); + .insert_specialized(symbol.name(), proper_layout, proc); } Err(error) => { panic!("TODO generate a RuntimeError message for {:?}", error); @@ -1583,7 +1581,7 @@ impl<'a> Call<'a> { match self.call_type { CallType::ByName { name, .. } => { - let it = std::iter::once(name) + let it = std::iter::once(name.name()) .chain(arguments.iter().copied()) .map(|s| symbol_to_doc(alloc, s)); @@ -1666,7 +1664,7 @@ impl UpdateModeIds { #[derive(Clone, Debug, PartialEq)] pub enum CallType<'a> { ByName { - name: Symbol, + name: LambdaName<'a>, ret_layout: &'a Layout<'a>, arg_layouts: &'a [Layout<'a>], specialization_id: CallSpecId, @@ -1686,7 +1684,7 @@ pub enum CallType<'a> { pub struct PassedFunction<'a> { /// name of the top-level function that is passed as an argument /// e.g. in `List.map xs Num.abs` this would be `Num.abs` - pub name: Symbol, + pub name: LambdaName<'a>, pub argument_layouts: &'a [Layout<'a>], pub return_layout: Layout<'a>, @@ -2616,19 +2614,19 @@ fn specialize_suspended<'a>( // TODO define our own Entry for Specialized? let partial_proc = if procs .specialized - .is_specialized(name.call_name(), &outside_layout) + .is_specialized(name.name(), &outside_layout) { // already specialized, just continue continue; } else { - match procs.partial_procs.symbol_to_id(name.source_name()) { + match procs.partial_procs.symbol_to_id(name.name()) { Some(v) => { // Mark this proc as in-progress, so if we're dealing with // mutually recursive functions, we don't loop forever. // (We had a bug around this before this system existed!) procs .specialized - .mark_in_progress(name.call_name(), outside_layout); + .mark_in_progress(name.name(), outside_layout); v } @@ -2643,9 +2641,9 @@ fn specialize_suspended<'a>( match specialize_variable(env, procs, name, layout_cache, var, &[], partial_proc) { Ok((proc, layout)) => { // TODO thiscode is duplicated elsewhere - let top_level = ProcLayout::from_raw(env.arena, layout); + let top_level = ProcLayout::from_raw(env.arena, layout, proc.name.captures_niche); - if procs.is_module_thunk(proc.name.source_name()) { + if procs.is_module_thunk(proc.name.name()) { debug_assert!( top_level.arguments.is_empty(), "{:?} from {:?}", @@ -2657,18 +2655,18 @@ fn specialize_suspended<'a>( debug_assert_eq!(outside_layout, top_level, " in {:?}", name); procs .specialized - .insert_specialized(name.call_name(), top_level, proc); + .insert_specialized(name.name(), top_level, proc); } Err(SpecializeFailure { attempted_layout, .. }) => { - let proc = generate_runtime_error_function(env, name.call_name(), attempted_layout); + let proc = generate_runtime_error_function(env, name.name(), attempted_layout); - let top_level = ProcLayout::from_raw(env.arena, attempted_layout); + let top_level = ProcLayout::from_raw(env.arena, attempted_layout, &[]); procs .specialized - .insert_specialized(name.call_name(), top_level, proc); + .insert_specialized(name.name(), top_level, proc); } } } @@ -2677,8 +2675,8 @@ fn specialize_suspended<'a>( pub fn specialize_all<'a>( env: &mut Env<'a, '_>, mut procs: Procs<'a>, - externals_others_need: std::vec::Vec, - specializations_for_host: HostSpecializations, + externals_others_need: std::vec::Vec>, + specializations_for_host: HostSpecializations<'a>, layout_cache: &mut LayoutCache<'a>, ) -> Procs<'a> { for externals in externals_others_need { @@ -2714,7 +2712,7 @@ fn specialize_host_specializations<'a>( env: &mut Env<'a, '_>, procs: &mut Procs<'a>, layout_cache: &mut LayoutCache<'a>, - host_specializations: HostSpecializations, + host_specializations: HostSpecializations<'a>, ) { let (store, it) = host_specializations.decompose(); @@ -2736,7 +2734,7 @@ fn specialize_external_specializations<'a>( env: &mut Env<'a, '_>, procs: &mut Procs<'a>, layout_cache: &mut LayoutCache<'a>, - externals_others_need: ExternalSpecializations, + externals_others_need: ExternalSpecializations<'a>, ) { let (store, it) = externals_others_need.decompose(); @@ -2765,11 +2763,11 @@ fn specialize_external_help<'a>( env: &mut Env<'a, '_>, procs: &mut Procs<'a>, layout_cache: &mut LayoutCache<'a>, - name: LambdaName, + name: LambdaName<'a>, variable: Variable, host_exposed_aliases: &[(Symbol, Variable)], ) { - let partial_proc_id = match procs.partial_procs.symbol_to_id(name.source_name()) { + let partial_proc_id = match procs.partial_procs.symbol_to_id(name.name()) { Some(v) => v, None => { panic!("Cannot find a partial proc for {:?}", name); @@ -2788,24 +2786,25 @@ fn specialize_external_help<'a>( match specialization_result { Ok((proc, layout)) => { - let top_level = ProcLayout::from_raw(env.arena, layout); + let top_level = ProcLayout::from_raw(env.arena, layout, proc.name.captures_niche); - if procs.is_module_thunk(name.source_name()) { + if procs.is_module_thunk(name.name()) { debug_assert!(top_level.arguments.is_empty()); } procs .specialized - .insert_specialized(name.call_name(), top_level, proc); + .insert_specialized(name.name(), top_level, proc); } Err(SpecializeFailure { attempted_layout }) => { - let proc = generate_runtime_error_function(env, name.call_name(), attempted_layout); + let proc = generate_runtime_error_function(env, name.name(), attempted_layout); - let top_level = ProcLayout::from_raw(env.arena, attempted_layout); + let top_level = + ProcLayout::from_raw(env.arena, attempted_layout, proc.name.captures_niche); procs .specialized - .insert_specialized(name.call_name(), top_level, proc); + .insert_specialized(name.name(), top_level, proc); } } } @@ -2861,7 +2860,7 @@ fn generate_runtime_error_function<'a>( fn specialize_external<'a>( env: &mut Env<'a, '_>, procs: &mut Procs<'a>, - lambda_name: LambdaName, + lambda_name: LambdaName<'a>, layout_cache: &mut LayoutCache<'a>, fn_var: Variable, host_exposed_variables: &[(Symbol, Variable)], @@ -2959,9 +2958,12 @@ fn specialize_external<'a>( host_exposed_layouts: HostExposedLayouts::NotHostExposed, }; + let captures_niche = &[]; + let top_level = ProcLayout::new( env.arena, top_level_arguments.into_bump_slice(), + captures_niche, *return_layout, ); @@ -3014,10 +3016,6 @@ fn specialize_external<'a>( }; // I'm not sure how to handle the closure case, does it ever occur? - debug_assert!( - matches!(captured_symbols, CapturedSymbols::None) && !lambda_name.is_multimorphic() - ); - let proc = Proc { name: lambda_name, args: &[], @@ -3227,7 +3225,7 @@ enum SpecializedLayout<'a> { fn build_specialized_proc_from_var<'a>( env: &mut Env<'a, '_>, layout_cache: &mut LayoutCache<'a>, - lambda_name: LambdaName, + lambda_name: LambdaName<'a>, pattern_symbols: &[Symbol], fn_var: Variable, ) -> Result, LayoutProblem> { @@ -3262,7 +3260,7 @@ fn build_specialized_proc_from_var<'a>( #[allow(clippy::type_complexity)] fn build_specialized_proc<'a>( arena: &'a Bump, - lambda_name: LambdaName, + lambda_name: LambdaName<'a>, pattern_symbols: &[Symbol], pattern_layouts: Vec<'a, Layout<'a>>, lambda_set: Option>, @@ -3296,7 +3294,7 @@ fn build_specialized_proc<'a>( // // then - let proc_name = lambda_name.call_name(); + let proc_name = lambda_name.name(); match lambda_set { Some(lambda_set) if pattern_symbols.last() == Some(&Symbol::ARG_CLOSURE) => { // here we define the lifted (now top-level) f function. Its final argument is `Symbol::ARG_CLOSURE`, @@ -3411,7 +3409,7 @@ type SpecializeSuccess<'a> = (Proc<'a>, RawFunctionLayout<'a>); fn specialize_variable<'a>( env: &mut Env<'a, '_>, procs: &mut Procs<'a>, - proc_name: LambdaName, + proc_name: LambdaName<'a>, layout_cache: &mut LayoutCache<'a>, fn_var: Variable, host_exposed_aliases: &[(Symbol, Variable)], @@ -3431,7 +3429,7 @@ fn specialize_variable<'a>( fn specialize_variable_help<'a, F>( env: &mut Env<'a, '_>, procs: &mut Procs<'a>, - proc_name: LambdaName, + proc_name: LambdaName<'a>, layout_cache: &mut LayoutCache<'a>, fn_var_thunk: F, host_exposed_variables: &[(Symbol, Variable)], @@ -3453,7 +3451,7 @@ where .raw_from_var(env.arena, fn_var, env.subs, env.multimorphic_names) .unwrap_or_else(|err| panic!("TODO handle invalid function {:?}", err)); - let raw = if procs.is_module_thunk(proc_name.source_name()) { + let raw = if procs.is_module_thunk(proc_name.name()) { match raw { RawFunctionLayout::Function(_, lambda_set, _) => { RawFunctionLayout::ZeroArgumentThunk(Layout::LambdaSet(lambda_set)) @@ -3511,11 +3509,17 @@ where #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct ProcLayout<'a> { pub arguments: &'a [Layout<'a>], + pub captures_niche: &'a [Layout<'a>], pub result: Layout<'a>, } impl<'a> ProcLayout<'a> { - pub fn new(arena: &'a Bump, old_arguments: &'a [Layout<'a>], result: Layout<'a>) -> Self { + pub fn new( + arena: &'a Bump, + old_arguments: &'a [Layout<'a>], + old_captures_niche: &'a [Layout<'a>], + result: Layout<'a>, + ) -> Self { let mut arguments = Vec::with_capacity_in(old_arguments.len(), arena); for old in old_arguments { @@ -3523,22 +3527,36 @@ impl<'a> ProcLayout<'a> { arguments.push(*other); } + let mut captures_niche = Vec::with_capacity_in(old_captures_niche.len(), arena); + + for old in old_captures_niche { + let other = old; + captures_niche.push(*other); + } + let other = result; let new_result = other; ProcLayout { arguments: arguments.into_bump_slice(), + captures_niche: captures_niche.into_bump_slice(), result: new_result, } } - pub fn from_raw(arena: &'a Bump, raw: RawFunctionLayout<'a>) -> Self { + pub fn from_raw( + arena: &'a Bump, + raw: RawFunctionLayout<'a>, + captures_niche: &'a [Layout<'a>], + ) -> Self { match raw { RawFunctionLayout::Function(arguments, lambda_set, result) => { let arguments = lambda_set.extend_argument_list(arena, arguments); - ProcLayout::new(arena, arguments, *result) + ProcLayout::new(arena, arguments, captures_niche, *result) + } + RawFunctionLayout::ZeroArgumentThunk(result) => { + ProcLayout::new(arena, &[], &[], result) } - RawFunctionLayout::ZeroArgumentThunk(result) => ProcLayout::new(arena, &[], result), } } } @@ -3560,7 +3578,7 @@ fn specialize_naked_symbol<'a>( env, procs, fn_var, - LambdaName::thunk(symbol), + symbol, std::vec::Vec::new(), layout_cache, assigned, @@ -3577,7 +3595,7 @@ fn specialize_naked_symbol<'a>( env, procs, variable, - LambdaName::thunk(symbol), + symbol, std::vec::Vec::new(), layout_cache, assigned, @@ -4454,10 +4472,6 @@ pub fn with_hole<'a>( RawFunctionLayout::Function(_, lambda_set, _) => { let lambda_name = find_lambda_name(env, layout_cache, lambda_set, name, &[]); - debug_assert!( - !lambda_name.is_multimorphic(), - "no captures, but somehow this lambda is multimorphic" - ); construct_closure_data( env, lambda_set, @@ -4736,7 +4750,7 @@ pub fn with_hole<'a>( env, procs, fn_var, - LambdaName::only_receiver(proc_name), + proc_name, loc_args, layout_cache, assigned, @@ -4751,7 +4765,7 @@ pub fn with_hole<'a>( env, procs, fn_var, - LambdaName::only_receiver(specialization_proc_name), + specialization_proc_name, loc_args, layout_cache, assigned, @@ -4887,10 +4901,7 @@ pub fn with_hole<'a>( ); let resolved_proc = match resolved_proc { - Resolved::Specialization(symbol) => { - // Ability members never capture, they cannot be multimorphic - LambdaName::only_receiver(symbol) - } + Resolved::Specialization(symbol) => symbol, Resolved::NeedsGenerated => { todo_abilities!("Generate impls for structural types") } @@ -5028,10 +5039,12 @@ pub fn with_hole<'a>( let closure_data_layout = return_on_layout_error!( env, - layout_cache.raw_from_var(env.arena, closure_data_var, env.subs,env.multimorphic_names,) + layout_cache.raw_from_var(env.arena, closure_data_var, env.subs,env.multimorphic_names) ); - let top_level = ProcLayout::from_raw(env.arena, closure_data_layout); + // NB: I don't think the top_level here can capture anything?? + let top_level_capture_niche = &[]; + let top_level = ProcLayout::from_raw(env.arena, closure_data_layout, top_level_capture_niche); let arena = env.arena; @@ -5065,7 +5078,7 @@ pub fn with_hole<'a>( self::Call { call_type: CallType::HigherOrder(arena.alloc(higher_order)), - arguments: arena.alloc([$($x,)* top_level_function, closure_data]), + arguments: arena.alloc([$($x,)* top_level_function.name(), closure_data]), } }, layout, @@ -5268,7 +5281,7 @@ fn find_lambda_name<'a, I>( lambda_set: LambdaSet<'a>, function_name: Symbol, captures: I, -) -> LambdaName +) -> LambdaName<'a> where I: IntoIterator, { @@ -5287,7 +5300,7 @@ where fn construct_closure_data<'a, I>( env: &mut Env<'a, '_>, lambda_set: LambdaSet<'a>, - name: LambdaName, + name: LambdaName<'a>, symbols: I, assigned: Symbol, hole: &'a Stmt<'a>, @@ -5371,11 +5384,7 @@ where debug_assert_eq!(symbols.len(), 0); debug_assert_eq!(lambda_set.set.len(), 2); - debug_assert!( - !name.is_multimorphic(), - "This lambda set has no lambdas that capture, but somehow the name is multimorphic" - ); - let tag_id = name != lambda_set.set[0].0; + let tag_id = name.name() != lambda_set.iter_set().next().unwrap().name(); let expr = Expr::Literal(Literal::Bool(tag_id)); Stmt::Let(assigned, expr, lambda_set_layout, hole) @@ -5384,11 +5393,10 @@ where debug_assert_eq!(symbols.len(), 0); debug_assert!(lambda_set.set.len() > 2); - debug_assert!( - !name.is_multimorphic(), - "This lambda set has no lambdas that capture, but somehow the name is multimorphic" - ); - let tag_id = lambda_set.set.iter().position(|(s, _)| *s == name).unwrap() as u8; + let tag_id = lambda_set + .iter_set() + .position(|s| s.name() == name.name()) + .unwrap() as u8; let expr = Expr::Literal(Literal::Byte(tag_id)); @@ -5711,10 +5719,7 @@ fn tag_union_to_function<'a>( RawFunctionLayout::Function(_, lambda_set, _) => { let lambda_name = find_lambda_name(env, layout_cache, lambda_set, proc_symbol, &[]); - debug_assert!( - !lambda_name.is_multimorphic(), - "no captures, but somehow this lambda is multimorphic" - ); + debug_assert!(lambda_name.no_captures()); construct_closure_data(env, lambda_set, lambda_name, &[], assigned, hole) } RawFunctionLayout::ZeroArgumentThunk(_) => unreachable!(), @@ -6445,8 +6450,8 @@ fn substitute_in_call<'a>( arg_layouts, ret_layout, specialization_id, - } => substitute(subs, *name).map(|new| CallType::ByName { - name: new, + } => substitute(subs, name.name()).map(|new| CallType::ByName { + name: name.replace_name(new), arg_layouts, ret_layout: *ret_layout, specialization_id: *specialization_id, @@ -7245,7 +7250,7 @@ fn force_thunk<'a>( ) -> Stmt<'a> { let call = self::Call { call_type: CallType::ByName { - name: thunk_name, + name: LambdaName::thunk(thunk_name), ret_layout: env.arena.alloc(layout), arg_layouts: &[], specialization_id: env.next_call_specialization_id(), @@ -7293,7 +7298,8 @@ fn specialize_symbol<'a>( }; let raw = RawFunctionLayout::ZeroArgumentThunk(layout); - let top_level = ProcLayout::from_raw(env.arena, raw); + let captures_niche = &[]; + let top_level = ProcLayout::from_raw(env.arena, raw, captures_niche); procs.insert_passed_by_name( env, @@ -7305,12 +7311,13 @@ fn specialize_symbol<'a>( force_thunk(env, original, layout, symbol, env.arena.alloc(result)) } else { - let top_level = ProcLayout::from_raw(env.arena, raw); + // Imported symbol, so it must have no captures niche (since + // top-levels can't capture) + let captures_niche = &[]; + let top_level = ProcLayout::from_raw(env.arena, raw, captures_niche); procs.insert_passed_by_name( env, arg_var, - // Imported symbol, so it must have exactly one receiver (since - // top-levels can't capture) LambdaName::only_receiver(original), top_level, layout_cache, @@ -7351,9 +7358,6 @@ fn specialize_symbol<'a>( match res_layout { RawFunctionLayout::Function(_, lambda_set, _) => { - // define the function pointer - let function_ptr_layout = ProcLayout::from_raw(env.arena, res_layout); - if captures { let symbols = match captured { CapturedSymbols::Captured(captured_symbols) => { @@ -7371,6 +7375,10 @@ fn specialize_symbol<'a>( symbols.iter().copied(), ); + // define the function pointer + let function_ptr_layout = + ProcLayout::from_raw(env.arena, res_layout, lambda_name.captures_niche); + // this is a closure by capture, meaning it itself captures local variables. procs.insert_passed_by_name( env, @@ -7397,7 +7405,7 @@ fn specialize_symbol<'a>( // let layout = Layout::Closure(argument_layouts, lambda_set, ret_layout); // panic!("suspicious"); let layout = Layout::LambdaSet(lambda_set); - let top_level = ProcLayout::new(env.arena, &[], layout); + let top_level = ProcLayout::new(env.arena, &[], &[], layout); procs.insert_passed_by_name( env, arg_var, @@ -7413,10 +7421,11 @@ fn specialize_symbol<'a>( let lambda_name = find_lambda_name(env, layout_cache, lambda_set, original, &[]); - debug_assert!( - !lambda_name.is_multimorphic(), - "no captures, but somehow this symbol is multimorphic" - ); + debug_assert!(lambda_name.no_captures()); + + // define the function pointer + let function_ptr_layout = + ProcLayout::from_raw(env.arena, res_layout, lambda_name.captures_niche); procs.insert_passed_by_name( env, @@ -7438,7 +7447,7 @@ fn specialize_symbol<'a>( } RawFunctionLayout::ZeroArgumentThunk(ret_layout) => { // this is a 0-argument thunk - let top_level = ProcLayout::new(env.arena, &[], ret_layout); + let top_level = ProcLayout::new(env.arena, &[], &[], ret_layout); procs.insert_passed_by_name( env, arg_var, @@ -7511,15 +7520,12 @@ fn add_needed_external<'a>( procs: &mut Procs<'a>, env: &mut Env<'a, '_>, fn_var: Variable, - name: LambdaName, + name: LambdaName<'a>, ) { // call of a function that is not in this module use hashbrown::hash_map::Entry::{Occupied, Vacant}; - let existing = match procs - .externals_we_need - .entry(name.source_name().module_id()) - { + let existing = match procs.externals_we_need.entry(name.name().module_id()) { Vacant(entry) => entry.insert(ExternalSpecializations::new()), Occupied(entry) => entry.into_mut(), }; @@ -7574,7 +7580,7 @@ fn call_by_name<'a>( env: &mut Env<'a, '_>, procs: &mut Procs<'a>, fn_var: Variable, - proc_name: LambdaName, + proc_name: Symbol, loc_args: std::vec::Vec<(Variable, Loc)>, layout_cache: &mut LayoutCache<'a>, assigned: Symbol, @@ -7599,13 +7605,13 @@ fn call_by_name<'a>( evaluate_arguments_then_runtime_error(env, procs, layout_cache, msg, loc_args) } Ok(RawFunctionLayout::Function(arg_layouts, lambda_set, ret_layout)) => { - if procs.is_module_thunk(proc_name.source_name()) { + if procs.is_module_thunk(proc_name) { if loc_args.is_empty() { call_by_name_module_thunk( env, procs, fn_var, - proc_name.call_name(), + proc_name, env.arena.alloc(Layout::LambdaSet(lambda_set)), layout_cache, assigned, @@ -7655,7 +7661,7 @@ fn call_by_name<'a>( env, procs, fn_var, - proc_name.call_name(), + proc_name, env.arena.alloc(Layout::LambdaSet(lambda_set)), layout_cache, closure_data_symbol, @@ -7682,21 +7688,21 @@ fn call_by_name<'a>( } } Ok(RawFunctionLayout::ZeroArgumentThunk(ret_layout)) => { - if procs.is_module_thunk(proc_name.source_name()) { + if procs.is_module_thunk(proc_name) { // here we turn a call to a module thunk into forcing of that thunk call_by_name_module_thunk( env, procs, fn_var, - proc_name.call_name(), + proc_name, env.arena.alloc(ret_layout), layout_cache, assigned, hole, ) - } else if env.is_imported_symbol(proc_name.source_name()) { - add_needed_external(procs, env, fn_var, LambdaName::thunk(proc_name.call_name())); - force_thunk(env, proc_name.call_name(), ret_layout, assigned, hole) + } else if env.is_imported_symbol(proc_name) { + add_needed_external(procs, env, fn_var, LambdaName::thunk(proc_name)); + force_thunk(env, proc_name, ret_layout, assigned, hole) } else { panic!("most likely we're trying to call something that is not a function"); } @@ -7709,7 +7715,7 @@ fn call_by_name_help<'a>( env: &mut Env<'a, '_>, procs: &mut Procs<'a>, fn_var: Variable, - proc_name: LambdaName, + proc_name: Symbol, loc_args: std::vec::Vec<(Variable, Loc)>, lambda_set: LambdaSet<'a>, argument_layouts: &'a [Layout<'a>], @@ -7727,12 +7733,43 @@ fn call_by_name_help<'a>( possible_reuse_symbol_or_specialize(env, procs, layout_cache, &arg_expr.value, *arg_var) })); + // THEORY: with a call by name, there are three options: + // - this is actually a thunk, and the lambda set is empty + // - the name references a function directly, like `main = \x -> ...`. In this case the + // lambda set includes only the function itself, and hence there is exactly one captures + // niche for the function. + // - the name references a value that yields a function, like + // `main = if b then \x -> .. else \y -> ..`. In that case the name being called never + // actually appears in the lambda set, and in fact has no capture set, and hence no + // captures niche. + // So, if this function has any captures niche, it will be the first one. + let mut iter_lambda_names = lambda_set + .iter_set() + .filter(|lam_name| lam_name.name() == proc_name); + let proc_name = match iter_lambda_names.next() { + Some(name) => { + debug_assert!( + iter_lambda_names.next().is_none(), + "Somehow, call by name for {:?} has multiple capture niches: {:?}", + proc_name, + lambda_set + ); + name + } + None => LambdaName::only_receiver(proc_name), + }; + // If required, add an extra argument to the layout that is the captured environment // afterwards, we MUST make sure the number of arguments in the layout matches the // number of arguments actually passed. let top_level_layout = { let argument_layouts = lambda_set.extend_argument_list(env.arena, argument_layouts); - ProcLayout::new(env.arena, argument_layouts, *ret_layout) + ProcLayout::new( + env.arena, + argument_layouts, + proc_name.captures_niche, + *ret_layout, + ) }; // the variables of the given arguments @@ -7753,7 +7790,7 @@ fn call_by_name_help<'a>( // If we've already specialized this one, no further work is needed. if procs .specialized - .is_specialized(proc_name.call_name(), &top_level_layout) + .is_specialized(proc_name.name(), &top_level_layout) { debug_assert_eq!( argument_layouts.len(), @@ -7766,7 +7803,7 @@ fn call_by_name_help<'a>( let call = self::Call { call_type: CallType::ByName { - name: proc_name.call_name(), + name: proc_name, ret_layout, arg_layouts: argument_layouts, specialization_id: env.next_call_specialization_id(), @@ -7778,15 +7815,15 @@ fn call_by_name_help<'a>( let iter = loc_args.into_iter().rev().zip(field_symbols.iter().rev()); assign_to_symbols(env, procs, layout_cache, iter, result) - } else if env.is_imported_symbol(proc_name.source_name()) { + } else if env.is_imported_symbol(proc_name.name()) { add_needed_external(procs, env, original_fn_var, proc_name); - debug_assert_ne!(proc_name.call_name().module_id(), ModuleId::ATTR); + debug_assert_ne!(proc_name.name().module_id(), ModuleId::ATTR); - if procs.is_imported_module_thunk(proc_name.call_name()) { + if procs.is_imported_module_thunk(proc_name.name()) { force_thunk( env, - proc_name.call_name(), + proc_name.name(), Layout::LambdaSet(lambda_set), assigned, hole, @@ -7796,10 +7833,7 @@ fn call_by_name_help<'a>( // imported symbols cannot capture anything let captured = &[]; - debug_assert!( - !proc_name.is_multimorphic(), - "no captures, but somehow this lambda is multimorphic" - ); + debug_assert!(proc_name.no_captures()); construct_closure_data(env, lambda_set, proc_name, captured, assigned, hole) } else { @@ -7814,7 +7848,7 @@ fn call_by_name_help<'a>( let call = self::Call { call_type: CallType::ByName { - name: proc_name.call_name(), + name: proc_name, ret_layout, arg_layouts: argument_layouts, specialization_id: env.next_call_specialization_id(), @@ -7839,13 +7873,13 @@ fn call_by_name_help<'a>( // the same specialization independently), we work through the // queue of pending specializations to complete each specialization // exactly once. - if procs.is_module_thunk(proc_name.source_name()) { + if procs.is_module_thunk(proc_name.name()) { debug_assert!(top_level_layout.arguments.is_empty()); } match &mut procs.pending_specializations { PendingSpecializations::Finding(suspended) => { - debug_assert!(!env.is_imported_symbol(proc_name.source_name())); + debug_assert!(!env.is_imported_symbol(proc_name.name())); // register the pending specialization, so this gets code genned later suspended.specialization(env.subs, proc_name, top_level_layout, fn_var); @@ -7868,7 +7902,7 @@ fn call_by_name_help<'a>( let call = self::Call { call_type: CallType::ByName { - name: proc_name.call_name(), + name: proc_name, ret_layout, arg_layouts: top_level_layout.arguments, specialization_id: env.next_call_specialization_id(), @@ -7885,10 +7919,7 @@ fn call_by_name_help<'a>( let result = assign_to_symbols(env, procs, layout_cache, iter, result); if has_captures { - let partial_proc = procs - .partial_procs - .get_symbol(proc_name.source_name()) - .unwrap(); + let partial_proc = procs.partial_procs.get_symbol(proc_name.name()).unwrap(); let captured = match partial_proc.captured_symbols { CapturedSymbols::None => &[], @@ -7908,7 +7939,7 @@ fn call_by_name_help<'a>( } } PendingSpecializations::Making => { - let opt_partial_proc = procs.partial_procs.symbol_to_id(proc_name.source_name()); + let opt_partial_proc = procs.partial_procs.symbol_to_id(proc_name.name()); let field_symbols = field_symbols.into_bump_slice(); @@ -7919,7 +7950,7 @@ fn call_by_name_help<'a>( // (We had a bug around this before this system existed!) procs .specialized - .mark_in_progress(proc_name.call_name(), top_level_layout); + .mark_in_progress(proc_name.name(), top_level_layout); match specialize_variable( env, @@ -7948,7 +7979,7 @@ fn call_by_name_help<'a>( Err(SpecializeFailure { attempted_layout }) => { let proc = generate_runtime_error_function( env, - proc_name.call_name(), + proc_name.name(), attempted_layout, ); @@ -7988,7 +8019,7 @@ fn call_by_name_module_thunk<'a>( assigned: Symbol, hole: &'a Stmt<'a>, ) -> Stmt<'a> { - let top_level_layout = ProcLayout::new(env.arena, &[], *ret_layout); + let top_level_layout = ProcLayout::new(env.arena, &[], &[], *ret_layout); let inner_layout = *ret_layout; @@ -8115,12 +8146,12 @@ fn call_specialized_proc<'a>( assigned: Symbol, hole: &'a Stmt<'a>, ) -> Stmt<'a> { - let function_layout = ProcLayout::from_raw(env.arena, layout); - let proc_name = proc.name; + let function_layout = ProcLayout::from_raw(env.arena, layout, proc_name.captures_niche); + procs .specialized - .insert_specialized(proc_name.call_name(), function_layout, proc); + .insert_specialized(proc_name.name(), function_layout, proc); if field_symbols.is_empty() { debug_assert!(loc_args.is_empty()); @@ -8137,7 +8168,7 @@ fn call_specialized_proc<'a>( // when the body is a closure, the function will return the closure environment let call = self::Call { call_type: CallType::ByName { - name: proc_name.call_name(), + name: proc_name, ret_layout: env.arena.alloc(function_layout.result), arg_layouts: function_layout.arguments, specialization_id: env.next_call_specialization_id(), @@ -8158,7 +8189,7 @@ fn call_specialized_proc<'a>( match procs .partial_procs - .get_symbol(proc_name.call_name()) + .get_symbol(proc_name.name()) .map(|pp| &pp.captured_symbols) { Some(&CapturedSymbols::Captured(captured_symbols)) => { @@ -8210,7 +8241,7 @@ fn call_specialized_proc<'a>( let call = self::Call { call_type: CallType::ByName { - name: proc_name.call_name(), + name: proc_name, ret_layout: env.arena.alloc(function_layout.result), arg_layouts: function_layout.arguments, specialization_id: env.next_call_specialization_id(), @@ -9095,7 +9126,13 @@ pub fn num_argument_to_int_or_float( } } -type ToLowLevelCallArguments<'a> = (Symbol, Symbol, Option>, CallSpecId, UpdateModeId); +type ToLowLevelCallArguments<'a> = ( + LambdaName<'a>, + Symbol, + Option>, + CallSpecId, + UpdateModeId, +); /// Use the lambda set to figure out how to make a lowlevel call #[allow(clippy::too_many_arguments)] @@ -9118,7 +9155,7 @@ where let result = lowlevel_union_lambda_set_to_switch( env, - lambda_set.set, + lambda_set.iter_set(), closure_tag_id_symbol, union_layout.tag_id_layout(), closure_data_symbol, @@ -9142,12 +9179,12 @@ where env.arena.alloc(result), ) } - Layout::Struct { .. } => match lambda_set.set.get(0) { - Some((function_symbol, _)) => { + Layout::Struct { .. } => match lambda_set.iter_set().next() { + Some(lambda_name) => { let call_spec_id = env.next_call_specialization_id(); let update_mode = env.next_update_mode_id(); let call = to_lowlevel_call(( - function_symbol.call_name(), + lambda_name, closure_data_symbol, lambda_set.is_represented(), call_spec_id, @@ -9172,7 +9209,7 @@ where lowlevel_enum_lambda_set_to_switch( env, - lambda_set.set, + lambda_set.iter_set(), closure_tag_id_symbol, Layout::Builtin(Builtin::Bool), closure_data_symbol, @@ -9188,7 +9225,7 @@ where lowlevel_enum_lambda_set_to_switch( env, - lambda_set.set, + lambda_set.iter_set(), closure_tag_id_symbol, Layout::Builtin(Builtin::Int(IntWidth::U8)), closure_data_symbol, @@ -9206,7 +9243,7 @@ where #[allow(clippy::too_many_arguments)] fn lowlevel_union_lambda_set_to_switch<'a, ToLowLevelCall>( env: &mut Env<'a, '_>, - lambda_set: &'a [(LambdaName, &'a [Layout<'a>])], + lambda_set: impl ExactSizeIterator> + 'a, closure_tag_id_symbol: Symbol, closure_tag_id_layout: Layout<'a>, closure_data_symbol: Symbol, @@ -9219,13 +9256,13 @@ fn lowlevel_union_lambda_set_to_switch<'a, ToLowLevelCall>( where ToLowLevelCall: Fn(ToLowLevelCallArguments<'a>) -> Call<'a> + Copy, { - debug_assert!(!lambda_set.is_empty()); + debug_assert_ne!(lambda_set.len(), 0); let join_point_id = JoinPointId(env.unique_symbol()); let mut branches = Vec::with_capacity_in(lambda_set.len(), env.arena); - for (i, (lambda_name, _)) in lambda_set.iter().enumerate() { + for (i, lambda_name) in lambda_set.into_iter().enumerate() { let assigned = env.unique_symbol(); let hole = Stmt::Jump(join_point_id, env.arena.alloc([assigned])); @@ -9233,7 +9270,7 @@ where let call_spec_id = env.next_call_specialization_id(); let update_mode = env.next_update_mode_id(); let call = to_lowlevel_call(( - lambda_name.call_name(), + lambda_name, closure_data_symbol, closure_env_layout, call_spec_id, @@ -9319,7 +9356,7 @@ fn match_on_lambda_set<'a>( field_layouts, field_order_hash, } => { - let function_symbol = lambda_set.set[0].0; + let function_symbol = lambda_set.iter_set().next().unwrap(); let closure_info = match field_layouts { [] => ClosureInfo::DoesNotCapture, @@ -9349,7 +9386,7 @@ fn match_on_lambda_set<'a>( enum_lambda_set_to_switch( env, - lambda_set.set, + lambda_set.iter_set(), closure_tag_id_symbol, Layout::Builtin(Builtin::Bool), closure_data_symbol, @@ -9365,7 +9402,7 @@ fn match_on_lambda_set<'a>( enum_lambda_set_to_switch( env, - lambda_set.set, + lambda_set.iter_set(), closure_tag_id_symbol, Layout::Builtin(Builtin::Int(IntWidth::U8)), closure_data_symbol, @@ -9408,20 +9445,21 @@ fn union_lambda_set_to_switch<'a>( let mut branches = Vec::with_capacity_in(lambda_set.set.len(), env.arena); - for (i, (function_symbol, capture_layouts)) in lambda_set.set.iter().enumerate() { - let closure_info = match capture_layouts { - [] => ClosureInfo::DoesNotCapture, - _ => ClosureInfo::Captures { + for (i, lambda_name) in lambda_set.iter_set().enumerate() { + let closure_info = if lambda_name.no_captures() { + ClosureInfo::DoesNotCapture + } else { + ClosureInfo::Captures { lambda_set, closure_data_symbol, closure_data_layout: closure_layout, - }, + } }; let stmt = union_lambda_set_branch( env, join_point_id, - *function_symbol, + lambda_name, closure_info, argument_symbols, argument_layouts, @@ -9462,7 +9500,7 @@ fn union_lambda_set_to_switch<'a>( fn union_lambda_set_branch<'a>( env: &mut Env<'a, '_>, join_point_id: JoinPointId, - lambda_name: LambdaName, + lambda_name: LambdaName<'a>, closure_info: ClosureInfo<'a>, argument_symbols_slice: &'a [Symbol], argument_layouts_slice: &'a [Layout<'a>], @@ -9498,7 +9536,7 @@ enum ClosureInfo<'a> { #[allow(clippy::too_many_arguments)] fn union_lambda_set_branch_help<'a>( env: &mut Env<'a, '_>, - lambda_name: LambdaName, + lambda_name: LambdaName<'a>, closure_info: ClosureInfo<'a>, argument_symbols_slice: &'a [Symbol], argument_layouts_slice: &'a [Layout<'a>], @@ -9550,7 +9588,7 @@ fn union_lambda_set_branch_help<'a>( // build the call let call = self::Call { call_type: CallType::ByName { - name: lambda_name.call_name(), + name: lambda_name, ret_layout: return_layout, arg_layouts: argument_layouts, specialization_id: env.next_call_specialization_id(), @@ -9564,7 +9602,7 @@ fn union_lambda_set_branch_help<'a>( #[allow(clippy::too_many_arguments)] fn enum_lambda_set_to_switch<'a>( env: &mut Env<'a, '_>, - lambda_set: &'a [(LambdaName, &'a [Layout<'a>])], + lambda_set: impl ExactSizeIterator>, closure_tag_id_symbol: Symbol, closure_tag_id_layout: Layout<'a>, closure_data_symbol: Symbol, @@ -9574,7 +9612,7 @@ fn enum_lambda_set_to_switch<'a>( assigned: Symbol, hole: &'a Stmt<'a>, ) -> Stmt<'a> { - debug_assert!(!lambda_set.is_empty()); + debug_assert_ne!(lambda_set.len(), 0); let join_point_id = JoinPointId(env.unique_symbol()); @@ -9582,11 +9620,11 @@ fn enum_lambda_set_to_switch<'a>( let closure_layout = closure_tag_id_layout; - for (i, (function_symbol, _)) in lambda_set.iter().enumerate() { + for (i, lambda_name) in lambda_set.into_iter().enumerate() { let stmt = enum_lambda_set_branch( env, join_point_id, - *function_symbol, + lambda_name, closure_data_symbol, closure_layout, argument_symbols, @@ -9628,7 +9666,7 @@ fn enum_lambda_set_to_switch<'a>( fn enum_lambda_set_branch<'a>( env: &mut Env<'a, '_>, join_point_id: JoinPointId, - lambda_name: LambdaName, + lambda_name: LambdaName<'a>, closure_data_symbol: Symbol, closure_data_layout: Layout<'a>, argument_symbols_slice: &'a [Symbol], @@ -9671,7 +9709,7 @@ fn enum_lambda_set_branch<'a>( let call = self::Call { call_type: CallType::ByName { - name: lambda_name.call_name(), + name: lambda_name, ret_layout: return_layout, arg_layouts: argument_layouts, specialization_id: env.next_call_specialization_id(), @@ -9684,7 +9722,7 @@ fn enum_lambda_set_branch<'a>( #[allow(clippy::too_many_arguments)] fn lowlevel_enum_lambda_set_to_switch<'a, ToLowLevelCall>( env: &mut Env<'a, '_>, - lambda_set: &'a [(LambdaName, &'a [Layout<'a>])], + lambda_set: impl ExactSizeIterator>, closure_tag_id_symbol: Symbol, closure_tag_id_layout: Layout<'a>, closure_data_symbol: Symbol, @@ -9697,13 +9735,13 @@ fn lowlevel_enum_lambda_set_to_switch<'a, ToLowLevelCall>( where ToLowLevelCall: Fn(ToLowLevelCallArguments<'a>) -> Call<'a> + Copy, { - debug_assert!(!lambda_set.is_empty()); + debug_assert_ne!(lambda_set.len(), 0); let join_point_id = JoinPointId(env.unique_symbol()); let mut branches = Vec::with_capacity_in(lambda_set.len(), env.arena); - for (i, (function_symbol, _)) in lambda_set.iter().enumerate() { + for (i, function_symbol) in lambda_set.into_iter().enumerate() { let result_symbol = env.unique_symbol(); let hole = Stmt::Jump(join_point_id, env.arena.alloc([result_symbol])); @@ -9711,7 +9749,7 @@ where let call_spec_id = env.next_call_specialization_id(); let update_mode = env.next_update_mode_id(); let call = to_lowlevel_call(( - function_symbol.call_name(), + function_symbol, closure_data_symbol, closure_env_layout, call_spec_id, diff --git a/crates/compiler/mono/src/layout.rs b/crates/compiler/mono/src/layout.rs index 5841dfd167..b08940c175 100644 --- a/crates/compiler/mono/src/layout.rs +++ b/crates/compiler/mono/src/layout.rs @@ -232,7 +232,7 @@ impl<'a> RawFunctionLayout<'a> { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct FieldOrderHash(u64); impl FieldOrderHash { @@ -254,7 +254,7 @@ impl FieldOrderHash { } /// Types for code gen must be monomorphic. No type variables allowed! -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum Layout<'a> { Builtin(Builtin<'a>), Struct { @@ -276,7 +276,7 @@ pub enum Layout<'a> { RecursivePointer, } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum UnionLayout<'a> { /// A non-recursive tag union /// e.g. `Result a e : [Ok a, Err e]` @@ -678,16 +678,13 @@ impl std::fmt::Debug for SetElement<'_> { impl std::fmt::Debug for LambdaSet<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { struct Helper<'a> { - set: &'a [(LambdaName, &'a [Layout<'a>])], + set: &'a [(Symbol, &'a [Layout<'a>])], } impl std::fmt::Debug for Helper<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let entries = self.set.iter().map(|x| SetElement { - symbol: match (x.0).0 { - LambdaNameInner::Name(name) => name, - LambdaNameInner::Multimorphic { alias, .. } => alias, - }, + symbol: x.0, layout: x.1, }); @@ -864,74 +861,61 @@ impl MultimorphicNames { } } +static_assertions::assert_eq_size!(&[Layout], Option<&[Layout]>); + #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] -enum LambdaNameInner { - /// Standard lambda name assigned during canonicalize/constrain - Name(Symbol), +pub struct LambdaName<'a> { + name: Symbol, /// Sometimes we can end up with lambdas of the same name and different captures in the same /// lambda set, like [[Thunk U8, Thunk Str]]. See also https://github.com/rtfeldman/roc/issues/3336. - /// We call such lambdas "multi-morphic". /// - /// The current compilation scheme in such cases is to assign an alias name for subsequent such - /// lambda names, and then code-gen those lambda variants under a different `Proc`. In our - /// example, the lambda set would be transformed to something like - /// [[Thunk U8, Multimorphic(Thunk, ThunkAliasStr) Str]] which tells us to specialize the - /// second variant using the proc `Thunk` but under the name `ThunkAliasStr`, with that - /// particular closure layout. - /// - /// Currently we do no de-duplication of alias names. This does make compilation faster, but - /// also we should expect redundant multimorphic aliases to be somewhat rare, as that means a - /// non-unitary lambda set is the same in multiple areas of a program. - Multimorphic { - /// The lambda we came from, e.g. `Thunk` in the example - source: Symbol, - /// The lambda we become, e.g. `ThunkAliasStr` in the example - alias: Symbol, - }, + /// By recording the captures layouts this lambda expects in its identifier, we can distinguish + /// between such differences. + pub captures_niche: &'a [Layout<'a>], } -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] -pub struct LambdaName(LambdaNameInner); - -impl LambdaName { +impl LambdaName<'_> { #[inline(always)] - pub fn source_name(&self) -> Symbol { - match self.0 { - LambdaNameInner::Name(name) => name, - LambdaNameInner::Multimorphic { source, .. } => source, - } + pub fn name(&self) -> Symbol { + self.name } #[inline(always)] - pub fn call_name(&self) -> Symbol { - match self.0 { - LambdaNameInner::Name(name) => name, - LambdaNameInner::Multimorphic { alias, .. } => alias, - } - } - - #[inline(always)] - pub fn is_multimorphic(&self) -> bool { - matches!(self.0, LambdaNameInner::Multimorphic { .. }) + pub fn no_captures(&self) -> bool { + self.captures_niche.is_empty() } #[inline(always)] pub fn thunk(name: Symbol) -> Self { - Self(LambdaNameInner::Name(name)) + Self { + name, + captures_niche: &[], + } } // When the function name is known, so there can only be one possible receiver, in such cases // the lambda cannot be multimorphic. #[inline(always)] pub fn only_receiver(name: Symbol) -> Self { - Self(LambdaNameInner::Name(name)) + Self { + name, + captures_niche: &[], + } + } + + #[inline(always)] + pub fn replace_name(&self, name: Symbol) -> Self { + Self { + name, + captures_niche: self.captures_niche, + } } } -#[derive(Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct LambdaSet<'a> { /// collection of function names and their closure arguments - pub set: &'a [(LambdaName, &'a [Layout<'a>])], + pub set: &'a [(Symbol, &'a [Layout<'a>])], /// how the closure will be represented at runtime representation: &'a Layout<'a>, } @@ -961,12 +945,8 @@ impl<'a> LambdaSet<'a> { } /// Does the lambda set contain the given symbol? - /// NOTE: for multimorphic variants, this checks the alias name. pub fn contains(&self, symbol: Symbol) -> bool { - self.set.iter().any(|(s, _)| match s.0 { - LambdaNameInner::Name(name) => name == symbol, - LambdaNameInner::Multimorphic { alias, .. } => alias == symbol, - }) + self.set.iter().any(|(s, _)| *s == symbol) } pub fn is_represented(&self) -> Option> { @@ -980,64 +960,54 @@ impl<'a> LambdaSet<'a> { } } + pub fn iter_set(&self) -> impl ExactSizeIterator> { + self.set.iter().map(|(name, captures_layouts)| LambdaName { + name: *name, + captures_niche: captures_layouts, + }) + } + pub fn layout_for_member_with_lambda_name( &self, lambda_name: LambdaName, ) -> ClosureRepresentation<'a> { - debug_assert!( - self.set.iter().any(|(s, _)| *s == lambda_name), - "lambda {:?} not in set {:#?}", - lambda_name, - self.set, - ); + debug_assert!(self.contains(lambda_name.name)); - let comparator = - |other_name: LambdaName, _other_captures_layouts: &[Layout]| other_name == lambda_name; + let comparator = |other_name: Symbol, other_captures_layouts: &[Layout]| { + other_name == lambda_name.name + && other_captures_layouts.iter().eq(lambda_name.captures_niche) + }; self.layout_for_member(comparator) } - fn contains_source(&self, symbol: Symbol) -> bool { - self.set.iter().any(|(s, _)| match s.0 { - LambdaNameInner::Name(name) => name == symbol, - LambdaNameInner::Multimorphic { source, .. } => source == symbol, - }) - } - /// Finds an alias name for a possible-multimorphic lambda variant in the lambda set. pub fn find_lambda_name( &self, function_symbol: Symbol, captures_layouts: &[Layout], - ) -> LambdaName { - debug_assert!( - self.contains_source(function_symbol), - "function symbol not in set" - ); + ) -> LambdaName<'a> { + debug_assert!(self.contains(function_symbol), "function symbol not in set"); - let comparator = |other_name: LambdaName, other_captures_layouts: &[Layout]| { - let other_name = match other_name.0 { - LambdaNameInner::Name(name) => name, - // Take the source, since we'll want to pick out the multimorphic name if it - // matches - LambdaNameInner::Multimorphic { source, .. } => source, - }; - other_name == function_symbol - && captures_layouts.iter().eq(other_captures_layouts.iter()) + let comparator = |other_name: Symbol, other_captures_layouts: &[Layout]| { + other_name == function_symbol && other_captures_layouts.iter().eq(captures_layouts) }; - let (name, _) = self + let (name, layouts) = self .set .iter() .find(|(name, layouts)| comparator(*name, layouts)) .expect("no lambda set found"); - *name + LambdaName { + name: *name, + captures_niche: layouts, + } } fn layout_for_member(&self, comparator: F) -> ClosureRepresentation<'a> where - F: Fn(LambdaName, &[Layout]) -> bool, + F: Fn(Symbol, &[Layout]) -> bool, { match self.representation { Layout::Union(union) => { @@ -1054,10 +1024,7 @@ impl<'a> LambdaSet<'a> { .find(|(_, (s, layouts))| comparator(*s, layouts)) .unwrap(); - let closure_name = match name.0 { - LambdaNameInner::Name(name) => name, - LambdaNameInner::Multimorphic { alias, .. } => alias, - }; + let closure_name = *name; ClosureRepresentation::Union { tag_id: index as TagIdIntType, @@ -1140,15 +1107,14 @@ impl<'a> LambdaSet<'a> { // sort the tags; make sure ordering stays intact! lambdas.sort_by_key(|(sym, _)| *sym); - let mut set: Vec<(LambdaName, &[Layout])> = - Vec::with_capacity_in(lambdas.len(), arena); - let mut set_for_making_repr: std::vec::Vec<(Symbol, std::vec::Vec)> = + let mut set: Vec<(Symbol, &[Layout])> = Vec::with_capacity_in(lambdas.len(), arena); + let mut set_with_variables: std::vec::Vec<(Symbol, std::vec::Vec)> = std::vec::Vec::with_capacity(lambdas.len()); let mut last_function_symbol = None; let mut lambdas_it = lambdas.iter().peekable(); - let mut has_multimorphic = false; + let mut has_duplicate_lambda_names = false; while let Some((function_symbol, variables)) = lambdas_it.next() { let mut arguments = Vec::with_capacity_in(variables.len(), arena); @@ -1174,37 +1140,42 @@ impl<'a> LambdaSet<'a> { } }; - let lambda_name = if is_multimorphic { - let alias = multimorphic_names.get_or_insert(*function_symbol, arguments); + has_duplicate_lambda_names = has_duplicate_lambda_names || is_multimorphic; - has_multimorphic = true; - - LambdaNameInner::Multimorphic { - source: *function_symbol, - alias, - } - } else { - LambdaNameInner::Name(*function_symbol) - }; - let lambda_name = LambdaName(lambda_name); - - set.push((lambda_name, arguments)); - set_for_making_repr.push((lambda_name.call_name(), variables.to_vec())); + set.push((*function_symbol, arguments)); + set_with_variables.push((*function_symbol, variables.to_vec())); last_function_symbol = Some(function_symbol); } - if has_multimorphic { - // Must re-sort the set in case we added multimorphic lambdas since they may under - // another name - set.sort_by_key(|(name, _)| name.call_name()); - set_for_making_repr.sort_by_key(|(name, _)| *name); - } + let (set, set_with_variables) = if has_duplicate_lambda_names { + // If we have a lambda set with duplicate names, then we sort first by name, + // and break ties by sorting on the layout. We need to do this again since the + // first sort would not have sorted on the layout. + + // TODO: be more efficient, we can compute the permutation once and then apply + // it to both vectors. + let mut joined = set + .into_iter() + .zip(set_with_variables.into_iter()) + .collect::>(); + joined.sort_by(|(lam_and_captures1, _), (lam_and_captures2, _)| { + lam_and_captures1.cmp(lam_and_captures2) + }); + let (set, set_with_variables): (std::vec::Vec<_>, std::vec::Vec<_>) = + joined.into_iter().unzip(); + + let set = Vec::from_iter_in(set, arena); + + (set, set_with_variables) + } else { + (set, set_with_variables) + }; let representation = arena.alloc(Self::make_representation( arena, subs, - set_for_making_repr, + set_with_variables, target_info, multimorphic_names, )); @@ -1323,7 +1294,7 @@ fn resolve_lambda_set(subs: &Subs, mut var: Variable) -> ResolvedLambdaSet { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum Builtin<'a> { Int(IntWidth), Float(FloatWidth), @@ -3433,7 +3404,7 @@ mod test { #[test] fn width_and_alignment_union_empty_struct() { let lambda_set = LambdaSet { - set: &[(LambdaName::only_receiver(Symbol::LIST_MAP), &[])], + set: &[(Symbol::LIST_MAP, &[])], representation: &Layout::UNIT, }; diff --git a/crates/compiler/mono/src/tail_recursion.rs b/crates/compiler/mono/src/tail_recursion.rs index 1bfc43d324..5d29f3f527 100644 --- a/crates/compiler/mono/src/tail_recursion.rs +++ b/crates/compiler/mono/src/tail_recursion.rs @@ -1,7 +1,7 @@ #![allow(clippy::manual_map)] use crate::ir::{CallType, Expr, JoinPointId, Param, Stmt}; -use crate::layout::Layout; +use crate::layout::{LambdaName, Layout}; use bumpalo::collections::Vec; use bumpalo::Bump; use roc_module::symbol::Symbol; @@ -31,7 +31,7 @@ use roc_module::symbol::Symbol; pub fn make_tail_recursive<'a>( arena: &'a Bump, id: JoinPointId, - needle: Symbol, + needle: LambdaName, stmt: Stmt<'a>, args: &'a [(Layout<'a>, Symbol, Symbol)], ret_layout: Layout, @@ -71,7 +71,7 @@ fn insert_jumps<'a>( arena: &'a Bump, stmt: &'a Stmt<'a>, goal_id: JoinPointId, - needle: Symbol, + needle: LambdaName, needle_arguments: &'a [(Layout<'a>, Symbol, Symbol)], needle_result: Layout, ) -> Option<&'a Stmt<'a>> { @@ -80,7 +80,7 @@ fn insert_jumps<'a>( // to insert a tail-call, it must not just be a call to the function itself, but it must also // have the same layout. In particular when lambda sets get involved, a self-recursive call may // have a different type and should not be converted to a jump! - let is_equal_function = |function_name: Symbol, arguments: &[_], result| { + let is_equal_function = |function_name: LambdaName, arguments: &[_], result| { let it = needle_arguments.iter().map(|t| &t.0); needle == function_name && it.eq(arguments.iter()) && needle_result == result }; diff --git a/crates/compiler/test_mono/generated/multimorphic_lambda_set_capture.txt b/crates/compiler/test_mono/generated/multimorphic_lambda_set_capture.txt index ba2cb0e203..50b76169e7 100644 --- a/crates/compiler/test_mono/generated/multimorphic_lambda_set_capture.txt +++ b/crates/compiler/test_mono/generated/multimorphic_lambda_set_capture.txt @@ -1,24 +1,29 @@ -procedure #Multimorphic.0 (Test.23, #Attr.12): - let Test.4 : Str = UnionAtIndex (Id 0) (Index 0) #Attr.12; - inc Test.4; - dec #Attr.12; - let Test.25 : Str = ""; - ret Test.25; +procedure Num.94 (#Attr.2): + let Num.273 : Str = lowlevel NumToStr #Attr.2; + ret Num.273; -procedure #Multimorphic.1 (Test.17, #Attr.12): - let Test.4 : {} = UnionAtIndex (Id 1) (Index 0) #Attr.12; - dec #Attr.12; - let Test.19 : Str = ""; - ret Test.19; +procedure Num.94 (#Attr.2): + let Num.274 : Str = lowlevel NumToStr #Attr.2; + ret Num.274; procedure Test.1 (Test.4): - let Test.16 : [C Str, C {}] = ClosureTag(#Multimorphic.1) Test.4; + let Test.16 : [C U8, C U64] = ClosureTag(Test.5) Test.4; ret Test.16; procedure Test.1 (Test.4): - let Test.22 : [C Str, C {}] = ClosureTag(#Multimorphic.0) Test.4; + let Test.22 : [C U8, C U64] = ClosureTag(Test.5) Test.4; ret Test.22; +procedure Test.5 (Test.17, #Attr.12): + let Test.4 : U64 = UnionAtIndex (Id 1) (Index 0) #Attr.12; + let Test.19 : Str = CallByName Num.94 Test.4; + ret Test.19; + +procedure Test.5 (Test.17, #Attr.12): + let Test.4 : U8 = UnionAtIndex (Id 0) (Index 0) #Attr.12; + let Test.25 : Str = CallByName Num.94 Test.4; + ret Test.25; + procedure Test.0 (): let Test.2 : Int1 = true; joinpoint Test.13 Test.3: @@ -29,21 +34,21 @@ procedure Test.0 (): in switch Test.9: case 0: - let Test.11 : Str = CallByName #Multimorphic.0 Test.8 Test.3; + let Test.11 : Str = CallByName Test.5 Test.8 Test.3; jump Test.10 Test.11; default: - let Test.12 : Str = CallByName #Multimorphic.1 Test.8 Test.3; + let Test.12 : Str = CallByName Test.5 Test.8 Test.3; jump Test.10 Test.12; in let Test.26 : Int1 = true; let Test.27 : Int1 = lowlevel Eq Test.26 Test.2; if Test.27 then - let Test.15 : {} = Struct {}; - let Test.14 : [C Str, C {}] = CallByName Test.1 Test.15; + let Test.15 : U64 = 123i64; + let Test.14 : [C U8, C U64] = CallByName Test.1 Test.15; jump Test.13 Test.14; else - let Test.21 : Str = ""; - let Test.20 : [C Str, C {}] = CallByName Test.1 Test.21; + let Test.21 : U8 = 18i64; + let Test.20 : [C U8, C U64] = CallByName Test.1 Test.21; jump Test.13 Test.20; diff --git a/crates/compiler/test_mono/generated/multimorphic_lambdas_have_captured_function_in_closure.txt b/crates/compiler/test_mono/generated/multimorphic_lambdas_have_captured_function_in_closure.txt index 644770023b..d7371b4421 100644 --- a/crates/compiler/test_mono/generated/multimorphic_lambdas_have_captured_function_in_closure.txt +++ b/crates/compiler/test_mono/generated/multimorphic_lambdas_have_captured_function_in_closure.txt @@ -1,24 +1,3 @@ -procedure #Multimorphic.0 (Test.29, #Attr.12): - let Test.8 : {} = UnionAtIndex (Id 0) (Index 1) #Attr.12; - let Test.7 : {} = UnionAtIndex (Id 0) (Index 0) #Attr.12; - let Test.49 : {} = Struct {}; - let Test.48 : Str = CallByName Test.16 Test.49; - let Test.45 : {Str} = CallByName Test.4 Test.48; - let Test.47 : {} = Struct {}; - let Test.46 : Str = CallByName Test.13 Test.47 Test.45; - ret Test.46; - -procedure #Multimorphic.1 (Test.29, #Attr.12): - let Test.8 : {} = UnionAtIndex (Id 1) (Index 1) #Attr.12; - let Test.7 : {} = UnionAtIndex (Id 1) (Index 0) #Attr.12; - let Test.35 : {} = Struct {}; - let Test.34 : Str = CallByName Test.15 Test.35; - let Test.31 : {} = CallByName Test.3 Test.34; - dec Test.34; - let Test.33 : {} = Struct {}; - let Test.32 : Str = CallByName Test.11 Test.33; - ret Test.32; - procedure Test.11 (Test.37): let Test.38 : Str = ""; ret Test.38; @@ -38,11 +17,11 @@ procedure Test.16 (Test.54): ret Test.56; procedure Test.2 (Test.7, Test.8): - let Test.9 : [C {} {}, C {} {}] = ClosureTag(#Multimorphic.0) Test.7 Test.8; + let Test.9 : [C {} {}, C {} {}] = ClosureTag(Test.9) Test.7 Test.8; ret Test.9; procedure Test.2 (Test.7, Test.8): - let Test.9 : [C {} {}, C {} {}] = ClosureTag(#Multimorphic.1) Test.7 Test.8; + let Test.9 : [C {} {}, C {} {}] = ClosureTag(Test.9) Test.7 Test.8; ret Test.9; procedure Test.3 (Test.17): @@ -53,6 +32,27 @@ procedure Test.4 (Test.18): let Test.50 : {Str} = Struct {Test.18}; ret Test.50; +procedure Test.9 (Test.29, #Attr.12): + let Test.8 : {} = UnionAtIndex (Id 0) (Index 1) #Attr.12; + let Test.7 : {} = UnionAtIndex (Id 0) (Index 0) #Attr.12; + let Test.35 : {} = Struct {}; + let Test.34 : Str = CallByName Test.15 Test.35; + let Test.31 : {} = CallByName Test.3 Test.34; + dec Test.34; + let Test.33 : {} = Struct {}; + let Test.32 : Str = CallByName Test.11 Test.33; + ret Test.32; + +procedure Test.9 (Test.29, #Attr.12): + let Test.8 : {} = UnionAtIndex (Id 1) (Index 1) #Attr.12; + let Test.7 : {} = UnionAtIndex (Id 1) (Index 0) #Attr.12; + let Test.49 : {} = Struct {}; + let Test.48 : Str = CallByName Test.16 Test.49; + let Test.45 : {Str} = CallByName Test.4 Test.48; + let Test.47 : {} = Struct {}; + let Test.46 : Str = CallByName Test.13 Test.47 Test.45; + ret Test.46; + procedure Test.0 (): let Test.5 : Int1 = true; joinpoint Test.25 Test.6: @@ -63,11 +63,11 @@ procedure Test.0 (): in switch Test.21: case 0: - let Test.23 : Str = CallByName #Multimorphic.0 Test.20 Test.6; + let Test.23 : Str = CallByName Test.9 Test.20 Test.6; jump Test.22 Test.23; default: - let Test.24 : Str = CallByName #Multimorphic.1 Test.20 Test.6; + let Test.24 : Str = CallByName Test.9 Test.20 Test.6; jump Test.22 Test.24; in diff --git a/crates/compiler/test_mono/generated/multimorphic_lambdas_with_non_capturing_function.txt b/crates/compiler/test_mono/generated/multimorphic_lambdas_with_non_capturing_function.txt index 3212379ff4..6ef000047c 100644 --- a/crates/compiler/test_mono/generated/multimorphic_lambdas_with_non_capturing_function.txt +++ b/crates/compiler/test_mono/generated/multimorphic_lambdas_with_non_capturing_function.txt @@ -1,25 +1,25 @@ -procedure #Multimorphic.0 (Test.28, #Attr.12): - let Test.5 : U64 = UnionAtIndex (Id 0) (Index 0) #Attr.12; - let Test.30 : Str = ""; - ret Test.30; - -procedure #Multimorphic.1 (Test.20, #Attr.12): - let Test.5 : {} = UnionAtIndex (Id 1) (Index 0) #Attr.12; - let Test.22 : Str = ""; - ret Test.22; - procedure Test.1 (Test.5): - let Test.19 : [C U64, C {}, C ] = ClosureTag(#Multimorphic.1) Test.5; + let Test.19 : [C , C U64, C {}] = ClosureTag(Test.6) Test.5; ret Test.19; procedure Test.1 (Test.5): - let Test.27 : [C U64, C {}, C ] = ClosureTag(#Multimorphic.0) Test.5; + let Test.27 : [C , C U64, C {}] = ClosureTag(Test.6) Test.5; ret Test.27; procedure Test.2 (Test.8): let Test.24 : Str = ""; ret Test.24; +procedure Test.6 (Test.20, #Attr.12): + let Test.5 : U64 = UnionAtIndex (Id 1) (Index 0) #Attr.12; + let Test.30 : Str = ""; + ret Test.30; + +procedure Test.6 (Test.20, #Attr.12): + let Test.5 : {} = UnionAtIndex (Id 2) (Index 0) #Attr.12; + let Test.22 : Str = ""; + ret Test.22; + procedure Test.0 (): let Test.3 : U8 = 0u8; joinpoint Test.16 Test.4: @@ -30,30 +30,30 @@ procedure Test.0 (): in switch Test.11: case 0: - let Test.13 : Str = CallByName #Multimorphic.0 Test.10 Test.4; + let Test.13 : Str = CallByName Test.2 Test.10; jump Test.12 Test.13; case 1: - let Test.14 : Str = CallByName #Multimorphic.1 Test.10 Test.4; + let Test.14 : Str = CallByName Test.6 Test.10 Test.4; jump Test.12 Test.14; default: - let Test.15 : Str = CallByName Test.2 Test.10; + let Test.15 : Str = CallByName Test.6 Test.10 Test.4; jump Test.12 Test.15; in switch Test.3: case 0: let Test.18 : {} = Struct {}; - let Test.17 : [C U64, C {}, C ] = CallByName Test.1 Test.18; + let Test.17 : [C , C U64, C {}] = CallByName Test.1 Test.18; jump Test.16 Test.17; case 1: - let Test.2 : [C U64, C {}, C ] = ClosureTag(Test.2) ; + let Test.2 : [C , C U64, C {}] = ClosureTag(Test.2) ; jump Test.16 Test.2; default: let Test.26 : U64 = 1i64; - let Test.25 : [C U64, C {}, C ] = CallByName Test.1 Test.26; + let Test.25 : [C , C U64, C {}] = CallByName Test.1 Test.26; jump Test.16 Test.25; diff --git a/crates/compiler/test_mono/generated/multimorphic_lambdas_with_other_lambda_capture.txt b/crates/compiler/test_mono/generated/multimorphic_lambdas_with_other_lambda_capture.txt index 4477e9c8d4..b553adfbc1 100644 --- a/crates/compiler/test_mono/generated/multimorphic_lambdas_with_other_lambda_capture.txt +++ b/crates/compiler/test_mono/generated/multimorphic_lambdas_with_other_lambda_capture.txt @@ -1,27 +1,27 @@ -procedure #Multimorphic.0 (Test.33, #Attr.12): - let Test.5 : U64 = UnionAtIndex (Id 0) (Index 0) #Attr.12; - dec #Attr.12; - let Test.35 : Str = ""; - ret Test.35; - -procedure #Multimorphic.1 (Test.21, #Attr.12): - let Test.5 : {} = UnionAtIndex (Id 1) (Index 0) #Attr.12; - dec #Attr.12; - let Test.23 : Str = ""; - ret Test.23; - procedure Test.1 (Test.5): - let Test.20 : [C U64, C {}, C Str] = ClosureTag(#Multimorphic.1) Test.5; + let Test.20 : [C U64, C {}, C Str] = ClosureTag(Test.6) Test.5; ret Test.20; procedure Test.1 (Test.5): - let Test.32 : [C U64, C {}, C Str] = ClosureTag(#Multimorphic.0) Test.5; + let Test.32 : [C U64, C {}, C Str] = ClosureTag(Test.6) Test.5; ret Test.32; procedure Test.2 (Test.7): let Test.26 : [C U64, C {}, C Str] = ClosureTag(Test.8) Test.7; ret Test.26; +procedure Test.6 (Test.21, #Attr.12): + let Test.5 : U64 = UnionAtIndex (Id 0) (Index 0) #Attr.12; + dec #Attr.12; + let Test.35 : Str = ""; + ret Test.35; + +procedure Test.6 (Test.21, #Attr.12): + let Test.5 : {} = UnionAtIndex (Id 1) (Index 0) #Attr.12; + dec #Attr.12; + let Test.23 : Str = ""; + ret Test.23; + procedure Test.8 (Test.27, #Attr.12): let Test.7 : Str = UnionAtIndex (Id 2) (Index 0) #Attr.12; inc Test.7; @@ -38,11 +38,11 @@ procedure Test.0 (): in switch Test.12: case 0: - let Test.14 : Str = CallByName #Multimorphic.0 Test.11 Test.4; + let Test.14 : Str = CallByName Test.6 Test.11 Test.4; jump Test.13 Test.14; case 1: - let Test.15 : Str = CallByName #Multimorphic.1 Test.11 Test.4; + let Test.15 : Str = CallByName Test.6 Test.11 Test.4; jump Test.13 Test.15; default: diff --git a/crates/compiler/test_mono/src/tests.rs b/crates/compiler/test_mono/src/tests.rs index b189a0e3a4..e1ca980d51 100644 --- a/crates/compiler/test_mono/src/tests.rs +++ b/crates/compiler/test_mono/src/tests.rs @@ -1530,19 +1530,18 @@ fn tail_call_with_different_layout() { fn multimorphic_lambda_set_capture() { indoc!( r#" - capture : a -> ({} -> Str) + capture : _ -> ({} -> Str) capture = \val -> \{} -> - when val is - _ -> "" + Num.toStr val x : [True, False] x = True fun = when x is - True -> capture {} - False -> capture "" + True -> capture 123u64 + False -> capture 18u8 fun {} "# diff --git a/crates/repl_eval/src/eval.rs b/crates/repl_eval/src/eval.rs index 8d323920d1..c1941c5ae0 100644 --- a/crates/repl_eval/src/eval.rs +++ b/crates/repl_eval/src/eval.rs @@ -61,6 +61,7 @@ pub fn jit_to_ast<'a, A: ReplApp<'a>>( ProcLayout { arguments: [], result, + .. } => { // this is a thunk jit_to_ast_help(&mut env, app, main_fn_name, &result, content) From d08aecf55fe6e2c34b6aa9c4b8cd1f2aa11aa97c Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Fri, 1 Jul 2022 15:56:06 -0400 Subject: [PATCH 25/66] Rename multimorphic tests to capture niche --- ...cialize_errors_behind_unified_branches.txt | 65 ++++++++++++++ compiler/test_mono/generated/list_get.txt | 35 ++++++++ .../generated/list_pass_to_function.txt | 39 +++++++++ .../test_mono/generated/closure_in_list.txt | 10 +++ crates/compiler/test_mono/generated/dict.txt | 8 +- .../generated/empty_list_of_function_type.txt | 41 ++++++++- .../compiler/test_mono/generated/encode.txt | 10 +++ .../test_mono/generated/factorial.txt | 8 +- .../if_guard_bind_variable_false.txt | 4 +- .../test_mono/generated/ir_int_add.txt | 14 ++- .../compiler/test_mono/generated/ir_plus.txt | 4 +- .../compiler/test_mono/generated/ir_round.txt | 4 +- .../test_mono/generated/ir_two_defs.txt | 4 +- .../test_mono/generated/ir_when_idiv.txt | 18 ++-- .../test_mono/generated/ir_when_just.txt | 4 +- .../lambda_capture_niche_u8_vs_u64.txt | 54 ++++++++++++ ...ches_have_captured_function_in_closure.txt | 85 +++++++++++++++++++ ...ure_niches_with_non_capturing_function.txt | 59 +++++++++++++ ...pture_niches_with_other_lambda_capture.txt | 68 +++++++++++++++ .../test_mono/generated/list_append.txt | 10 +++ .../generated/list_append_closure.txt | 10 +++ .../generated/list_cannot_update_inplace.txt | 56 +++++++++++- .../compiler/test_mono/generated/list_len.txt | 22 ++++- .../generated/list_map_closure_borrows.txt | 54 +++++++++++- .../generated/list_map_closure_owns.txt | 52 +++++++++++- .../test_mono/generated/list_sort_asc.txt | 30 ++++++- .../multimorphic_lambda_set_capture.txt | 8 +- .../generated/nested_pattern_match.txt | 4 +- .../test_mono/generated/optional_when.txt | 4 +- .../test_mono/generated/quicksort_help.txt | 12 +-- .../test_mono/generated/quicksort_swap.txt | 62 +++++++++++++- ...optional_field_function_no_use_default.txt | 4 +- ...rd_optional_field_function_use_default.txt | 4 +- ...cord_optional_field_let_no_use_default.txt | 4 +- .../record_optional_field_let_use_default.txt | 4 +- .../compiler/test_mono/generated/rigids.txt | 62 +++++++++++++- .../generated/specialize_closures.txt | 8 +- .../generated/specialize_lowlevel.txt | 8 +- .../generated/tail_call_elimination.txt | 8 +- .../test_mono/generated/when_nested_maybe.txt | 4 +- .../test_mono/generated/when_on_record.txt | 4 +- .../generated/when_on_two_values.txt | 4 +- crates/compiler/test_mono/src/tests.rs | 8 +- 43 files changed, 889 insertions(+), 91 deletions(-) create mode 100644 compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt create mode 100644 compiler/test_mono/generated/list_get.txt create mode 100644 compiler/test_mono/generated/list_pass_to_function.txt create mode 100644 crates/compiler/test_mono/generated/lambda_capture_niche_u8_vs_u64.txt create mode 100644 crates/compiler/test_mono/generated/lambda_capture_niches_have_captured_function_in_closure.txt create mode 100644 crates/compiler/test_mono/generated/lambda_capture_niches_with_non_capturing_function.txt create mode 100644 crates/compiler/test_mono/generated/lambda_capture_niches_with_other_lambda_capture.txt diff --git a/compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt b/compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt new file mode 100644 index 0000000000..23285418ff --- /dev/null +++ b/compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt @@ -0,0 +1,65 @@ +procedure List.2 (List.71, List.72): + let List.228 : U64 = CallByName List.6 List.71; + let List.224 : Int1 = CallByName Num.22 List.72 List.228; + if List.224 then + let List.226 : I64 = CallByName List.60 List.71 List.72; + let List.225 : [C {}, C I64] = Ok List.226; + ret List.225; + else + let List.223 : {} = Struct {}; + let List.222 : [C {}, C I64] = Err List.223; + ret List.222; + +procedure List.6 (#Attr.2): + let List.229 : U64 = lowlevel ListLen #Attr.2; + ret List.229; + +procedure List.60 (#Attr.2, #Attr.3): + let List.227 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.227; + +procedure List.9 (List.147): + let List.220 : U64 = 0i64; + let List.213 : [C {}, C I64] = CallByName List.2 List.147 List.220; + let List.217 : U8 = 1i64; + let List.218 : U8 = GetTagId List.213; + let List.219 : Int1 = lowlevel Eq List.217 List.218; + if List.219 then + let List.148 : I64 = UnionAtIndex (Id 1) (Index 0) List.213; + let List.214 : [C Int1, C I64] = Ok List.148; + ret List.214; + else + let List.216 : Int1 = true; + let List.215 : [C Int1, C I64] = Err List.216; + ret List.215; + +procedure Num.22 (#Attr.2, #Attr.3): + let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.188; + +procedure Str.27 (#Attr.2): + let #Attr.3 : {I64, U8} = lowlevel StrToNum #Attr.2; + let Str.41 : U8 = StructAtIndex 1 #Attr.3; + let Str.42 : U8 = 0i64; + let Str.38 : Int1 = lowlevel NumGt Str.41 Str.42; + if Str.38 then + let Str.40 : Int1 = false; + let Str.39 : [C Int1, C I64] = Err Str.40; + ret Str.39; + else + let Str.37 : I64 = StructAtIndex 0 #Attr.3; + let Str.36 : [C Int1, C I64] = Ok Str.37; + ret Str.36; + +procedure Test.0 (): + let Test.4 : Int1 = true; + if Test.4 then + let Test.6 : List I64 = Array []; + let Test.5 : [C Int1, C I64] = CallByName List.9 Test.6; + dec Test.6; + ret Test.5; + else + let Test.3 : Str = ""; + let Test.2 : [C Int1, C I64] = CallByName Str.27 Test.3; + dec Test.3; + ret Test.2; diff --git a/compiler/test_mono/generated/list_get.txt b/compiler/test_mono/generated/list_get.txt new file mode 100644 index 0000000000..44e45be867 --- /dev/null +++ b/compiler/test_mono/generated/list_get.txt @@ -0,0 +1,35 @@ +procedure List.2 (List.71, List.72): + let List.219 : U64 = CallByName List.6 List.71; + let List.215 : Int1 = CallByName Num.22 List.72 List.219; + if List.215 then + let List.217 : I64 = CallByName List.60 List.71 List.72; + let List.216 : [C {}, C I64] = Ok List.217; + ret List.216; + else + let List.214 : {} = Struct {}; + let List.213 : [C {}, C I64] = Err List.214; + ret List.213; + +procedure List.6 (#Attr.2): + let List.222 : U64 = lowlevel ListLen #Attr.2; + ret List.222; + +procedure List.60 (#Attr.2, #Attr.3): + let List.221 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.221; + +procedure Num.22 (#Attr.2, #Attr.3): + let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.188; + +procedure Test.1 (Test.2): + let Test.6 : List I64 = Array [1i64, 2i64, 3i64]; + let Test.7 : U64 = 0i64; + let Test.5 : [C {}, C I64] = CallByName List.2 Test.6 Test.7; + dec Test.6; + ret Test.5; + +procedure Test.0 (): + let Test.4 : {} = Struct {}; + let Test.3 : [C {}, C I64] = CallByName Test.1 Test.4; + ret Test.3; diff --git a/compiler/test_mono/generated/list_pass_to_function.txt b/compiler/test_mono/generated/list_pass_to_function.txt new file mode 100644 index 0000000000..6d7eba5654 --- /dev/null +++ b/compiler/test_mono/generated/list_pass_to_function.txt @@ -0,0 +1,39 @@ +procedure List.3 (List.79, List.80, List.81): + let List.214 : {List I64, I64} = CallByName List.57 List.79 List.80 List.81; + let List.213 : List I64 = StructAtIndex 0 List.214; + inc List.213; + dec List.214; + ret List.213; + +procedure List.57 (List.76, List.77, List.78): + let List.220 : U64 = CallByName List.6 List.76; + let List.217 : Int1 = CallByName Num.22 List.77 List.220; + if List.217 then + let List.218 : {List I64, I64} = CallByName List.61 List.76 List.77 List.78; + ret List.218; + else + let List.216 : {List I64, I64} = Struct {List.76, List.78}; + ret List.216; + +procedure List.6 (#Attr.2): + let List.221 : U64 = lowlevel ListLen #Attr.2; + ret List.221; + +procedure List.61 (#Attr.2, #Attr.3, #Attr.4): + let List.219 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; + ret List.219; + +procedure Num.22 (#Attr.2, #Attr.3): + let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.188; + +procedure Test.2 (Test.3): + let Test.6 : U64 = 0i64; + let Test.7 : I64 = 0i64; + let Test.5 : List I64 = CallByName List.3 Test.3 Test.6 Test.7; + ret Test.5; + +procedure Test.0 (): + let Test.1 : List I64 = Array [1i64, 2i64, 3i64]; + let Test.4 : List I64 = CallByName Test.2 Test.1; + ret Test.4; diff --git a/crates/compiler/test_mono/generated/closure_in_list.txt b/crates/compiler/test_mono/generated/closure_in_list.txt index e26803f469..643a75fdad 100644 --- a/crates/compiler/test_mono/generated/closure_in_list.txt +++ b/crates/compiler/test_mono/generated/closure_in_list.txt @@ -1,6 +1,16 @@ procedure List.6 (#Attr.2): +<<<<<<< HEAD let List.284 : U64 = lowlevel ListLen #Attr.2; ret List.284; +======= +<<<<<<< HEAD:crates/compiler/test_mono/generated/closure_in_list.txt + let List.259 : U64 = lowlevel ListLen #Attr.2; + ret List.259; +======= + let List.213 : U64 = lowlevel ListLen #Attr.2; + ret List.213; +>>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/closure_in_list.txt +>>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) procedure Test.1 (Test.5): let Test.2 : I64 = 41i64; diff --git a/crates/compiler/test_mono/generated/dict.txt b/crates/compiler/test_mono/generated/dict.txt index 31ae33d719..bd378e9414 100644 --- a/crates/compiler/test_mono/generated/dict.txt +++ b/crates/compiler/test_mono/generated/dict.txt @@ -1,11 +1,11 @@ procedure Dict.1 (): - let Dict.28 : Dict [] [] = lowlevel DictEmpty ; - ret Dict.28; + let Dict.16 : Dict [] [] = lowlevel DictEmpty ; + ret Dict.16; procedure Dict.7 (#Attr.2): - let Dict.27 : U64 = lowlevel DictSize #Attr.2; + let Dict.15 : U64 = lowlevel DictSize #Attr.2; dec #Attr.2; - ret Dict.27; + ret Dict.15; procedure Test.0 (): let Test.2 : Dict [] [] = CallByName Dict.1; diff --git a/crates/compiler/test_mono/generated/empty_list_of_function_type.txt b/crates/compiler/test_mono/generated/empty_list_of_function_type.txt index 617e58feba..d1ffaafe5b 100644 --- a/crates/compiler/test_mono/generated/empty_list_of_function_type.txt +++ b/crates/compiler/test_mono/generated/empty_list_of_function_type.txt @@ -1,3 +1,4 @@ +<<<<<<< HEAD procedure List.2 (List.75, List.76): let List.290 : U64 = CallByName List.6 List.75; let List.286 : Int1 = CallByName Num.22 List.76 List.290; @@ -5,6 +6,16 @@ procedure List.2 (List.75, List.76): let List.288 : {} = CallByName List.60 List.75 List.76; let List.287 : [C {}, C {}] = Ok List.288; ret List.287; +======= +<<<<<<< HEAD:crates/compiler/test_mono/generated/empty_list_of_function_type.txt +procedure List.2 (List.73, List.74): + let List.265 : U64 = CallByName List.6 List.73; + let List.261 : Int1 = CallByName Num.22 List.74 List.265; + if List.261 then + let List.263 : {} = CallByName List.60 List.73 List.74; + let List.262 : [C {}, C {}] = Ok List.263; + ret List.262; +>>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) else let List.285 : {} = Struct {}; let List.284 : [C {}, C {}] = Err List.285; @@ -15,12 +26,38 @@ procedure List.6 (#Attr.2): ret List.293; procedure List.60 (#Attr.2, #Attr.3): +<<<<<<< HEAD let List.292 : {} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; ret List.292; +======= + let List.267 : {} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.267; +======= +procedure List.2 (List.71, List.72): + let List.219 : U64 = CallByName List.6 List.71; + let List.215 : Int1 = CallByName Num.22 List.72 List.219; + if List.215 then + let List.217 : {} = CallByName List.60 List.71 List.72; + let List.216 : [C {}, C {}] = Ok List.217; + ret List.216; + else + let List.214 : {} = Struct {}; + let List.213 : [C {}, C {}] = Err List.214; + ret List.213; + +procedure List.6 (#Attr.2): + let List.222 : U64 = lowlevel ListLen #Attr.2; + ret List.222; + +procedure List.60 (#Attr.2, #Attr.3): + let List.221 : {} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.221; +>>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/empty_list_of_function_type.txt +>>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) procedure Num.22 (#Attr.2, #Attr.3): - let Num.273 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.273; + let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.188; procedure Test.2 (Test.6): let Test.18 : Str = "bar"; diff --git a/crates/compiler/test_mono/generated/encode.txt b/crates/compiler/test_mono/generated/encode.txt index cf24697b6f..7115be0912 100644 --- a/crates/compiler/test_mono/generated/encode.txt +++ b/crates/compiler/test_mono/generated/encode.txt @@ -1,6 +1,16 @@ procedure List.4 (#Attr.2, #Attr.3): +<<<<<<< HEAD let List.284 : List U8 = lowlevel ListAppend #Attr.2 #Attr.3; ret List.284; +======= +<<<<<<< HEAD:crates/compiler/test_mono/generated/encode.txt + let List.259 : List U8 = lowlevel ListAppend #Attr.2 #Attr.3; + ret List.259; +======= + let List.213 : List U8 = lowlevel ListAppend #Attr.2 #Attr.3; + ret List.213; +>>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/encode.txt +>>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) procedure Test.20 (Test.22): let Test.34 : {U8} = Struct {Test.22}; diff --git a/crates/compiler/test_mono/generated/factorial.txt b/crates/compiler/test_mono/generated/factorial.txt index a0ad7c91ac..165dc059a3 100644 --- a/crates/compiler/test_mono/generated/factorial.txt +++ b/crates/compiler/test_mono/generated/factorial.txt @@ -1,10 +1,10 @@ procedure Num.20 (#Attr.2, #Attr.3): - let Num.274 : I64 = lowlevel NumSub #Attr.2 #Attr.3; - ret Num.274; + let Num.189 : I64 = lowlevel NumSub #Attr.2 #Attr.3; + ret Num.189; procedure Num.21 (#Attr.2, #Attr.3): - let Num.273 : I64 = lowlevel NumMul #Attr.2 #Attr.3; - ret Num.273; + let Num.188 : I64 = lowlevel NumMul #Attr.2 #Attr.3; + ret Num.188; procedure Test.1 (Test.15, Test.16): joinpoint Test.7 Test.2 Test.3: diff --git a/crates/compiler/test_mono/generated/if_guard_bind_variable_false.txt b/crates/compiler/test_mono/generated/if_guard_bind_variable_false.txt index cf492ae71d..7d73011b72 100644 --- a/crates/compiler/test_mono/generated/if_guard_bind_variable_false.txt +++ b/crates/compiler/test_mono/generated/if_guard_bind_variable_false.txt @@ -1,6 +1,6 @@ procedure Bool.7 (#Attr.2, #Attr.3): - let Bool.14 : Int1 = lowlevel Eq #Attr.2 #Attr.3; - ret Bool.14; + let Bool.9 : Int1 = lowlevel Eq #Attr.2 #Attr.3; + ret Bool.9; procedure Test.1 (Test.3): let Test.6 : I64 = 10i64; diff --git a/crates/compiler/test_mono/generated/ir_int_add.txt b/crates/compiler/test_mono/generated/ir_int_add.txt index 60f044c4cd..11f2565ef7 100644 --- a/crates/compiler/test_mono/generated/ir_int_add.txt +++ b/crates/compiler/test_mono/generated/ir_int_add.txt @@ -1,10 +1,20 @@ procedure List.6 (#Attr.2): +<<<<<<< HEAD let List.284 : U64 = lowlevel ListLen #Attr.2; ret List.284; +======= +<<<<<<< HEAD:crates/compiler/test_mono/generated/ir_int_add.txt + let List.259 : U64 = lowlevel ListLen #Attr.2; + ret List.259; +======= + let List.213 : U64 = lowlevel ListLen #Attr.2; + ret List.213; +>>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/ir_int_add.txt +>>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) procedure Num.19 (#Attr.2, #Attr.3): - let Num.275 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.275; + let Num.190 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.190; procedure Test.0 (): let Test.1 : List I64 = Array [1i64, 2i64]; diff --git a/crates/compiler/test_mono/generated/ir_plus.txt b/crates/compiler/test_mono/generated/ir_plus.txt index 8a3eff1485..7a8550c5e0 100644 --- a/crates/compiler/test_mono/generated/ir_plus.txt +++ b/crates/compiler/test_mono/generated/ir_plus.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.273 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.273; + let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.188; procedure Test.0 (): let Test.2 : I64 = 1i64; diff --git a/crates/compiler/test_mono/generated/ir_round.txt b/crates/compiler/test_mono/generated/ir_round.txt index 19f78559ec..233527563b 100644 --- a/crates/compiler/test_mono/generated/ir_round.txt +++ b/crates/compiler/test_mono/generated/ir_round.txt @@ -1,6 +1,6 @@ procedure Num.45 (#Attr.2): - let Num.273 : I64 = lowlevel NumRound #Attr.2; - ret Num.273; + let Num.188 : I64 = lowlevel NumRound #Attr.2; + ret Num.188; procedure Test.0 (): let Test.2 : Float64 = 3.6f64; diff --git a/crates/compiler/test_mono/generated/ir_two_defs.txt b/crates/compiler/test_mono/generated/ir_two_defs.txt index bdfba4a214..52214a8c65 100644 --- a/crates/compiler/test_mono/generated/ir_two_defs.txt +++ b/crates/compiler/test_mono/generated/ir_two_defs.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.273 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.273; + let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.188; procedure Test.0 (): let Test.1 : I64 = 3i64; diff --git a/crates/compiler/test_mono/generated/ir_when_idiv.txt b/crates/compiler/test_mono/generated/ir_when_idiv.txt index a4c3915acf..9706bd86fa 100644 --- a/crates/compiler/test_mono/generated/ir_when_idiv.txt +++ b/crates/compiler/test_mono/generated/ir_when_idiv.txt @@ -1,14 +1,14 @@ procedure Num.40 (#Attr.2, #Attr.3): - let Num.278 : I64 = 0i64; - let Num.275 : Int1 = lowlevel NotEq #Attr.3 Num.278; - if Num.275 then - let Num.277 : I64 = lowlevel NumDivUnchecked #Attr.2 #Attr.3; - let Num.276 : [C {}, C I64] = Ok Num.277; - ret Num.276; + let Num.193 : I64 = 0i64; + let Num.190 : Int1 = lowlevel NotEq #Attr.3 Num.193; + if Num.190 then + let Num.192 : I64 = lowlevel NumDivUnchecked #Attr.2 #Attr.3; + let Num.191 : [C {}, C I64] = Ok Num.192; + ret Num.191; else - let Num.274 : {} = Struct {}; - let Num.273 : [C {}, C I64] = Err Num.274; - ret Num.273; + let Num.189 : {} = Struct {}; + let Num.188 : [C {}, C I64] = Err Num.189; + ret Num.188; procedure Test.0 (): let Test.8 : I64 = 1000i64; diff --git a/crates/compiler/test_mono/generated/ir_when_just.txt b/crates/compiler/test_mono/generated/ir_when_just.txt index d6619036ab..8959b2d342 100644 --- a/crates/compiler/test_mono/generated/ir_when_just.txt +++ b/crates/compiler/test_mono/generated/ir_when_just.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.273 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.273; + let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.188; procedure Test.0 (): let Test.10 : I64 = 41i64; diff --git a/crates/compiler/test_mono/generated/lambda_capture_niche_u8_vs_u64.txt b/crates/compiler/test_mono/generated/lambda_capture_niche_u8_vs_u64.txt new file mode 100644 index 0000000000..b7cc9bd341 --- /dev/null +++ b/crates/compiler/test_mono/generated/lambda_capture_niche_u8_vs_u64.txt @@ -0,0 +1,54 @@ +procedure Num.94 (#Attr.2): + let Num.188 : Str = lowlevel NumToStr #Attr.2; + ret Num.188; + +procedure Num.94 (#Attr.2): + let Num.189 : Str = lowlevel NumToStr #Attr.2; + ret Num.189; + +procedure Test.1 (Test.4): + let Test.16 : [C U8, C U64] = ClosureTag(Test.5) Test.4; + ret Test.16; + +procedure Test.1 (Test.4): + let Test.22 : [C U8, C U64] = ClosureTag(Test.5) Test.4; + ret Test.22; + +procedure Test.5 (Test.17, #Attr.12): + let Test.4 : U64 = UnionAtIndex (Id 1) (Index 0) #Attr.12; + let Test.19 : Str = CallByName Num.94 Test.4; + ret Test.19; + +procedure Test.5 (Test.17, #Attr.12): + let Test.4 : U8 = UnionAtIndex (Id 0) (Index 0) #Attr.12; + let Test.25 : Str = CallByName Num.94 Test.4; + ret Test.25; + +procedure Test.0 (): + let Test.2 : Int1 = true; + joinpoint Test.13 Test.3: + let Test.8 : {} = Struct {}; + let Test.9 : U8 = GetTagId Test.3; + joinpoint Test.10 Test.7: + ret Test.7; + in + switch Test.9: + case 0: + let Test.11 : Str = CallByName Test.5 Test.8 Test.3; + jump Test.10 Test.11; + + default: + let Test.12 : Str = CallByName Test.5 Test.8 Test.3; + jump Test.10 Test.12; + + in + let Test.26 : Int1 = true; + let Test.27 : Int1 = lowlevel Eq Test.26 Test.2; + if Test.27 then + let Test.15 : U64 = 123i64; + let Test.14 : [C U8, C U64] = CallByName Test.1 Test.15; + jump Test.13 Test.14; + else + let Test.21 : U8 = 18i64; + let Test.20 : [C U8, C U64] = CallByName Test.1 Test.21; + jump Test.13 Test.20; diff --git a/crates/compiler/test_mono/generated/lambda_capture_niches_have_captured_function_in_closure.txt b/crates/compiler/test_mono/generated/lambda_capture_niches_have_captured_function_in_closure.txt new file mode 100644 index 0000000000..d7371b4421 --- /dev/null +++ b/crates/compiler/test_mono/generated/lambda_capture_niches_have_captured_function_in_closure.txt @@ -0,0 +1,85 @@ +procedure Test.11 (Test.37): + let Test.38 : Str = ""; + ret Test.38; + +procedure Test.13 (Test.51, #Attr.12): + let Test.12 : Str = StructAtIndex 0 #Attr.12; + inc Test.12; + dec #Attr.12; + ret Test.12; + +procedure Test.15 (Test.39): + let Test.40 : Str = ""; + ret Test.40; + +procedure Test.16 (Test.54): + let Test.56 : Str = "s1"; + ret Test.56; + +procedure Test.2 (Test.7, Test.8): + let Test.9 : [C {} {}, C {} {}] = ClosureTag(Test.9) Test.7 Test.8; + ret Test.9; + +procedure Test.2 (Test.7, Test.8): + let Test.9 : [C {} {}, C {} {}] = ClosureTag(Test.9) Test.7 Test.8; + ret Test.9; + +procedure Test.3 (Test.17): + let Test.36 : {} = Struct {}; + ret Test.36; + +procedure Test.4 (Test.18): + let Test.50 : {Str} = Struct {Test.18}; + ret Test.50; + +procedure Test.9 (Test.29, #Attr.12): + let Test.8 : {} = UnionAtIndex (Id 0) (Index 1) #Attr.12; + let Test.7 : {} = UnionAtIndex (Id 0) (Index 0) #Attr.12; + let Test.35 : {} = Struct {}; + let Test.34 : Str = CallByName Test.15 Test.35; + let Test.31 : {} = CallByName Test.3 Test.34; + dec Test.34; + let Test.33 : {} = Struct {}; + let Test.32 : Str = CallByName Test.11 Test.33; + ret Test.32; + +procedure Test.9 (Test.29, #Attr.12): + let Test.8 : {} = UnionAtIndex (Id 1) (Index 1) #Attr.12; + let Test.7 : {} = UnionAtIndex (Id 1) (Index 0) #Attr.12; + let Test.49 : {} = Struct {}; + let Test.48 : Str = CallByName Test.16 Test.49; + let Test.45 : {Str} = CallByName Test.4 Test.48; + let Test.47 : {} = Struct {}; + let Test.46 : Str = CallByName Test.13 Test.47 Test.45; + ret Test.46; + +procedure Test.0 (): + let Test.5 : Int1 = true; + joinpoint Test.25 Test.6: + let Test.20 : {} = Struct {}; + let Test.21 : U8 = GetTagId Test.6; + joinpoint Test.22 Test.19: + ret Test.19; + in + switch Test.21: + case 0: + let Test.23 : Str = CallByName Test.9 Test.20 Test.6; + jump Test.22 Test.23; + + default: + let Test.24 : Str = CallByName Test.9 Test.20 Test.6; + jump Test.22 Test.24; + + in + let Test.57 : Int1 = true; + let Test.58 : Int1 = lowlevel Eq Test.57 Test.5; + if Test.58 then + let Test.27 : {} = Struct {}; + let Test.28 : {} = Struct {}; + let Test.26 : [C {} {}, C {} {}] = CallByName Test.2 Test.27 Test.28; + jump Test.25 Test.26; + else + let Test.42 : {} = Struct {}; + let Test.43 : {} = Struct {}; + let Test.41 : [C {} {}, C {} {}] = CallByName Test.2 Test.42 Test.43; + jump Test.25 Test.41; diff --git a/crates/compiler/test_mono/generated/lambda_capture_niches_with_non_capturing_function.txt b/crates/compiler/test_mono/generated/lambda_capture_niches_with_non_capturing_function.txt new file mode 100644 index 0000000000..6ef000047c --- /dev/null +++ b/crates/compiler/test_mono/generated/lambda_capture_niches_with_non_capturing_function.txt @@ -0,0 +1,59 @@ +procedure Test.1 (Test.5): + let Test.19 : [C , C U64, C {}] = ClosureTag(Test.6) Test.5; + ret Test.19; + +procedure Test.1 (Test.5): + let Test.27 : [C , C U64, C {}] = ClosureTag(Test.6) Test.5; + ret Test.27; + +procedure Test.2 (Test.8): + let Test.24 : Str = ""; + ret Test.24; + +procedure Test.6 (Test.20, #Attr.12): + let Test.5 : U64 = UnionAtIndex (Id 1) (Index 0) #Attr.12; + let Test.30 : Str = ""; + ret Test.30; + +procedure Test.6 (Test.20, #Attr.12): + let Test.5 : {} = UnionAtIndex (Id 2) (Index 0) #Attr.12; + let Test.22 : Str = ""; + ret Test.22; + +procedure Test.0 (): + let Test.3 : U8 = 0u8; + joinpoint Test.16 Test.4: + let Test.10 : {} = Struct {}; + let Test.11 : U8 = GetTagId Test.4; + joinpoint Test.12 Test.9: + ret Test.9; + in + switch Test.11: + case 0: + let Test.13 : Str = CallByName Test.2 Test.10; + jump Test.12 Test.13; + + case 1: + let Test.14 : Str = CallByName Test.6 Test.10 Test.4; + jump Test.12 Test.14; + + default: + let Test.15 : Str = CallByName Test.6 Test.10 Test.4; + jump Test.12 Test.15; + + in + switch Test.3: + case 0: + let Test.18 : {} = Struct {}; + let Test.17 : [C , C U64, C {}] = CallByName Test.1 Test.18; + jump Test.16 Test.17; + + case 1: + let Test.2 : [C , C U64, C {}] = ClosureTag(Test.2) ; + jump Test.16 Test.2; + + default: + let Test.26 : U64 = 1i64; + let Test.25 : [C , C U64, C {}] = CallByName Test.1 Test.26; + jump Test.16 Test.25; + diff --git a/crates/compiler/test_mono/generated/lambda_capture_niches_with_other_lambda_capture.txt b/crates/compiler/test_mono/generated/lambda_capture_niches_with_other_lambda_capture.txt new file mode 100644 index 0000000000..b553adfbc1 --- /dev/null +++ b/crates/compiler/test_mono/generated/lambda_capture_niches_with_other_lambda_capture.txt @@ -0,0 +1,68 @@ +procedure Test.1 (Test.5): + let Test.20 : [C U64, C {}, C Str] = ClosureTag(Test.6) Test.5; + ret Test.20; + +procedure Test.1 (Test.5): + let Test.32 : [C U64, C {}, C Str] = ClosureTag(Test.6) Test.5; + ret Test.32; + +procedure Test.2 (Test.7): + let Test.26 : [C U64, C {}, C Str] = ClosureTag(Test.8) Test.7; + ret Test.26; + +procedure Test.6 (Test.21, #Attr.12): + let Test.5 : U64 = UnionAtIndex (Id 0) (Index 0) #Attr.12; + dec #Attr.12; + let Test.35 : Str = ""; + ret Test.35; + +procedure Test.6 (Test.21, #Attr.12): + let Test.5 : {} = UnionAtIndex (Id 1) (Index 0) #Attr.12; + dec #Attr.12; + let Test.23 : Str = ""; + ret Test.23; + +procedure Test.8 (Test.27, #Attr.12): + let Test.7 : Str = UnionAtIndex (Id 2) (Index 0) #Attr.12; + inc Test.7; + dec #Attr.12; + ret Test.7; + +procedure Test.0 (): + let Test.3 : U8 = 0u8; + joinpoint Test.17 Test.4: + let Test.11 : {} = Struct {}; + let Test.12 : U8 = GetTagId Test.4; + joinpoint Test.13 Test.10: + ret Test.10; + in + switch Test.12: + case 0: + let Test.14 : Str = CallByName Test.6 Test.11 Test.4; + jump Test.13 Test.14; + + case 1: + let Test.15 : Str = CallByName Test.6 Test.11 Test.4; + jump Test.13 Test.15; + + default: + let Test.16 : Str = CallByName Test.8 Test.11 Test.4; + jump Test.13 Test.16; + + in + switch Test.3: + case 0: + let Test.19 : {} = Struct {}; + let Test.18 : [C U64, C {}, C Str] = CallByName Test.1 Test.19; + jump Test.17 Test.18; + + case 1: + let Test.25 : Str = "foo"; + let Test.24 : [C U64, C {}, C Str] = CallByName Test.2 Test.25; + jump Test.17 Test.24; + + default: + let Test.31 : U64 = 1i64; + let Test.30 : [C U64, C {}, C Str] = CallByName Test.1 Test.31; + jump Test.17 Test.30; + diff --git a/crates/compiler/test_mono/generated/list_append.txt b/crates/compiler/test_mono/generated/list_append.txt index ba872126f2..9d9624a7a0 100644 --- a/crates/compiler/test_mono/generated/list_append.txt +++ b/crates/compiler/test_mono/generated/list_append.txt @@ -1,6 +1,16 @@ procedure List.4 (#Attr.2, #Attr.3): +<<<<<<< HEAD let List.284 : List I64 = lowlevel ListAppend #Attr.2 #Attr.3; ret List.284; +======= +<<<<<<< HEAD:crates/compiler/test_mono/generated/list_append.txt + let List.259 : List I64 = lowlevel ListAppend #Attr.2 #Attr.3; + ret List.259; +======= + let List.213 : List I64 = lowlevel ListAppend #Attr.2 #Attr.3; + ret List.213; +>>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/list_append.txt +>>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) procedure Test.0 (): let Test.2 : List I64 = Array [1i64]; diff --git a/crates/compiler/test_mono/generated/list_append_closure.txt b/crates/compiler/test_mono/generated/list_append_closure.txt index c02bd4ddcb..9566c7d113 100644 --- a/crates/compiler/test_mono/generated/list_append_closure.txt +++ b/crates/compiler/test_mono/generated/list_append_closure.txt @@ -1,6 +1,16 @@ procedure List.4 (#Attr.2, #Attr.3): +<<<<<<< HEAD let List.284 : List I64 = lowlevel ListAppend #Attr.2 #Attr.3; ret List.284; +======= +<<<<<<< HEAD:crates/compiler/test_mono/generated/list_append_closure.txt + let List.259 : List I64 = lowlevel ListAppend #Attr.2 #Attr.3; + ret List.259; +======= + let List.213 : List I64 = lowlevel ListAppend #Attr.2 #Attr.3; + ret List.213; +>>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/list_append_closure.txt +>>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) procedure Test.1 (Test.2): let Test.6 : I64 = 42i64; diff --git a/crates/compiler/test_mono/generated/list_cannot_update_inplace.txt b/crates/compiler/test_mono/generated/list_cannot_update_inplace.txt index f98eb4a9d0..8d0977f122 100644 --- a/crates/compiler/test_mono/generated/list_cannot_update_inplace.txt +++ b/crates/compiler/test_mono/generated/list_cannot_update_inplace.txt @@ -1,3 +1,4 @@ +<<<<<<< HEAD procedure List.3 (List.84, List.85, List.86): let List.287 : {List I64, I64} = CallByName List.57 List.84 List.85 List.86; let List.286 : List I64 = StructAtIndex 0 List.287; @@ -11,6 +12,22 @@ procedure List.57 (List.81, List.82, List.83): if List.290 then let List.291 : {List I64, I64} = CallByName List.61 List.81 List.82 List.83; ret List.291; +======= +<<<<<<< HEAD:crates/compiler/test_mono/generated/list_cannot_update_inplace.txt +procedure List.3 (List.82, List.83, List.84): + let List.262 : {List I64, I64} = CallByName List.57 List.82 List.83 List.84; + let List.261 : List I64 = StructAtIndex 0 List.262; + inc List.261; + dec List.262; + ret List.261; + +procedure List.57 (List.79, List.80, List.81): + let List.268 : U64 = CallByName List.6 List.79; + let List.265 : Int1 = CallByName Num.22 List.80 List.268; + if List.265 then + let List.266 : {List I64, I64} = CallByName List.61 List.79 List.80 List.81; + ret List.266; +>>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) else let List.289 : {List I64, I64} = Struct {List.81, List.83}; ret List.289; @@ -20,16 +37,47 @@ procedure List.6 (#Attr.2): ret List.285; procedure List.61 (#Attr.2, #Attr.3, #Attr.4): +<<<<<<< HEAD let List.292 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; ret List.292; +======= + let List.267 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; + ret List.267; +======= +procedure List.3 (List.79, List.80, List.81): + let List.216 : {List I64, I64} = CallByName List.57 List.79 List.80 List.81; + let List.215 : List I64 = StructAtIndex 0 List.216; + inc List.215; + dec List.216; + ret List.215; + +procedure List.57 (List.76, List.77, List.78): + let List.222 : U64 = CallByName List.6 List.76; + let List.219 : Int1 = CallByName Num.22 List.77 List.222; + if List.219 then + let List.220 : {List I64, I64} = CallByName List.61 List.76 List.77 List.78; + ret List.220; + else + let List.218 : {List I64, I64} = Struct {List.76, List.78}; + ret List.218; + +procedure List.6 (#Attr.2): + let List.214 : U64 = lowlevel ListLen #Attr.2; + ret List.214; + +procedure List.61 (#Attr.2, #Attr.3, #Attr.4): + let List.221 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; + ret List.221; +>>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/list_cannot_update_inplace.txt +>>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) procedure Num.19 (#Attr.2, #Attr.3): - let Num.273 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.273; + let Num.188 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.188; procedure Num.22 (#Attr.2, #Attr.3): - let Num.274 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.274; + let Num.189 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.189; procedure Test.1 (): let Test.8 : List I64 = Array [1i64, 2i64, 3i64]; diff --git a/crates/compiler/test_mono/generated/list_len.txt b/crates/compiler/test_mono/generated/list_len.txt index 246d7c2fb4..01f1647f98 100644 --- a/crates/compiler/test_mono/generated/list_len.txt +++ b/crates/compiler/test_mono/generated/list_len.txt @@ -1,14 +1,32 @@ procedure List.6 (#Attr.2): +<<<<<<< HEAD let List.284 : U64 = lowlevel ListLen #Attr.2; ret List.284; procedure List.6 (#Attr.2): let List.285 : U64 = lowlevel ListLen #Attr.2; ret List.285; +======= +<<<<<<< HEAD:crates/compiler/test_mono/generated/list_len.txt + let List.259 : U64 = lowlevel ListLen #Attr.2; + ret List.259; + +procedure List.6 (#Attr.2): + let List.260 : U64 = lowlevel ListLen #Attr.2; + ret List.260; +======= + let List.213 : U64 = lowlevel ListLen #Attr.2; + ret List.213; + +procedure List.6 (#Attr.2): + let List.214 : U64 = lowlevel ListLen #Attr.2; + ret List.214; +>>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/list_len.txt +>>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) procedure Num.19 (#Attr.2, #Attr.3): - let Num.273 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.273; + let Num.188 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.188; procedure Test.0 (): let Test.1 : List I64 = Array [1i64, 2i64, 3i64]; diff --git a/crates/compiler/test_mono/generated/list_map_closure_borrows.txt b/crates/compiler/test_mono/generated/list_map_closure_borrows.txt index 338e6a59b2..3e4c8afc1a 100644 --- a/crates/compiler/test_mono/generated/list_map_closure_borrows.txt +++ b/crates/compiler/test_mono/generated/list_map_closure_borrows.txt @@ -1,3 +1,4 @@ +<<<<<<< HEAD procedure List.2 (List.75, List.76): let List.290 : U64 = CallByName List.6 List.75; let List.286 : Int1 = CallByName Num.22 List.76 List.290; @@ -5,6 +6,16 @@ procedure List.2 (List.75, List.76): let List.288 : Str = CallByName List.60 List.75 List.76; let List.287 : [C {}, C Str] = Ok List.288; ret List.287; +======= +<<<<<<< HEAD:crates/compiler/test_mono/generated/list_map_closure_borrows.txt +procedure List.2 (List.73, List.74): + let List.265 : U64 = CallByName List.6 List.73; + let List.261 : Int1 = CallByName Num.22 List.74 List.265; + if List.261 then + let List.263 : Str = CallByName List.60 List.73 List.74; + let List.262 : [C {}, C Str] = Ok List.263; + ret List.262; +>>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) else let List.285 : {} = Struct {}; let List.284 : [C {}, C Str] = Err List.285; @@ -19,20 +30,59 @@ procedure List.6 (#Attr.2): ret List.294; procedure List.60 (#Attr.2, #Attr.3): +<<<<<<< HEAD let List.293 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; ret List.293; +======= + let List.268 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.268; +======= +procedure List.2 (List.71, List.72): + let List.219 : U64 = CallByName List.6 List.71; + let List.215 : Int1 = CallByName Num.22 List.72 List.219; + if List.215 then + let List.217 : Str = CallByName List.60 List.71 List.72; + let List.216 : [C {}, C Str] = Ok List.217; + ret List.216; + else + let List.214 : {} = Struct {}; + let List.213 : [C {}, C Str] = Err List.214; + ret List.213; + +procedure List.5 (#Attr.2, #Attr.3): + let List.221 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3; + ret List.221; + +procedure List.6 (#Attr.2): + let List.223 : U64 = lowlevel ListLen #Attr.2; + ret List.223; + +procedure List.60 (#Attr.2, #Attr.3): + let List.222 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.222; +>>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/list_map_closure_borrows.txt +>>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) procedure Num.22 (#Attr.2, #Attr.3): - let Num.273 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.273; + let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.188; procedure Str.16 (#Attr.2, #Attr.3): +<<<<<<< HEAD:crates/compiler/test_mono/generated/list_map_closure_borrows.txt let Str.67 : Str = lowlevel StrRepeat #Attr.2 #Attr.3; ret Str.67; procedure Str.3 (#Attr.2, #Attr.3): let Str.68 : Str = lowlevel StrConcat #Attr.2 #Attr.3; ret Str.68; +======= + let Str.36 : Str = lowlevel StrRepeat #Attr.2 #Attr.3; + ret Str.36; + +procedure Str.3 (#Attr.2, #Attr.3): + let Str.37 : Str = lowlevel StrConcat #Attr.2 #Attr.3; + ret Str.37; +>>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/list_map_closure_borrows.txt procedure Test.1 (): let Test.21 : Str = "lllllllllllllllllllllooooooooooong"; diff --git a/crates/compiler/test_mono/generated/list_map_closure_owns.txt b/crates/compiler/test_mono/generated/list_map_closure_owns.txt index 8818b60944..db2a916558 100644 --- a/crates/compiler/test_mono/generated/list_map_closure_owns.txt +++ b/crates/compiler/test_mono/generated/list_map_closure_owns.txt @@ -1,3 +1,4 @@ +<<<<<<< HEAD procedure List.2 (List.75, List.76): let List.290 : U64 = CallByName List.6 List.75; let List.286 : Int1 = CallByName Num.22 List.76 List.290; @@ -5,6 +6,16 @@ procedure List.2 (List.75, List.76): let List.288 : Str = CallByName List.60 List.75 List.76; let List.287 : [C {}, C Str] = Ok List.288; ret List.287; +======= +<<<<<<< HEAD:crates/compiler/test_mono/generated/list_map_closure_owns.txt +procedure List.2 (List.73, List.74): + let List.265 : U64 = CallByName List.6 List.73; + let List.261 : Int1 = CallByName Num.22 List.74 List.265; + if List.261 then + let List.263 : Str = CallByName List.60 List.73 List.74; + let List.262 : [C {}, C Str] = Ok List.263; + ret List.262; +>>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) else let List.285 : {} = Struct {}; let List.284 : [C {}, C Str] = Err List.285; @@ -21,16 +32,53 @@ procedure List.6 (#Attr.2): ret List.294; procedure List.60 (#Attr.2, #Attr.3): +<<<<<<< HEAD let List.293 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; ret List.293; +======= + let List.268 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.268; +======= +procedure List.2 (List.71, List.72): + let List.219 : U64 = CallByName List.6 List.71; + let List.215 : Int1 = CallByName Num.22 List.72 List.219; + if List.215 then + let List.217 : Str = CallByName List.60 List.71 List.72; + let List.216 : [C {}, C Str] = Ok List.217; + ret List.216; + else + let List.214 : {} = Struct {}; + let List.213 : [C {}, C Str] = Err List.214; + ret List.213; + +procedure List.5 (#Attr.2, #Attr.3): + inc #Attr.2; + let List.221 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3; + decref #Attr.2; + ret List.221; + +procedure List.6 (#Attr.2): + let List.223 : U64 = lowlevel ListLen #Attr.2; + ret List.223; + +procedure List.60 (#Attr.2, #Attr.3): + let List.222 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.222; +>>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/list_map_closure_owns.txt +>>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) procedure Num.22 (#Attr.2, #Attr.3): - let Num.273 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.273; + let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.188; procedure Str.3 (#Attr.2, #Attr.3): +<<<<<<< HEAD:crates/compiler/test_mono/generated/list_map_closure_owns.txt let Str.68 : Str = lowlevel StrConcat #Attr.2 #Attr.3; ret Str.68; +======= + let Str.37 : Str = lowlevel StrConcat #Attr.2 #Attr.3; + ret Str.37; +>>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/list_map_closure_owns.txt procedure Test.1 (): let Test.21 : Str = "lllllllllllllllllllllooooooooooong"; diff --git a/crates/compiler/test_mono/generated/list_sort_asc.txt b/crates/compiler/test_mono/generated/list_sort_asc.txt index fa7e00c424..a385b049f6 100644 --- a/crates/compiler/test_mono/generated/list_sort_asc.txt +++ b/crates/compiler/test_mono/generated/list_sort_asc.txt @@ -1,5 +1,10 @@ procedure List.28 (#Attr.2, #Attr.3): +<<<<<<< HEAD let List.287 : List I64 = lowlevel ListSortWith { xs: `#Attr.#arg1` } #Attr.2 Num.46 #Attr.3; +======= +<<<<<<< HEAD:crates/compiler/test_mono/generated/list_sort_asc.txt + let List.262 : List I64 = lowlevel ListSortWith { xs: `#Attr.#arg1` } #Attr.2 Num.46 #Attr.3; +>>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) let Bool.14 : Int1 = lowlevel ListIsUnique #Attr.2; if Bool.14 then ret List.287; @@ -7,14 +12,35 @@ procedure List.28 (#Attr.2, #Attr.3): decref #Attr.2; ret List.287; +<<<<<<< HEAD procedure List.54 (List.196): let List.285 : {} = Struct {}; let List.284 : List I64 = CallByName List.28 List.196 List.285; ret List.284; +======= +procedure List.54 (List.178): + let List.260 : {} = Struct {}; + let List.259 : List I64 = CallByName List.28 List.178 List.260; + ret List.259; +======= + let List.216 : List I64 = lowlevel ListSortWith { xs: `#Attr.#arg1` } #Attr.2 Num.46 #Attr.3; + let Bool.9 : Int1 = lowlevel ListIsUnique #Attr.2; + if Bool.9 then + ret List.216; + else + decref #Attr.2; + ret List.216; + +procedure List.54 (List.142): + let List.214 : {} = Struct {}; + let List.213 : List I64 = CallByName List.28 List.142 List.214; + ret List.213; +>>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/list_sort_asc.txt +>>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) procedure Num.46 (#Attr.2, #Attr.3): - let Num.273 : U8 = lowlevel NumCompare #Attr.2 #Attr.3; - ret Num.273; + let Num.188 : U8 = lowlevel NumCompare #Attr.2 #Attr.3; + ret Num.188; procedure Test.0 (): let Test.2 : List I64 = Array [4i64, 3i64, 2i64, 1i64]; diff --git a/crates/compiler/test_mono/generated/multimorphic_lambda_set_capture.txt b/crates/compiler/test_mono/generated/multimorphic_lambda_set_capture.txt index 50b76169e7..b7cc9bd341 100644 --- a/crates/compiler/test_mono/generated/multimorphic_lambda_set_capture.txt +++ b/crates/compiler/test_mono/generated/multimorphic_lambda_set_capture.txt @@ -1,10 +1,10 @@ procedure Num.94 (#Attr.2): - let Num.273 : Str = lowlevel NumToStr #Attr.2; - ret Num.273; + let Num.188 : Str = lowlevel NumToStr #Attr.2; + ret Num.188; procedure Num.94 (#Attr.2): - let Num.274 : Str = lowlevel NumToStr #Attr.2; - ret Num.274; + let Num.189 : Str = lowlevel NumToStr #Attr.2; + ret Num.189; procedure Test.1 (Test.4): let Test.16 : [C U8, C U64] = ClosureTag(Test.5) Test.4; diff --git a/crates/compiler/test_mono/generated/nested_pattern_match.txt b/crates/compiler/test_mono/generated/nested_pattern_match.txt index 272fa01449..6673261ca3 100644 --- a/crates/compiler/test_mono/generated/nested_pattern_match.txt +++ b/crates/compiler/test_mono/generated/nested_pattern_match.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.273 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.273; + let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.188; procedure Test.0 (): let Test.19 : I64 = 41i64; diff --git a/crates/compiler/test_mono/generated/optional_when.txt b/crates/compiler/test_mono/generated/optional_when.txt index 239dbc721f..00f3ca57b7 100644 --- a/crates/compiler/test_mono/generated/optional_when.txt +++ b/crates/compiler/test_mono/generated/optional_when.txt @@ -1,6 +1,6 @@ procedure Num.21 (#Attr.2, #Attr.3): - let Num.275 : I64 = lowlevel NumMul #Attr.2 #Attr.3; - ret Num.275; + let Num.190 : I64 = lowlevel NumMul #Attr.2 #Attr.3; + ret Num.190; procedure Test.1 (Test.6): let Test.21 : Int1 = false; diff --git a/crates/compiler/test_mono/generated/quicksort_help.txt b/crates/compiler/test_mono/generated/quicksort_help.txt index 2c00aa804b..ba35a161f7 100644 --- a/crates/compiler/test_mono/generated/quicksort_help.txt +++ b/crates/compiler/test_mono/generated/quicksort_help.txt @@ -1,14 +1,14 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.273 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.273; + let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.188; procedure Num.20 (#Attr.2, #Attr.3): - let Num.274 : I64 = lowlevel NumSub #Attr.2 #Attr.3; - ret Num.274; + let Num.189 : I64 = lowlevel NumSub #Attr.2 #Attr.3; + ret Num.189; procedure Num.22 (#Attr.2, #Attr.3): - let Num.275 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.275; + let Num.190 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.190; procedure Test.1 (Test.24, Test.25, Test.26): joinpoint Test.12 Test.2 Test.3 Test.4: diff --git a/crates/compiler/test_mono/generated/quicksort_swap.txt b/crates/compiler/test_mono/generated/quicksort_swap.txt index 84bedc1a65..43dac09ce8 100644 --- a/crates/compiler/test_mono/generated/quicksort_swap.txt +++ b/crates/compiler/test_mono/generated/quicksort_swap.txt @@ -1,3 +1,4 @@ +<<<<<<< HEAD procedure List.2 (List.75, List.76): let List.304 : U64 = CallByName List.6 List.75; let List.300 : Int1 = CallByName Num.22 List.76 List.304; @@ -5,6 +6,16 @@ procedure List.2 (List.75, List.76): let List.302 : I64 = CallByName List.60 List.75 List.76; let List.301 : [C {}, C I64] = Ok List.302; ret List.301; +======= +<<<<<<< HEAD:crates/compiler/test_mono/generated/quicksort_swap.txt +procedure List.2 (List.73, List.74): + let List.279 : U64 = CallByName List.6 List.73; + let List.275 : Int1 = CallByName Num.22 List.74 List.279; + if List.275 then + let List.277 : I64 = CallByName List.60 List.73 List.74; + let List.276 : [C {}, C I64] = Ok List.277; + ret List.276; +>>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) else let List.299 : {} = Struct {}; let List.298 : [C {}, C I64] = Err List.299; @@ -36,12 +47,59 @@ procedure List.60 (#Attr.2, #Attr.3): ret List.312; procedure List.61 (#Attr.2, #Attr.3, #Attr.4): +<<<<<<< HEAD let List.309 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; ret List.309; +======= + let List.284 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; + ret List.284; +======= +procedure List.2 (List.71, List.72): + let List.233 : U64 = CallByName List.6 List.71; + let List.229 : Int1 = CallByName Num.22 List.72 List.233; + if List.229 then + let List.231 : I64 = CallByName List.60 List.71 List.72; + let List.230 : [C {}, C I64] = Ok List.231; + ret List.230; + else + let List.228 : {} = Struct {}; + let List.227 : [C {}, C I64] = Err List.228; + ret List.227; + +procedure List.3 (List.79, List.80, List.81): + let List.217 : {List I64, I64} = CallByName List.57 List.79 List.80 List.81; + let List.216 : List I64 = StructAtIndex 0 List.217; + inc List.216; + dec List.217; + ret List.216; + +procedure List.57 (List.76, List.77, List.78): + let List.239 : U64 = CallByName List.6 List.76; + let List.236 : Int1 = CallByName Num.22 List.77 List.239; + if List.236 then + let List.237 : {List I64, I64} = CallByName List.61 List.76 List.77 List.78; + ret List.237; + else + let List.235 : {List I64, I64} = Struct {List.76, List.78}; + ret List.235; + +procedure List.6 (#Attr.2): + let List.240 : U64 = lowlevel ListLen #Attr.2; + ret List.240; + +procedure List.60 (#Attr.2, #Attr.3): + let List.241 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.241; + +procedure List.61 (#Attr.2, #Attr.3, #Attr.4): + let List.238 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; + ret List.238; +>>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/quicksort_swap.txt +>>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) procedure Num.22 (#Attr.2, #Attr.3): - let Num.275 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.275; + let Num.190 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.190; procedure Test.1 (Test.2): let Test.28 : U64 = 0i64; diff --git a/crates/compiler/test_mono/generated/record_optional_field_function_no_use_default.txt b/crates/compiler/test_mono/generated/record_optional_field_function_no_use_default.txt index 82db9ad2db..2c661940cc 100644 --- a/crates/compiler/test_mono/generated/record_optional_field_function_no_use_default.txt +++ b/crates/compiler/test_mono/generated/record_optional_field_function_no_use_default.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.273 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.273; + let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.188; procedure Test.1 (Test.4): let Test.2 : I64 = StructAtIndex 0 Test.4; diff --git a/crates/compiler/test_mono/generated/record_optional_field_function_use_default.txt b/crates/compiler/test_mono/generated/record_optional_field_function_use_default.txt index eaba7d9de5..2145dec395 100644 --- a/crates/compiler/test_mono/generated/record_optional_field_function_use_default.txt +++ b/crates/compiler/test_mono/generated/record_optional_field_function_use_default.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.273 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.273; + let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.188; procedure Test.1 (Test.4): let Test.2 : I64 = 10i64; diff --git a/crates/compiler/test_mono/generated/record_optional_field_let_no_use_default.txt b/crates/compiler/test_mono/generated/record_optional_field_let_no_use_default.txt index 959902f6d8..84a986e3c6 100644 --- a/crates/compiler/test_mono/generated/record_optional_field_let_no_use_default.txt +++ b/crates/compiler/test_mono/generated/record_optional_field_let_no_use_default.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.273 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.273; + let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.188; procedure Test.1 (Test.2): let Test.3 : I64 = StructAtIndex 0 Test.2; diff --git a/crates/compiler/test_mono/generated/record_optional_field_let_use_default.txt b/crates/compiler/test_mono/generated/record_optional_field_let_use_default.txt index aad801aab9..ede661ab6b 100644 --- a/crates/compiler/test_mono/generated/record_optional_field_let_use_default.txt +++ b/crates/compiler/test_mono/generated/record_optional_field_let_use_default.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.273 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.273; + let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.188; procedure Test.1 (Test.2): let Test.3 : I64 = 10i64; diff --git a/crates/compiler/test_mono/generated/rigids.txt b/crates/compiler/test_mono/generated/rigids.txt index 2285fe56ac..c3ca7f7ca5 100644 --- a/crates/compiler/test_mono/generated/rigids.txt +++ b/crates/compiler/test_mono/generated/rigids.txt @@ -1,3 +1,4 @@ +<<<<<<< HEAD procedure List.2 (List.75, List.76): let List.304 : U64 = CallByName List.6 List.75; let List.300 : Int1 = CallByName Num.22 List.76 List.304; @@ -5,6 +6,16 @@ procedure List.2 (List.75, List.76): let List.302 : I64 = CallByName List.60 List.75 List.76; let List.301 : [C {}, C I64] = Ok List.302; ret List.301; +======= +<<<<<<< HEAD:crates/compiler/test_mono/generated/rigids.txt +procedure List.2 (List.73, List.74): + let List.279 : U64 = CallByName List.6 List.73; + let List.275 : Int1 = CallByName Num.22 List.74 List.279; + if List.275 then + let List.277 : I64 = CallByName List.60 List.73 List.74; + let List.276 : [C {}, C I64] = Ok List.277; + ret List.276; +>>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) else let List.299 : {} = Struct {}; let List.298 : [C {}, C I64] = Err List.299; @@ -36,12 +47,59 @@ procedure List.60 (#Attr.2, #Attr.3): ret List.312; procedure List.61 (#Attr.2, #Attr.3, #Attr.4): +<<<<<<< HEAD let List.309 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; ret List.309; +======= + let List.284 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; + ret List.284; +======= +procedure List.2 (List.71, List.72): + let List.233 : U64 = CallByName List.6 List.71; + let List.229 : Int1 = CallByName Num.22 List.72 List.233; + if List.229 then + let List.231 : I64 = CallByName List.60 List.71 List.72; + let List.230 : [C {}, C I64] = Ok List.231; + ret List.230; + else + let List.228 : {} = Struct {}; + let List.227 : [C {}, C I64] = Err List.228; + ret List.227; + +procedure List.3 (List.79, List.80, List.81): + let List.217 : {List I64, I64} = CallByName List.57 List.79 List.80 List.81; + let List.216 : List I64 = StructAtIndex 0 List.217; + inc List.216; + dec List.217; + ret List.216; + +procedure List.57 (List.76, List.77, List.78): + let List.239 : U64 = CallByName List.6 List.76; + let List.236 : Int1 = CallByName Num.22 List.77 List.239; + if List.236 then + let List.237 : {List I64, I64} = CallByName List.61 List.76 List.77 List.78; + ret List.237; + else + let List.235 : {List I64, I64} = Struct {List.76, List.78}; + ret List.235; + +procedure List.6 (#Attr.2): + let List.240 : U64 = lowlevel ListLen #Attr.2; + ret List.240; + +procedure List.60 (#Attr.2, #Attr.3): + let List.241 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.241; + +procedure List.61 (#Attr.2, #Attr.3, #Attr.4): + let List.238 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; + ret List.238; +>>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/rigids.txt +>>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) procedure Num.22 (#Attr.2, #Attr.3): - let Num.275 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.275; + let Num.190 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.190; procedure Test.1 (Test.2, Test.3, Test.4): let Test.29 : [C {}, C I64] = CallByName List.2 Test.4 Test.3; diff --git a/crates/compiler/test_mono/generated/specialize_closures.txt b/crates/compiler/test_mono/generated/specialize_closures.txt index 251e80285d..30e609df83 100644 --- a/crates/compiler/test_mono/generated/specialize_closures.txt +++ b/crates/compiler/test_mono/generated/specialize_closures.txt @@ -1,10 +1,10 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.274 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.274; + let Num.189 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.189; procedure Num.21 (#Attr.2, #Attr.3): - let Num.273 : I64 = lowlevel NumMul #Attr.2 #Attr.3; - ret Num.273; + let Num.188 : I64 = lowlevel NumMul #Attr.2 #Attr.3; + ret Num.188; procedure Test.1 (Test.2, Test.3): let Test.17 : U8 = GetTagId Test.2; diff --git a/crates/compiler/test_mono/generated/specialize_lowlevel.txt b/crates/compiler/test_mono/generated/specialize_lowlevel.txt index 74bc5c3bbb..e460b2b108 100644 --- a/crates/compiler/test_mono/generated/specialize_lowlevel.txt +++ b/crates/compiler/test_mono/generated/specialize_lowlevel.txt @@ -1,10 +1,10 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.274 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.274; + let Num.189 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.189; procedure Num.21 (#Attr.2, #Attr.3): - let Num.273 : I64 = lowlevel NumMul #Attr.2 #Attr.3; - ret Num.273; + let Num.188 : I64 = lowlevel NumMul #Attr.2 #Attr.3; + ret Num.188; procedure Test.6 (Test.8, #Attr.12): let Test.4 : I64 = UnionAtIndex (Id 0) (Index 0) #Attr.12; diff --git a/crates/compiler/test_mono/generated/tail_call_elimination.txt b/crates/compiler/test_mono/generated/tail_call_elimination.txt index 236038343a..fb7a162575 100644 --- a/crates/compiler/test_mono/generated/tail_call_elimination.txt +++ b/crates/compiler/test_mono/generated/tail_call_elimination.txt @@ -1,10 +1,10 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.273 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.273; + let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.188; procedure Num.20 (#Attr.2, #Attr.3): - let Num.274 : I64 = lowlevel NumSub #Attr.2 #Attr.3; - ret Num.274; + let Num.189 : I64 = lowlevel NumSub #Attr.2 #Attr.3; + ret Num.189; procedure Test.1 (Test.15, Test.16): joinpoint Test.7 Test.2 Test.3: diff --git a/crates/compiler/test_mono/generated/when_nested_maybe.txt b/crates/compiler/test_mono/generated/when_nested_maybe.txt index 272fa01449..6673261ca3 100644 --- a/crates/compiler/test_mono/generated/when_nested_maybe.txt +++ b/crates/compiler/test_mono/generated/when_nested_maybe.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.273 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.273; + let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.188; procedure Test.0 (): let Test.19 : I64 = 41i64; diff --git a/crates/compiler/test_mono/generated/when_on_record.txt b/crates/compiler/test_mono/generated/when_on_record.txt index 71fec29389..2cc52368b6 100644 --- a/crates/compiler/test_mono/generated/when_on_record.txt +++ b/crates/compiler/test_mono/generated/when_on_record.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.273 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.273; + let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.188; procedure Test.0 (): let Test.5 : I64 = 2i64; diff --git a/crates/compiler/test_mono/generated/when_on_two_values.txt b/crates/compiler/test_mono/generated/when_on_two_values.txt index f3d84fff9e..c5361ba0b9 100644 --- a/crates/compiler/test_mono/generated/when_on_two_values.txt +++ b/crates/compiler/test_mono/generated/when_on_two_values.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.273 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.273; + let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.188; procedure Test.0 (): let Test.15 : I64 = 3i64; diff --git a/crates/compiler/test_mono/src/tests.rs b/crates/compiler/test_mono/src/tests.rs index e1ca980d51..2b0fd0fafa 100644 --- a/crates/compiler/test_mono/src/tests.rs +++ b/crates/compiler/test_mono/src/tests.rs @@ -1527,7 +1527,7 @@ fn tail_call_with_different_layout() { } #[mono_test] -fn multimorphic_lambda_set_capture() { +fn lambda_capture_niche_u8_vs_u64() { indoc!( r#" capture : _ -> ({} -> Str) @@ -1549,7 +1549,7 @@ fn multimorphic_lambda_set_capture() { } #[mono_test] -fn multimorphic_lambdas_with_other_lambda_capture() { +fn lambda_capture_niches_with_other_lambda_capture() { indoc!( r#" capture : a -> ({} -> Str) @@ -1575,7 +1575,7 @@ fn multimorphic_lambdas_with_other_lambda_capture() { } #[mono_test] -fn multimorphic_lambdas_with_non_capturing_function() { +fn lambda_capture_niches_with_non_capturing_function() { indoc!( r#" capture : a -> ({} -> Str) @@ -1601,7 +1601,7 @@ fn multimorphic_lambdas_with_non_capturing_function() { } #[mono_test] -fn multimorphic_lambdas_have_captured_function_in_closure() { +fn lambda_capture_niches_have_captured_function_in_closure() { indoc!( r#" Lazy a : {} -> a From 276db791bd283e38987fd5ff93e469a0f5ad9018 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Fri, 1 Jul 2022 15:56:32 -0400 Subject: [PATCH 26/66] Update capture niche tests --- crates/compiler/test_gen/src/gen_primitives.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/compiler/test_gen/src/gen_primitives.rs b/crates/compiler/test_gen/src/gen_primitives.rs index 458990b60c..06d2985b5a 100644 --- a/crates/compiler/test_gen/src/gen_primitives.rs +++ b/crates/compiler/test_gen/src/gen_primitives.rs @@ -3501,7 +3501,7 @@ fn polymorphic_lambda_captures_polymorphic_value() { #[test] #[cfg(any(feature = "gen-llvm"))] -fn multimorphic_lambda_set_u64_vs_u8_capture() { +fn lambda_capture_niche_u64_vs_u8_capture() { assert_evals_to!( indoc!( r#" @@ -3528,7 +3528,7 @@ fn multimorphic_lambda_set_u64_vs_u8_capture() { #[test] #[cfg(any(feature = "gen-llvm"))] -fn multimorphic_lambdas_with_other_lambda_capture() { +fn lambda_capture_niches_with_other_lambda_capture() { assert_evals_to!( indoc!( r#" @@ -3561,7 +3561,7 @@ fn multimorphic_lambdas_with_other_lambda_capture() { #[test] #[cfg(any(feature = "gen-llvm"))] -fn multimorphic_lambdas_with_non_capturing_function() { +fn lambda_capture_niches_with_non_capturing_function() { assert_evals_to!( indoc!( r#" @@ -3594,7 +3594,7 @@ fn multimorphic_lambdas_with_non_capturing_function() { #[test] #[cfg(any(feature = "gen-llvm"))] -fn multimorphic_lambdas_have_captured_function_in_closure() { +fn lambda_capture_niches_have_captured_function_in_closure() { assert_evals_to!( indoc!( r#" From 641bd95d04d1fd4662ee2c189eaaf3cd6265a342 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Fri, 1 Jul 2022 16:01:27 -0400 Subject: [PATCH 27/66] Make sure accessor closures get the name of their assigned def, if any --- crates/compiler/can/src/def.rs | 54 +++++++++++++++++----------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index adaf215ae4..6463029cc8 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -1730,34 +1730,34 @@ fn canonicalize_pending_body<'a>( } // Turn f = .foo into f = \rcd -[f]-> rcd.foo - // ( - // Pattern::Identifier(defined_symbol) - // | Pattern::AbilityMemberSpecialization { - // ident: defined_symbol, - // .. - // }, - // ast::Expr::AccessorFunction(field), - // ) => { - // let (loc_can_expr, can_output) = ( - // Loc::at( - // loc_expr.region, - // Accessor(AccessorData { - // name: scope.gen_unique_symbol(), - // function_var: var_store.fresh(), - // record_var: var_store.fresh(), - // ext_var: var_store.fresh(), - // closure_var: var_store.fresh(), - // field_var: var_store.fresh(), - // field: (*field).into(), - // }), - // ), - // Output::default(), - // ); - // let def_references = DefReferences::Value(can_output.references.clone()); - // output.union(can_output); + ( + Pattern::Identifier(defined_symbol) + | Pattern::AbilityMemberSpecialization { + ident: defined_symbol, + .. + }, + ast::Expr::AccessorFunction(field), + ) => { + let (loc_can_expr, can_output) = ( + Loc::at( + loc_expr.region, + Accessor(AccessorData { + name: *defined_symbol, + function_var: var_store.fresh(), + record_var: var_store.fresh(), + ext_var: var_store.fresh(), + closure_var: var_store.fresh(), + field_var: var_store.fresh(), + field: (*field).into(), + }), + ), + Output::default(), + ); + let def_references = DefReferences::Value(can_output.references.clone()); + output.union(can_output); - // (loc_can_expr, def_references) - // } + (loc_can_expr, def_references) + } _ => { let (loc_can_expr, can_output) = From a5ea4f8c11777af3645991cd4dc1bd31d0ad98e7 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Fri, 1 Jul 2022 16:05:49 -0400 Subject: [PATCH 28/66] Remove references to multimorphic names --- crates/bindgen/src/load.rs | 11 +- crates/bindgen/src/types.rs | 18 +- crates/compiler/load_internal/src/file.rs | 60 +----- crates/compiler/module/src/symbol.rs | 25 +-- crates/compiler/mono/src/ir.rs | 204 ++++++------------ crates/compiler/mono/src/layout.rs | 207 +------------------ crates/compiler/test_gen/src/wasm_linking.rs | 1 + crates/repl_cli/src/lib.rs | 4 - crates/repl_eval/src/eval.rs | 21 +- crates/repl_wasm/src/repl.rs | 3 - 10 files changed, 107 insertions(+), 447 deletions(-) diff --git a/crates/bindgen/src/load.rs b/crates/bindgen/src/load.rs index e7c04ebed0..c66e2216d1 100644 --- a/crates/bindgen/src/load.rs +++ b/crates/bindgen/src/load.rs @@ -1,7 +1,6 @@ use crate::types::{Env, Types}; use bumpalo::Bump; use roc_load::{LoadedModule, Threading}; -use roc_mono::layout::MultimorphicNames; use roc_reporting::report::RenderTarget; use roc_target::{Architecture, TargetInfo}; use std::io; @@ -37,8 +36,6 @@ pub fn load_types( ) .expect("Problem loading platform module"); - let mut multimorphic_names = MultimorphicNames::default(); - let decls = declarations_by_id.remove(&home).unwrap(); let subs = solved.inner_mut(); @@ -77,13 +74,7 @@ pub fn load_types( let types_and_targets = Architecture::iter() .map(|arch| { let target_info = arch.into(); - let mut env = Env::new( - arena, - subs, - &mut interns, - target_info, - &mut multimorphic_names, - ); + let mut env = Env::new(arena, subs, &mut interns, target_info); (env.vars_to_types(variables.clone()), target_info) }) diff --git a/crates/bindgen/src/types.rs b/crates/bindgen/src/types.rs index 003d2ab639..9d171980c6 100644 --- a/crates/bindgen/src/types.rs +++ b/crates/bindgen/src/types.rs @@ -9,7 +9,7 @@ use roc_collections::VecMap; use roc_module::symbol::{Interns, Symbol}; use roc_mono::layout::{ cmp_fields, ext_var_is_empty_tag_union, round_up_to_alignment, Builtin, Layout, LayoutCache, - MultimorphicNames, UnionLayout, + UnionLayout, }; use roc_target::TargetInfo; use roc_types::{ @@ -416,7 +416,6 @@ pub struct Env<'a> { pending_recursive_types: VecMap>, known_recursive_types: VecMap, TypeId>, target: TargetInfo, - multimorphic_names: &'a mut MultimorphicNames, } impl<'a> Env<'a> { @@ -425,7 +424,6 @@ impl<'a> Env<'a> { subs: &'a Subs, interns: &'a mut Interns, target: TargetInfo, - multimorphic_names: &'a mut MultimorphicNames, ) -> Self { Env { arena, @@ -437,7 +435,6 @@ impl<'a> Env<'a> { known_recursive_types: Default::default(), layout_cache: LayoutCache::new(target), target, - multimorphic_names, } } @@ -459,7 +456,7 @@ impl<'a> Env<'a> { fn add_type(&mut self, var: Variable, types: &mut Types) -> TypeId { let layout = self .layout_cache - .from_var(self.arena, var, self.subs, self.multimorphic_names) + .from_var(self.arena, var, self.subs) .expect("Something weird ended up in the content"); add_type_help(self, layout, var, None, types) @@ -595,7 +592,7 @@ fn add_type_help<'a>( let type_id = types.add(RocType::RecursivePointer(TypeId::PENDING), layout); let structure_layout = env .layout_cache - .from_var(env.arena, *structure, subs, env.multimorphic_names) + .from_var(env.arena, *structure, subs) .unwrap(); env.pending_recursive_types @@ -703,7 +700,7 @@ where label, field_var, env.layout_cache - .from_var(env.arena, field_var, subs, env.multimorphic_names) + .from_var(env.arena, field_var, subs) .unwrap(), )); } @@ -773,7 +770,7 @@ fn add_tag_union<'a>( let payload_var = payload_vars.get(0).unwrap(); let payload_layout = env .layout_cache - .from_var(env.arena, *payload_var, env.subs, env.multimorphic_names) + .from_var(env.arena, *payload_var, env.subs) .expect("Something weird ended up in the content"); let payload_id = add_type_help(env, payload_layout, *payload_var, None, types); @@ -922,10 +919,7 @@ fn struct_fields_needed>(env: &mut Env<'_>, var let arena = env.arena; vars.into_iter().fold(0, |count, var| { - let layout = env - .layout_cache - .from_var(arena, var, subs, env.multimorphic_names) - .unwrap(); + let layout = env.layout_cache.from_var(arena, var, subs).unwrap(); if layout.is_dropped_because_empty() { count diff --git a/crates/compiler/load_internal/src/file.rs b/crates/compiler/load_internal/src/file.rs index 3e6b2ef5b2..493c875801 100644 --- a/crates/compiler/load_internal/src/file.rs +++ b/crates/compiler/load_internal/src/file.rs @@ -33,7 +33,7 @@ use roc_mono::ir::{ CapturedSymbols, EntryPoint, ExternalSpecializations, PartialProc, Proc, ProcLayout, Procs, ProcsBase, UpdateModeIds, }; -use roc_mono::layout::{LambdaName, Layout, LayoutCache, LayoutProblem, MultimorphicNames}; +use roc_mono::layout::{LambdaName, Layout, LayoutCache, LayoutProblem}; use roc_parse::ast::{self, Defs, ExtractSpaces, Spaced, StrLiteral, TypeAnnotation}; use roc_parse::header::{ExposedName, ImportsEntry, PackageEntry, PlatformHeader, To, TypedIdent}; use roc_parse::header::{HeaderFor, ModuleNameEnum, PackageName}; @@ -428,7 +428,6 @@ fn start_phase<'a>( exposed_to_host: state.exposed_to_host.clone(), abilities_store, derived_symbols, - multimorphic_names: state.multimorphic_names.clone(), } } Phase::MakeSpecializations => { @@ -498,7 +497,6 @@ fn start_phase<'a>( module_timing, world_abilities: state.world_abilities.clone_ref(), derived_symbols, - multimorphic_names: state.multimorphic_names.clone(), } } } @@ -828,8 +826,6 @@ struct State<'a> { pub render: RenderTarget, - pub multimorphic_names: MultimorphicNames, - /// All abilities across all modules. pub world_abilities: WorldAbilities, @@ -880,7 +876,6 @@ impl<'a> State<'a> { layout_caches: std::vec::Vec::with_capacity(number_of_workers), cached_subs: Arc::new(Mutex::new(cached_subs)), render, - multimorphic_names: MultimorphicNames::default(), make_specializations_pass: MakeSpecializationsPass::Pass(1), world_abilities: Default::default(), } @@ -1005,7 +1000,6 @@ enum BuildTask<'a> { exposed_to_host: ExposedToHost, abilities_store: AbilitiesStore, derived_symbols: GlobalDerivedSymbols, - multimorphic_names: MultimorphicNames, }, MakeSpecializations { module_id: ModuleId, @@ -1017,7 +1011,6 @@ enum BuildTask<'a> { module_timing: ModuleTiming, world_abilities: WorldAbilities, derived_symbols: GlobalDerivedSymbols, - multimorphic_names: MultimorphicNames, }, } @@ -2599,16 +2592,7 @@ fn finish_specialization( .into_inner() .into_module_ids(); - let mut all_ident_ids = state.constrained_ident_ids; - let multimorphic_idents = state - .multimorphic_names - .try_unwrap_names() - .expect("There were still outstanding Arc references to multimorphic_names"); - let old_idents = all_ident_ids.insert(ModuleId::MULTIMORPHIC, multimorphic_idents); - debug_assert!( - old_idents.is_none() || old_idents.unwrap().is_empty(), - "duplicate multimorphic idents" - ); + let all_ident_ids = state.constrained_ident_ids; let interns = Interns { module_ids, @@ -4431,7 +4415,6 @@ fn make_specializations<'a>( target_info: TargetInfo, world_abilities: WorldAbilities, derived_symbols: GlobalDerivedSymbols, - mut multimorphic_names: MultimorphicNames, ) -> Msg<'a> { let make_specializations_start = SystemTime::now(); let mut update_mode_ids = UpdateModeIds::new(); @@ -4447,7 +4430,6 @@ fn make_specializations<'a>( call_specialization_counter: 1, abilities: AbilitiesView::World(world_abilities), derived_symbols: &derived_symbols, - multimorphic_names: &mut multimorphic_names, }; let mut procs = Procs::new_in(arena); @@ -4511,7 +4493,6 @@ fn build_pending_specializations<'a>( exposed_to_host: ExposedToHost, // TODO remove abilities_store: AbilitiesStore, derived_symbols: GlobalDerivedSymbols, - mut multimorphic_names: MultimorphicNames, ) -> Msg<'a> { let find_specializations_start = SystemTime::now(); @@ -4541,7 +4522,6 @@ fn build_pending_specializations<'a>( // do we need a global view. abilities: AbilitiesView::Module(&abilities_store), derived_symbols: &derived_symbols, - multimorphic_names: &mut multimorphic_names, }; // Add modules' decls to Procs @@ -4568,12 +4548,8 @@ fn build_pending_specializations<'a>( // never gets called by Roc code, it will never // get specialized! if is_host_exposed { - let layout_result = layout_cache.raw_from_var( - mono_env.arena, - expr_var, - mono_env.subs, - mono_env.multimorphic_names, - ); + let layout_result = + layout_cache.raw_from_var(mono_env.arena, expr_var, mono_env.subs); // cannot specialize when e.g. main's type contains type variables if let Err(e) = layout_result { @@ -4632,12 +4608,8 @@ fn build_pending_specializations<'a>( // never gets called by Roc code, it will never // get specialized! if is_host_exposed { - let layout_result = layout_cache.raw_from_var( - mono_env.arena, - expr_var, - mono_env.subs, - mono_env.multimorphic_names, - ); + let layout_result = + layout_cache.raw_from_var(mono_env.arena, expr_var, mono_env.subs); // cannot specialize when e.g. main's type contains type variables if let Err(e) = layout_result { @@ -4714,12 +4686,8 @@ fn build_pending_specializations<'a>( // never gets called by Roc code, it will never // get specialized! if is_host_exposed { - let layout_result = layout_cache.raw_from_var( - mono_env.arena, - expr_var, - mono_env.subs, - mono_env.multimorphic_names, - ); + let layout_result = + layout_cache.raw_from_var(mono_env.arena, expr_var, mono_env.subs); // cannot specialize when e.g. main's type contains type variables if let Err(e) = layout_result { @@ -4778,12 +4746,8 @@ fn build_pending_specializations<'a>( // never gets called by Roc code, it will never // get specialized! if is_host_exposed { - let layout_result = layout_cache.raw_from_var( - mono_env.arena, - expr_var, - mono_env.subs, - mono_env.multimorphic_names, - ); + let layout_result = + layout_cache.raw_from_var(mono_env.arena, expr_var, mono_env.subs); // cannot specialize when e.g. main's type contains type variables if let Err(e) = layout_result { @@ -4936,7 +4900,6 @@ fn run_task<'a>( exposed_to_host, abilities_store, derived_symbols, - multimorphic_names, } => Ok(build_pending_specializations( arena, solved_subs, @@ -4950,7 +4913,6 @@ fn run_task<'a>( exposed_to_host, abilities_store, derived_symbols, - multimorphic_names, )), MakeSpecializations { module_id, @@ -4962,7 +4924,6 @@ fn run_task<'a>( module_timing, world_abilities, derived_symbols, - multimorphic_names, } => Ok(make_specializations( arena, module_id, @@ -4975,7 +4936,6 @@ fn run_task<'a>( target_info, world_abilities, derived_symbols, - multimorphic_names, )), }?; diff --git a/crates/compiler/module/src/symbol.rs b/crates/compiler/module/src/symbol.rs index dd3c534252..4e2614d286 100644 --- a/crates/compiler/module/src/symbol.rs +++ b/crates/compiler/module/src/symbol.rs @@ -998,10 +998,7 @@ define_builtins! { // Fake module for storing derived function symbols 1 DERIVED: "#Derived" => { } - // Fake module for storing fresh multimorphic function symbol names - 2 MULTIMORPHIC: "#Multimorphic" => { - } - 3 NUM: "Num" => { + 2 NUM: "Num" => { 0 NUM_NUM: "Num" // the Num.Num type alias 1 NUM_I128: "I128" // the Num.I128 type alias 2 NUM_U128: "U128" // the Num.U128 type alias @@ -1144,7 +1141,7 @@ define_builtins! { 139 NUM_MAX_F64: "maxF64" 140 NUM_MIN_F64: "minF64" } - 4 BOOL: "Bool" => { + 3 BOOL: "Bool" => { 0 BOOL_BOOL: "Bool" // the Bool.Bool type alias 1 BOOL_FALSE: "False" imported // Bool.Bool = [False, True] // NB: not strictly needed; used for finding tag names in error suggestions @@ -1157,7 +1154,7 @@ define_builtins! { 7 BOOL_EQ: "isEq" 8 BOOL_NEQ: "isNotEq" } - 5 STR: "Str" => { + 4 STR: "Str" => { 0 STR_STR: "Str" imported // the Str.Str type alias 1 STR_IS_EMPTY: "isEmpty" 2 STR_APPEND: "#append" // unused @@ -1194,7 +1191,7 @@ define_builtins! { 33 STR_TO_I8: "toI8" 34 STR_TO_SCALARS: "toScalars" } - 6 LIST: "List" => { + 5 LIST: "List" => { 0 LIST_LIST: "List" imported // the List.List type alias 1 LIST_IS_EMPTY: "isEmpty" 2 LIST_GET: "get" @@ -1260,7 +1257,7 @@ define_builtins! { 62 LIST_WITH_CAPACITY: "withCapacity" 63 LIST_ITERATE: "iterate" } - 7 RESULT: "Result" => { + 6 RESULT: "Result" => { 0 RESULT_RESULT: "Result" // the Result.Result type alias 1 RESULT_OK: "Ok" imported // Result.Result a e = [Ok a, Err e] // NB: not strictly needed; used for finding tag names in error suggestions @@ -1273,7 +1270,7 @@ define_builtins! { 7 RESULT_IS_OK: "isOk" 8 RESULT_IS_ERR: "isErr" } - 8 DICT: "Dict" => { + 7 DICT: "Dict" => { 0 DICT_DICT: "Dict" imported // the Dict.Dict type alias 1 DICT_EMPTY: "empty" 2 DICT_SINGLE: "single" @@ -1292,7 +1289,7 @@ define_builtins! { 13 DICT_INTERSECTION: "intersection" 14 DICT_DIFFERENCE: "difference" } - 9 SET: "Set" => { + 8 SET: "Set" => { 0 SET_SET: "Set" imported // the Set.Set type alias 1 SET_EMPTY: "empty" 2 SET_SINGLE: "single" @@ -1309,12 +1306,12 @@ define_builtins! { 13 SET_CONTAINS: "contains" 14 SET_TO_DICT: "toDict" } - 10 BOX: "Box" => { + 9 BOX: "Box" => { 0 BOX_BOX_TYPE: "Box" imported // the Box.Box opaque type 1 BOX_BOX_FUNCTION: "box" // Box.box 2 BOX_UNBOX: "unbox" } - 11 ENCODE: "Encode" => { + 10 ENCODE: "Encode" => { 0 ENCODE_ENCODER: "Encoder" 1 ENCODE_ENCODING: "Encoding" 2 ENCODE_TO_ENCODER: "toEncoder" @@ -1342,9 +1339,9 @@ define_builtins! { 24 ENCODE_APPEND: "append" 25 ENCODE_TO_BYTES: "toBytes" } - 12 JSON: "Json" => { + 11 JSON: "Json" => { 0 JSON_JSON: "Json" } - num_modules: 13 // Keep this count up to date by hand! (TODO: see the mut_map! macro for how we could determine this count correctly in the macro) + num_modules: 12 // Keep this count up to date by hand! (TODO: see the mut_map! macro for how we could determine this count correctly in the macro) } diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index be52b1a912..17977c4949 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -2,7 +2,7 @@ use crate::layout::{ Builtin, ClosureRepresentation, LambdaName, LambdaSet, Layout, LayoutCache, LayoutProblem, - MultimorphicNames, RawFunctionLayout, TagIdIntType, TagOrClosure, UnionLayout, WrappedVariant, + RawFunctionLayout, TagIdIntType, TagOrClosure, UnionLayout, WrappedVariant, }; use bumpalo::collections::{CollectIn, Vec}; use bumpalo::Bump; @@ -824,25 +824,19 @@ impl<'a> SymbolSpecializations<'a> { let arena = env.arena; let subs: &Subs = env.subs; - let layout = - match layout_cache.from_var(arena, specialization_var, subs, env.multimorphic_names) { - Ok(layout) => layout, - // This can happen when the def symbol has a type error. In such cases just use the - // def symbol, which is erroring. - Err(_) => return symbol, - }; + let layout = match layout_cache.from_var(arena, specialization_var, subs) { + Ok(layout) => layout, + // This can happen when the def symbol has a type error. In such cases just use the + // def symbol, which is erroring. + Err(_) => return symbol, + }; let is_closure = matches!( subs.get_content_without_compacting(specialization_var), Content::Structure(FlatType::Func(..)) ); let function_mark = if is_closure { - let fn_layout = match layout_cache.raw_from_var( - arena, - specialization_var, - subs, - env.multimorphic_names, - ) { + let fn_layout = match layout_cache.raw_from_var(arena, specialization_var, subs) { Ok(layout) => layout, // This can happen when the def symbol has a type error. In such cases just use the // def symbol, which is erroring. @@ -1024,7 +1018,7 @@ impl<'a> Procs<'a> { layout_cache: &mut LayoutCache<'a>, ) -> Result, RuntimeError> { let raw_layout = layout_cache - .raw_from_var(env.arena, annotation, env.subs, env.multimorphic_names) + .raw_from_var(env.arena, annotation, env.subs) .unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err)); let top_level = ProcLayout::from_raw(env.arena, raw_layout, name.captures_niche); @@ -1303,7 +1297,6 @@ pub struct Env<'a, 'i> { pub call_specialization_counter: u32, pub abilities: AbilitiesView<'i>, pub derived_symbols: &'i GlobalDerivedSymbols, - pub multimorphic_names: &'i mut MultimorphicNames, } impl<'a, 'i> Env<'a, 'i> { @@ -2902,7 +2895,7 @@ fn specialize_external<'a>( for (symbol, variable) in host_exposed_variables { let layout = layout_cache - .raw_from_var(env.arena, *variable, env.subs, env.multimorphic_names) + .raw_from_var(env.arena, *variable, env.subs) .unwrap(); let name = env.unique_symbol(); @@ -3229,7 +3222,7 @@ fn build_specialized_proc_from_var<'a>( pattern_symbols: &[Symbol], fn_var: Variable, ) -> Result, LayoutProblem> { - match layout_cache.raw_from_var(env.arena, fn_var, env.subs, env.multimorphic_names)? { + match layout_cache.raw_from_var(env.arena, fn_var, env.subs)? { RawFunctionLayout::Function(pattern_layouts, closure_layout, ret_layout) => { let mut pattern_layouts_vec = Vec::with_capacity_in(pattern_layouts.len(), env.arena); pattern_layouts_vec.extend_from_slice(pattern_layouts); @@ -3448,7 +3441,7 @@ where // for debugging only let raw = layout_cache - .raw_from_var(env.arena, fn_var, env.subs, env.multimorphic_names) + .raw_from_var(env.arena, fn_var, env.subs) .unwrap_or_else(|err| panic!("TODO handle invalid function {:?}", err)); let raw = if procs.is_module_thunk(proc_name.name()) { @@ -3587,7 +3580,7 @@ fn specialize_naked_symbol<'a>( return result; } else if env.is_imported_symbol(symbol) { - match layout_cache.from_var(env.arena, variable, env.subs, env.multimorphic_names) { + match layout_cache.from_var(env.arena, variable, env.subs) { Err(e) => panic!("invalid layout {:?}", e), Ok(_) => { // this is a 0-arity thunk @@ -3969,7 +3962,6 @@ pub fn with_hole<'a>( record_var, env.subs, env.target_info, - env.multimorphic_names, ) { Ok(fields) => fields, Err(_) => return Stmt::RuntimeError("Can't create record with improper layout"), @@ -4023,7 +4015,7 @@ pub fn with_hole<'a>( // creating a record from the var will unpack it if it's just a single field. let layout = layout_cache - .from_var(env.arena, record_var, env.subs, env.multimorphic_names) + .from_var(env.arena, record_var, env.subs) .unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err)); let field_symbols = field_symbols.into_bump_slice(); @@ -4081,8 +4073,8 @@ pub fn with_hole<'a>( final_else, } => { match ( - layout_cache.from_var(env.arena, branch_var, env.subs, env.multimorphic_names), - layout_cache.from_var(env.arena, cond_var, env.subs, env.multimorphic_names), + layout_cache.from_var(env.arena, branch_var, env.subs), + layout_cache.from_var(env.arena, cond_var, env.subs), ) { (Ok(ret_layout), Ok(cond_layout)) => { // if the hole is a return, then we don't need to merge the two @@ -4181,7 +4173,7 @@ pub fn with_hole<'a>( } let layout = layout_cache - .from_var(env.arena, branch_var, env.subs, env.multimorphic_names) + .from_var(env.arena, branch_var, env.subs) .unwrap_or_else(|err| { panic!("TODO turn fn_var into a RuntimeError {:?}", err) }); @@ -4248,7 +4240,7 @@ pub fn with_hole<'a>( ); let layout = layout_cache - .from_var(env.arena, expr_var, env.subs, env.multimorphic_names) + .from_var(env.arena, expr_var, env.subs) .unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err)); let param = Param { @@ -4271,8 +4263,7 @@ pub fn with_hole<'a>( .. } if loc_elems.is_empty() => { // because an empty list has an unknown element type, it is handled differently - let opt_elem_layout = - layout_cache.from_var(env.arena, elem_var, env.subs, env.multimorphic_names); + let opt_elem_layout = layout_cache.from_var(env.arena, elem_var, env.subs); match opt_elem_layout { Ok(elem_layout) => { @@ -4326,7 +4317,7 @@ pub fn with_hole<'a>( let arg_symbols = arg_symbols.into_bump_slice(); let elem_layout = layout_cache - .from_var(env.arena, elem_var, env.subs, env.multimorphic_names) + .from_var(env.arena, elem_var, env.subs) .unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err)); let expr = Expr::Array { @@ -4362,7 +4353,6 @@ pub fn with_hole<'a>( record_var, env.subs, env.target_info, - env.multimorphic_names, ) { Ok(fields) => fields, Err(_) => return Stmt::RuntimeError("Can't access record with improper layout"), @@ -4413,7 +4403,7 @@ pub fn with_hole<'a>( }; let layout = layout_cache - .from_var(env.arena, field_var, env.subs, env.multimorphic_names) + .from_var(env.arena, field_var, env.subs) .unwrap_or_else(|err| { panic!("TODO turn fn_var into a RuntimeError {:?}", err) }); @@ -4460,12 +4450,7 @@ pub fn with_hole<'a>( Ok(_) => { let raw_layout = return_on_layout_error!( env, - layout_cache.raw_from_var( - env.arena, - function_type, - env.subs, - env.multimorphic_names, - ) + layout_cache.raw_from_var(env.arena, function_type, env.subs,) ); match raw_layout { @@ -4513,7 +4498,6 @@ pub fn with_hole<'a>( record_var, env.subs, env.target_info, - env.multimorphic_names, ) { Ok(fields) => fields, Err(_) => return Stmt::RuntimeError("Can't update record with improper layout"), @@ -4558,7 +4542,7 @@ pub fn with_hole<'a>( let symbols = symbols.into_bump_slice(); let record_layout = layout_cache - .from_var(env.arena, record_var, env.subs, env.multimorphic_names) + .from_var(env.arena, record_var, env.subs) .unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err)); let field_layouts = match &record_layout { @@ -4666,12 +4650,7 @@ pub fn with_hole<'a>( }) => { let loc_body = *boxed_body; - let raw = layout_cache.raw_from_var( - env.arena, - function_type, - env.subs, - env.multimorphic_names, - ); + let raw = layout_cache.raw_from_var(env.arena, function_type, env.subs); match return_on_layout_error!(env, raw) { RawFunctionLayout::ZeroArgumentThunk(_) => { @@ -4805,12 +4784,7 @@ pub fn with_hole<'a>( let full_layout = return_on_layout_error!( env, - layout_cache.raw_from_var( - env.arena, - fn_var, - env.subs, - env.multimorphic_names, - ) + layout_cache.raw_from_var(env.arena, fn_var, env.subs,) ); // if the function expression (loc_expr) is already a symbol, @@ -4988,10 +4962,8 @@ pub fn with_hole<'a>( let arg_symbols = arg_symbols.into_bump_slice(); // layout of the return type - let layout = return_on_layout_error!( - env, - layout_cache.from_var(env.arena, ret_var, env.subs, env.multimorphic_names,) - ); + let layout = + return_on_layout_error!(env, layout_cache.from_var(env.arena, ret_var, env.subs,)); let call = self::Call { call_type: CallType::Foreign { @@ -5026,10 +4998,8 @@ pub fn with_hole<'a>( let arg_symbols = arg_symbols.into_bump_slice(); // layout of the return type - let layout = return_on_layout_error!( - env, - layout_cache.from_var(env.arena, ret_var, env.subs, env.multimorphic_names,) - ); + let layout = + return_on_layout_error!(env, layout_cache.from_var(env.arena, ret_var, env.subs,)); macro_rules! match_on_closure_argument { ( $ho:ident, [$($x:ident),* $(,)?]) => {{ @@ -5039,7 +5009,7 @@ pub fn with_hole<'a>( let closure_data_layout = return_on_layout_error!( env, - layout_cache.raw_from_var(env.arena, closure_data_var, env.subs,env.multimorphic_names) + layout_cache.raw_from_var(env.arena, closure_data_var, env.subs) ); // NB: I don't think the top_level here can capture anything?? @@ -5289,7 +5259,7 @@ where .into_iter() .map(|(_, var)| { layout_cache - .from_var(env.arena, *var, env.subs, env.multimorphic_names) + .from_var(env.arena, *var, env.subs) .expect("layout problem for capture") }) .collect_in::>(env.arena); @@ -5421,13 +5391,8 @@ fn convert_tag_union<'a>( arena: &'a Bump, ) -> Stmt<'a> { use crate::layout::UnionVariant::*; - let res_variant = crate::layout::union_sorted_tags( - env.arena, - variant_var, - env.subs, - env.target_info, - env.multimorphic_names, - ); + let res_variant = + crate::layout::union_sorted_tags(env.arena, variant_var, env.subs, env.target_info); let variant = match res_variant { Ok(cached) => cached, Err(LayoutProblem::UnresolvedTypeVar(_)) => { @@ -5486,7 +5451,7 @@ fn convert_tag_union<'a>( // Layout will unpack this unwrapped tack if it only has one (non-zero-sized) field let layout = layout_cache - .from_var(env.arena, variant_var, env.subs, env.multimorphic_names) + .from_var(env.arena, variant_var, env.subs) .unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err)); // even though this was originally a Tag, we treat it as a Struct from now on @@ -5514,7 +5479,7 @@ fn convert_tag_union<'a>( // version is not the same as the minimal version. let union_layout = match return_on_layout_error!( env, - layout_cache.from_var(env.arena, variant_var, env.subs, env.multimorphic_names,) + layout_cache.from_var(env.arena, variant_var, env.subs,) ) { Layout::Union(ul) => ul, _ => unreachable!(), @@ -5712,7 +5677,7 @@ fn tag_union_to_function<'a>( // only need to construct closure data let raw_layout = return_on_layout_error!( env, - layout_cache.raw_from_var(env.arena, whole_var, env.subs, env.multimorphic_names,) + layout_cache.raw_from_var(env.arena, whole_var, env.subs,) ); match raw_layout { @@ -5753,7 +5718,7 @@ fn sorted_field_symbols<'a>( for (var, mut arg) in args.drain(..) { // Layout will unpack this unwrapped tag if it only has one (non-zero-sized) field - let layout = match layout_cache.from_var(env.arena, var, env.subs, env.multimorphic_names) { + let layout = match layout_cache.from_var(env.arena, var, env.subs) { Ok(cached) => cached, Err(LayoutProblem::UnresolvedTypeVar(_)) => { // this argument has type `forall a. a`, which is isomorphic to @@ -5851,13 +5816,7 @@ fn register_capturing_closure<'a>( let captured_symbols = match *env.subs.get_content_without_compacting(function_type) { Content::Structure(FlatType::Func(_, closure_var, _)) => { - match LambdaSet::from_var( - env.arena, - env.subs, - closure_var, - env.target_info, - env.multimorphic_names, - ) { + match LambdaSet::from_var(env.arena, env.subs, closure_var, env.target_info) { Ok(lambda_set) => { if let Layout::Struct { field_layouts: &[], .. @@ -5888,12 +5847,7 @@ fn register_capturing_closure<'a>( captured_symbols.is_empty(), "{:?} with layout {:?} {:?} {:?}", &captured_symbols, - layout_cache.raw_from_var( - env.arena, - function_type, - env.subs, - env.multimorphic_names, - ), + layout_cache.raw_from_var(env.arena, function_type, env.subs,), env.subs, (function_type, closure_type), ); @@ -5972,10 +5926,10 @@ pub fn from_can<'a>( final_else, } => { let ret_layout = layout_cache - .from_var(env.arena, branch_var, env.subs, env.multimorphic_names) + .from_var(env.arena, branch_var, env.subs) .expect("invalid ret_layout"); let cond_layout = layout_cache - .from_var(env.arena, cond_var, env.subs, env.multimorphic_names) + .from_var(env.arena, cond_var, env.subs) .expect("invalid cond_layout"); let mut stmt = from_can(env, branch_var, final_else.value, procs, layout_cache); @@ -6019,8 +5973,7 @@ pub fn from_can<'a>( let mut layouts = Vec::with_capacity_in(lookups_in_cond.len(), env.arena); for (_, var) in lookups_in_cond { - let res_layout = - layout_cache.from_var(env.arena, var, env.subs, env.multimorphic_names); + let res_layout = layout_cache.from_var(env.arena, var, env.subs); let layout = return_on_layout_error!(env, res_layout); layouts.push(layout); } @@ -6187,15 +6140,11 @@ fn from_can_when<'a>( } let opt_branches = to_opt_branches(env, procs, branches, exhaustive_mark, layout_cache); - let cond_layout = return_on_layout_error!( - env, - layout_cache.from_var(env.arena, cond_var, env.subs, env.multimorphic_names,) - ); + let cond_layout = + return_on_layout_error!(env, layout_cache.from_var(env.arena, cond_var, env.subs,)); - let ret_layout = return_on_layout_error!( - env, - layout_cache.from_var(env.arena, expr_var, env.subs, env.multimorphic_names,) - ); + let ret_layout = + return_on_layout_error!(env, layout_cache.from_var(env.arena, expr_var, env.subs,)); let arena = env.arena; let it = opt_branches @@ -7188,8 +7137,7 @@ where for (_, (variable, left)) in needed_specializations_of_left { add_needed_external(procs, env, variable, LambdaName::thunk(right)); - let res_layout = - layout_cache.from_var(env.arena, variable, env.subs, env.multimorphic_names); + let res_layout = layout_cache.from_var(env.arena, variable, env.subs); let layout = return_on_layout_error!(env, res_layout); result = force_thunk(env, right, layout, left, env.arena.alloc(result)); @@ -7279,12 +7227,7 @@ fn specialize_symbol<'a>( None => { match arg_var { Some(arg_var) if env.is_imported_symbol(original) => { - let raw = match layout_cache.raw_from_var( - env.arena, - arg_var, - env.subs, - env.multimorphic_names, - ) { + let raw = match layout_cache.raw_from_var(env.arena, arg_var, env.subs) { Ok(v) => v, Err(e) => return_on_layout_error_help!(env, e), }; @@ -7347,7 +7290,7 @@ fn specialize_symbol<'a>( // to it in the IR. let res_layout = return_on_layout_error!( env, - layout_cache.raw_from_var(env.arena, arg_var, env.subs, env.multimorphic_names,) + layout_cache.raw_from_var(env.arena, arg_var, env.subs,) ); // we have three kinds of functions really. Plain functions, closures by capture, @@ -7587,7 +7530,7 @@ fn call_by_name<'a>( hole: &'a Stmt<'a>, ) -> Stmt<'a> { // Register a pending_specialization for this function - match layout_cache.raw_from_var(env.arena, fn_var, env.subs, env.multimorphic_names) { + match layout_cache.raw_from_var(env.arena, fn_var, env.subs) { Err(LayoutProblem::UnresolvedTypeVar(var)) => { let msg = format!( "Hit an unresolved type variable {:?} when creating a layout for {:?} (var {:?})", @@ -7775,7 +7718,7 @@ fn call_by_name_help<'a>( // the variables of the given arguments let mut pattern_vars = Vec::with_capacity_in(loc_args.len(), arena); for (var, _) in &loc_args { - match layout_cache.from_var(env.arena, *var, env.subs, env.multimorphic_names) { + match layout_cache.from_var(env.arena, *var, env.subs) { Ok(_) => { pattern_vars.push(*var); } @@ -8436,14 +8379,9 @@ fn from_can_pattern_help<'a>( use crate::layout::UnionVariant::*; use roc_exhaustive::Union; - let res_variant = crate::layout::union_sorted_tags( - env.arena, - *whole_var, - env.subs, - env.target_info, - env.multimorphic_names, - ) - .map_err(Into::into); + let res_variant = + crate::layout::union_sorted_tags(env.arena, *whole_var, env.subs, env.target_info) + .map_err(Into::into); let variant = match res_variant { Ok(cached) => cached, @@ -8526,12 +8464,12 @@ fn from_can_pattern_help<'a>( arguments.sort_by(|arg1, arg2| { let size1 = layout_cache - .from_var(env.arena, arg1.0, env.subs, env.multimorphic_names) + .from_var(env.arena, arg1.0, env.subs) .map(|x| x.alignment_bytes(env.target_info)) .unwrap_or(0); let size2 = layout_cache - .from_var(env.arena, arg2.0, env.subs, env.multimorphic_names) + .from_var(env.arena, arg2.0, env.subs) .map(|x| x.alignment_bytes(env.target_info)) .unwrap_or(0); @@ -8566,12 +8504,10 @@ fn from_can_pattern_help<'a>( let mut temp = arguments.clone(); temp.sort_by(|arg1, arg2| { - let layout1 = layout_cache - .from_var(env.arena, arg1.0, env.subs, env.multimorphic_names) - .unwrap(); - let layout2 = layout_cache - .from_var(env.arena, arg2.0, env.subs, env.multimorphic_names) - .unwrap(); + let layout1 = + layout_cache.from_var(env.arena, arg1.0, env.subs).unwrap(); + let layout2 = + layout_cache.from_var(env.arena, arg2.0, env.subs).unwrap(); let size1 = layout1.alignment_bytes(env.target_info); let size2 = layout2.alignment_bytes(env.target_info); @@ -8586,12 +8522,7 @@ fn from_can_pattern_help<'a>( // from `layouts` would unroll recursive tag unions, and that leads to // problems down the line because we hash layouts and an unrolled // version is not the same as the minimal version. - let layout = match layout_cache.from_var( - env.arena, - *whole_var, - env.subs, - env.multimorphic_names, - ) { + let layout = match layout_cache.from_var(env.arena, *whole_var, env.subs) { Ok(Layout::Union(ul)) => ul, _ => unreachable!(), }; @@ -8882,7 +8813,7 @@ fn from_can_pattern_help<'a>( } => { let (arg_var, loc_arg_pattern) = &(**argument); let arg_layout = layout_cache - .from_var(env.arena, *arg_var, env.subs, env.multimorphic_names) + .from_var(env.arena, *arg_var, env.subs) .unwrap(); let mono_arg_pattern = from_can_pattern_help( env, @@ -8903,14 +8834,9 @@ fn from_can_pattern_help<'a>( .. } => { // sorted fields based on the type - let sorted_fields = crate::layout::sort_record_fields( - env.arena, - *whole_var, - env.subs, - env.target_info, - env.multimorphic_names, - ) - .map_err(RuntimeError::from)?; + let sorted_fields = + crate::layout::sort_record_fields(env.arena, *whole_var, env.subs, env.target_info) + .map_err(RuntimeError::from)?; // sorted fields based on the destruct let mut mono_destructs = Vec::with_capacity_in(destructs.len(), env.arena); diff --git a/crates/compiler/mono/src/layout.rs b/crates/compiler/mono/src/layout.rs index b08940c175..53c4168c98 100644 --- a/crates/compiler/mono/src/layout.rs +++ b/crates/compiler/mono/src/layout.rs @@ -1,12 +1,11 @@ use crate::ir::Parens; -use bumpalo::collections::{CollectIn, Vec}; +use bumpalo::collections::Vec; use bumpalo::Bump; use roc_builtins::bitcode::{FloatWidth, IntWidth}; use roc_collections::all::{default_hasher, MutMap}; -use roc_collections::VecMap; use roc_error_macros::{internal_error, todo_abilities}; use roc_module::ident::{Lowercase, TagName}; -use roc_module::symbol::{IdentIds, Interns, ModuleId, Symbol}; +use roc_module::symbol::{Interns, Symbol}; use roc_problem::can::RuntimeError; use roc_target::{PtrWidth, TargetInfo}; use roc_types::subs::{ @@ -17,7 +16,6 @@ use std::cmp::Ordering; use std::collections::hash_map::{DefaultHasher, Entry}; use std::collections::HashMap; use std::hash::{Hash, Hasher}; -use std::sync::{Arc, Mutex}; use ven_pretty::{DocAllocator, DocBuilder}; // if your changes cause this number to go down, great! @@ -185,13 +183,8 @@ impl<'a> RawFunctionLayout<'a> { let fn_args = fn_args.into_bump_slice(); let ret = arena.alloc(ret); - let lambda_set = LambdaSet::from_var( - env.arena, - env.subs, - closure_var, - env.target_info, - env.multimorphic_names, - )?; + let lambda_set = + LambdaSet::from_var(env.arena, env.subs, closure_var, env.target_info)?; Ok(Self::Function(fn_args, lambda_set, ret)) } @@ -699,170 +692,6 @@ impl std::fmt::Debug for LambdaSet<'_> { } } -#[derive(Default, Debug)] -struct MultimorphicNamesTable { - /// (source symbol, captures layouts) -> multimorphic alias - /// - /// SAFETY: actually, the `Layout` is alive only as long as the `arena` is alive. We take care - /// to promote new layouts to the owned arena. Since we are using a bump-allocating arena, the - /// references will never be invalidated until the arena is dropped, which happens when this - /// struct is dropped. - /// Also, the `Layout`s we owned are never exposed back via the public API. - inner: VecMap<(Symbol, &'static [Layout<'static>]), Symbol>, - arena: Bump, - ident_ids: IdentIds, -} - -impl MultimorphicNamesTable { - fn get<'b>(&self, name: Symbol, captures_layouts: &'b [Layout<'b>]) -> Option { - self.inner.get(&(name, captures_layouts)).copied() - } - - fn insert<'b>(&mut self, name: Symbol, captures_layouts: &'b [Layout<'b>]) -> Symbol { - debug_assert!(!self.inner.contains_key(&(name, captures_layouts))); - - let new_ident = self.ident_ids.gen_unique(); - let new_symbol = Symbol::new(ModuleId::MULTIMORPHIC, new_ident); - - let captures_layouts = self.promote_layout_slice(captures_layouts); - - self.inner.insert((name, captures_layouts), new_symbol); - - new_symbol - } - - fn alloc_st(&self, v: T) -> &'static T { - unsafe { std::mem::transmute::<_, &'static Bump>(&self.arena) }.alloc(v) - } - - fn promote_layout<'b>(&self, layout: Layout<'b>) -> Layout<'static> { - match layout { - Layout::Builtin(builtin) => Layout::Builtin(self.promote_builtin(builtin)), - Layout::Struct { - field_order_hash, - field_layouts, - } => Layout::Struct { - field_order_hash, - field_layouts: self.promote_layout_slice(field_layouts), - }, - Layout::Boxed(layout) => Layout::Boxed(self.alloc_st(self.promote_layout(*layout))), - Layout::Union(union_layout) => Layout::Union(self.promote_union_layout(union_layout)), - Layout::LambdaSet(lambda_set) => Layout::LambdaSet(self.promote_lambda_set(lambda_set)), - Layout::RecursivePointer => Layout::RecursivePointer, - } - } - - fn promote_layout_slice<'b>(&self, layouts: &'b [Layout<'b>]) -> &'static [Layout<'static>] { - layouts - .iter() - .map(|layout| self.promote_layout(*layout)) - .collect_in::>(unsafe { std::mem::transmute(&self.arena) }) - .into_bump_slice() - } - - fn promote_layout_slice_slices<'b>( - &self, - layout_slices: &'b [&'b [Layout<'b>]], - ) -> &'static [&'static [Layout<'static>]] { - layout_slices - .iter() - .map(|slice| self.promote_layout_slice(slice)) - .collect_in::>(unsafe { std::mem::transmute(&self.arena) }) - .into_bump_slice() - } - - fn promote_builtin(&self, builtin: Builtin) -> Builtin<'static> { - match builtin { - Builtin::Int(w) => Builtin::Int(w), - Builtin::Float(w) => Builtin::Float(w), - Builtin::Bool => Builtin::Bool, - Builtin::Decimal => Builtin::Decimal, - Builtin::Str => Builtin::Str, - Builtin::Dict(k, v) => Builtin::Dict( - self.alloc_st(self.promote_layout(*k)), - self.alloc_st(self.promote_layout(*v)), - ), - Builtin::Set(k) => Builtin::Set(self.alloc_st(self.promote_layout(*k))), - Builtin::List(l) => Builtin::Set(self.alloc_st(self.promote_layout(*l))), - } - } - - fn promote_union_layout(&self, union_layout: UnionLayout) -> UnionLayout<'static> { - match union_layout { - UnionLayout::NonRecursive(slices) => { - UnionLayout::NonRecursive(self.promote_layout_slice_slices(slices)) - } - UnionLayout::Recursive(slices) => { - UnionLayout::Recursive(self.promote_layout_slice_slices(slices)) - } - UnionLayout::NonNullableUnwrapped(slice) => { - UnionLayout::NonNullableUnwrapped(self.promote_layout_slice(slice)) - } - UnionLayout::NullableWrapped { - nullable_id, - other_tags, - } => UnionLayout::NullableWrapped { - nullable_id, - other_tags: self.promote_layout_slice_slices(other_tags), - }, - UnionLayout::NullableUnwrapped { - nullable_id, - other_fields, - } => UnionLayout::NullableUnwrapped { - nullable_id, - other_fields: self.promote_layout_slice(other_fields), - }, - } - } - - fn promote_lambda_set(&self, lambda_set: LambdaSet) -> LambdaSet<'static> { - let LambdaSet { - set, - representation, - } = lambda_set; - let set = set - .iter() - .map(|(name, slice)| (*name, self.promote_layout_slice(slice))) - .collect_in::>(unsafe { std::mem::transmute(&self.arena) }) - .into_bump_slice(); - LambdaSet { - set, - representation: self.alloc_st(self.promote_layout(*representation)), - } - } -} - -#[derive(Default, Debug)] -pub struct MultimorphicNames(Arc>); - -impl Clone for MultimorphicNames { - fn clone(&self) -> Self { - Self(Arc::clone(&self.0)) - } -} - -impl MultimorphicNames { - fn get_or_insert<'b>(&self, name: Symbol, captures_layouts: &'b [Layout<'b>]) -> Symbol { - let mut table = self.0.lock().unwrap(); - match table.get(name, captures_layouts) { - Some(symbol) => symbol, - None => table.insert(name, captures_layouts), - } - } - - /// Assumes there is only one clone still alive. - /// If there is more than one clone alive, `self` is returned. - pub fn try_unwrap_names(self) -> Result { - let mutex = Arc::try_unwrap(self.0).map_err(Self)?; - let table = mutex - .into_inner() - .expect("how can there be another lock if we consumed the only ref?"); - Ok(table.ident_ids) - } -} - -static_assertions::assert_eq_size!(&[Layout], Option<&[Layout]>); - #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] pub struct LambdaName<'a> { name: Symbol, @@ -1100,7 +929,6 @@ impl<'a> LambdaSet<'a> { subs: &Subs, closure_var: Variable, target_info: TargetInfo, - multimorphic_names: &mut MultimorphicNames, ) -> Result { match resolve_lambda_set(subs, closure_var) { ResolvedLambdaSet::Set(mut lambdas) => { @@ -1123,7 +951,6 @@ impl<'a> LambdaSet<'a> { subs, seen: Vec::new_in(arena), target_info, - multimorphic_names, }; for var in variables { @@ -1177,7 +1004,6 @@ impl<'a> LambdaSet<'a> { subs, set_with_variables, target_info, - multimorphic_names, )); Ok(LambdaSet { @@ -1201,11 +1027,9 @@ impl<'a> LambdaSet<'a> { subs: &Subs, tags: std::vec::Vec<(Symbol, std::vec::Vec)>, target_info: TargetInfo, - multimorphic_names: &mut MultimorphicNames, ) -> Layout<'a> { // otherwise, this is a closure with a payload - let variant = - union_sorted_tags_help(arena, tags, None, subs, target_info, multimorphic_names); + let variant = union_sorted_tags_help(arena, tags, None, subs, target_info); use UnionVariant::*; match variant { @@ -1311,7 +1135,6 @@ pub struct Env<'a, 'b> { arena: &'a Bump, seen: Vec<'a, Variable>, subs: &'b Subs, - multimorphic_names: &'b mut MultimorphicNames, } impl<'a, 'b> Env<'a, 'b> { @@ -1752,7 +1575,6 @@ impl<'a> LayoutCache<'a> { arena: &'a Bump, var: Variable, subs: &Subs, - multimorphic_names: &mut MultimorphicNames, ) -> Result, LayoutProblem> { // Store things according to the root Variable, to avoid duplicate work. let var = subs.get_root_key_without_compacting(var); @@ -1762,7 +1584,6 @@ impl<'a> LayoutCache<'a> { subs, seen: Vec::new_in(arena), target_info: self.target_info, - multimorphic_names, }; Layout::from_var(&mut env, var) @@ -1773,7 +1594,6 @@ impl<'a> LayoutCache<'a> { arena: &'a Bump, var: Variable, subs: &Subs, - multimorphic_names: &mut MultimorphicNames, ) -> Result, LayoutProblem> { // Store things according to the root Variable, to avoid duplicate work. let var = subs.get_root_key_without_compacting(var); @@ -1783,7 +1603,6 @@ impl<'a> LayoutCache<'a> { subs, seen: Vec::new_in(arena), target_info: self.target_info, - multimorphic_names, }; RawFunctionLayout::from_var(&mut env, var) } @@ -2177,13 +1996,8 @@ fn layout_from_flat_type<'a>( } } Func(_, closure_var, _) => { - let lambda_set = LambdaSet::from_var( - env.arena, - env.subs, - closure_var, - env.target_info, - env.multimorphic_names, - )?; + let lambda_set = + LambdaSet::from_var(env.arena, env.subs, closure_var, env.target_info)?; Ok(Layout::LambdaSet(lambda_set)) } @@ -2267,14 +2081,12 @@ pub fn sort_record_fields<'a>( var: Variable, subs: &Subs, target_info: TargetInfo, - multimorphic_names: &mut MultimorphicNames, ) -> Result>, LayoutProblem> { let mut env = Env { arena, subs, seen: Vec::new_in(arena), target_info, - multimorphic_names, }; let (it, _) = match gather_fields_unsorted_iter(subs, RecordFields::empty(), var) { @@ -2481,7 +2293,6 @@ pub fn union_sorted_tags<'a>( var: Variable, subs: &Subs, target_info: TargetInfo, - multimorphic_names: &mut MultimorphicNames, ) -> Result, LayoutProblem> { let var = if let Content::RecursionVar { structure, .. } = subs.get_content_without_compacting(var) { @@ -2502,7 +2313,7 @@ pub fn union_sorted_tags<'a>( | Err((_, Content::FlexVar(_) | Content::RigidVar(_))) | Err((_, Content::RecursionVar { .. })) => { let opt_rec_var = get_recursion_var(subs, var); - union_sorted_tags_help(arena, tags_vec, opt_rec_var, subs, target_info, multimorphic_names) + union_sorted_tags_help(arena, tags_vec, opt_rec_var, subs, target_info) } Err((_, Content::Error)) => return Err(LayoutProblem::Erroneous), Err(other) => panic!("invalid content in tag union variable: {:?}", other), @@ -2728,7 +2539,6 @@ pub fn union_sorted_tags_help<'a, L>( opt_rec_var: Option, subs: &Subs, target_info: TargetInfo, - multimorphic_names: &mut MultimorphicNames, ) -> UnionVariant<'a> where L: Into + Ord + Clone, @@ -2741,7 +2551,6 @@ where subs, seen: Vec::new_in(arena), target_info, - multimorphic_names, }; match tags_vec.len() { diff --git a/crates/compiler/test_gen/src/wasm_linking.rs b/crates/compiler/test_gen/src/wasm_linking.rs index 0a2869880f..c6995d7502 100644 --- a/crates/compiler/test_gen/src/wasm_linking.rs +++ b/crates/compiler/test_gen/src/wasm_linking.rs @@ -123,6 +123,7 @@ fn build_app_mono<'a>( let proc_layout = ProcLayout { arguments: &[], result: int_layout, + captures_niche: &[], }; let mut app = MutMap::default(); diff --git a/crates/repl_cli/src/lib.rs b/crates/repl_cli/src/lib.rs index 0c8e588503..dace5fb484 100644 --- a/crates/repl_cli/src/lib.rs +++ b/crates/repl_cli/src/lib.rs @@ -2,7 +2,6 @@ use bumpalo::Bump; use const_format::concatcp; use inkwell::context::Context; use libloading::Library; -use roc_mono::layout::MultimorphicNames; use rustyline::highlight::{Highlighter, PromptInfo}; use rustyline::validate::{self, ValidationContext, ValidationResult, Validator}; use rustyline_derive::{Completer, Helper, Hinter}; @@ -306,8 +305,6 @@ fn gen_and_eval_llvm<'a>( let app = CliApp { lib }; - let mut multimorphic_names = MultimorphicNames::default(); - let res_answer = jit_to_ast( &arena, &app, @@ -315,7 +312,6 @@ fn gen_and_eval_llvm<'a>( main_fn_layout, content, &subs, - &mut multimorphic_names, target_info, ); diff --git a/crates/repl_eval/src/eval.rs b/crates/repl_eval/src/eval.rs index c1941c5ae0..65c1d92136 100644 --- a/crates/repl_eval/src/eval.rs +++ b/crates/repl_eval/src/eval.rs @@ -9,8 +9,7 @@ use roc_module::ident::TagName; use roc_module::symbol::Symbol; use roc_mono::ir::ProcLayout; use roc_mono::layout::{ - union_sorted_tags_help, Builtin, Layout, LayoutCache, MultimorphicNames, UnionLayout, - UnionVariant, WrappedVariant, + union_sorted_tags_help, Builtin, Layout, LayoutCache, UnionLayout, UnionVariant, WrappedVariant, }; use roc_parse::ast::{AssignedField, Collection, Expr, StrLiteral}; use roc_region::all::{Loc, Region}; @@ -24,7 +23,6 @@ struct Env<'a> { arena: &'a Bump, subs: &'a Subs, target_info: TargetInfo, - multimorphic_names: &'a mut MultimorphicNames, } pub enum ToAstProblem { @@ -47,14 +45,12 @@ pub fn jit_to_ast<'a, A: ReplApp<'a>>( layout: ProcLayout<'a>, content: &'a Content, subs: &'a Subs, - multimorphic_names: &'a mut MultimorphicNames, target_info: TargetInfo, ) -> Result, ToAstProblem> { let mut env = Env { arena, subs, target_info, - multimorphic_names, }; match layout { @@ -185,14 +181,8 @@ fn get_tags_vars_and_variant<'a>( let vars_of_tag: MutMap<_, _> = tags_vec.iter().cloned().collect(); - let union_variant = union_sorted_tags_help( - env.arena, - tags_vec, - opt_rec_var, - env.subs, - env.target_info, - env.multimorphic_names, - ); + let union_variant = + union_sorted_tags_help(env.arena, tags_vec, opt_rec_var, env.subs, env.target_info); (vars_of_tag, union_variant) } @@ -911,7 +901,7 @@ fn struct_to_ast<'a, M: ReplAppMemory>( let inner_content = env.subs.get_content_without_compacting(field.into_inner()); let field_layout = layout_cache - .from_var(arena, field.into_inner(), env.subs, env.multimorphic_names) + .from_var(arena, field.into_inner(), env.subs) .unwrap(); let inner_layouts = arena.alloc([field_layout]); @@ -950,7 +940,7 @@ fn struct_to_ast<'a, M: ReplAppMemory>( for (label, field) in record_fields.sorted_iterator(subs, Variable::EMPTY_RECORD) { let content = subs.get_content_without_compacting(field.into_inner()); let field_layout = layout_cache - .from_var(arena, field.into_inner(), env.subs, env.multimorphic_names) + .from_var(arena, field.into_inner(), env.subs) .unwrap(); let loc_expr = &*arena.alloc(Loc { @@ -1150,7 +1140,6 @@ fn byte_to_ast<'a, M: ReplAppMemory>( None, env.subs, env.target_info, - env.multimorphic_names, ); match union_variant { diff --git a/crates/repl_wasm/src/repl.rs b/crates/repl_wasm/src/repl.rs index 4674cdda6a..d57782982b 100644 --- a/crates/repl_wasm/src/repl.rs +++ b/crates/repl_wasm/src/repl.rs @@ -1,5 +1,4 @@ use bumpalo::{collections::vec::Vec, Bump}; -use roc_mono::layout::MultimorphicNames; use std::mem::size_of; use roc_collections::all::MutSet; @@ -242,7 +241,6 @@ pub async fn entrypoint_from_js(src: String) -> Result { .map_err(|js| format!("{:?}", js))?; let app = WasmReplApp { arena }; - let mut multimorphic_names = MultimorphicNames::default(); // Run the app and transform the result value to an AST `Expr` // Restore type constructor names, and other user-facing info that was erased during compilation. @@ -253,7 +251,6 @@ pub async fn entrypoint_from_js(src: String) -> Result { main_fn_layout, content, &subs, - &mut multimorphic_names, target_info, ); From 71d612078a7f7944b8b0eb8cf470ddf44e3b1321 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Fri, 1 Jul 2022 16:06:48 -0400 Subject: [PATCH 29/66] no_niche to represent lack of captures niche --- crates/compiler/build/src/program.rs | 1 + crates/compiler/load_internal/src/file.rs | 8 ++-- crates/compiler/mono/src/code_gen_help/mod.rs | 6 +-- crates/compiler/mono/src/ir.rs | 37 +++++++++++-------- crates/compiler/mono/src/layout.rs | 12 +----- crates/compiler/test_gen/src/wasm_linking.rs | 2 +- 6 files changed, 31 insertions(+), 35 deletions(-) diff --git a/crates/compiler/build/src/program.rs b/crates/compiler/build/src/program.rs index 8f5a704f4f..cbcdcd6339 100644 --- a/crates/compiler/build/src/program.rs +++ b/crates/compiler/build/src/program.rs @@ -261,6 +261,7 @@ pub fn gen_from_mono_module_llvm( exposed_to_host: loaded.exposed_to_host.values.keys().copied().collect(), }; + dbg!(21); roc_gen_llvm::llvm::build::build_procedures( &env, opt_level, diff --git a/crates/compiler/load_internal/src/file.rs b/crates/compiler/load_internal/src/file.rs index 493c875801..4c312d8e27 100644 --- a/crates/compiler/load_internal/src/file.rs +++ b/crates/compiler/load_internal/src/file.rs @@ -4574,7 +4574,7 @@ fn build_pending_specializations<'a>( procs_base.host_specializations.insert_host_exposed( mono_env.subs, - LambdaName::only_receiver(symbol), + LambdaName::no_niche(symbol), annotation, expr_var, ); @@ -4634,7 +4634,7 @@ fn build_pending_specializations<'a>( procs_base.host_specializations.insert_host_exposed( mono_env.subs, - LambdaName::only_receiver(symbol), + LambdaName::no_niche(symbol), annotation, expr_var, ); @@ -4712,7 +4712,7 @@ fn build_pending_specializations<'a>( procs_base.host_specializations.insert_host_exposed( mono_env.subs, - LambdaName::only_receiver(symbol), + LambdaName::no_niche(symbol), annotation, expr_var, ); @@ -4772,7 +4772,7 @@ fn build_pending_specializations<'a>( procs_base.host_specializations.insert_host_exposed( mono_env.subs, - LambdaName::only_receiver(symbol), + LambdaName::no_niche(symbol), annotation, expr_var, ); diff --git a/crates/compiler/mono/src/code_gen_help/mod.rs b/crates/compiler/mono/src/code_gen_help/mod.rs index a02b04cf3f..35c5f1be8a 100644 --- a/crates/compiler/mono/src/code_gen_help/mod.rs +++ b/crates/compiler/mono/src/code_gen_help/mod.rs @@ -170,7 +170,7 @@ impl<'a> CodeGenHelp<'a> { let arg_layouts = self.arena.alloc([layout]); let expr = Expr::Call(Call { call_type: CallType::ByName { - name: LambdaName::only_receiver(proc_name), + name: LambdaName::no_niche(proc_name), ret_layout, arg_layouts, specialization_id: CallSpecId::BACKEND_DUMMY, @@ -262,7 +262,7 @@ impl<'a> CodeGenHelp<'a> { Some(Expr::Call(Call { call_type: CallType::ByName { - name: LambdaName::only_receiver(proc_name), + name: LambdaName::no_niche(proc_name), ret_layout, arg_layouts, specialization_id: CallSpecId::BACKEND_DUMMY, @@ -343,7 +343,7 @@ impl<'a> CodeGenHelp<'a> { }; self.specializations[spec_index].proc = Some(Proc { - name: LambdaName::only_receiver(proc_symbol), + name: LambdaName::no_niche(proc_symbol), args, body, closure_data_layout: None, diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index 17977c4949..8040f70eae 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -2839,7 +2839,7 @@ fn generate_runtime_error_function<'a>( }; Proc { - name: LambdaName::only_receiver(name), + name: LambdaName::no_niche(name), args, body: runtime_error, closure_data_layout: None, @@ -2941,7 +2941,7 @@ fn specialize_external<'a>( ); let proc = Proc { - name: LambdaName::only_receiver(name), + name: LambdaName::no_niche(name), args: proc_arguments.into_bump_slice(), body, closure_data_layout: None, @@ -4439,7 +4439,7 @@ pub fn with_hole<'a>( match procs.insert_anonymous( env, - LambdaName::only_receiver(name), + LambdaName::no_niche(name), function_type, arguments, *loc_body, @@ -4798,7 +4798,12 @@ pub fn with_hole<'a>( Imported(thunk_name) => { debug_assert!(procs.is_imported_module_thunk(thunk_name)); - add_needed_external(procs, env, fn_var, LambdaName::thunk(thunk_name)); + add_needed_external( + procs, + env, + fn_var, + LambdaName::no_niche(thunk_name), + ); let function_symbol = env.unique_symbol(); @@ -5659,7 +5664,7 @@ fn tag_union_to_function<'a>( }); // Lambda does not capture anything, can't be multimorphic - let lambda_name = LambdaName::only_receiver(proc_symbol); + let lambda_name = LambdaName::no_niche(proc_symbol); let inserted = procs.insert_anonymous( env, @@ -7135,7 +7140,7 @@ where // specialized, and wrap the original in a function pointer. let mut result = result; for (_, (variable, left)) in needed_specializations_of_left { - add_needed_external(procs, env, variable, LambdaName::thunk(right)); + add_needed_external(procs, env, variable, LambdaName::no_niche(right)); let res_layout = layout_cache.from_var(env.arena, variable, env.subs); let layout = return_on_layout_error!(env, res_layout); @@ -7146,7 +7151,7 @@ where } else if env.is_imported_symbol(right) { // if this is an imported symbol, then we must make sure it is // specialized, and wrap the original in a function pointer. - add_needed_external(procs, env, variable, LambdaName::only_receiver(right)); + add_needed_external(procs, env, variable, LambdaName::no_niche(right)); // then we must construct its closure; since imported symbols have no closure, we use the empty struct let_empty_struct(left, env.arena.alloc(result)) @@ -7198,7 +7203,7 @@ fn force_thunk<'a>( ) -> Stmt<'a> { let call = self::Call { call_type: CallType::ByName { - name: LambdaName::thunk(thunk_name), + name: LambdaName::no_niche(thunk_name), ret_layout: env.arena.alloc(layout), arg_layouts: &[], specialization_id: env.next_call_specialization_id(), @@ -7247,7 +7252,7 @@ fn specialize_symbol<'a>( procs.insert_passed_by_name( env, arg_var, - LambdaName::thunk(original), + LambdaName::no_niche(original), top_level, layout_cache, ); @@ -7261,7 +7266,7 @@ fn specialize_symbol<'a>( procs.insert_passed_by_name( env, arg_var, - LambdaName::only_receiver(original), + LambdaName::no_niche(original), top_level, layout_cache, ); @@ -7352,7 +7357,7 @@ fn specialize_symbol<'a>( procs.insert_passed_by_name( env, arg_var, - LambdaName::thunk(original), + LambdaName::no_niche(original), top_level, layout_cache, ); @@ -7394,7 +7399,7 @@ fn specialize_symbol<'a>( procs.insert_passed_by_name( env, arg_var, - LambdaName::thunk(original), + LambdaName::no_niche(original), top_level, layout_cache, ); @@ -7644,7 +7649,7 @@ fn call_by_name<'a>( hole, ) } else if env.is_imported_symbol(proc_name) { - add_needed_external(procs, env, fn_var, LambdaName::thunk(proc_name)); + add_needed_external(procs, env, fn_var, LambdaName::no_niche(proc_name)); force_thunk(env, proc_name, ret_layout, assigned, hole) } else { panic!("most likely we're trying to call something that is not a function"); @@ -7699,7 +7704,7 @@ fn call_by_name_help<'a>( ); name } - None => LambdaName::only_receiver(proc_name), + None => LambdaName::no_niche(proc_name), }; // If required, add an extra argument to the layout that is the captured environment @@ -7996,7 +8001,7 @@ fn call_by_name_module_thunk<'a>( // register the pending specialization, so this gets code genned later suspended.specialization( env.subs, - LambdaName::thunk(proc_name), + LambdaName::no_niche(proc_name), top_level_layout, fn_var, ); @@ -8018,7 +8023,7 @@ fn call_by_name_module_thunk<'a>( match specialize_variable( env, procs, - LambdaName::thunk(proc_name), + LambdaName::no_niche(proc_name), layout_cache, fn_var, &[], diff --git a/crates/compiler/mono/src/layout.rs b/crates/compiler/mono/src/layout.rs index 53c4168c98..8996148a85 100644 --- a/crates/compiler/mono/src/layout.rs +++ b/crates/compiler/mono/src/layout.rs @@ -715,17 +715,7 @@ impl LambdaName<'_> { } #[inline(always)] - pub fn thunk(name: Symbol) -> Self { - Self { - name, - captures_niche: &[], - } - } - - // When the function name is known, so there can only be one possible receiver, in such cases - // the lambda cannot be multimorphic. - #[inline(always)] - pub fn only_receiver(name: Symbol) -> Self { + pub fn no_niche(name: Symbol) -> Self { Self { name, captures_niche: &[], diff --git a/crates/compiler/test_gen/src/wasm_linking.rs b/crates/compiler/test_gen/src/wasm_linking.rs index c6995d7502..0aa1b05504 100644 --- a/crates/compiler/test_gen/src/wasm_linking.rs +++ b/crates/compiler/test_gen/src/wasm_linking.rs @@ -110,7 +110,7 @@ fn build_app_mono<'a>( ); let proc = Proc { - name: LambdaName::only_receiver(app_proc), + name: LambdaName::no_niche(app_proc), args: &[], body, closure_data_layout: None, From 8855f269ba56ae6638e85c694215576c4bac4619 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Fri, 1 Jul 2022 16:23:44 -0400 Subject: [PATCH 30/66] Make captures niche more private --- crates/compiler/alias_analysis/src/lib.rs | 36 +++++---- crates/compiler/gen_llvm/src/llvm/build.rs | 34 +++++--- crates/compiler/gen_wasm/src/backend.rs | 2 +- crates/compiler/gen_wasm/src/low_level.rs | 4 +- crates/compiler/load_internal/src/file.rs | 4 +- crates/compiler/mono/src/borrow.rs | 6 +- crates/compiler/mono/src/code_gen_help/mod.rs | 10 +-- crates/compiler/mono/src/inc_dec.rs | 4 +- crates/compiler/mono/src/ir.rs | 78 +++++++++---------- crates/compiler/mono/src/layout.rs | 54 ++++++++++--- crates/compiler/test_gen/src/wasm_linking.rs | 4 +- 11 files changed, 140 insertions(+), 96 deletions(-) diff --git a/crates/compiler/alias_analysis/src/lib.rs b/crates/compiler/alias_analysis/src/lib.rs index 5ad45686f9..a5c3026d81 100644 --- a/crates/compiler/alias_analysis/src/lib.rs +++ b/crates/compiler/alias_analysis/src/lib.rs @@ -12,7 +12,7 @@ use roc_mono::ir::{ Call, CallType, Expr, HigherOrderLowLevel, HostExposedLayouts, ListLiteralElement, Literal, ModifyRc, OptLevel, Proc, Stmt, }; -use roc_mono::layout::{Builtin, Layout, RawFunctionLayout, UnionLayout}; +use roc_mono::layout::{Builtin, CapturesNiche, Layout, RawFunctionLayout, UnionLayout}; // just using one module for now pub const MOD_APP: ModName = ModName(b"UserApp"); @@ -26,7 +26,7 @@ pub fn func_name_bytes(proc: &Proc) -> [u8; SIZE] { let bytes = func_name_bytes_help( proc.name.name(), proc.args.iter().map(|x| x.0), - proc.name.captures_niche.iter().copied(), + proc.name.captures_niche(), &proc.ret_layout, ); bytes @@ -67,15 +67,14 @@ impl TagUnionId { } } -pub fn func_name_bytes_help<'a, I1, I2>( +pub fn func_name_bytes_help<'a, I>( symbol: Symbol, - argument_layouts: I2, - captures_niche: I1, + argument_layouts: I, + captures_niche: CapturesNiche<'a>, return_layout: &Layout<'a>, ) -> [u8; SIZE] where - I1: IntoIterator>, - I2: IntoIterator>, + I: IntoIterator>, { let mut name_bytes = [0u8; SIZE]; @@ -90,9 +89,7 @@ where layout.hash(&mut hasher); } - for capture_layout in captures_niche { - capture_layout.hash(&mut hasher); - } + captures_niche.hash(&mut hasher); return_layout.hash(&mut hasher); @@ -185,7 +182,12 @@ where match layout { RawFunctionLayout::Function(_, _, _) => { let it = top_level.arguments.iter().copied(); - let bytes = func_name_bytes_help(*symbol, it, [], &top_level.result); + let bytes = func_name_bytes_help( + *symbol, + it, + CapturesNiche::no_niche(), + &top_level.result, + ); host_exposed_functions.push((bytes, top_level.arguments)); } @@ -193,7 +195,7 @@ where let bytes = func_name_bytes_help( *symbol, [Layout::UNIT], - [], + CapturesNiche::no_niche(), &top_level.result, ); @@ -223,7 +225,7 @@ where let roc_main_bytes = func_name_bytes_help( entry_point.symbol, entry_point.layout.arguments.iter().copied(), - std::iter::empty(), + CapturesNiche::no_niche(), &entry_point.layout.result, ); let roc_main = FuncName(&roc_main_bytes); @@ -658,8 +660,8 @@ fn call_spec( let arg_value_id = build_tuple_value(builder, env, block, call.arguments)?; let args_it = arg_layouts.iter().copied(); - let captures_it = name.captures_niche.iter().copied(); - let bytes = func_name_bytes_help(name.name(), args_it, captures_it, ret_layout); + let captures_niche = name.captures_niche(); + let bytes = func_name_bytes_help(name.name(), args_it, captures_niche, ret_layout); let name = FuncName(&bytes); let module = MOD_APP; builder.add_call(block, spec_var, module, name, arg_value_id) @@ -703,11 +705,11 @@ fn call_spec( let update_mode_var = UpdateModeVar(&mode); let args_it = passed_function.argument_layouts.iter().copied(); - let captures_it = passed_function.name.captures_niche.iter().copied(); + let captures_niche = passed_function.name.captures_niche(); let bytes = func_name_bytes_help( passed_function.name.name(), args_it, - captures_it, + captures_niche, &passed_function.return_layout, ); let name = FuncName(&bytes); diff --git a/crates/compiler/gen_llvm/src/llvm/build.rs b/crates/compiler/gen_llvm/src/llvm/build.rs index 683cd41504..06b6c1ee05 100644 --- a/crates/compiler/gen_llvm/src/llvm/build.rs +++ b/crates/compiler/gen_llvm/src/llvm/build.rs @@ -63,7 +63,7 @@ use roc_mono::ir::{ ModifyRc, OptLevel, ProcLayout, }; use roc_mono::layout::{ - Builtin, LambdaName, LambdaSet, Layout, LayoutIds, TagIdIntType, UnionLayout, + Builtin, CapturesNiche, LambdaName, LambdaSet, Layout, LayoutIds, TagIdIntType, UnionLayout, }; use roc_std::RocDec; use roc_target::{PtrWidth, TargetInfo}; @@ -717,7 +717,12 @@ fn promote_to_main_function<'a, 'ctx, 'env>( top_level: ProcLayout<'a>, ) -> (&'static str, FunctionValue<'ctx>) { let it = top_level.arguments.iter().copied(); - let bytes = roc_alias_analysis::func_name_bytes_help(symbol, it, [], &top_level.result); + let bytes = roc_alias_analysis::func_name_bytes_help( + symbol, + it, + CapturesNiche::no_niche(), + &top_level.result, + ); let func_name = FuncName(&bytes); let func_solutions = mod_solutions.func_solutions(func_name).unwrap(); @@ -729,7 +734,14 @@ fn promote_to_main_function<'a, 'ctx, 'env>( ); // NOTE fake layout; it is only used for debug prints - let roc_main_fn = function_value_by_func_spec(env, *func_spec, symbol, &[], &[], &Layout::UNIT); + let roc_main_fn = function_value_by_func_spec( + env, + *func_spec, + symbol, + &[], + CapturesNiche::no_niche(), + &Layout::UNIT, + ); let main_fn_name = "$Test.main"; @@ -3298,7 +3310,7 @@ fn expose_function_to_host<'a, 'ctx, 'env>( symbol: Symbol, roc_function: FunctionValue<'ctx>, arguments: &'a [Layout<'a>], - captures_niche: &'a [Layout<'a>], + captures_niche: CapturesNiche<'a>, return_layout: Layout<'a>, layout_ids: &mut LayoutIds<'a>, ) { @@ -4305,7 +4317,7 @@ fn build_proc_header<'a, 'ctx, 'env>( symbol, fn_val, arguments.into_bump_slice(), - proc.name.captures_niche, + proc.name.captures_niche(), proc.ret_layout, layout_ids, ); @@ -4538,7 +4550,7 @@ pub fn build_proc<'a, 'ctx, 'env>( let bytes = roc_alias_analysis::func_name_bytes_help( symbol, it, - [], + CapturesNiche::no_niche(), &top_level.result, ); let func_name = FuncName(&bytes); @@ -4557,7 +4569,7 @@ pub fn build_proc<'a, 'ctx, 'env>( *func_spec, symbol, top_level.arguments, - &[], + CapturesNiche::no_niche(), &top_level.result, ) } @@ -4634,7 +4646,7 @@ fn function_value_by_func_spec<'a, 'ctx, 'env>( func_spec: FuncSpec, symbol: Symbol, arguments: &[Layout<'a>], - captures_niche: &[Layout<'a>], + captures_niche: CapturesNiche<'a>, result: &Layout<'a>, ) -> FunctionValue<'ctx> { let fn_name = func_spec_name(env.arena, &env.interns, symbol, func_spec); @@ -4646,7 +4658,7 @@ fn function_value_by_func_spec<'a, 'ctx, 'env>( fn function_value_by_name_help<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, arguments: &[Layout<'a>], - _captures_niche: &[Layout<'a>], + _captures_niche: CapturesNiche<'a>, result: &Layout<'a>, symbol: Symbol, fn_name: &str, @@ -4696,7 +4708,7 @@ fn roc_call_with_args<'a, 'ctx, 'env>( func_spec, name.name(), argument_layouts, - name.captures_niche, + name.captures_niche(), result_layout, ); @@ -4889,7 +4901,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>( func_spec, function_name.name(), argument_layouts, - function_name.captures_niche, + function_name.captures_niche(), return_layout, ); diff --git a/crates/compiler/gen_wasm/src/backend.rs b/crates/compiler/gen_wasm/src/backend.rs index b931234f6a..ce374c31e1 100644 --- a/crates/compiler/gen_wasm/src/backend.rs +++ b/crates/compiler/gen_wasm/src/backend.rs @@ -1125,7 +1125,7 @@ impl<'a> WasmBackend<'a> { let proc_layout = ProcLayout { arguments: arg_layouts, result: **result, - captures_niche: func_sym.captures_niche, + captures_niche: func_sym.captures_niche(), }; self.expr_call_by_name( func_sym.name(), diff --git a/crates/compiler/gen_wasm/src/low_level.rs b/crates/compiler/gen_wasm/src/low_level.rs index 0386723340..2c6e24cb7d 100644 --- a/crates/compiler/gen_wasm/src/low_level.rs +++ b/crates/compiler/gen_wasm/src/low_level.rs @@ -1951,7 +1951,7 @@ pub fn call_higher_order_lowlevel<'a>( let passed_proc_layout = ProcLayout { arguments: argument_layouts, result: *result_layout, - captures_niche: fn_name.captures_niche, + captures_niche: fn_name.captures_niche(), }; let passed_proc_index = backend .proc_lookup @@ -1985,7 +1985,7 @@ pub fn call_higher_order_lowlevel<'a>( ProcLayout { arguments: wrapper_arg_layouts.into_bump_slice(), result: Layout::UNIT, - captures_niche: &[], + captures_niche: fn_name.captures_niche(), } }; diff --git a/crates/compiler/load_internal/src/file.rs b/crates/compiler/load_internal/src/file.rs index 4c312d8e27..3a89dee23b 100644 --- a/crates/compiler/load_internal/src/file.rs +++ b/crates/compiler/load_internal/src/file.rs @@ -33,7 +33,7 @@ use roc_mono::ir::{ CapturedSymbols, EntryPoint, ExternalSpecializations, PartialProc, Proc, ProcLayout, Procs, ProcsBase, UpdateModeIds, }; -use roc_mono::layout::{LambdaName, Layout, LayoutCache, LayoutProblem}; +use roc_mono::layout::{CapturesNiche, LambdaName, Layout, LayoutCache, LayoutProblem}; use roc_parse::ast::{self, Defs, ExtractSpaces, Spaced, StrLiteral, TypeAnnotation}; use roc_parse::header::{ExposedName, ImportsEntry, PackageEntry, PlatformHeader, To, TypedIdent}; use roc_parse::header::{HeaderFor, ModuleNameEnum, PackageName}; @@ -2662,7 +2662,7 @@ fn finish_specialization( layout: roc_mono::ir::ProcLayout { arguments: &[], result: Layout::struct_no_name_order(&[]), - captures_niche: &[], + captures_niche: CapturesNiche::no_niche(), }, symbol, } diff --git a/crates/compiler/mono/src/borrow.rs b/crates/compiler/mono/src/borrow.rs index 1d819b3d7e..3ec6f0f689 100644 --- a/crates/compiler/mono/src/borrow.rs +++ b/crates/compiler/mono/src/borrow.rs @@ -502,7 +502,7 @@ impl<'a> BorrowInfState<'a> { .. } => { let top_level = - ProcLayout::new(self.arena, arg_layouts, name.captures_niche, **ret_layout); + ProcLayout::new(self.arena, arg_layouts, name.captures_niche(), **ret_layout); // get the borrow signature of the applied function let ps = param_map @@ -545,7 +545,7 @@ impl<'a> BorrowInfState<'a> { let closure_layout = ProcLayout { arguments: passed_function.argument_layouts, result: passed_function.return_layout, - captures_niche: passed_function.name.captures_niche, + captures_niche: passed_function.name.captures_niche(), }; let function_ps = @@ -747,7 +747,7 @@ impl<'a> BorrowInfState<'a> { ) = (v, b) { let top_level = - ProcLayout::new(self.arena, arg_layouts, g.captures_niche, **ret_layout); + ProcLayout::new(self.arena, arg_layouts, g.captures_niche(), **ret_layout); if self.current_proc == g.name() && x == *z { // anonymous functions (for which the ps may not be known) diff --git a/crates/compiler/mono/src/code_gen_help/mod.rs b/crates/compiler/mono/src/code_gen_help/mod.rs index 35c5f1be8a..c1b83dcaaa 100644 --- a/crates/compiler/mono/src/code_gen_help/mod.rs +++ b/crates/compiler/mono/src/code_gen_help/mod.rs @@ -8,7 +8,7 @@ use crate::ir::{ Call, CallSpecId, CallType, Expr, HostExposedLayouts, JoinPointId, ModifyRc, Proc, ProcLayout, SelfRecursive, Stmt, UpdateModeId, }; -use crate::layout::{Builtin, LambdaName, Layout, UnionLayout}; +use crate::layout::{Builtin, CapturesNiche, LambdaName, Layout, UnionLayout}; mod equality; mod refcount; @@ -375,23 +375,23 @@ impl<'a> CodeGenHelp<'a> { HelperOp::Inc => ProcLayout { arguments: self.arena.alloc([*layout, self.layout_isize]), result: LAYOUT_UNIT, - captures_niche: &[], + captures_niche: CapturesNiche::no_niche(), }, HelperOp::Dec => ProcLayout { arguments: self.arena.alloc([*layout]), result: LAYOUT_UNIT, - captures_niche: &[], + captures_niche: CapturesNiche::no_niche(), }, HelperOp::Reset => ProcLayout { arguments: self.arena.alloc([*layout]), result: *layout, - captures_niche: &[], + captures_niche: CapturesNiche::no_niche(), }, HelperOp::DecRef(_) => unreachable!("No generated Proc for DecRef"), HelperOp::Eq => ProcLayout { arguments: self.arena.alloc([*layout, *layout]), result: LAYOUT_BOOL, - captures_niche: &[], + captures_niche: CapturesNiche::no_niche(), }, }; diff --git a/crates/compiler/mono/src/inc_dec.rs b/crates/compiler/mono/src/inc_dec.rs index bdcd0765ec..c570451131 100644 --- a/crates/compiler/mono/src/inc_dec.rs +++ b/crates/compiler/mono/src/inc_dec.rs @@ -565,7 +565,7 @@ impl<'a> Context<'a> { .. } => { let top_level = - ProcLayout::new(self.arena, arg_layouts, name.captures_niche, **ret_layout); + ProcLayout::new(self.arena, arg_layouts, name.captures_niche(), **ret_layout); // get the borrow signature let ps = self @@ -615,7 +615,7 @@ impl<'a> Context<'a> { let function_layout = ProcLayout { arguments: passed_function.argument_layouts, result: passed_function.return_layout, - captures_niche: passed_function.name.captures_niche, + captures_niche: passed_function.name.captures_niche(), }; let function_ps = match self diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index 8040f70eae..27be46a12a 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -1,8 +1,8 @@ #![allow(clippy::manual_map)] use crate::layout::{ - Builtin, ClosureRepresentation, LambdaName, LambdaSet, Layout, LayoutCache, LayoutProblem, - RawFunctionLayout, TagIdIntType, TagOrClosure, UnionLayout, WrappedVariant, + Builtin, CapturesNiche, ClosureRepresentation, LambdaName, LambdaSet, Layout, LayoutCache, + LayoutProblem, RawFunctionLayout, TagIdIntType, TagOrClosure, UnionLayout, WrappedVariant, }; use bumpalo::collections::{CollectIn, Vec}; use bumpalo::Bump; @@ -1021,7 +1021,7 @@ impl<'a> Procs<'a> { .raw_from_var(env.arena, annotation, env.subs) .unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err)); - let top_level = ProcLayout::from_raw(env.arena, raw_layout, name.captures_niche); + let top_level = ProcLayout::from_raw(env.arena, raw_layout, name.captures_niche()); // anonymous functions cannot reference themselves, therefore cannot be tail-recursive // EXCEPT when the closure conversion makes it tail-recursive. @@ -1131,7 +1131,7 @@ impl<'a> Procs<'a> { let top_level = ProcLayout::from_raw( env.arena, layout, - proc.name.captures_niche, + proc.name.captures_niche(), ); debug_assert_eq!( @@ -1238,7 +1238,7 @@ impl<'a> Procs<'a> { let proper_layout = ProcLayout { arguments, result: proc.ret_layout, - captures_niche: proc.name.captures_niche, + captures_niche: proc.name.captures_niche(), }; // NOTE: some function are specialized to have a closure, but don't actually @@ -2634,7 +2634,7 @@ fn specialize_suspended<'a>( match specialize_variable(env, procs, name, layout_cache, var, &[], partial_proc) { Ok((proc, layout)) => { // TODO thiscode is duplicated elsewhere - let top_level = ProcLayout::from_raw(env.arena, layout, proc.name.captures_niche); + let top_level = ProcLayout::from_raw(env.arena, layout, proc.name.captures_niche()); if procs.is_module_thunk(proc.name.name()) { debug_assert!( @@ -2655,7 +2655,8 @@ fn specialize_suspended<'a>( }) => { let proc = generate_runtime_error_function(env, name.name(), attempted_layout); - let top_level = ProcLayout::from_raw(env.arena, attempted_layout, &[]); + let top_level = + ProcLayout::from_raw(env.arena, attempted_layout, CapturesNiche::no_niche()); procs .specialized @@ -2779,7 +2780,7 @@ fn specialize_external_help<'a>( match specialization_result { Ok((proc, layout)) => { - let top_level = ProcLayout::from_raw(env.arena, layout, proc.name.captures_niche); + let top_level = ProcLayout::from_raw(env.arena, layout, proc.name.captures_niche()); if procs.is_module_thunk(name.name()) { debug_assert!(top_level.arguments.is_empty()); @@ -2793,7 +2794,7 @@ fn specialize_external_help<'a>( let proc = generate_runtime_error_function(env, name.name(), attempted_layout); let top_level = - ProcLayout::from_raw(env.arena, attempted_layout, proc.name.captures_niche); + ProcLayout::from_raw(env.arena, attempted_layout, proc.name.captures_niche()); procs .specialized @@ -2951,12 +2952,10 @@ fn specialize_external<'a>( host_exposed_layouts: HostExposedLayouts::NotHostExposed, }; - let captures_niche = &[]; - let top_level = ProcLayout::new( env.arena, top_level_arguments.into_bump_slice(), - captures_niche, + CapturesNiche::no_niche(), *return_layout, ); @@ -3502,15 +3501,15 @@ where #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct ProcLayout<'a> { pub arguments: &'a [Layout<'a>], - pub captures_niche: &'a [Layout<'a>], pub result: Layout<'a>, + pub captures_niche: CapturesNiche<'a>, } impl<'a> ProcLayout<'a> { pub fn new( arena: &'a Bump, old_arguments: &'a [Layout<'a>], - old_captures_niche: &'a [Layout<'a>], + old_captures_niche: CapturesNiche<'a>, result: Layout<'a>, ) -> Self { let mut arguments = Vec::with_capacity_in(old_arguments.len(), arena); @@ -3520,19 +3519,12 @@ impl<'a> ProcLayout<'a> { arguments.push(*other); } - let mut captures_niche = Vec::with_capacity_in(old_captures_niche.len(), arena); - - for old in old_captures_niche { - let other = old; - captures_niche.push(*other); - } - let other = result; let new_result = other; ProcLayout { arguments: arguments.into_bump_slice(), - captures_niche: captures_niche.into_bump_slice(), + captures_niche: old_captures_niche, result: new_result, } } @@ -3540,7 +3532,7 @@ impl<'a> ProcLayout<'a> { pub fn from_raw( arena: &'a Bump, raw: RawFunctionLayout<'a>, - captures_niche: &'a [Layout<'a>], + captures_niche: CapturesNiche<'a>, ) -> Self { match raw { RawFunctionLayout::Function(arguments, lambda_set, result) => { @@ -3548,7 +3540,7 @@ impl<'a> ProcLayout<'a> { ProcLayout::new(arena, arguments, captures_niche, *result) } RawFunctionLayout::ZeroArgumentThunk(result) => { - ProcLayout::new(arena, &[], &[], result) + ProcLayout::new(arena, &[], CapturesNiche::no_niche(), result) } } } @@ -5017,8 +5009,8 @@ pub fn with_hole<'a>( layout_cache.raw_from_var(env.arena, closure_data_var, env.subs) ); - // NB: I don't think the top_level here can capture anything?? - let top_level_capture_niche = &[]; + // NB: I don't think the top_level here can have a captures niche? + let top_level_capture_niche = CapturesNiche::no_niche(); let top_level = ProcLayout::from_raw(env.arena, closure_data_layout, top_level_capture_niche); let arena = env.arena; @@ -7246,8 +7238,8 @@ fn specialize_symbol<'a>( }; let raw = RawFunctionLayout::ZeroArgumentThunk(layout); - let captures_niche = &[]; - let top_level = ProcLayout::from_raw(env.arena, raw, captures_niche); + let top_level = + ProcLayout::from_raw(env.arena, raw, CapturesNiche::no_niche()); procs.insert_passed_by_name( env, @@ -7261,8 +7253,8 @@ fn specialize_symbol<'a>( } else { // Imported symbol, so it must have no captures niche (since // top-levels can't capture) - let captures_niche = &[]; - let top_level = ProcLayout::from_raw(env.arena, raw, captures_niche); + let top_level = + ProcLayout::from_raw(env.arena, raw, CapturesNiche::no_niche()); procs.insert_passed_by_name( env, arg_var, @@ -7324,8 +7316,11 @@ fn specialize_symbol<'a>( ); // define the function pointer - let function_ptr_layout = - ProcLayout::from_raw(env.arena, res_layout, lambda_name.captures_niche); + let function_ptr_layout = ProcLayout::from_raw( + env.arena, + res_layout, + lambda_name.captures_niche(), + ); // this is a closure by capture, meaning it itself captures local variables. procs.insert_passed_by_name( @@ -7353,7 +7348,8 @@ fn specialize_symbol<'a>( // let layout = Layout::Closure(argument_layouts, lambda_set, ret_layout); // panic!("suspicious"); let layout = Layout::LambdaSet(lambda_set); - let top_level = ProcLayout::new(env.arena, &[], &[], layout); + let top_level = + ProcLayout::new(env.arena, &[], CapturesNiche::no_niche(), layout); procs.insert_passed_by_name( env, arg_var, @@ -7372,8 +7368,11 @@ fn specialize_symbol<'a>( debug_assert!(lambda_name.no_captures()); // define the function pointer - let function_ptr_layout = - ProcLayout::from_raw(env.arena, res_layout, lambda_name.captures_niche); + let function_ptr_layout = ProcLayout::from_raw( + env.arena, + res_layout, + lambda_name.captures_niche(), + ); procs.insert_passed_by_name( env, @@ -7395,7 +7394,8 @@ fn specialize_symbol<'a>( } RawFunctionLayout::ZeroArgumentThunk(ret_layout) => { // this is a 0-argument thunk - let top_level = ProcLayout::new(env.arena, &[], &[], ret_layout); + let top_level = + ProcLayout::new(env.arena, &[], CapturesNiche::no_niche(), ret_layout); procs.insert_passed_by_name( env, arg_var, @@ -7715,7 +7715,7 @@ fn call_by_name_help<'a>( ProcLayout::new( env.arena, argument_layouts, - proc_name.captures_niche, + proc_name.captures_niche(), *ret_layout, ) }; @@ -7967,7 +7967,7 @@ fn call_by_name_module_thunk<'a>( assigned: Symbol, hole: &'a Stmt<'a>, ) -> Stmt<'a> { - let top_level_layout = ProcLayout::new(env.arena, &[], &[], *ret_layout); + let top_level_layout = ProcLayout::new(env.arena, &[], CapturesNiche::no_niche(), *ret_layout); let inner_layout = *ret_layout; @@ -8095,7 +8095,7 @@ fn call_specialized_proc<'a>( hole: &'a Stmt<'a>, ) -> Stmt<'a> { let proc_name = proc.name; - let function_layout = ProcLayout::from_raw(env.arena, layout, proc_name.captures_niche); + let function_layout = ProcLayout::from_raw(env.arena, layout, proc_name.captures_niche()); procs .specialized diff --git a/crates/compiler/mono/src/layout.rs b/crates/compiler/mono/src/layout.rs index 8996148a85..95feff6d35 100644 --- a/crates/compiler/mono/src/layout.rs +++ b/crates/compiler/mono/src/layout.rs @@ -692,33 +692,61 @@ impl std::fmt::Debug for LambdaSet<'_> { } } +/// Sometimes we can end up with lambdas of the same name and different captures in the same +/// lambda set, like `fun` having lambda set `[[thunk U64, thunk U8]]` due to the following program: +/// +/// ```roc +/// capture : _ -> ({} -> Str) +/// capture = \val -> +/// thunk = \{} -> Num.toStr val +/// thunk +/// +/// fun = \x -> +/// when x is +/// True -> capture 123u64 +/// False -> capture 18u8 +/// ``` +/// +/// By recording the captures layouts this lambda expects in its identifier, we can distinguish +/// between such differences when constructing closure capture data. +/// +/// See also https://github.com/rtfeldman/roc/issues/3336. +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +pub struct CapturesNiche<'a>(&'a [Layout<'a>]); + +impl CapturesNiche<'_> { + pub fn no_niche() -> Self { + Self(&[]) + } +} + #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] pub struct LambdaName<'a> { name: Symbol, - /// Sometimes we can end up with lambdas of the same name and different captures in the same - /// lambda set, like [[Thunk U8, Thunk Str]]. See also https://github.com/rtfeldman/roc/issues/3336. - /// - /// By recording the captures layouts this lambda expects in its identifier, we can distinguish - /// between such differences. - pub captures_niche: &'a [Layout<'a>], + captures_niche: CapturesNiche<'a>, } -impl LambdaName<'_> { +impl<'a> LambdaName<'a> { #[inline(always)] pub fn name(&self) -> Symbol { self.name } + #[inline(always)] + pub fn captures_niche(&self) -> CapturesNiche<'a> { + self.captures_niche + } + #[inline(always)] pub fn no_captures(&self) -> bool { - self.captures_niche.is_empty() + self.captures_niche.0.is_empty() } #[inline(always)] pub fn no_niche(name: Symbol) -> Self { Self { name, - captures_niche: &[], + captures_niche: CapturesNiche::no_niche(), } } @@ -782,7 +810,7 @@ impl<'a> LambdaSet<'a> { pub fn iter_set(&self) -> impl ExactSizeIterator> { self.set.iter().map(|(name, captures_layouts)| LambdaName { name: *name, - captures_niche: captures_layouts, + captures_niche: CapturesNiche(captures_layouts), }) } @@ -794,7 +822,9 @@ impl<'a> LambdaSet<'a> { let comparator = |other_name: Symbol, other_captures_layouts: &[Layout]| { other_name == lambda_name.name - && other_captures_layouts.iter().eq(lambda_name.captures_niche) + && other_captures_layouts + .iter() + .eq(lambda_name.captures_niche.0) }; self.layout_for_member(comparator) @@ -820,7 +850,7 @@ impl<'a> LambdaSet<'a> { LambdaName { name: *name, - captures_niche: layouts, + captures_niche: CapturesNiche(layouts), } } diff --git a/crates/compiler/test_gen/src/wasm_linking.rs b/crates/compiler/test_gen/src/wasm_linking.rs index 0aa1b05504..a3e1c7eab2 100644 --- a/crates/compiler/test_gen/src/wasm_linking.rs +++ b/crates/compiler/test_gen/src/wasm_linking.rs @@ -18,7 +18,7 @@ use roc_mono::ir::{ Call, CallType, Expr, HostExposedLayouts, Literal, Proc, ProcLayout, SelfRecursive, Stmt, UpdateModeId, }; -use roc_mono::layout::{Builtin, LambdaName, Layout}; +use roc_mono::layout::{Builtin, CapturesNiche, LambdaName, Layout}; const LINKING_TEST_HOST_WASM: &str = "build/wasm_linking_test_host.wasm"; const LINKING_TEST_HOST_NATIVE: &str = "build/wasm_linking_test_host"; @@ -123,7 +123,7 @@ fn build_app_mono<'a>( let proc_layout = ProcLayout { arguments: &[], result: int_layout, - captures_niche: &[], + captures_niche: CapturesNiche::no_niche(), }; let mut app = MutMap::default(); From 74a55664dd23a65c99a64d8b1893e55beff4135c Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Fri, 1 Jul 2022 16:29:17 -0400 Subject: [PATCH 31/66] Remove some unneeded changes --- crates/bindgen/src/types.rs | 2 +- crates/compiler/build/src/program.rs | 1 - crates/compiler/derive_key/Cargo.toml | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/bindgen/src/types.rs b/crates/bindgen/src/types.rs index 9d171980c6..6886612794 100644 --- a/crates/bindgen/src/types.rs +++ b/crates/bindgen/src/types.rs @@ -410,7 +410,7 @@ pub struct Env<'a> { arena: &'a Bump, subs: &'a Subs, layout_cache: LayoutCache<'a>, - interns: &'a mut Interns, + interns: &'a Interns, struct_names: Structs, enum_names: Enums, pending_recursive_types: VecMap>, diff --git a/crates/compiler/build/src/program.rs b/crates/compiler/build/src/program.rs index cbcdcd6339..8f5a704f4f 100644 --- a/crates/compiler/build/src/program.rs +++ b/crates/compiler/build/src/program.rs @@ -261,7 +261,6 @@ pub fn gen_from_mono_module_llvm( exposed_to_host: loaded.exposed_to_host.values.keys().copied().collect(), }; - dbg!(21); roc_gen_llvm::llvm::build::build_procedures( &env, opt_level, diff --git a/crates/compiler/derive_key/Cargo.toml b/crates/compiler/derive_key/Cargo.toml index 36f154b059..53551245e0 100644 --- a/crates/compiler/derive_key/Cargo.toml +++ b/crates/compiler/derive_key/Cargo.toml @@ -14,4 +14,4 @@ roc_types = { path = "../types" } [features] default = [] -debug-derived-symbols = ["roc_module/debug-symbols"] +debug-derived-symbols = [] From cb2f85ed9538bdc501afc01fa3c3d9ef2ef1128a Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Fri, 1 Jul 2022 16:55:07 -0400 Subject: [PATCH 32/66] Fix some typos --- crates/compiler/mono/src/ir.rs | 10 ++++++---- crates/compiler/mono/src/layout.rs | 3 --- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index 27be46a12a..563f7fc7ad 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -458,7 +458,7 @@ impl<'a> Proc<'a> { pub struct HostSpecializations<'a> { /// Not a bumpalo vec because bumpalo is not thread safe /// Separate array so we can search for membership quickly - /// If it's a value and not a lambda, the value is recorded as LambdaName::thunk. + /// If it's a value and not a lambda, the value is recorded as LambdaName::no_niche. symbol_or_lambdas: std::vec::Vec>, storage_subs: StorageSubs, /// For each symbol, what types to specialize it for, points into the storage_subs @@ -542,7 +542,7 @@ impl<'a> HostSpecializations<'a> { pub struct ExternalSpecializations<'a> { /// Not a bumpalo vec because bumpalo is not thread safe /// Separate array so we can search for membership quickly - /// If it's a value and not a lambda, the value is recorded as LambdaName::thunk. + /// If it's a value and not a lambda, the value is recorded as LambdaName::no_niche. pub symbol_or_lambda: std::vec::Vec>, storage_subs: StorageSubs, /// For each symbol, what types to specialize it for, points into the storage_subs @@ -606,7 +606,7 @@ impl<'a> ExternalSpecializations<'a> { #[derive(Clone, Debug)] pub struct Suspended<'a> { pub store: StorageSubs, - /// LambdaName::thunk if it's a value + /// LambdaName::no_niche if it's a value pub symbol_or_lambdas: Vec<'a, LambdaName<'a>>, pub layouts: Vec<'a, ProcLayout<'a>>, pub variables: Vec<'a, Variable>, @@ -3008,6 +3008,8 @@ fn specialize_external<'a>( }; // I'm not sure how to handle the closure case, does it ever occur? + debug_assert!(matches!(captured_symbols, CapturedSymbols::None)); + let proc = Proc { name: lambda_name, args: &[], @@ -5655,7 +5657,7 @@ fn tag_union_to_function<'a>( ext_var, }); - // Lambda does not capture anything, can't be multimorphic + // Lambda does not capture anything, can't have a captures niche let lambda_name = LambdaName::no_niche(proc_symbol); let inserted = procs.insert_anonymous( diff --git a/crates/compiler/mono/src/layout.rs b/crates/compiler/mono/src/layout.rs index 95feff6d35..fdc6e5ec73 100644 --- a/crates/compiler/mono/src/layout.rs +++ b/crates/compiler/mono/src/layout.rs @@ -3074,9 +3074,6 @@ fn dict_layout_from_key_value<'a>( ))) } -pub trait FreshMultimorphicSymbol: FnMut() -> Symbol {} -impl FreshMultimorphicSymbol for T where T: FnMut() -> Symbol {} - pub fn list_layout_from_elem<'a>( env: &mut Env<'a, '_>, element_var: Variable, From a3ffc12569984b2d4b7237cce1037c356a420808 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Fri, 1 Jul 2022 16:56:43 -0400 Subject: [PATCH 33/66] Revert unneeded changes --- Cargo.lock | 1 - crates/repl_cli/src/lib.rs | 8 +++--- crates/repl_eval/src/eval.rs | 52 ++++++++++++++++++------------------ crates/repl_eval/src/lib.rs | 4 +-- crates/repl_wasm/Cargo.toml | 1 - crates/repl_wasm/src/repl.rs | 8 +++--- 6 files changed, 36 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d5e0279fa4..eb989d06e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3963,7 +3963,6 @@ dependencies = [ "roc_collections", "roc_gen_wasm", "roc_load", - "roc_mono", "roc_parse", "roc_repl_eval", "roc_reporting", diff --git a/crates/repl_cli/src/lib.rs b/crates/repl_cli/src/lib.rs index dace5fb484..cadc017eab 100644 --- a/crates/repl_cli/src/lib.rs +++ b/crates/repl_cli/src/lib.rs @@ -130,9 +130,9 @@ impl<'a> ReplApp<'a> for CliApp { /// Run user code that returns a type with a `Builtin` layout /// Size of the return value is statically determined from its Rust type - fn call_function(&self, main_fn_name: &str, mut transform: F) -> Expr<'a> + fn call_function(&self, main_fn_name: &str, transform: F) -> Expr<'a> where - F: FnMut(&'a Self::Memory, Return) -> Expr<'a>, + F: Fn(&'a Self::Memory, Return) -> Expr<'a>, Self::Memory: 'a, { run_jit_function!(self.lib, main_fn_name, Return, |v| transform(&CliMemory, v)) @@ -143,10 +143,10 @@ impl<'a> ReplApp<'a> for CliApp { &self, main_fn_name: &str, ret_bytes: usize, - mut transform: F, + transform: F, ) -> T where - F: FnMut(&'a Self::Memory, usize) -> T, + F: Fn(&'a Self::Memory, usize) -> T, Self::Memory: 'a, { run_jit_function_dynamic_type!(self.lib, main_fn_name, ret_bytes, |v| transform( diff --git a/crates/repl_eval/src/eval.rs b/crates/repl_eval/src/eval.rs index 65c1d92136..df2ca2562a 100644 --- a/crates/repl_eval/src/eval.rs +++ b/crates/repl_eval/src/eval.rs @@ -19,9 +19,9 @@ use roc_types::subs::{Content, FlatType, GetSubsSlice, RecordFields, Subs, Union use crate::{ReplApp, ReplAppMemory}; -struct Env<'a> { +struct Env<'a, 'env> { arena: &'a Bump, - subs: &'a Subs, + subs: &'env Subs, target_info: TargetInfo, } @@ -47,7 +47,7 @@ pub fn jit_to_ast<'a, A: ReplApp<'a>>( subs: &'a Subs, target_info: TargetInfo, ) -> Result, ToAstProblem> { - let mut env = Env { + let env = Env { arena, subs, target_info, @@ -57,10 +57,10 @@ pub fn jit_to_ast<'a, A: ReplApp<'a>>( ProcLayout { arguments: [], result, - .. + captures_niche: _, } => { // this is a thunk - jit_to_ast_help(&mut env, app, main_fn_name, &result, content) + jit_to_ast_help(&env, app, main_fn_name, &result, content) } _ => Err(ToAstProblem::FunctionLayout), } @@ -87,7 +87,7 @@ enum NewtypeKind<'a> { /// /// Returns (new type containers, optional alias content, real content). fn unroll_newtypes_and_aliases<'a>( - env: &Env<'a>, + env: &Env<'a, 'a>, mut content: &'a Content, ) -> (Vec<'a, NewtypeKind<'a>>, Option<&'a Content>, &'a Content) { let mut newtype_containers = Vec::with_capacity_in(1, env.arena); @@ -135,7 +135,7 @@ fn unroll_newtypes_and_aliases<'a>( } fn apply_newtypes<'a>( - env: &Env<'a>, + env: &Env<'a, '_>, newtype_containers: Vec<'a, NewtypeKind<'a>>, mut expr: Expr<'a>, ) -> Expr<'a> { @@ -162,7 +162,7 @@ fn apply_newtypes<'a>( expr } -fn unroll_recursion_var<'a>(env: &Env<'a>, mut content: &'a Content) -> &'a Content { +fn unroll_recursion_var<'a>(env: &Env<'a, 'a>, mut content: &'a Content) -> &'a Content { while let Content::RecursionVar { structure, .. } = content { content = env.subs.get_content_without_compacting(*structure); } @@ -170,7 +170,7 @@ fn unroll_recursion_var<'a>(env: &Env<'a>, mut content: &'a Content) -> &'a Cont } fn get_tags_vars_and_variant<'a>( - env: &mut Env<'a>, + env: &Env<'a, '_>, tags: &UnionTags, opt_rec_var: Option, ) -> (MutMap>, UnionVariant<'a>) { @@ -188,7 +188,7 @@ fn get_tags_vars_and_variant<'a>( } fn expr_of_tag<'a, M: ReplAppMemory>( - env: &mut Env<'a>, + env: &Env<'a, 'a>, mem: &'a M, data_addr: usize, tag_name: &TagName, @@ -212,7 +212,7 @@ fn expr_of_tag<'a, M: ReplAppMemory>( /// Gets the tag ID of a union variant, assuming that the tag ID is stored alongside (after) the /// tag data. The caller is expected to check that the tag ID is indeed stored this way. fn tag_id_from_data<'a, M: ReplAppMemory>( - env: &Env<'a>, + env: &Env<'a, 'a>, mem: &M, union_layout: UnionLayout, data_addr: usize, @@ -240,7 +240,7 @@ fn tag_id_from_data<'a, M: ReplAppMemory>( /// - the tag ID /// - the address of the data of the union variant, unmasked if the pointer held the tag ID fn tag_id_from_recursive_ptr<'a, M: ReplAppMemory>( - env: &Env<'a>, + env: &Env<'a, 'a>, mem: &M, union_layout: UnionLayout, rec_addr: usize, @@ -265,7 +265,7 @@ const OPAQUE_FUNCTION: Expr = Expr::Var { }; fn jit_to_ast_help<'a, A: ReplApp<'a>>( - env: &mut Env<'a>, + env: &Env<'a, 'a>, app: &'a A, main_fn_name: &str, layout: &Layout<'a>, @@ -345,11 +345,6 @@ fn jit_to_ast_help<'a, A: ReplApp<'a>>( todo!("add support for rendering builtin {:?} to the REPL", other) } Layout::Struct { field_layouts, .. } => { - let fields = [Layout::u64(), *layout]; - let layout = Layout::struct_no_name_order(&fields); - - let result_stack_size = layout.stack_size(env.target_info); - let struct_addr_to_ast = |mem: &'a A::Memory, addr: usize| match raw_content { Content::Structure(FlatType::Record(fields, _)) => { Ok(struct_to_ast(env, mem, addr, *fields)) @@ -395,6 +390,11 @@ fn jit_to_ast_help<'a, A: ReplApp<'a>>( } }; + let fields = [Layout::u64(), *layout]; + let layout = Layout::struct_no_name_order(&fields); + + let result_stack_size = layout.stack_size(env.target_info); + app.call_function_dynamic_size( main_fn_name, result_stack_size as usize, @@ -463,7 +463,7 @@ fn jit_to_ast_help<'a, A: ReplApp<'a>>( result.map(|e| apply_newtypes(env, newtype_containers, e)) } -fn tag_name_to_expr<'a>(env: &Env<'a>, tag_name: &TagName) -> Expr<'a> { +fn tag_name_to_expr<'a>(env: &Env<'a, '_>, tag_name: &TagName) -> Expr<'a> { Expr::Tag(env.arena.alloc_str(&tag_name.as_ident_str())) } @@ -476,7 +476,7 @@ enum WhenRecursive<'a> { } fn addr_to_ast<'a, M: ReplAppMemory>( - env: &mut Env<'a>, + env: &Env<'a, 'a>, mem: &'a M, addr: usize, layout: &Layout<'a>, @@ -771,7 +771,7 @@ fn addr_to_ast<'a, M: ReplAppMemory>( } fn list_to_ast<'a, M: ReplAppMemory>( - env: &mut Env<'a>, + env: &Env<'a, 'a>, mem: &'a M, addr: usize, len: usize, @@ -823,7 +823,7 @@ fn list_to_ast<'a, M: ReplAppMemory>( } fn single_tag_union_to_ast<'a, M: ReplAppMemory>( - env: &mut Env<'a>, + env: &Env<'a, 'a>, mem: &'a M, addr: usize, field_layouts: &'a [Layout<'a>], @@ -850,7 +850,7 @@ fn single_tag_union_to_ast<'a, M: ReplAppMemory>( } fn sequence_of_expr<'a, I, M: ReplAppMemory>( - env: &mut Env<'a>, + env: &Env<'a, 'a>, mem: &'a M, addr: usize, sequence: I, @@ -882,7 +882,7 @@ where } fn struct_to_ast<'a, M: ReplAppMemory>( - env: &mut Env<'a>, + env: &Env<'a, 'a>, mem: &'a M, addr: usize, record_fields: RecordFields, @@ -1007,7 +1007,7 @@ fn unpack_two_element_tag_union( } fn bool_to_ast<'a, M: ReplAppMemory>( - env: &Env<'a>, + env: &Env<'a, '_>, mem: &M, value: bool, content: &Content, @@ -1082,7 +1082,7 @@ fn bool_to_ast<'a, M: ReplAppMemory>( } fn byte_to_ast<'a, M: ReplAppMemory>( - env: &mut Env<'a>, + env: &Env<'a, '_>, mem: &M, value: u8, content: &Content, diff --git a/crates/repl_eval/src/lib.rs b/crates/repl_eval/src/lib.rs index ac84dcac3c..898777d4e4 100644 --- a/crates/repl_eval/src/lib.rs +++ b/crates/repl_eval/src/lib.rs @@ -12,7 +12,7 @@ pub trait ReplApp<'a> { /// The `transform` callback takes the app's memory and the returned value fn call_function(&self, main_fn_name: &str, transform: F) -> Expr<'a> where - F: FnMut(&'a Self::Memory, Return) -> Expr<'a>, + F: Fn(&'a Self::Memory, Return) -> Expr<'a>, Self::Memory: 'a; /// Run user code that returns a struct or union, whose size is provided as an argument @@ -24,7 +24,7 @@ pub trait ReplApp<'a> { transform: F, ) -> T where - F: FnMut(&'a Self::Memory, usize) -> T, + F: Fn(&'a Self::Memory, usize) -> T, Self::Memory: 'a; } diff --git a/crates/repl_wasm/Cargo.toml b/crates/repl_wasm/Cargo.toml index dbea9fb8f7..4a859465e9 100644 --- a/crates/repl_wasm/Cargo.toml +++ b/crates/repl_wasm/Cargo.toml @@ -26,7 +26,6 @@ roc_repl_eval = {path = "../repl_eval"} roc_reporting = {path = "../reporting"} roc_target = {path = "../compiler/roc_target"} roc_types = {path = "../compiler/types"} -roc_mono = {path = "../compiler/mono"} [features] wasmer = ["futures"] diff --git a/crates/repl_wasm/src/repl.rs b/crates/repl_wasm/src/repl.rs index d57782982b..52164d496b 100644 --- a/crates/repl_wasm/src/repl.rs +++ b/crates/repl_wasm/src/repl.rs @@ -108,9 +108,9 @@ impl<'a> ReplApp<'a> for WasmReplApp<'a> { /// Size of the return value is statically determined from its Rust type /// The `transform` callback takes the app's memory and the returned value /// _main_fn_name is always the same and we don't use it here - fn call_function(&self, _main_fn_name: &str, mut transform: F) -> Expr<'a> + fn call_function(&self, _main_fn_name: &str, transform: F) -> Expr<'a> where - F: FnMut(&'a Self::Memory, Return) -> Expr<'a>, + F: Fn(&'a Self::Memory, Return) -> Expr<'a>, Self::Memory: 'a, { let app_final_memory_size: usize = js_run_app(); @@ -138,10 +138,10 @@ impl<'a> ReplApp<'a> for WasmReplApp<'a> { &self, _main_fn_name: &str, _ret_bytes: usize, - mut transform: F, + transform: F, ) -> T where - F: FnMut(&'a Self::Memory, usize) -> T, + F: Fn(&'a Self::Memory, usize) -> T, Self::Memory: 'a, { let app_final_memory_size: usize = js_run_app(); From 6c9b10af4e629697faf2115983c8b4e9eb1e05c4 Mon Sep 17 00:00:00 2001 From: Ayaz <20735482+ayazhafiz@users.noreply.github.com> Date: Fri, 1 Jul 2022 18:20:53 -0400 Subject: [PATCH 34/66] Update wasm layout sizes --- crates/compiler/mono/src/ir.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index 563f7fc7ad..0b784c2c7e 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -55,9 +55,9 @@ pub fn pretty_print_ir_symbols() -> bool { roc_error_macros::assert_sizeof_wasm!(Literal, 24); roc_error_macros::assert_sizeof_wasm!(Expr, 48); roc_error_macros::assert_sizeof_wasm!(Stmt, 120); -roc_error_macros::assert_sizeof_wasm!(ProcLayout, 32); -roc_error_macros::assert_sizeof_wasm!(Call, 36); -roc_error_macros::assert_sizeof_wasm!(CallType, 28); +roc_error_macros::assert_sizeof_wasm!(ProcLayout, 40); +roc_error_macros::assert_sizeof_wasm!(Call, 44); +roc_error_macros::assert_sizeof_wasm!(CallType, 36); roc_error_macros::assert_sizeof_non_wasm!(Literal, 3 * 8); roc_error_macros::assert_sizeof_non_wasm!(Expr, 10 * 8); From ae410fbf5d3bd693e7f6665105a33def8944b2b6 Mon Sep 17 00:00:00 2001 From: ayazhafiz Date: Sat, 2 Jul 2022 18:20:29 -0400 Subject: [PATCH 35/66] Fix mono tests --- .../test_mono/generated/closure_in_list.txt | 5 ++ .../generated/empty_list_of_function_type.txt | 22 +++++++ .../compiler/test_mono/generated/encode.txt | 5 ++ .../test_mono/generated/ir_int_add.txt | 5 ++ ...cialize_errors_behind_unified_branches.txt | 61 +++++++++++++++---- .../test_mono/generated/list_append.txt | 5 ++ .../generated/list_append_closure.txt | 5 ++ .../generated/list_cannot_update_inplace.txt | 29 +++++++-- .../compiler/test_mono/generated/list_get.txt | 26 +++++++- .../compiler/test_mono/generated/list_len.txt | 9 +++ .../generated/list_map_closure_borrows.txt | 45 +++++++++----- .../generated/list_map_closure_owns.txt | 37 ++++++++--- .../generated/list_pass_to_function.txt | 31 +++++++++- .../test_mono/generated/list_sort_asc.txt | 15 ++++- .../test_mono/generated/quicksort_swap.txt | 52 ++++++++++++---- .../compiler/test_mono/generated/rigids.txt | 52 ++++++++++++---- 16 files changed, 335 insertions(+), 69 deletions(-) diff --git a/crates/compiler/test_mono/generated/closure_in_list.txt b/crates/compiler/test_mono/generated/closure_in_list.txt index 643a75fdad..115677d475 100644 --- a/crates/compiler/test_mono/generated/closure_in_list.txt +++ b/crates/compiler/test_mono/generated/closure_in_list.txt @@ -1,4 +1,5 @@ procedure List.6 (#Attr.2): +<<<<<<< HEAD <<<<<<< HEAD let List.284 : U64 = lowlevel ListLen #Attr.2; ret List.284; @@ -11,6 +12,10 @@ procedure List.6 (#Attr.2): ret List.213; >>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/closure_in_list.txt >>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) +======= + let List.238 : U64 = lowlevel ListLen #Attr.2; + ret List.238; +>>>>>>> 61fcac491 (Fix mono tests) procedure Test.1 (Test.5): let Test.2 : I64 = 41i64; diff --git a/crates/compiler/test_mono/generated/empty_list_of_function_type.txt b/crates/compiler/test_mono/generated/empty_list_of_function_type.txt index d1ffaafe5b..b023cf0456 100644 --- a/crates/compiler/test_mono/generated/empty_list_of_function_type.txt +++ b/crates/compiler/test_mono/generated/empty_list_of_function_type.txt @@ -1,4 +1,5 @@ <<<<<<< HEAD +<<<<<<< HEAD procedure List.2 (List.75, List.76): let List.290 : U64 = CallByName List.6 List.75; let List.286 : Int1 = CallByName Num.22 List.76 List.290; @@ -54,6 +55,27 @@ procedure List.60 (#Attr.2, #Attr.3): ret List.221; >>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/empty_list_of_function_type.txt >>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) +======= +procedure List.2 (List.72, List.73): + let List.244 : U64 = CallByName List.6 List.72; + let List.240 : Int1 = CallByName Num.22 List.73 List.244; + if List.240 then + let List.242 : {} = CallByName List.60 List.72 List.73; + let List.241 : [C {}, C {}] = Ok List.242; + ret List.241; + else + let List.239 : {} = Struct {}; + let List.238 : [C {}, C {}] = Err List.239; + ret List.238; + +procedure List.6 (#Attr.2): + let List.247 : U64 = lowlevel ListLen #Attr.2; + ret List.247; + +procedure List.60 (#Attr.2, #Attr.3): + let List.246 : {} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.246; +>>>>>>> 61fcac491 (Fix mono tests) procedure Num.22 (#Attr.2, #Attr.3): let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/encode.txt b/crates/compiler/test_mono/generated/encode.txt index 7115be0912..037f3703d0 100644 --- a/crates/compiler/test_mono/generated/encode.txt +++ b/crates/compiler/test_mono/generated/encode.txt @@ -1,4 +1,5 @@ procedure List.4 (#Attr.2, #Attr.3): +<<<<<<< HEAD <<<<<<< HEAD let List.284 : List U8 = lowlevel ListAppend #Attr.2 #Attr.3; ret List.284; @@ -11,6 +12,10 @@ procedure List.4 (#Attr.2, #Attr.3): ret List.213; >>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/encode.txt >>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) +======= + let List.238 : List U8 = lowlevel ListAppend #Attr.2 #Attr.3; + ret List.238; +>>>>>>> 61fcac491 (Fix mono tests) procedure Test.20 (Test.22): let Test.34 : {U8} = Struct {Test.22}; diff --git a/crates/compiler/test_mono/generated/ir_int_add.txt b/crates/compiler/test_mono/generated/ir_int_add.txt index 11f2565ef7..1db9bb3158 100644 --- a/crates/compiler/test_mono/generated/ir_int_add.txt +++ b/crates/compiler/test_mono/generated/ir_int_add.txt @@ -1,4 +1,5 @@ procedure List.6 (#Attr.2): +<<<<<<< HEAD <<<<<<< HEAD let List.284 : U64 = lowlevel ListLen #Attr.2; ret List.284; @@ -11,6 +12,10 @@ procedure List.6 (#Attr.2): ret List.213; >>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/ir_int_add.txt >>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) +======= + let List.238 : U64 = lowlevel ListLen #Attr.2; + ret List.238; +>>>>>>> 61fcac491 (Fix mono tests) procedure Num.19 (#Attr.2, #Attr.3): let Num.190 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt b/crates/compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt index 1b417f48fe..e7a1852e6f 100644 --- a/crates/compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt +++ b/crates/compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt @@ -1,3 +1,4 @@ +<<<<<<< HEAD procedure List.2 (List.75, List.76): let List.299 : U64 = CallByName List.6 List.75; let List.295 : Int1 = CallByName Num.22 List.76 List.299; @@ -32,24 +33,60 @@ procedure List.9 (List.202): let List.287 : Int1 = true; let List.286 : [C Int1, C I64] = Err List.287; ret List.286; +======= +procedure List.2 (List.72, List.73): + let List.253 : U64 = CallByName List.6 List.72; + let List.249 : Int1 = CallByName Num.22 List.73 List.253; + if List.249 then + let List.251 : I64 = CallByName List.60 List.72 List.73; + let List.250 : [C {}, C I64] = Ok List.251; + ret List.250; + else + let List.248 : {} = Struct {}; + let List.247 : [C {}, C I64] = Err List.248; + ret List.247; + +procedure List.6 (#Attr.2): + let List.254 : U64 = lowlevel ListLen #Attr.2; + ret List.254; + +procedure List.60 (#Attr.2, #Attr.3): + let List.252 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.252; + +procedure List.9 (List.168): + let List.245 : U64 = 0i64; + let List.238 : [C {}, C I64] = CallByName List.2 List.168 List.245; + let List.242 : U8 = 1i64; + let List.243 : U8 = GetTagId List.238; + let List.244 : Int1 = lowlevel Eq List.242 List.243; + if List.244 then + let List.169 : I64 = UnionAtIndex (Id 1) (Index 0) List.238; + let List.239 : [C Int1, C I64] = Ok List.169; + ret List.239; + else + let List.241 : Int1 = true; + let List.240 : [C Int1, C I64] = Err List.241; + ret List.240; +>>>>>>> 61fcac491 (Fix mono tests) procedure Num.22 (#Attr.2, #Attr.3): - let Num.273 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.273; + let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.188; procedure Str.27 (#Attr.2): let #Attr.3 : {I64, U8} = lowlevel StrToNum #Attr.2; - let Str.72 : U8 = StructAtIndex 1 #Attr.3; - let Str.73 : U8 = 0i64; - let Str.69 : Int1 = lowlevel NumGt Str.72 Str.73; - if Str.69 then - let Str.71 : Int1 = false; - let Str.70 : [C Int1, C I64] = Err Str.71; - ret Str.70; + let Str.42 : U8 = StructAtIndex 1 #Attr.3; + let Str.43 : U8 = 0i64; + let Str.39 : Int1 = lowlevel NumGt Str.42 Str.43; + if Str.39 then + let Str.41 : Int1 = false; + let Str.40 : [C Int1, C I64] = Err Str.41; + ret Str.40; else - let Str.68 : I64 = StructAtIndex 0 #Attr.3; - let Str.67 : [C Int1, C I64] = Ok Str.68; - ret Str.67; + let Str.38 : I64 = StructAtIndex 0 #Attr.3; + let Str.37 : [C Int1, C I64] = Ok Str.38; + ret Str.37; procedure Test.0 (): let Test.4 : Int1 = true; diff --git a/crates/compiler/test_mono/generated/list_append.txt b/crates/compiler/test_mono/generated/list_append.txt index 9d9624a7a0..a89e4d6eab 100644 --- a/crates/compiler/test_mono/generated/list_append.txt +++ b/crates/compiler/test_mono/generated/list_append.txt @@ -1,4 +1,5 @@ procedure List.4 (#Attr.2, #Attr.3): +<<<<<<< HEAD <<<<<<< HEAD let List.284 : List I64 = lowlevel ListAppend #Attr.2 #Attr.3; ret List.284; @@ -11,6 +12,10 @@ procedure List.4 (#Attr.2, #Attr.3): ret List.213; >>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/list_append.txt >>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) +======= + let List.238 : List I64 = lowlevel ListAppend #Attr.2 #Attr.3; + ret List.238; +>>>>>>> 61fcac491 (Fix mono tests) procedure Test.0 (): let Test.2 : List I64 = Array [1i64]; diff --git a/crates/compiler/test_mono/generated/list_append_closure.txt b/crates/compiler/test_mono/generated/list_append_closure.txt index 9566c7d113..bc217613db 100644 --- a/crates/compiler/test_mono/generated/list_append_closure.txt +++ b/crates/compiler/test_mono/generated/list_append_closure.txt @@ -1,4 +1,5 @@ procedure List.4 (#Attr.2, #Attr.3): +<<<<<<< HEAD <<<<<<< HEAD let List.284 : List I64 = lowlevel ListAppend #Attr.2 #Attr.3; ret List.284; @@ -11,6 +12,10 @@ procedure List.4 (#Attr.2, #Attr.3): ret List.213; >>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/list_append_closure.txt >>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) +======= + let List.238 : List I64 = lowlevel ListAppend #Attr.2 #Attr.3; + ret List.238; +>>>>>>> 61fcac491 (Fix mono tests) procedure Test.1 (Test.2): let Test.6 : I64 = 42i64; diff --git a/crates/compiler/test_mono/generated/list_cannot_update_inplace.txt b/crates/compiler/test_mono/generated/list_cannot_update_inplace.txt index 8d0977f122..18465beb56 100644 --- a/crates/compiler/test_mono/generated/list_cannot_update_inplace.txt +++ b/crates/compiler/test_mono/generated/list_cannot_update_inplace.txt @@ -1,4 +1,5 @@ <<<<<<< HEAD +<<<<<<< HEAD procedure List.3 (List.84, List.85, List.86): let List.287 : {List I64, I64} = CallByName List.57 List.84 List.85 List.86; let List.286 : List I64 = StructAtIndex 0 List.287; @@ -57,19 +58,39 @@ procedure List.57 (List.76, List.77, List.78): if List.219 then let List.220 : {List I64, I64} = CallByName List.61 List.76 List.77 List.78; ret List.220; +======= +procedure List.3 (List.80, List.81, List.82): + let List.241 : {List I64, I64} = CallByName List.57 List.80 List.81 List.82; + let List.240 : List I64 = StructAtIndex 0 List.241; + inc List.240; + dec List.241; + ret List.240; + +procedure List.57 (List.77, List.78, List.79): + let List.247 : U64 = CallByName List.6 List.77; + let List.244 : Int1 = CallByName Num.22 List.78 List.247; + if List.244 then + let List.245 : {List I64, I64} = CallByName List.61 List.77 List.78 List.79; + ret List.245; +>>>>>>> 61fcac491 (Fix mono tests) else - let List.218 : {List I64, I64} = Struct {List.76, List.78}; - ret List.218; + let List.243 : {List I64, I64} = Struct {List.77, List.79}; + ret List.243; procedure List.6 (#Attr.2): - let List.214 : U64 = lowlevel ListLen #Attr.2; - ret List.214; + let List.239 : U64 = lowlevel ListLen #Attr.2; + ret List.239; procedure List.61 (#Attr.2, #Attr.3, #Attr.4): +<<<<<<< HEAD let List.221 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; ret List.221; >>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/list_cannot_update_inplace.txt >>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) +======= + let List.246 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; + ret List.246; +>>>>>>> 61fcac491 (Fix mono tests) procedure Num.19 (#Attr.2, #Attr.3): let Num.188 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/list_get.txt b/crates/compiler/test_mono/generated/list_get.txt index 17c1f4fc6d..16382b100a 100644 --- a/crates/compiler/test_mono/generated/list_get.txt +++ b/crates/compiler/test_mono/generated/list_get.txt @@ -1,3 +1,4 @@ +<<<<<<< HEAD procedure List.2 (List.75, List.76): let List.290 : U64 = CallByName List.6 List.75; let List.286 : Int1 = CallByName Num.22 List.76 List.290; @@ -17,10 +18,31 @@ procedure List.6 (#Attr.2): procedure List.60 (#Attr.2, #Attr.3): let List.292 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; ret List.292; +======= +procedure List.2 (List.72, List.73): + let List.244 : U64 = CallByName List.6 List.72; + let List.240 : Int1 = CallByName Num.22 List.73 List.244; + if List.240 then + let List.242 : I64 = CallByName List.60 List.72 List.73; + let List.241 : [C {}, C I64] = Ok List.242; + ret List.241; + else + let List.239 : {} = Struct {}; + let List.238 : [C {}, C I64] = Err List.239; + ret List.238; + +procedure List.6 (#Attr.2): + let List.247 : U64 = lowlevel ListLen #Attr.2; + ret List.247; + +procedure List.60 (#Attr.2, #Attr.3): + let List.246 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.246; +>>>>>>> 61fcac491 (Fix mono tests) procedure Num.22 (#Attr.2, #Attr.3): - let Num.273 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.273; + let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.188; procedure Test.1 (Test.2): let Test.6 : List I64 = Array [1i64, 2i64, 3i64]; diff --git a/crates/compiler/test_mono/generated/list_len.txt b/crates/compiler/test_mono/generated/list_len.txt index 01f1647f98..08700aee17 100644 --- a/crates/compiler/test_mono/generated/list_len.txt +++ b/crates/compiler/test_mono/generated/list_len.txt @@ -1,4 +1,5 @@ procedure List.6 (#Attr.2): +<<<<<<< HEAD <<<<<<< HEAD let List.284 : U64 = lowlevel ListLen #Attr.2; ret List.284; @@ -23,6 +24,14 @@ procedure List.6 (#Attr.2): ret List.214; >>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/list_len.txt >>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) +======= + let List.238 : U64 = lowlevel ListLen #Attr.2; + ret List.238; + +procedure List.6 (#Attr.2): + let List.239 : U64 = lowlevel ListLen #Attr.2; + ret List.239; +>>>>>>> 61fcac491 (Fix mono tests) procedure Num.19 (#Attr.2, #Attr.3): let Num.188 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/list_map_closure_borrows.txt b/crates/compiler/test_mono/generated/list_map_closure_borrows.txt index 3e4c8afc1a..1e30f9109e 100644 --- a/crates/compiler/test_mono/generated/list_map_closure_borrows.txt +++ b/crates/compiler/test_mono/generated/list_map_closure_borrows.txt @@ -1,4 +1,5 @@ <<<<<<< HEAD +<<<<<<< HEAD procedure List.2 (List.75, List.76): let List.290 : U64 = CallByName List.6 List.75; let List.286 : Int1 = CallByName Num.22 List.76 List.290; @@ -62,27 +63,43 @@ procedure List.60 (#Attr.2, #Attr.3): ret List.222; >>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/list_map_closure_borrows.txt >>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) +======= +procedure List.2 (List.72, List.73): + let List.244 : U64 = CallByName List.6 List.72; + let List.240 : Int1 = CallByName Num.22 List.73 List.244; + if List.240 then + let List.242 : Str = CallByName List.60 List.72 List.73; + let List.241 : [C {}, C Str] = Ok List.242; + ret List.241; + else + let List.239 : {} = Struct {}; + let List.238 : [C {}, C Str] = Err List.239; + ret List.238; + +procedure List.5 (#Attr.2, #Attr.3): + let List.246 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3; + ret List.246; + +procedure List.6 (#Attr.2): + let List.248 : U64 = lowlevel ListLen #Attr.2; + ret List.248; + +procedure List.60 (#Attr.2, #Attr.3): + let List.247 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.247; +>>>>>>> 61fcac491 (Fix mono tests) procedure Num.22 (#Attr.2, #Attr.3): let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; ret Num.188; procedure Str.16 (#Attr.2, #Attr.3): -<<<<<<< HEAD:crates/compiler/test_mono/generated/list_map_closure_borrows.txt - let Str.67 : Str = lowlevel StrRepeat #Attr.2 #Attr.3; - ret Str.67; - -procedure Str.3 (#Attr.2, #Attr.3): - let Str.68 : Str = lowlevel StrConcat #Attr.2 #Attr.3; - ret Str.68; -======= - let Str.36 : Str = lowlevel StrRepeat #Attr.2 #Attr.3; - ret Str.36; - -procedure Str.3 (#Attr.2, #Attr.3): - let Str.37 : Str = lowlevel StrConcat #Attr.2 #Attr.3; + let Str.37 : Str = lowlevel StrRepeat #Attr.2 #Attr.3; ret Str.37; ->>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/list_map_closure_borrows.txt + +procedure Str.3 (#Attr.2, #Attr.3): + let Str.38 : Str = lowlevel StrConcat #Attr.2 #Attr.3; + ret Str.38; procedure Test.1 (): let Test.21 : Str = "lllllllllllllllllllllooooooooooong"; diff --git a/crates/compiler/test_mono/generated/list_map_closure_owns.txt b/crates/compiler/test_mono/generated/list_map_closure_owns.txt index db2a916558..47f476ec71 100644 --- a/crates/compiler/test_mono/generated/list_map_closure_owns.txt +++ b/crates/compiler/test_mono/generated/list_map_closure_owns.txt @@ -1,4 +1,5 @@ <<<<<<< HEAD +<<<<<<< HEAD procedure List.2 (List.75, List.76): let List.290 : U64 = CallByName List.6 List.75; let List.286 : Int1 = CallByName Num.22 List.76 List.290; @@ -66,19 +67,41 @@ procedure List.60 (#Attr.2, #Attr.3): ret List.222; >>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/list_map_closure_owns.txt >>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) +======= +procedure List.2 (List.72, List.73): + let List.244 : U64 = CallByName List.6 List.72; + let List.240 : Int1 = CallByName Num.22 List.73 List.244; + if List.240 then + let List.242 : Str = CallByName List.60 List.72 List.73; + let List.241 : [C {}, C Str] = Ok List.242; + ret List.241; + else + let List.239 : {} = Struct {}; + let List.238 : [C {}, C Str] = Err List.239; + ret List.238; + +procedure List.5 (#Attr.2, #Attr.3): + inc #Attr.2; + let List.246 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3; + decref #Attr.2; + ret List.246; + +procedure List.6 (#Attr.2): + let List.248 : U64 = lowlevel ListLen #Attr.2; + ret List.248; + +procedure List.60 (#Attr.2, #Attr.3): + let List.247 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.247; +>>>>>>> 61fcac491 (Fix mono tests) procedure Num.22 (#Attr.2, #Attr.3): let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; ret Num.188; procedure Str.3 (#Attr.2, #Attr.3): -<<<<<<< HEAD:crates/compiler/test_mono/generated/list_map_closure_owns.txt - let Str.68 : Str = lowlevel StrConcat #Attr.2 #Attr.3; - ret Str.68; -======= - let Str.37 : Str = lowlevel StrConcat #Attr.2 #Attr.3; - ret Str.37; ->>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/list_map_closure_owns.txt + let Str.38 : Str = lowlevel StrConcat #Attr.2 #Attr.3; + ret Str.38; procedure Test.1 (): let Test.21 : Str = "lllllllllllllllllllllooooooooooong"; diff --git a/crates/compiler/test_mono/generated/list_pass_to_function.txt b/crates/compiler/test_mono/generated/list_pass_to_function.txt index 28b6e420a1..3c33945478 100644 --- a/crates/compiler/test_mono/generated/list_pass_to_function.txt +++ b/crates/compiler/test_mono/generated/list_pass_to_function.txt @@ -1,3 +1,4 @@ +<<<<<<< HEAD procedure List.3 (List.84, List.85, List.86): let List.285 : {List I64, I64} = CallByName List.57 List.84 List.85 List.86; let List.284 : List I64 = StructAtIndex 0 List.285; @@ -22,10 +23,36 @@ procedure List.6 (#Attr.2): procedure List.61 (#Attr.2, #Attr.3, #Attr.4): let List.290 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; ret List.290; +======= +procedure List.3 (List.80, List.81, List.82): + let List.239 : {List I64, I64} = CallByName List.57 List.80 List.81 List.82; + let List.238 : List I64 = StructAtIndex 0 List.239; + inc List.238; + dec List.239; + ret List.238; + +procedure List.57 (List.77, List.78, List.79): + let List.245 : U64 = CallByName List.6 List.77; + let List.242 : Int1 = CallByName Num.22 List.78 List.245; + if List.242 then + let List.243 : {List I64, I64} = CallByName List.61 List.77 List.78 List.79; + ret List.243; + else + let List.241 : {List I64, I64} = Struct {List.77, List.79}; + ret List.241; + +procedure List.6 (#Attr.2): + let List.246 : U64 = lowlevel ListLen #Attr.2; + ret List.246; + +procedure List.61 (#Attr.2, #Attr.3, #Attr.4): + let List.244 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; + ret List.244; +>>>>>>> 61fcac491 (Fix mono tests) procedure Num.22 (#Attr.2, #Attr.3): - let Num.273 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.273; + let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.188; procedure Test.2 (Test.3): let Test.6 : U64 = 0i64; diff --git a/crates/compiler/test_mono/generated/list_sort_asc.txt b/crates/compiler/test_mono/generated/list_sort_asc.txt index a385b049f6..f207733107 100644 --- a/crates/compiler/test_mono/generated/list_sort_asc.txt +++ b/crates/compiler/test_mono/generated/list_sort_asc.txt @@ -1,4 +1,5 @@ procedure List.28 (#Attr.2, #Attr.3): +<<<<<<< HEAD <<<<<<< HEAD let List.287 : List I64 = lowlevel ListSortWith { xs: `#Attr.#arg1` } #Attr.2 Num.46 #Attr.3; ======= @@ -24,19 +25,29 @@ procedure List.54 (List.178): ret List.259; ======= let List.216 : List I64 = lowlevel ListSortWith { xs: `#Attr.#arg1` } #Attr.2 Num.46 #Attr.3; +======= + let List.241 : List I64 = lowlevel ListSortWith { xs: `#Attr.#arg1` } #Attr.2 Num.46 #Attr.3; +>>>>>>> 61fcac491 (Fix mono tests) let Bool.9 : Int1 = lowlevel ListIsUnique #Attr.2; if Bool.9 then - ret List.216; + ret List.241; else decref #Attr.2; - ret List.216; + ret List.241; +<<<<<<< HEAD procedure List.54 (List.142): let List.214 : {} = Struct {}; let List.213 : List I64 = CallByName List.28 List.142 List.214; ret List.213; >>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/list_sort_asc.txt >>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) +======= +procedure List.54 (List.163): + let List.239 : {} = Struct {}; + let List.238 : List I64 = CallByName List.28 List.163 List.239; + ret List.238; +>>>>>>> 61fcac491 (Fix mono tests) procedure Num.46 (#Attr.2, #Attr.3): let Num.188 : U8 = lowlevel NumCompare #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/quicksort_swap.txt b/crates/compiler/test_mono/generated/quicksort_swap.txt index 43dac09ce8..4dbe05edb7 100644 --- a/crates/compiler/test_mono/generated/quicksort_swap.txt +++ b/crates/compiler/test_mono/generated/quicksort_swap.txt @@ -1,4 +1,5 @@ <<<<<<< HEAD +<<<<<<< HEAD procedure List.2 (List.75, List.76): let List.304 : U64 = CallByName List.6 List.75; let List.300 : Int1 = CallByName Num.22 List.76 List.304; @@ -72,30 +73,55 @@ procedure List.3 (List.79, List.80, List.81): inc List.216; dec List.217; ret List.216; - -procedure List.57 (List.76, List.77, List.78): - let List.239 : U64 = CallByName List.6 List.76; - let List.236 : Int1 = CallByName Num.22 List.77 List.239; - if List.236 then - let List.237 : {List I64, I64} = CallByName List.61 List.76 List.77 List.78; - ret List.237; +======= +procedure List.2 (List.72, List.73): + let List.258 : U64 = CallByName List.6 List.72; + let List.254 : Int1 = CallByName Num.22 List.73 List.258; + if List.254 then + let List.256 : I64 = CallByName List.60 List.72 List.73; + let List.255 : [C {}, C I64] = Ok List.256; + ret List.255; else - let List.235 : {List I64, I64} = Struct {List.76, List.78}; - ret List.235; + let List.253 : {} = Struct {}; + let List.252 : [C {}, C I64] = Err List.253; + ret List.252; + +procedure List.3 (List.80, List.81, List.82): + let List.242 : {List I64, I64} = CallByName List.57 List.80 List.81 List.82; + let List.241 : List I64 = StructAtIndex 0 List.242; + inc List.241; + dec List.242; + ret List.241; +>>>>>>> 61fcac491 (Fix mono tests) + +procedure List.57 (List.77, List.78, List.79): + let List.264 : U64 = CallByName List.6 List.77; + let List.261 : Int1 = CallByName Num.22 List.78 List.264; + if List.261 then + let List.262 : {List I64, I64} = CallByName List.61 List.77 List.78 List.79; + ret List.262; + else + let List.260 : {List I64, I64} = Struct {List.77, List.79}; + ret List.260; procedure List.6 (#Attr.2): - let List.240 : U64 = lowlevel ListLen #Attr.2; - ret List.240; + let List.265 : U64 = lowlevel ListLen #Attr.2; + ret List.265; procedure List.60 (#Attr.2, #Attr.3): - let List.241 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.241; + let List.266 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.266; procedure List.61 (#Attr.2, #Attr.3, #Attr.4): +<<<<<<< HEAD let List.238 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; ret List.238; >>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/quicksort_swap.txt >>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) +======= + let List.263 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; + ret List.263; +>>>>>>> 61fcac491 (Fix mono tests) procedure Num.22 (#Attr.2, #Attr.3): let Num.190 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/rigids.txt b/crates/compiler/test_mono/generated/rigids.txt index c3ca7f7ca5..83854197b8 100644 --- a/crates/compiler/test_mono/generated/rigids.txt +++ b/crates/compiler/test_mono/generated/rigids.txt @@ -1,4 +1,5 @@ <<<<<<< HEAD +<<<<<<< HEAD procedure List.2 (List.75, List.76): let List.304 : U64 = CallByName List.6 List.75; let List.300 : Int1 = CallByName Num.22 List.76 List.304; @@ -72,30 +73,55 @@ procedure List.3 (List.79, List.80, List.81): inc List.216; dec List.217; ret List.216; - -procedure List.57 (List.76, List.77, List.78): - let List.239 : U64 = CallByName List.6 List.76; - let List.236 : Int1 = CallByName Num.22 List.77 List.239; - if List.236 then - let List.237 : {List I64, I64} = CallByName List.61 List.76 List.77 List.78; - ret List.237; +======= +procedure List.2 (List.72, List.73): + let List.258 : U64 = CallByName List.6 List.72; + let List.254 : Int1 = CallByName Num.22 List.73 List.258; + if List.254 then + let List.256 : I64 = CallByName List.60 List.72 List.73; + let List.255 : [C {}, C I64] = Ok List.256; + ret List.255; else - let List.235 : {List I64, I64} = Struct {List.76, List.78}; - ret List.235; + let List.253 : {} = Struct {}; + let List.252 : [C {}, C I64] = Err List.253; + ret List.252; + +procedure List.3 (List.80, List.81, List.82): + let List.242 : {List I64, I64} = CallByName List.57 List.80 List.81 List.82; + let List.241 : List I64 = StructAtIndex 0 List.242; + inc List.241; + dec List.242; + ret List.241; +>>>>>>> 61fcac491 (Fix mono tests) + +procedure List.57 (List.77, List.78, List.79): + let List.264 : U64 = CallByName List.6 List.77; + let List.261 : Int1 = CallByName Num.22 List.78 List.264; + if List.261 then + let List.262 : {List I64, I64} = CallByName List.61 List.77 List.78 List.79; + ret List.262; + else + let List.260 : {List I64, I64} = Struct {List.77, List.79}; + ret List.260; procedure List.6 (#Attr.2): - let List.240 : U64 = lowlevel ListLen #Attr.2; - ret List.240; + let List.265 : U64 = lowlevel ListLen #Attr.2; + ret List.265; procedure List.60 (#Attr.2, #Attr.3): - let List.241 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.241; + let List.266 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.266; procedure List.61 (#Attr.2, #Attr.3, #Attr.4): +<<<<<<< HEAD let List.238 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; ret List.238; >>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/rigids.txt >>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) +======= + let List.263 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; + ret List.263; +>>>>>>> 61fcac491 (Fix mono tests) procedure Num.22 (#Attr.2, #Attr.3): let Num.190 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; From 91050d99895ca4996076597c4adf379725ab0e20 Mon Sep 17 00:00:00 2001 From: ayazhafiz Date: Sat, 2 Jul 2022 18:50:56 -0400 Subject: [PATCH 36/66] Add solve test for same layout different niche --- crates/compiler/solve/tests/solve_expr.rs | 27 +++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/crates/compiler/solve/tests/solve_expr.rs b/crates/compiler/solve/tests/solve_expr.rs index a0200c0dd9..0f00ca6f47 100644 --- a/crates/compiler/solve/tests/solve_expr.rs +++ b/crates/compiler/solve/tests/solve_expr.rs @@ -7023,4 +7023,31 @@ mod solve_expr { print_only_under_alias = true, ); } + + #[test] + fn lambda_set_niche_same_layout_different_constructor() { + infer_queries!( + indoc!( + r#" + capture : a -> ({} -> Str) + capture = \val -> + thunk = + \{} -> + when val is + _ -> "" + thunk + + x : [True, False] + + fun = + when x is + True -> capture {a: ""} + False -> capture (A "") + fun + #^^^{-1} + "# + ), + &["fun : {} -[[thunk(5) [A Str]*, thunk(5) { a : Str }]]-> Str",] + ); + } } From 3385c708c68e49fdedfbb458cd2b22bae475125d Mon Sep 17 00:00:00 2001 From: ayazhafiz Date: Sat, 2 Jul 2022 19:04:23 -0400 Subject: [PATCH 37/66] Deduplicate capture niches --- crates/compiler/mono/src/layout.rs | 5 +++++ crates/compiler/test_mono/src/tests.rs | 24 ++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/crates/compiler/mono/src/layout.rs b/crates/compiler/mono/src/layout.rs index fdc6e5ec73..976a2c903a 100644 --- a/crates/compiler/mono/src/layout.rs +++ b/crates/compiler/mono/src/layout.rs @@ -1009,6 +1009,11 @@ impl<'a> LambdaSet<'a> { joined.sort_by(|(lam_and_captures1, _), (lam_and_captures2, _)| { lam_and_captures1.cmp(lam_and_captures2) }); + // Remove duplicate lambda captures layouts unification can't see as + // duplicates, for example [[Thunk {a: Str}, Thunk [A Str]]], each of which are + // newtypes over the lambda layout `Thunk Str`. + joined.dedup_by_key(|((name, captures), _)| (*name, *captures)); + let (set, set_with_variables): (std::vec::Vec<_>, std::vec::Vec<_>) = joined.into_iter().unzip(); diff --git a/crates/compiler/test_mono/src/tests.rs b/crates/compiler/test_mono/src/tests.rs index 2b0fd0fafa..39da628f64 100644 --- a/crates/compiler/test_mono/src/tests.rs +++ b/crates/compiler/test_mono/src/tests.rs @@ -1628,3 +1628,27 @@ fn lambda_capture_niches_have_captured_function_in_closure() { "# ) } + +#[mono_test] +fn lambda_set_niche_same_layout_different_constructor() { + indoc!( + r#" + capture : a -> ({} -> Str) + capture = \val -> + thunk = + \{} -> + when val is + _ -> "" + thunk + + x : [True, False] + x = True + + fun = + when x is + True -> capture {a: ""} + False -> capture (A "") + fun + "# + ) +} From 71e3e0baff0334aa7732792d4ca388266ddf324b Mon Sep 17 00:00:00 2001 From: ayazhafiz Date: Sat, 2 Jul 2022 19:04:34 -0400 Subject: [PATCH 38/66] Add dedup niche test golden --- ...iche_same_layout_different_constructor.txt | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 crates/compiler/test_mono/generated/lambda_set_niche_same_layout_different_constructor.txt diff --git a/crates/compiler/test_mono/generated/lambda_set_niche_same_layout_different_constructor.txt b/crates/compiler/test_mono/generated/lambda_set_niche_same_layout_different_constructor.txt new file mode 100644 index 0000000000..ae024a188f --- /dev/null +++ b/crates/compiler/test_mono/generated/lambda_set_niche_same_layout_different_constructor.txt @@ -0,0 +1,26 @@ +procedure Test.1 (Test.4): + let Test.5 : {Str} = Struct {Test.4}; + ret Test.5; + +procedure Test.5 (Test.12, #Attr.12): + let Test.4 : Str = StructAtIndex 0 #Attr.12; + inc Test.4; + dec #Attr.12; + let Test.14 : Str = ""; + ret Test.14; + +procedure Test.0 (): + let Test.2 : Int1 = true; + joinpoint Test.9 Test.3: + ret Test.3; + in + let Test.19 : Int1 = true; + let Test.20 : Int1 = lowlevel Eq Test.19 Test.2; + if Test.20 then + let Test.15 : Str = ""; + let Test.10 : {Str} = CallByName Test.1 Test.15; + jump Test.9 Test.10; + else + let Test.18 : Str = ""; + let Test.16 : {Str} = CallByName Test.1 Test.18; + jump Test.9 Test.16; From 671a19b29de09bbf495745a96ce9a068058c36b1 Mon Sep 17 00:00:00 2001 From: ayazhafiz Date: Sun, 3 Jul 2022 10:39:55 -0400 Subject: [PATCH 39/66] Fix mono --- .../test_mono/generated/closure_in_list.txt | 19 +-- .../generated/empty_list_of_function_type.txt | 87 ++--------- .../compiler/test_mono/generated/encode.txt | 19 +-- .../test_mono/generated/ir_int_add.txt | 19 +-- ...cialize_errors_behind_unified_branches.txt | 91 ++++------- .../test_mono/generated/list_append.txt | 19 +-- .../generated/list_append_closure.txt | 19 +-- .../generated/list_cannot_update_inplace.txt | 99 ++---------- .../compiler/test_mono/generated/list_get.txt | 50 ++---- .../compiler/test_mono/generated/list_len.txt | 35 +---- .../generated/list_map_closure_borrows.txt | 99 ++---------- .../generated/list_map_closure_owns.txt | 103 ++----------- .../generated/list_pass_to_function.txt | 63 +++----- .../test_mono/generated/list_sort_asc.txt | 51 +------ .../test_mono/generated/quicksort_swap.txt | 144 ++++-------------- .../compiler/test_mono/generated/rigids.txt | 144 ++++-------------- 16 files changed, 201 insertions(+), 860 deletions(-) diff --git a/crates/compiler/test_mono/generated/closure_in_list.txt b/crates/compiler/test_mono/generated/closure_in_list.txt index 115677d475..8efa4cf634 100644 --- a/crates/compiler/test_mono/generated/closure_in_list.txt +++ b/crates/compiler/test_mono/generated/closure_in_list.txt @@ -1,21 +1,6 @@ procedure List.6 (#Attr.2): -<<<<<<< HEAD -<<<<<<< HEAD - let List.284 : U64 = lowlevel ListLen #Attr.2; - ret List.284; -======= -<<<<<<< HEAD:crates/compiler/test_mono/generated/closure_in_list.txt - let List.259 : U64 = lowlevel ListLen #Attr.2; - ret List.259; -======= - let List.213 : U64 = lowlevel ListLen #Attr.2; - ret List.213; ->>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/closure_in_list.txt ->>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) -======= - let List.238 : U64 = lowlevel ListLen #Attr.2; - ret List.238; ->>>>>>> 61fcac491 (Fix mono tests) + let List.266 : U64 = lowlevel ListLen #Attr.2; + ret List.266; procedure Test.1 (Test.5): let Test.2 : I64 = 41i64; diff --git a/crates/compiler/test_mono/generated/empty_list_of_function_type.txt b/crates/compiler/test_mono/generated/empty_list_of_function_type.txt index b023cf0456..0694fc9cbc 100644 --- a/crates/compiler/test_mono/generated/empty_list_of_function_type.txt +++ b/crates/compiler/test_mono/generated/empty_list_of_function_type.txt @@ -1,81 +1,22 @@ -<<<<<<< HEAD -<<<<<<< HEAD -procedure List.2 (List.75, List.76): - let List.290 : U64 = CallByName List.6 List.75; - let List.286 : Int1 = CallByName Num.22 List.76 List.290; - if List.286 then - let List.288 : {} = CallByName List.60 List.75 List.76; - let List.287 : [C {}, C {}] = Ok List.288; - ret List.287; -======= -<<<<<<< HEAD:crates/compiler/test_mono/generated/empty_list_of_function_type.txt -procedure List.2 (List.73, List.74): - let List.265 : U64 = CallByName List.6 List.73; - let List.261 : Int1 = CallByName Num.22 List.74 List.265; - if List.261 then - let List.263 : {} = CallByName List.60 List.73 List.74; - let List.262 : [C {}, C {}] = Ok List.263; - ret List.262; ->>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) +procedure List.2 (List.74, List.75): + let List.272 : U64 = CallByName List.6 List.74; + let List.268 : Int1 = CallByName Num.22 List.75 List.272; + if List.268 then + let List.270 : {} = CallByName List.60 List.74 List.75; + let List.269 : [C {}, C {}] = Ok List.270; + ret List.269; else - let List.285 : {} = Struct {}; - let List.284 : [C {}, C {}] = Err List.285; - ret List.284; + let List.267 : {} = Struct {}; + let List.266 : [C {}, C {}] = Err List.267; + ret List.266; procedure List.6 (#Attr.2): - let List.293 : U64 = lowlevel ListLen #Attr.2; - ret List.293; + let List.275 : U64 = lowlevel ListLen #Attr.2; + ret List.275; procedure List.60 (#Attr.2, #Attr.3): -<<<<<<< HEAD - let List.292 : {} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.292; -======= - let List.267 : {} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.267; -======= -procedure List.2 (List.71, List.72): - let List.219 : U64 = CallByName List.6 List.71; - let List.215 : Int1 = CallByName Num.22 List.72 List.219; - if List.215 then - let List.217 : {} = CallByName List.60 List.71 List.72; - let List.216 : [C {}, C {}] = Ok List.217; - ret List.216; - else - let List.214 : {} = Struct {}; - let List.213 : [C {}, C {}] = Err List.214; - ret List.213; - -procedure List.6 (#Attr.2): - let List.222 : U64 = lowlevel ListLen #Attr.2; - ret List.222; - -procedure List.60 (#Attr.2, #Attr.3): - let List.221 : {} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.221; ->>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/empty_list_of_function_type.txt ->>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) -======= -procedure List.2 (List.72, List.73): - let List.244 : U64 = CallByName List.6 List.72; - let List.240 : Int1 = CallByName Num.22 List.73 List.244; - if List.240 then - let List.242 : {} = CallByName List.60 List.72 List.73; - let List.241 : [C {}, C {}] = Ok List.242; - ret List.241; - else - let List.239 : {} = Struct {}; - let List.238 : [C {}, C {}] = Err List.239; - ret List.238; - -procedure List.6 (#Attr.2): - let List.247 : U64 = lowlevel ListLen #Attr.2; - ret List.247; - -procedure List.60 (#Attr.2, #Attr.3): - let List.246 : {} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.246; ->>>>>>> 61fcac491 (Fix mono tests) + let List.274 : {} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.274; procedure Num.22 (#Attr.2, #Attr.3): let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/encode.txt b/crates/compiler/test_mono/generated/encode.txt index 037f3703d0..fdebaf3203 100644 --- a/crates/compiler/test_mono/generated/encode.txt +++ b/crates/compiler/test_mono/generated/encode.txt @@ -1,21 +1,6 @@ procedure List.4 (#Attr.2, #Attr.3): -<<<<<<< HEAD -<<<<<<< HEAD - let List.284 : List U8 = lowlevel ListAppend #Attr.2 #Attr.3; - ret List.284; -======= -<<<<<<< HEAD:crates/compiler/test_mono/generated/encode.txt - let List.259 : List U8 = lowlevel ListAppend #Attr.2 #Attr.3; - ret List.259; -======= - let List.213 : List U8 = lowlevel ListAppend #Attr.2 #Attr.3; - ret List.213; ->>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/encode.txt ->>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) -======= - let List.238 : List U8 = lowlevel ListAppend #Attr.2 #Attr.3; - ret List.238; ->>>>>>> 61fcac491 (Fix mono tests) + let List.266 : List U8 = lowlevel ListAppend #Attr.2 #Attr.3; + ret List.266; procedure Test.20 (Test.22): let Test.34 : {U8} = Struct {Test.22}; diff --git a/crates/compiler/test_mono/generated/ir_int_add.txt b/crates/compiler/test_mono/generated/ir_int_add.txt index 1db9bb3158..f4d92f2cfc 100644 --- a/crates/compiler/test_mono/generated/ir_int_add.txt +++ b/crates/compiler/test_mono/generated/ir_int_add.txt @@ -1,21 +1,6 @@ procedure List.6 (#Attr.2): -<<<<<<< HEAD -<<<<<<< HEAD - let List.284 : U64 = lowlevel ListLen #Attr.2; - ret List.284; -======= -<<<<<<< HEAD:crates/compiler/test_mono/generated/ir_int_add.txt - let List.259 : U64 = lowlevel ListLen #Attr.2; - ret List.259; -======= - let List.213 : U64 = lowlevel ListLen #Attr.2; - ret List.213; ->>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/ir_int_add.txt ->>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) -======= - let List.238 : U64 = lowlevel ListLen #Attr.2; - ret List.238; ->>>>>>> 61fcac491 (Fix mono tests) + let List.266 : U64 = lowlevel ListLen #Attr.2; + ret List.266; procedure Num.19 (#Attr.2, #Attr.3): let Num.190 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt b/crates/compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt index e7a1852e6f..d80b9bb3c5 100644 --- a/crates/compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt +++ b/crates/compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt @@ -1,74 +1,37 @@ -<<<<<<< HEAD -procedure List.2 (List.75, List.76): - let List.299 : U64 = CallByName List.6 List.75; - let List.295 : Int1 = CallByName Num.22 List.76 List.299; - if List.295 then - let List.297 : I64 = CallByName List.60 List.75 List.76; - let List.296 : [C {}, C I64] = Ok List.297; - ret List.296; +procedure List.2 (List.74, List.75): + let List.281 : U64 = CallByName List.6 List.74; + let List.277 : Int1 = CallByName Num.22 List.75 List.281; + if List.277 then + let List.279 : I64 = CallByName List.60 List.74 List.75; + let List.278 : [C {}, C I64] = Ok List.279; + ret List.278; else - let List.294 : {} = Struct {}; - let List.293 : [C {}, C I64] = Err List.294; - ret List.293; + let List.276 : {} = Struct {}; + let List.275 : [C {}, C I64] = Err List.276; + ret List.275; procedure List.6 (#Attr.2): - let List.300 : U64 = lowlevel ListLen #Attr.2; - ret List.300; + let List.282 : U64 = lowlevel ListLen #Attr.2; + ret List.282; procedure List.60 (#Attr.2, #Attr.3): - let List.298 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.298; + let List.280 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.280; -procedure List.9 (List.202): - let List.291 : U64 = 0i64; - let List.284 : [C {}, C I64] = CallByName List.2 List.202 List.291; - let List.288 : U8 = 1i64; - let List.289 : U8 = GetTagId List.284; - let List.290 : Int1 = lowlevel Eq List.288 List.289; - if List.290 then - let List.203 : I64 = UnionAtIndex (Id 1) (Index 0) List.284; - let List.285 : [C Int1, C I64] = Ok List.203; - ret List.285; +procedure List.9 (List.188): + let List.273 : U64 = 0i64; + let List.266 : [C {}, C I64] = CallByName List.2 List.188 List.273; + let List.270 : U8 = 1i64; + let List.271 : U8 = GetTagId List.266; + let List.272 : Int1 = lowlevel Eq List.270 List.271; + if List.272 then + let List.189 : I64 = UnionAtIndex (Id 1) (Index 0) List.266; + let List.267 : [C Int1, C I64] = Ok List.189; + ret List.267; else - let List.287 : Int1 = true; - let List.286 : [C Int1, C I64] = Err List.287; - ret List.286; -======= -procedure List.2 (List.72, List.73): - let List.253 : U64 = CallByName List.6 List.72; - let List.249 : Int1 = CallByName Num.22 List.73 List.253; - if List.249 then - let List.251 : I64 = CallByName List.60 List.72 List.73; - let List.250 : [C {}, C I64] = Ok List.251; - ret List.250; - else - let List.248 : {} = Struct {}; - let List.247 : [C {}, C I64] = Err List.248; - ret List.247; - -procedure List.6 (#Attr.2): - let List.254 : U64 = lowlevel ListLen #Attr.2; - ret List.254; - -procedure List.60 (#Attr.2, #Attr.3): - let List.252 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.252; - -procedure List.9 (List.168): - let List.245 : U64 = 0i64; - let List.238 : [C {}, C I64] = CallByName List.2 List.168 List.245; - let List.242 : U8 = 1i64; - let List.243 : U8 = GetTagId List.238; - let List.244 : Int1 = lowlevel Eq List.242 List.243; - if List.244 then - let List.169 : I64 = UnionAtIndex (Id 1) (Index 0) List.238; - let List.239 : [C Int1, C I64] = Ok List.169; - ret List.239; - else - let List.241 : Int1 = true; - let List.240 : [C Int1, C I64] = Err List.241; - ret List.240; ->>>>>>> 61fcac491 (Fix mono tests) + let List.269 : Int1 = true; + let List.268 : [C Int1, C I64] = Err List.269; + ret List.268; procedure Num.22 (#Attr.2, #Attr.3): let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/list_append.txt b/crates/compiler/test_mono/generated/list_append.txt index a89e4d6eab..bd91996634 100644 --- a/crates/compiler/test_mono/generated/list_append.txt +++ b/crates/compiler/test_mono/generated/list_append.txt @@ -1,21 +1,6 @@ procedure List.4 (#Attr.2, #Attr.3): -<<<<<<< HEAD -<<<<<<< HEAD - let List.284 : List I64 = lowlevel ListAppend #Attr.2 #Attr.3; - ret List.284; -======= -<<<<<<< HEAD:crates/compiler/test_mono/generated/list_append.txt - let List.259 : List I64 = lowlevel ListAppend #Attr.2 #Attr.3; - ret List.259; -======= - let List.213 : List I64 = lowlevel ListAppend #Attr.2 #Attr.3; - ret List.213; ->>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/list_append.txt ->>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) -======= - let List.238 : List I64 = lowlevel ListAppend #Attr.2 #Attr.3; - ret List.238; ->>>>>>> 61fcac491 (Fix mono tests) + let List.266 : List I64 = lowlevel ListAppend #Attr.2 #Attr.3; + ret List.266; procedure Test.0 (): let Test.2 : List I64 = Array [1i64]; diff --git a/crates/compiler/test_mono/generated/list_append_closure.txt b/crates/compiler/test_mono/generated/list_append_closure.txt index bc217613db..d3b8b4a722 100644 --- a/crates/compiler/test_mono/generated/list_append_closure.txt +++ b/crates/compiler/test_mono/generated/list_append_closure.txt @@ -1,21 +1,6 @@ procedure List.4 (#Attr.2, #Attr.3): -<<<<<<< HEAD -<<<<<<< HEAD - let List.284 : List I64 = lowlevel ListAppend #Attr.2 #Attr.3; - ret List.284; -======= -<<<<<<< HEAD:crates/compiler/test_mono/generated/list_append_closure.txt - let List.259 : List I64 = lowlevel ListAppend #Attr.2 #Attr.3; - ret List.259; -======= - let List.213 : List I64 = lowlevel ListAppend #Attr.2 #Attr.3; - ret List.213; ->>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/list_append_closure.txt ->>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) -======= - let List.238 : List I64 = lowlevel ListAppend #Attr.2 #Attr.3; - ret List.238; ->>>>>>> 61fcac491 (Fix mono tests) + let List.266 : List I64 = lowlevel ListAppend #Attr.2 #Attr.3; + ret List.266; procedure Test.1 (Test.2): let Test.6 : I64 = 42i64; diff --git a/crates/compiler/test_mono/generated/list_cannot_update_inplace.txt b/crates/compiler/test_mono/generated/list_cannot_update_inplace.txt index 18465beb56..ed68035643 100644 --- a/crates/compiler/test_mono/generated/list_cannot_update_inplace.txt +++ b/crates/compiler/test_mono/generated/list_cannot_update_inplace.txt @@ -1,96 +1,27 @@ -<<<<<<< HEAD -<<<<<<< HEAD -procedure List.3 (List.84, List.85, List.86): - let List.287 : {List I64, I64} = CallByName List.57 List.84 List.85 List.86; - let List.286 : List I64 = StructAtIndex 0 List.287; - inc List.286; - dec List.287; - ret List.286; - -procedure List.57 (List.81, List.82, List.83): - let List.293 : U64 = CallByName List.6 List.81; - let List.290 : Int1 = CallByName Num.22 List.82 List.293; - if List.290 then - let List.291 : {List I64, I64} = CallByName List.61 List.81 List.82 List.83; - ret List.291; -======= -<<<<<<< HEAD:crates/compiler/test_mono/generated/list_cannot_update_inplace.txt procedure List.3 (List.82, List.83, List.84): - let List.262 : {List I64, I64} = CallByName List.57 List.82 List.83 List.84; - let List.261 : List I64 = StructAtIndex 0 List.262; - inc List.261; - dec List.262; - ret List.261; + let List.269 : {List I64, I64} = CallByName List.57 List.82 List.83 List.84; + let List.268 : List I64 = StructAtIndex 0 List.269; + inc List.268; + dec List.269; + ret List.268; procedure List.57 (List.79, List.80, List.81): - let List.268 : U64 = CallByName List.6 List.79; - let List.265 : Int1 = CallByName Num.22 List.80 List.268; - if List.265 then - let List.266 : {List I64, I64} = CallByName List.61 List.79 List.80 List.81; - ret List.266; ->>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) + let List.275 : U64 = CallByName List.6 List.79; + let List.272 : Int1 = CallByName Num.22 List.80 List.275; + if List.272 then + let List.273 : {List I64, I64} = CallByName List.61 List.79 List.80 List.81; + ret List.273; else - let List.289 : {List I64, I64} = Struct {List.81, List.83}; - ret List.289; + let List.271 : {List I64, I64} = Struct {List.79, List.81}; + ret List.271; procedure List.6 (#Attr.2): - let List.285 : U64 = lowlevel ListLen #Attr.2; - ret List.285; - -procedure List.61 (#Attr.2, #Attr.3, #Attr.4): -<<<<<<< HEAD - let List.292 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; - ret List.292; -======= - let List.267 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; + let List.267 : U64 = lowlevel ListLen #Attr.2; ret List.267; -======= -procedure List.3 (List.79, List.80, List.81): - let List.216 : {List I64, I64} = CallByName List.57 List.79 List.80 List.81; - let List.215 : List I64 = StructAtIndex 0 List.216; - inc List.215; - dec List.216; - ret List.215; - -procedure List.57 (List.76, List.77, List.78): - let List.222 : U64 = CallByName List.6 List.76; - let List.219 : Int1 = CallByName Num.22 List.77 List.222; - if List.219 then - let List.220 : {List I64, I64} = CallByName List.61 List.76 List.77 List.78; - ret List.220; -======= -procedure List.3 (List.80, List.81, List.82): - let List.241 : {List I64, I64} = CallByName List.57 List.80 List.81 List.82; - let List.240 : List I64 = StructAtIndex 0 List.241; - inc List.240; - dec List.241; - ret List.240; - -procedure List.57 (List.77, List.78, List.79): - let List.247 : U64 = CallByName List.6 List.77; - let List.244 : Int1 = CallByName Num.22 List.78 List.247; - if List.244 then - let List.245 : {List I64, I64} = CallByName List.61 List.77 List.78 List.79; - ret List.245; ->>>>>>> 61fcac491 (Fix mono tests) - else - let List.243 : {List I64, I64} = Struct {List.77, List.79}; - ret List.243; - -procedure List.6 (#Attr.2): - let List.239 : U64 = lowlevel ListLen #Attr.2; - ret List.239; procedure List.61 (#Attr.2, #Attr.3, #Attr.4): -<<<<<<< HEAD - let List.221 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; - ret List.221; ->>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/list_cannot_update_inplace.txt ->>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) -======= - let List.246 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; - ret List.246; ->>>>>>> 61fcac491 (Fix mono tests) + let List.274 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; + ret List.274; procedure Num.19 (#Attr.2, #Attr.3): let Num.188 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/list_get.txt b/crates/compiler/test_mono/generated/list_get.txt index 16382b100a..3bdeee4a87 100644 --- a/crates/compiler/test_mono/generated/list_get.txt +++ b/crates/compiler/test_mono/generated/list_get.txt @@ -1,44 +1,22 @@ -<<<<<<< HEAD -procedure List.2 (List.75, List.76): - let List.290 : U64 = CallByName List.6 List.75; - let List.286 : Int1 = CallByName Num.22 List.76 List.290; - if List.286 then - let List.288 : I64 = CallByName List.60 List.75 List.76; - let List.287 : [C {}, C I64] = Ok List.288; - ret List.287; +procedure List.2 (List.74, List.75): + let List.272 : U64 = CallByName List.6 List.74; + let List.268 : Int1 = CallByName Num.22 List.75 List.272; + if List.268 then + let List.270 : I64 = CallByName List.60 List.74 List.75; + let List.269 : [C {}, C I64] = Ok List.270; + ret List.269; else - let List.285 : {} = Struct {}; - let List.284 : [C {}, C I64] = Err List.285; - ret List.284; + let List.267 : {} = Struct {}; + let List.266 : [C {}, C I64] = Err List.267; + ret List.266; procedure List.6 (#Attr.2): - let List.293 : U64 = lowlevel ListLen #Attr.2; - ret List.293; + let List.275 : U64 = lowlevel ListLen #Attr.2; + ret List.275; procedure List.60 (#Attr.2, #Attr.3): - let List.292 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.292; -======= -procedure List.2 (List.72, List.73): - let List.244 : U64 = CallByName List.6 List.72; - let List.240 : Int1 = CallByName Num.22 List.73 List.244; - if List.240 then - let List.242 : I64 = CallByName List.60 List.72 List.73; - let List.241 : [C {}, C I64] = Ok List.242; - ret List.241; - else - let List.239 : {} = Struct {}; - let List.238 : [C {}, C I64] = Err List.239; - ret List.238; - -procedure List.6 (#Attr.2): - let List.247 : U64 = lowlevel ListLen #Attr.2; - ret List.247; - -procedure List.60 (#Attr.2, #Attr.3): - let List.246 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.246; ->>>>>>> 61fcac491 (Fix mono tests) + let List.274 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.274; procedure Num.22 (#Attr.2, #Attr.3): let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/list_len.txt b/crates/compiler/test_mono/generated/list_len.txt index 08700aee17..f06fa86d6b 100644 --- a/crates/compiler/test_mono/generated/list_len.txt +++ b/crates/compiler/test_mono/generated/list_len.txt @@ -1,37 +1,10 @@ procedure List.6 (#Attr.2): -<<<<<<< HEAD -<<<<<<< HEAD - let List.284 : U64 = lowlevel ListLen #Attr.2; - ret List.284; + let List.266 : U64 = lowlevel ListLen #Attr.2; + ret List.266; procedure List.6 (#Attr.2): - let List.285 : U64 = lowlevel ListLen #Attr.2; - ret List.285; -======= -<<<<<<< HEAD:crates/compiler/test_mono/generated/list_len.txt - let List.259 : U64 = lowlevel ListLen #Attr.2; - ret List.259; - -procedure List.6 (#Attr.2): - let List.260 : U64 = lowlevel ListLen #Attr.2; - ret List.260; -======= - let List.213 : U64 = lowlevel ListLen #Attr.2; - ret List.213; - -procedure List.6 (#Attr.2): - let List.214 : U64 = lowlevel ListLen #Attr.2; - ret List.214; ->>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/list_len.txt ->>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) -======= - let List.238 : U64 = lowlevel ListLen #Attr.2; - ret List.238; - -procedure List.6 (#Attr.2): - let List.239 : U64 = lowlevel ListLen #Attr.2; - ret List.239; ->>>>>>> 61fcac491 (Fix mono tests) + let List.267 : U64 = lowlevel ListLen #Attr.2; + ret List.267; procedure Num.19 (#Attr.2, #Attr.3): let Num.188 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/list_map_closure_borrows.txt b/crates/compiler/test_mono/generated/list_map_closure_borrows.txt index 1e30f9109e..0121e1773e 100644 --- a/crates/compiler/test_mono/generated/list_map_closure_borrows.txt +++ b/crates/compiler/test_mono/generated/list_map_closure_borrows.txt @@ -1,93 +1,26 @@ -<<<<<<< HEAD -<<<<<<< HEAD -procedure List.2 (List.75, List.76): - let List.290 : U64 = CallByName List.6 List.75; - let List.286 : Int1 = CallByName Num.22 List.76 List.290; - if List.286 then - let List.288 : Str = CallByName List.60 List.75 List.76; - let List.287 : [C {}, C Str] = Ok List.288; - ret List.287; -======= -<<<<<<< HEAD:crates/compiler/test_mono/generated/list_map_closure_borrows.txt -procedure List.2 (List.73, List.74): - let List.265 : U64 = CallByName List.6 List.73; - let List.261 : Int1 = CallByName Num.22 List.74 List.265; - if List.261 then - let List.263 : Str = CallByName List.60 List.73 List.74; - let List.262 : [C {}, C Str] = Ok List.263; - ret List.262; ->>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) +procedure List.2 (List.74, List.75): + let List.272 : U64 = CallByName List.6 List.74; + let List.268 : Int1 = CallByName Num.22 List.75 List.272; + if List.268 then + let List.270 : Str = CallByName List.60 List.74 List.75; + let List.269 : [C {}, C Str] = Ok List.270; + ret List.269; else - let List.285 : {} = Struct {}; - let List.284 : [C {}, C Str] = Err List.285; - ret List.284; + let List.267 : {} = Struct {}; + let List.266 : [C {}, C Str] = Err List.267; + ret List.266; procedure List.5 (#Attr.2, #Attr.3): - let List.292 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3; - ret List.292; + let List.274 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3; + ret List.274; procedure List.6 (#Attr.2): - let List.294 : U64 = lowlevel ListLen #Attr.2; - ret List.294; + let List.276 : U64 = lowlevel ListLen #Attr.2; + ret List.276; procedure List.60 (#Attr.2, #Attr.3): -<<<<<<< HEAD - let List.293 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.293; -======= - let List.268 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.268; -======= -procedure List.2 (List.71, List.72): - let List.219 : U64 = CallByName List.6 List.71; - let List.215 : Int1 = CallByName Num.22 List.72 List.219; - if List.215 then - let List.217 : Str = CallByName List.60 List.71 List.72; - let List.216 : [C {}, C Str] = Ok List.217; - ret List.216; - else - let List.214 : {} = Struct {}; - let List.213 : [C {}, C Str] = Err List.214; - ret List.213; - -procedure List.5 (#Attr.2, #Attr.3): - let List.221 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3; - ret List.221; - -procedure List.6 (#Attr.2): - let List.223 : U64 = lowlevel ListLen #Attr.2; - ret List.223; - -procedure List.60 (#Attr.2, #Attr.3): - let List.222 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.222; ->>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/list_map_closure_borrows.txt ->>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) -======= -procedure List.2 (List.72, List.73): - let List.244 : U64 = CallByName List.6 List.72; - let List.240 : Int1 = CallByName Num.22 List.73 List.244; - if List.240 then - let List.242 : Str = CallByName List.60 List.72 List.73; - let List.241 : [C {}, C Str] = Ok List.242; - ret List.241; - else - let List.239 : {} = Struct {}; - let List.238 : [C {}, C Str] = Err List.239; - ret List.238; - -procedure List.5 (#Attr.2, #Attr.3): - let List.246 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3; - ret List.246; - -procedure List.6 (#Attr.2): - let List.248 : U64 = lowlevel ListLen #Attr.2; - ret List.248; - -procedure List.60 (#Attr.2, #Attr.3): - let List.247 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.247; ->>>>>>> 61fcac491 (Fix mono tests) + let List.275 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.275; procedure Num.22 (#Attr.2, #Attr.3): let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/list_map_closure_owns.txt b/crates/compiler/test_mono/generated/list_map_closure_owns.txt index 47f476ec71..e62826e3c2 100644 --- a/crates/compiler/test_mono/generated/list_map_closure_owns.txt +++ b/crates/compiler/test_mono/generated/list_map_closure_owns.txt @@ -1,99 +1,28 @@ -<<<<<<< HEAD -<<<<<<< HEAD -procedure List.2 (List.75, List.76): - let List.290 : U64 = CallByName List.6 List.75; - let List.286 : Int1 = CallByName Num.22 List.76 List.290; - if List.286 then - let List.288 : Str = CallByName List.60 List.75 List.76; - let List.287 : [C {}, C Str] = Ok List.288; - ret List.287; -======= -<<<<<<< HEAD:crates/compiler/test_mono/generated/list_map_closure_owns.txt -procedure List.2 (List.73, List.74): - let List.265 : U64 = CallByName List.6 List.73; - let List.261 : Int1 = CallByName Num.22 List.74 List.265; - if List.261 then - let List.263 : Str = CallByName List.60 List.73 List.74; - let List.262 : [C {}, C Str] = Ok List.263; - ret List.262; ->>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) +procedure List.2 (List.74, List.75): + let List.272 : U64 = CallByName List.6 List.74; + let List.268 : Int1 = CallByName Num.22 List.75 List.272; + if List.268 then + let List.270 : Str = CallByName List.60 List.74 List.75; + let List.269 : [C {}, C Str] = Ok List.270; + ret List.269; else - let List.285 : {} = Struct {}; - let List.284 : [C {}, C Str] = Err List.285; - ret List.284; + let List.267 : {} = Struct {}; + let List.266 : [C {}, C Str] = Err List.267; + ret List.266; procedure List.5 (#Attr.2, #Attr.3): inc #Attr.2; - let List.292 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3; + let List.274 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3; decref #Attr.2; - ret List.292; + ret List.274; procedure List.6 (#Attr.2): - let List.294 : U64 = lowlevel ListLen #Attr.2; - ret List.294; + let List.276 : U64 = lowlevel ListLen #Attr.2; + ret List.276; procedure List.60 (#Attr.2, #Attr.3): -<<<<<<< HEAD - let List.293 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.293; -======= - let List.268 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.268; -======= -procedure List.2 (List.71, List.72): - let List.219 : U64 = CallByName List.6 List.71; - let List.215 : Int1 = CallByName Num.22 List.72 List.219; - if List.215 then - let List.217 : Str = CallByName List.60 List.71 List.72; - let List.216 : [C {}, C Str] = Ok List.217; - ret List.216; - else - let List.214 : {} = Struct {}; - let List.213 : [C {}, C Str] = Err List.214; - ret List.213; - -procedure List.5 (#Attr.2, #Attr.3): - inc #Attr.2; - let List.221 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3; - decref #Attr.2; - ret List.221; - -procedure List.6 (#Attr.2): - let List.223 : U64 = lowlevel ListLen #Attr.2; - ret List.223; - -procedure List.60 (#Attr.2, #Attr.3): - let List.222 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.222; ->>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/list_map_closure_owns.txt ->>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) -======= -procedure List.2 (List.72, List.73): - let List.244 : U64 = CallByName List.6 List.72; - let List.240 : Int1 = CallByName Num.22 List.73 List.244; - if List.240 then - let List.242 : Str = CallByName List.60 List.72 List.73; - let List.241 : [C {}, C Str] = Ok List.242; - ret List.241; - else - let List.239 : {} = Struct {}; - let List.238 : [C {}, C Str] = Err List.239; - ret List.238; - -procedure List.5 (#Attr.2, #Attr.3): - inc #Attr.2; - let List.246 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3; - decref #Attr.2; - ret List.246; - -procedure List.6 (#Attr.2): - let List.248 : U64 = lowlevel ListLen #Attr.2; - ret List.248; - -procedure List.60 (#Attr.2, #Attr.3): - let List.247 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.247; ->>>>>>> 61fcac491 (Fix mono tests) + let List.275 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.275; procedure Num.22 (#Attr.2, #Attr.3): let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/list_pass_to_function.txt b/crates/compiler/test_mono/generated/list_pass_to_function.txt index 3c33945478..e97bf720c0 100644 --- a/crates/compiler/test_mono/generated/list_pass_to_function.txt +++ b/crates/compiler/test_mono/generated/list_pass_to_function.txt @@ -1,54 +1,27 @@ -<<<<<<< HEAD -procedure List.3 (List.84, List.85, List.86): - let List.285 : {List I64, I64} = CallByName List.57 List.84 List.85 List.86; - let List.284 : List I64 = StructAtIndex 0 List.285; - inc List.284; - dec List.285; - ret List.284; +procedure List.3 (List.82, List.83, List.84): + let List.267 : {List I64, I64} = CallByName List.57 List.82 List.83 List.84; + let List.266 : List I64 = StructAtIndex 0 List.267; + inc List.266; + dec List.267; + ret List.266; -procedure List.57 (List.81, List.82, List.83): - let List.291 : U64 = CallByName List.6 List.81; - let List.288 : Int1 = CallByName Num.22 List.82 List.291; - if List.288 then - let List.289 : {List I64, I64} = CallByName List.61 List.81 List.82 List.83; - ret List.289; +procedure List.57 (List.79, List.80, List.81): + let List.273 : U64 = CallByName List.6 List.79; + let List.270 : Int1 = CallByName Num.22 List.80 List.273; + if List.270 then + let List.271 : {List I64, I64} = CallByName List.61 List.79 List.80 List.81; + ret List.271; else - let List.287 : {List I64, I64} = Struct {List.81, List.83}; - ret List.287; + let List.269 : {List I64, I64} = Struct {List.79, List.81}; + ret List.269; procedure List.6 (#Attr.2): - let List.292 : U64 = lowlevel ListLen #Attr.2; - ret List.292; + let List.274 : U64 = lowlevel ListLen #Attr.2; + ret List.274; procedure List.61 (#Attr.2, #Attr.3, #Attr.4): - let List.290 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; - ret List.290; -======= -procedure List.3 (List.80, List.81, List.82): - let List.239 : {List I64, I64} = CallByName List.57 List.80 List.81 List.82; - let List.238 : List I64 = StructAtIndex 0 List.239; - inc List.238; - dec List.239; - ret List.238; - -procedure List.57 (List.77, List.78, List.79): - let List.245 : U64 = CallByName List.6 List.77; - let List.242 : Int1 = CallByName Num.22 List.78 List.245; - if List.242 then - let List.243 : {List I64, I64} = CallByName List.61 List.77 List.78 List.79; - ret List.243; - else - let List.241 : {List I64, I64} = Struct {List.77, List.79}; - ret List.241; - -procedure List.6 (#Attr.2): - let List.246 : U64 = lowlevel ListLen #Attr.2; - ret List.246; - -procedure List.61 (#Attr.2, #Attr.3, #Attr.4): - let List.244 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; - ret List.244; ->>>>>>> 61fcac491 (Fix mono tests) + let List.272 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; + ret List.272; procedure Num.22 (#Attr.2, #Attr.3): let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/list_sort_asc.txt b/crates/compiler/test_mono/generated/list_sort_asc.txt index f207733107..7762a77377 100644 --- a/crates/compiler/test_mono/generated/list_sort_asc.txt +++ b/crates/compiler/test_mono/generated/list_sort_asc.txt @@ -1,53 +1,16 @@ procedure List.28 (#Attr.2, #Attr.3): -<<<<<<< HEAD -<<<<<<< HEAD - let List.287 : List I64 = lowlevel ListSortWith { xs: `#Attr.#arg1` } #Attr.2 Num.46 #Attr.3; -======= -<<<<<<< HEAD:crates/compiler/test_mono/generated/list_sort_asc.txt - let List.262 : List I64 = lowlevel ListSortWith { xs: `#Attr.#arg1` } #Attr.2 Num.46 #Attr.3; ->>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) - let Bool.14 : Int1 = lowlevel ListIsUnique #Attr.2; - if Bool.14 then - ret List.287; - else - decref #Attr.2; - ret List.287; - -<<<<<<< HEAD -procedure List.54 (List.196): - let List.285 : {} = Struct {}; - let List.284 : List I64 = CallByName List.28 List.196 List.285; - ret List.284; -======= -procedure List.54 (List.178): - let List.260 : {} = Struct {}; - let List.259 : List I64 = CallByName List.28 List.178 List.260; - ret List.259; -======= - let List.216 : List I64 = lowlevel ListSortWith { xs: `#Attr.#arg1` } #Attr.2 Num.46 #Attr.3; -======= - let List.241 : List I64 = lowlevel ListSortWith { xs: `#Attr.#arg1` } #Attr.2 Num.46 #Attr.3; ->>>>>>> 61fcac491 (Fix mono tests) + let List.269 : List I64 = lowlevel ListSortWith { xs: `#Attr.#arg1` } #Attr.2 Num.46 #Attr.3; let Bool.9 : Int1 = lowlevel ListIsUnique #Attr.2; if Bool.9 then - ret List.241; + ret List.269; else decref #Attr.2; - ret List.241; + ret List.269; -<<<<<<< HEAD -procedure List.54 (List.142): - let List.214 : {} = Struct {}; - let List.213 : List I64 = CallByName List.28 List.142 List.214; - ret List.213; ->>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/list_sort_asc.txt ->>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) -======= -procedure List.54 (List.163): - let List.239 : {} = Struct {}; - let List.238 : List I64 = CallByName List.28 List.163 List.239; - ret List.238; ->>>>>>> 61fcac491 (Fix mono tests) +procedure List.54 (List.183): + let List.267 : {} = Struct {}; + let List.266 : List I64 = CallByName List.28 List.183 List.267; + ret List.266; procedure Num.46 (#Attr.2, #Attr.3): let Num.188 : U8 = lowlevel NumCompare #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/quicksort_swap.txt b/crates/compiler/test_mono/generated/quicksort_swap.txt index 4dbe05edb7..47d50ff7d9 100644 --- a/crates/compiler/test_mono/generated/quicksort_swap.txt +++ b/crates/compiler/test_mono/generated/quicksort_swap.txt @@ -1,127 +1,43 @@ -<<<<<<< HEAD -<<<<<<< HEAD -procedure List.2 (List.75, List.76): - let List.304 : U64 = CallByName List.6 List.75; - let List.300 : Int1 = CallByName Num.22 List.76 List.304; - if List.300 then - let List.302 : I64 = CallByName List.60 List.75 List.76; - let List.301 : [C {}, C I64] = Ok List.302; - ret List.301; -======= -<<<<<<< HEAD:crates/compiler/test_mono/generated/quicksort_swap.txt -procedure List.2 (List.73, List.74): - let List.279 : U64 = CallByName List.6 List.73; - let List.275 : Int1 = CallByName Num.22 List.74 List.279; - if List.275 then - let List.277 : I64 = CallByName List.60 List.73 List.74; - let List.276 : [C {}, C I64] = Ok List.277; - ret List.276; ->>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) +procedure List.2 (List.74, List.75): + let List.286 : U64 = CallByName List.6 List.74; + let List.282 : Int1 = CallByName Num.22 List.75 List.286; + if List.282 then + let List.284 : I64 = CallByName List.60 List.74 List.75; + let List.283 : [C {}, C I64] = Ok List.284; + ret List.283; else - let List.299 : {} = Struct {}; - let List.298 : [C {}, C I64] = Err List.299; - ret List.298; + let List.281 : {} = Struct {}; + let List.280 : [C {}, C I64] = Err List.281; + ret List.280; -procedure List.3 (List.84, List.85, List.86): - let List.288 : {List I64, I64} = CallByName List.57 List.84 List.85 List.86; - let List.287 : List I64 = StructAtIndex 0 List.288; - inc List.287; - dec List.288; - ret List.287; +procedure List.3 (List.82, List.83, List.84): + let List.270 : {List I64, I64} = CallByName List.57 List.82 List.83 List.84; + let List.269 : List I64 = StructAtIndex 0 List.270; + inc List.269; + dec List.270; + ret List.269; -procedure List.57 (List.81, List.82, List.83): - let List.310 : U64 = CallByName List.6 List.81; - let List.307 : Int1 = CallByName Num.22 List.82 List.310; - if List.307 then - let List.308 : {List I64, I64} = CallByName List.61 List.81 List.82 List.83; - ret List.308; +procedure List.57 (List.79, List.80, List.81): + let List.292 : U64 = CallByName List.6 List.79; + let List.289 : Int1 = CallByName Num.22 List.80 List.292; + if List.289 then + let List.290 : {List I64, I64} = CallByName List.61 List.79 List.80 List.81; + ret List.290; else - let List.306 : {List I64, I64} = Struct {List.81, List.83}; - ret List.306; + let List.288 : {List I64, I64} = Struct {List.79, List.81}; + ret List.288; procedure List.6 (#Attr.2): - let List.311 : U64 = lowlevel ListLen #Attr.2; - ret List.311; + let List.293 : U64 = lowlevel ListLen #Attr.2; + ret List.293; procedure List.60 (#Attr.2, #Attr.3): - let List.312 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.312; + let List.294 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.294; procedure List.61 (#Attr.2, #Attr.3, #Attr.4): -<<<<<<< HEAD - let List.309 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; - ret List.309; -======= - let List.284 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; - ret List.284; -======= -procedure List.2 (List.71, List.72): - let List.233 : U64 = CallByName List.6 List.71; - let List.229 : Int1 = CallByName Num.22 List.72 List.233; - if List.229 then - let List.231 : I64 = CallByName List.60 List.71 List.72; - let List.230 : [C {}, C I64] = Ok List.231; - ret List.230; - else - let List.228 : {} = Struct {}; - let List.227 : [C {}, C I64] = Err List.228; - ret List.227; - -procedure List.3 (List.79, List.80, List.81): - let List.217 : {List I64, I64} = CallByName List.57 List.79 List.80 List.81; - let List.216 : List I64 = StructAtIndex 0 List.217; - inc List.216; - dec List.217; - ret List.216; -======= -procedure List.2 (List.72, List.73): - let List.258 : U64 = CallByName List.6 List.72; - let List.254 : Int1 = CallByName Num.22 List.73 List.258; - if List.254 then - let List.256 : I64 = CallByName List.60 List.72 List.73; - let List.255 : [C {}, C I64] = Ok List.256; - ret List.255; - else - let List.253 : {} = Struct {}; - let List.252 : [C {}, C I64] = Err List.253; - ret List.252; - -procedure List.3 (List.80, List.81, List.82): - let List.242 : {List I64, I64} = CallByName List.57 List.80 List.81 List.82; - let List.241 : List I64 = StructAtIndex 0 List.242; - inc List.241; - dec List.242; - ret List.241; ->>>>>>> 61fcac491 (Fix mono tests) - -procedure List.57 (List.77, List.78, List.79): - let List.264 : U64 = CallByName List.6 List.77; - let List.261 : Int1 = CallByName Num.22 List.78 List.264; - if List.261 then - let List.262 : {List I64, I64} = CallByName List.61 List.77 List.78 List.79; - ret List.262; - else - let List.260 : {List I64, I64} = Struct {List.77, List.79}; - ret List.260; - -procedure List.6 (#Attr.2): - let List.265 : U64 = lowlevel ListLen #Attr.2; - ret List.265; - -procedure List.60 (#Attr.2, #Attr.3): - let List.266 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.266; - -procedure List.61 (#Attr.2, #Attr.3, #Attr.4): -<<<<<<< HEAD - let List.238 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; - ret List.238; ->>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/quicksort_swap.txt ->>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) -======= - let List.263 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; - ret List.263; ->>>>>>> 61fcac491 (Fix mono tests) + let List.291 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; + ret List.291; procedure Num.22 (#Attr.2, #Attr.3): let Num.190 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/rigids.txt b/crates/compiler/test_mono/generated/rigids.txt index 83854197b8..d5dcc8bf25 100644 --- a/crates/compiler/test_mono/generated/rigids.txt +++ b/crates/compiler/test_mono/generated/rigids.txt @@ -1,127 +1,43 @@ -<<<<<<< HEAD -<<<<<<< HEAD -procedure List.2 (List.75, List.76): - let List.304 : U64 = CallByName List.6 List.75; - let List.300 : Int1 = CallByName Num.22 List.76 List.304; - if List.300 then - let List.302 : I64 = CallByName List.60 List.75 List.76; - let List.301 : [C {}, C I64] = Ok List.302; - ret List.301; -======= -<<<<<<< HEAD:crates/compiler/test_mono/generated/rigids.txt -procedure List.2 (List.73, List.74): - let List.279 : U64 = CallByName List.6 List.73; - let List.275 : Int1 = CallByName Num.22 List.74 List.279; - if List.275 then - let List.277 : I64 = CallByName List.60 List.73 List.74; - let List.276 : [C {}, C I64] = Ok List.277; - ret List.276; ->>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) +procedure List.2 (List.74, List.75): + let List.286 : U64 = CallByName List.6 List.74; + let List.282 : Int1 = CallByName Num.22 List.75 List.286; + if List.282 then + let List.284 : I64 = CallByName List.60 List.74 List.75; + let List.283 : [C {}, C I64] = Ok List.284; + ret List.283; else - let List.299 : {} = Struct {}; - let List.298 : [C {}, C I64] = Err List.299; - ret List.298; + let List.281 : {} = Struct {}; + let List.280 : [C {}, C I64] = Err List.281; + ret List.280; -procedure List.3 (List.84, List.85, List.86): - let List.288 : {List I64, I64} = CallByName List.57 List.84 List.85 List.86; - let List.287 : List I64 = StructAtIndex 0 List.288; - inc List.287; - dec List.288; - ret List.287; +procedure List.3 (List.82, List.83, List.84): + let List.270 : {List I64, I64} = CallByName List.57 List.82 List.83 List.84; + let List.269 : List I64 = StructAtIndex 0 List.270; + inc List.269; + dec List.270; + ret List.269; -procedure List.57 (List.81, List.82, List.83): - let List.310 : U64 = CallByName List.6 List.81; - let List.307 : Int1 = CallByName Num.22 List.82 List.310; - if List.307 then - let List.308 : {List I64, I64} = CallByName List.61 List.81 List.82 List.83; - ret List.308; +procedure List.57 (List.79, List.80, List.81): + let List.292 : U64 = CallByName List.6 List.79; + let List.289 : Int1 = CallByName Num.22 List.80 List.292; + if List.289 then + let List.290 : {List I64, I64} = CallByName List.61 List.79 List.80 List.81; + ret List.290; else - let List.306 : {List I64, I64} = Struct {List.81, List.83}; - ret List.306; + let List.288 : {List I64, I64} = Struct {List.79, List.81}; + ret List.288; procedure List.6 (#Attr.2): - let List.311 : U64 = lowlevel ListLen #Attr.2; - ret List.311; + let List.293 : U64 = lowlevel ListLen #Attr.2; + ret List.293; procedure List.60 (#Attr.2, #Attr.3): - let List.312 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.312; + let List.294 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.294; procedure List.61 (#Attr.2, #Attr.3, #Attr.4): -<<<<<<< HEAD - let List.309 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; - ret List.309; -======= - let List.284 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; - ret List.284; -======= -procedure List.2 (List.71, List.72): - let List.233 : U64 = CallByName List.6 List.71; - let List.229 : Int1 = CallByName Num.22 List.72 List.233; - if List.229 then - let List.231 : I64 = CallByName List.60 List.71 List.72; - let List.230 : [C {}, C I64] = Ok List.231; - ret List.230; - else - let List.228 : {} = Struct {}; - let List.227 : [C {}, C I64] = Err List.228; - ret List.227; - -procedure List.3 (List.79, List.80, List.81): - let List.217 : {List I64, I64} = CallByName List.57 List.79 List.80 List.81; - let List.216 : List I64 = StructAtIndex 0 List.217; - inc List.216; - dec List.217; - ret List.216; -======= -procedure List.2 (List.72, List.73): - let List.258 : U64 = CallByName List.6 List.72; - let List.254 : Int1 = CallByName Num.22 List.73 List.258; - if List.254 then - let List.256 : I64 = CallByName List.60 List.72 List.73; - let List.255 : [C {}, C I64] = Ok List.256; - ret List.255; - else - let List.253 : {} = Struct {}; - let List.252 : [C {}, C I64] = Err List.253; - ret List.252; - -procedure List.3 (List.80, List.81, List.82): - let List.242 : {List I64, I64} = CallByName List.57 List.80 List.81 List.82; - let List.241 : List I64 = StructAtIndex 0 List.242; - inc List.241; - dec List.242; - ret List.241; ->>>>>>> 61fcac491 (Fix mono tests) - -procedure List.57 (List.77, List.78, List.79): - let List.264 : U64 = CallByName List.6 List.77; - let List.261 : Int1 = CallByName Num.22 List.78 List.264; - if List.261 then - let List.262 : {List I64, I64} = CallByName List.61 List.77 List.78 List.79; - ret List.262; - else - let List.260 : {List I64, I64} = Struct {List.77, List.79}; - ret List.260; - -procedure List.6 (#Attr.2): - let List.265 : U64 = lowlevel ListLen #Attr.2; - ret List.265; - -procedure List.60 (#Attr.2, #Attr.3): - let List.266 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.266; - -procedure List.61 (#Attr.2, #Attr.3, #Attr.4): -<<<<<<< HEAD - let List.238 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; - ret List.238; ->>>>>>> a5fc399f4 (Rename multimorphic tests to capture niche):compiler/test_mono/generated/rigids.txt ->>>>>>> 25bb3751b (Rename multimorphic tests to capture niche) -======= - let List.263 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; - ret List.263; ->>>>>>> 61fcac491 (Fix mono tests) + let List.291 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; + ret List.291; procedure Num.22 (#Attr.2, #Attr.3): let Num.190 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; From 375b3e064a4fca6eb83d9fcbc9c3c8c45e5bb13e Mon Sep 17 00:00:00 2001 From: ayazhafiz Date: Sun, 3 Jul 2022 12:58:27 -0400 Subject: [PATCH 40/66] Use insta snapshot tests in reporting tests --- Cargo.lock | 1 + crates/reporting/Cargo.toml | 1 + crates/reporting/tests/test_reporting.rs | 1167 ++++++++++------------ 3 files changed, 542 insertions(+), 627 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eb989d06e2..87e157cd88 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3980,6 +3980,7 @@ dependencies = [ "bumpalo", "distance", "indoc", + "insta", "pretty_assertions", "roc_builtins", "roc_can", diff --git a/crates/reporting/Cargo.toml b/crates/reporting/Cargo.toml index 46a53791a7..e1af05fbc6 100644 --- a/crates/reporting/Cargo.toml +++ b/crates/reporting/Cargo.toml @@ -31,3 +31,4 @@ roc_target = { path = "../compiler/roc_target" } roc_test_utils = { path = "../test_utils" } pretty_assertions = "1.0.0" indoc = "1.0.3" +insta = "1.15.0" diff --git a/crates/reporting/tests/test_reporting.rs b/crates/reporting/tests/test_reporting.rs index 5917c331ac..edba4c7c93 100644 --- a/crates/reporting/tests/test_reporting.rs +++ b/crates/reporting/tests/test_reporting.rs @@ -401,7 +401,7 @@ mod test_reporting { } /// Do not call this directly! Use the test_report macro below! - fn __new_report_problem_as(subdir: &str, src: &str, expected_rendering: &str) { + fn __new_report_problem_as(subdir: &str, src: &str, check_render: impl FnOnce(&str)) { let arena = Bump::new(); let finalize_render = |doc: RocDocBuilder<'_>, buf: &mut String| { @@ -412,21 +412,16 @@ mod test_reporting { let buf = list_reports_new(subdir, &arena, src, finalize_render); - // convenient to copy-paste the generated message - if buf != expected_rendering { - for line in buf.split('\n') { - println!(" {}", line); - } - } - - assert_multiline_str_eq!(expected_rendering, buf.as_str()); + check_render(buf.as_str()); } macro_rules! test_report { - ($test_name:ident, $program:expr, $output:expr) => { + ($test_name:ident, $program:expr, @$output:literal) => { #[test] fn $test_name() { - __new_report_problem_as(std::stringify!($test_name), $program, $output) + __new_report_problem_as(std::stringify!($test_name), $program, |golden| { + insta::assert_snapshot!(golden, @$output) + }) } }; } @@ -1386,22 +1381,20 @@ mod test_reporting { f "# ), - indoc!( - r#" - ── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─ + @r#" + ── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─ - I'm inferring a weird self-referential type for `f`: + I'm inferring a weird self-referential type for `f`: - 5│ f = \x -> f [x] - ^ + 5│ f = \x -> f [x] + ^ - Here is my best effort at writing down the type. You will see ∞ for - parts of the type that repeat something already printed out - infinitely. + Here is my best effort at writing down the type. You will see ∞ for + parts of the type that repeat something already printed out + infinitely. - List ∞ -> a - "# - ) + List ∞ -> a + "# ); test_report!( @@ -1414,22 +1407,20 @@ mod test_reporting { f "# ), - indoc!( - r#" - ── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─ + @r#" + ── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─ - I'm inferring a weird self-referential type for `x`: + I'm inferring a weird self-referential type for `x`: - 5│ f = \x -> f [x] - ^ + 5│ f = \x -> f [x] + ^ - Here is my best effort at writing down the type. You will see ∞ for - parts of the type that repeat something already printed out - infinitely. + Here is my best effort at writing down the type. You will see ∞ for + parts of the type that repeat something already printed out + infinitely. - List ∞ - "# - ) + List ∞ + "# ); test_report!( @@ -1443,59 +1434,57 @@ mod test_reporting { f "# ), - indoc!( - // TODO: the second error is duplicated because when solving `f : _ -> List _`, we - // introduce the variable for `f` twice: once to solve `f` without generalization, - // and then a second time to properly generalize it. When a def is unannotated - // (like in `g`) the same variable gets used both times, because the type of `g` is - // only an unbound type variable. However, for `f`, we run `type_to_var` twice, - // receiving two separate variables, and the second variable doesn't have the cycle - // error already recorded for the first. - // The way to resolve this is to always give type annotation signatures an extra - // variables they can put themselves in, and to run the constraint algorithm - // against that extra variable, rather than possibly having to translate a `Type` - // again. - r#" - ── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─ + // TODO: the second error is duplicated because when solving `f : _ -> List _`, we + // introduce the variable for `f` twice: once to solve `f` without generalization, + // and then a second time to properly generalize it. When a def is unannotated + // (like in `g`) the same variable gets used both times, because the type of `g` is + // only an unbound type variable. However, for `f`, we run `type_to_var` twice, + // receiving two separate variables, and the second variable doesn't have the cycle + // error already recorded for the first. + // The way to resolve this is to always give type annotation signatures an extra + // variables they can put themselves in, and to run the constraint algorithm + // against that extra variable, rather than possibly having to translate a `Type` + // again. + @r#" + ── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─ - I'm inferring a weird self-referential type for `f`: + I'm inferring a weird self-referential type for `f`: - 5│ f = \x -> g x - ^ + 5│ f = \x -> g x + ^ - Here is my best effort at writing down the type. You will see ∞ for - parts of the type that repeat something already printed out - infinitely. + Here is my best effort at writing down the type. You will see ∞ for + parts of the type that repeat something already printed out + infinitely. - List ∞ -> List a + List ∞ -> List a - ── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─ + ── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─ - I'm inferring a weird self-referential type for `g`: + I'm inferring a weird self-referential type for `g`: - 6│ g = \x -> f [x] - ^ + 6│ g = \x -> f [x] + ^ - Here is my best effort at writing down the type. You will see ∞ for - parts of the type that repeat something already printed out - infinitely. + Here is my best effort at writing down the type. You will see ∞ for + parts of the type that repeat something already printed out + infinitely. - List ∞ -> List a + List ∞ -> List a - ── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─ + ── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─ - I'm inferring a weird self-referential type for `f`: + I'm inferring a weird self-referential type for `f`: - 5│ f = \x -> g x - ^ + 5│ f = \x -> g x + ^ - Here is my best effort at writing down the type. You will see ∞ for - parts of the type that repeat something already printed out - infinitely. + Here is my best effort at writing down the type. You will see ∞ for + parts of the type that repeat something already printed out + infinitely. - List ∞ -> List a - "# - ) + List ∞ -> List a + "# ); test_report!( @@ -1509,22 +1498,20 @@ mod test_reporting { f "# ), - indoc!( - r#" - ── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─ + @r#" + ── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─ - I'm inferring a weird self-referential type for `x`: + I'm inferring a weird self-referential type for `x`: - 6│ g = \x -> f [x] - ^ + 6│ g = \x -> f [x] + ^ - Here is my best effort at writing down the type. You will see ∞ for - parts of the type that repeat something already printed out - infinitely. + Here is my best effort at writing down the type. You will see ∞ for + parts of the type that repeat something already printed out + infinitely. - List ∞ - "# - ) + List ∞ + "# ); #[test] @@ -9028,19 +9015,17 @@ All branches in an `if` must have the same type! 1 "# ), - indoc!( - r#" - ── UNFINISHED ABILITY ─── tmp/ability_demands_not_indented_with_first/Test.roc ─ + @r#" + ── UNFINISHED ABILITY ─── tmp/ability_demands_not_indented_with_first/Test.roc ─ - I was partway through parsing an ability definition, but I got stuck - here: + I was partway through parsing an ability definition, but I got stuck + here: - 5│ eq : a, a -> U64 | a has Eq - 6│ neq : a, a -> U64 | a has Eq - ^ + 5│ eq : a, a -> U64 | a has Eq + 6│ neq : a, a -> U64 | a has Eq + ^ - I suspect this line is indented too much (by 4 spaces)"# - ) + I suspect this line is indented too much (by 4 spaces)"# ); test_report!( @@ -9053,19 +9038,17 @@ All branches in an `if` must have the same type! 1 "# ), - indoc!( - r#" - ── UNFINISHED ABILITY ───────────── tmp/ability_demand_value_has_args/Test.roc ─ + @r#" + ── UNFINISHED ABILITY ───────────── tmp/ability_demand_value_has_args/Test.roc ─ - I was partway through parsing an ability definition, but I got stuck - here: + I was partway through parsing an ability definition, but I got stuck + here: - 5│ eq b c : a, a -> U64 | a has Eq - ^ + 5│ eq b c : a, a -> U64 | a has Eq + ^ - I was expecting to see a : annotating the signature of this value - next."# - ) + I was expecting to see a : annotating the signature of this value + next."# ); #[test] @@ -9238,29 +9221,27 @@ All branches in an `if` must have the same type! hash : a -> U64 | a has Hash "# ), - indoc!( - r#" - ── ABILITY HAS TYPE VARIABLES ──────────────────────────── /code/proj/Main.roc ─ + @r#" + ── ABILITY HAS TYPE VARIABLES ──────────────────────────── /code/proj/Main.roc ─ - The definition of the `Hash` ability includes type variables: + The definition of the `Hash` ability includes type variables: - 3│ Hash a b c has - ^^^^^ + 3│ Hash a b c has + ^^^^^ - Abilities cannot depend on type variables, but their member values - can! + Abilities cannot depend on type variables, but their member values + can! - ── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─ + ── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─ - `Hash` is not used anywhere in your code. + `Hash` is not used anywhere in your code. - 3│ Hash a b c has - ^^^^ + 3│ Hash a b c has + ^^^^ - If you didn't intend on using `Hash` then remove it so future readers of - your code don't wonder why it is there. - "# - ) + If you didn't intend on using `Hash` then remove it so future readers of + your code don't wonder why it is there. + "# ); test_report!( @@ -9272,16 +9253,14 @@ All branches in an `if` must have the same type! Hash has hash : a, b -> Num.U64 | a has Hash, b has Bool.Bool "# ), - indoc!( - r#" - ── HAS CLAUSE IS NOT AN ABILITY ────────────────────────── /code/proj/Main.roc ─ + @r#" + ── HAS CLAUSE IS NOT AN ABILITY ────────────────────────── /code/proj/Main.roc ─ - The type referenced in this "has" clause is not an ability: + The type referenced in this "has" clause is not an ability: - 3│ Hash has hash : a, b -> Num.U64 | a has Hash, b has Bool.Bool - ^^^^^^^^^ - "# - ) + 3│ Hash has hash : a, b -> Num.U64 | a has Hash, b has Bool.Bool + ^^^^^^^^^ + "# ); test_report!( @@ -9293,24 +9272,22 @@ All branches in an `if` must have the same type! Ab1 has ab1 : a -> {} | a has Ab1, a has Ab1 "# ), - indoc!( - r#" - ── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─ + @r#" + ── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─ - The `a` name is first defined here: + The `a` name is first defined here: - 3│ Ab1 has ab1 : a -> {} | a has Ab1, a has Ab1 - ^^^^^^^^^ + 3│ Ab1 has ab1 : a -> {} | a has Ab1, a has Ab1 + ^^^^^^^^^ - But then it's defined a second time here: + But then it's defined a second time here: - 3│ Ab1 has ab1 : a -> {} | a has Ab1, a has Ab1 - ^^^^^^^^^ + 3│ Ab1 has ab1 : a -> {} | a has Ab1, a has Ab1 + ^^^^^^^^^ - Since these variables have the same name, it's easy to use the wrong - one on accident. Give one of them a new name. - "# - ) + Since these variables have the same name, it's easy to use the wrong + one on accident. Give one of them a new name. + "# ); test_report!( @@ -9324,24 +9301,22 @@ All branches in an `if` must have the same type! Ability has ab1 : a -> U64 | a has Ability "# ), - indoc!( - r#" - ── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─ + @r#" + ── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─ - The `Ability` name is first defined here: + The `Ability` name is first defined here: - 3│ Ability has ab : a -> U64 | a has Ability - ^^^^^^^ + 3│ Ability has ab : a -> U64 | a has Ability + ^^^^^^^ - But then it's defined a second time here: + But then it's defined a second time here: - 5│ Ability has ab1 : a -> U64 | a has Ability - ^^^^^^^ + 5│ Ability has ab1 : a -> U64 | a has Ability + ^^^^^^^ - Since these abilities have the same name, it's easy to use the wrong - one on accident. Give one of them a new name. - "# - ) + Since these abilities have the same name, it's easy to use the wrong + one on accident. Give one of them a new name. + "# ); test_report!( @@ -9353,34 +9328,32 @@ All branches in an `if` must have the same type! Ability has ab : {} -> {} "# ), - indoc!( - r#" - ── ABILITY MEMBER MISSING HAS CLAUSE ───────────────────── /code/proj/Main.roc ─ + @r#" + ── ABILITY MEMBER MISSING HAS CLAUSE ───────────────────── /code/proj/Main.roc ─ - The definition of the ability member `ab` does not include a `has` clause - binding a type variable to the ability `Ability`: + The definition of the ability member `ab` does not include a `has` clause + binding a type variable to the ability `Ability`: - 3│ Ability has ab : {} -> {} - ^^ + 3│ Ability has ab : {} -> {} + ^^ - Ability members must include a `has` clause binding a type variable to - an ability, like + Ability members must include a `has` clause binding a type variable to + an ability, like - a has Ability + a has Ability - Otherwise, the function does not need to be part of the ability! + Otherwise, the function does not need to be part of the ability! - ── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─ + ── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─ - `Ability` is not used anywhere in your code. + `Ability` is not used anywhere in your code. - 3│ Ability has ab : {} -> {} - ^^^^^^^ + 3│ Ability has ab : {} -> {} + ^^^^^^^ - If you didn't intend on using `Ability` then remove it so future readers - of your code don't wonder why it is there. - "# - ) + If you didn't intend on using `Ability` then remove it so future readers + of your code don't wonder why it is there. + "# ); test_report!( @@ -9392,23 +9365,21 @@ All branches in an `if` must have the same type! Eq has eq : a, b -> Bool.Bool | a has Eq, b has Eq "# ), - indoc!( - r#" - ── ABILITY MEMBER BINDS MULTIPLE VARIABLES ─────────────── /code/proj/Main.roc ─ + @r#" + ── ABILITY MEMBER BINDS MULTIPLE VARIABLES ─────────────── /code/proj/Main.roc ─ - The definition of the ability member `eq` includes multiple variables - bound to the `Eq`` ability:` + The definition of the ability member `eq` includes multiple variables + bound to the `Eq`` ability:` - 3│ Eq has eq : a, b -> Bool.Bool | a has Eq, b has Eq - ^^^^^^^^^^^^^^^^^^ + 3│ Eq has eq : a, b -> Bool.Bool | a has Eq, b has Eq + ^^^^^^^^^^^^^^^^^^ - Ability members can only bind one type variable to their parent - ability. Otherwise, I wouldn't know what type implements an ability by - looking at specializations! + Ability members can only bind one type variable to their parent + ability. Otherwise, I wouldn't know what type implements an ability by + looking at specializations! - Hint: Did you mean to only bind `a` to `Eq`? - "# - ) + Hint: Did you mean to only bind `a` to `Eq`? + "# ); test_report!( @@ -9422,33 +9393,31 @@ All branches in an `if` must have the same type! f : a -> Num.U64 | a has Hash "# ), - indoc!( - r#" - ── ILLEGAL HAS CLAUSE ──────────────────────────────────── /code/proj/Main.roc ─ + @r#" + ── ILLEGAL HAS CLAUSE ──────────────────────────────────── /code/proj/Main.roc ─ - A `has` clause is not allowed here: + A `has` clause is not allowed here: - 3│ Hash has hash : (a | a has Hash) -> Num.U64 - ^^^^^^^^^^ + 3│ Hash has hash : (a | a has Hash) -> Num.U64 + ^^^^^^^^^^ - `has` clauses can only be specified on the top-level type annotations. + `has` clauses can only be specified on the top-level type annotations. - ── ABILITY MEMBER MISSING HAS CLAUSE ───────────────────── /code/proj/Main.roc ─ + ── ABILITY MEMBER MISSING HAS CLAUSE ───────────────────── /code/proj/Main.roc ─ - The definition of the ability member `hash` does not include a `has` - clause binding a type variable to the ability `Hash`: + The definition of the ability member `hash` does not include a `has` + clause binding a type variable to the ability `Hash`: - 3│ Hash has hash : (a | a has Hash) -> Num.U64 - ^^^^ + 3│ Hash has hash : (a | a has Hash) -> Num.U64 + ^^^^ - Ability members must include a `has` clause binding a type variable to - an ability, like + Ability members must include a `has` clause binding a type variable to + an ability, like - a has Hash + a has Hash - Otherwise, the function does not need to be part of the ability! - "# - ) + Otherwise, the function does not need to be part of the ability! + "# ); test_report!( @@ -9462,24 +9431,22 @@ All branches in an `if` must have the same type! hash = \{} -> 0u64 "# ), - indoc!( - r#" - ── ILLEGAL SPECIALIZATION ──────────────────────────────── /code/proj/Main.roc ─ + @r#" + ── ILLEGAL SPECIALIZATION ──────────────────────────────── /code/proj/Main.roc ─ - This specialization of `hash` is for a non-opaque type: + This specialization of `hash` is for a non-opaque type: - 5│ hash = \{} -> 0u64 - ^^^^ + 5│ hash = \{} -> 0u64 + ^^^^ - It is specialized for + It is specialized for - {}a + {}a - but structural types can never specialize abilities! + but structural types can never specialize abilities! - Note: `hash` is a member of `#UserApp.Hash` - "# - ) + Note: `hash` is a member of `#UserApp.Hash` + "# ); test_report!( @@ -9495,24 +9462,22 @@ All branches in an `if` must have the same type! hash = \@Id n -> n "# ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + @r#" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - Something is off with this specialization of `hash`: + Something is off with this specialization of `hash`: - 7│ hash = \@Id n -> n - ^^^^ + 7│ hash = \@Id n -> n + ^^^^ - This value is a declared specialization of type: + This value is a declared specialization of type: - Id -> U32 + Id -> U32 - But the type annotation on `hash` says it must match: + But the type annotation on `hash` says it must match: - Id -> U64 - "# - ) + Id -> U64 + "# ); test_report!( @@ -9530,19 +9495,17 @@ All branches in an `if` must have the same type! eq = \@Id m, @Id n -> m == n "# ), - indoc!( - r#" - ── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─ + @r#" + ── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─ - The type `Id` does not fully implement the ability `Eq`. The following - specializations are missing: + The type `Id` does not fully implement the ability `Eq`. The following + specializations are missing: - A specialization for `le`, which is defined here: + A specialization for `le`, which is defined here: - 5│ le : a, a -> Bool | a has Eq - ^^ - "# - ) + 5│ le : a, a -> Bool | a has Eq + ^^ + "# ); test_report!( @@ -9557,31 +9520,29 @@ All branches in an `if` must have the same type! hash = \_ -> 0u64 "# ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + @r#" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - This specialization of `hash` is overly general: + This specialization of `hash` is overly general: - 6│ hash = \_ -> 0u64 - ^^^^ + 6│ hash = \_ -> 0u64 + ^^^^ - This value is a declared specialization of type: + This value is a declared specialization of type: - a -> U64 + a -> U64 - But the type annotation on `hash` says it must match: + But the type annotation on `hash` says it must match: - a -> U64 | a has Hash + a -> U64 | a has Hash - Note: The specialized type is too general, and does not provide a - concrete type where a type variable is bound to an ability. + Note: The specialized type is too general, and does not provide a + concrete type where a type variable is bound to an ability. - Specializations can only be made for concrete types. If you have a - generic implementation for this value, perhaps you don't need an - ability? - "# - ) + Specializations can only be made for concrete types. If you have a + generic implementation for this value, perhaps you don't need an + ability? + "# ); test_report!( @@ -9599,29 +9560,27 @@ All branches in an `if` must have the same type! eq = \@You {}, @AndI {} -> False "# ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + @r#" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - Something is off with this specialization of `eq`: + Something is off with this specialization of `eq`: - 9│ eq = \@You {}, @AndI {} -> False - ^^ + 9│ eq = \@You {}, @AndI {} -> False + ^^ - This value is a declared specialization of type: + This value is a declared specialization of type: - You, AndI -> [False, True] + You, AndI -> [False, True] - But the type annotation on `eq` says it must match: + But the type annotation on `eq` says it must match: - You, You -> Bool + You, You -> Bool - Tip: Type comparisons between an opaque type are only ever equal if - both types are the same opaque type. Did you mean to create an opaque - type by wrapping it? If I have an opaque type Age := U32 I can create - an instance of this opaque type by doing @Age 23. - "# - ) + Tip: Type comparisons between an opaque type are only ever equal if + both types are the same opaque type. Did you mean to create an opaque + type by wrapping it? If I have an opaque type Age := U32 I can create + an instance of this opaque type by doing @Age 23. + "# ); test_report!( @@ -9639,40 +9598,38 @@ All branches in an `if` must have the same type! hash = \@Id n -> n "# ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + @r#" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - Something is off with the body of the `hash` definition: + Something is off with the body of the `hash` definition: - 8│ hash : Id -> U32 - 9│ hash = \@Id n -> n - ^ + 8│ hash : Id -> U32 + 9│ hash = \@Id n -> n + ^ - This `n` value is a: + This `n` value is a: - U64 + U64 - But the type annotation on `hash` says it should be: + But the type annotation on `hash` says it should be: - U32 + U32 - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - Something is off with this specialization of `hash`: + Something is off with this specialization of `hash`: - 9│ hash = \@Id n -> n - ^^^^^^^^^^^ + 9│ hash = \@Id n -> n + ^^^^^^^^^^^ - This value is a declared specialization of type: + This value is a declared specialization of type: - Id -> U32 + Id -> U32 - But the type annotation on `hash` says it must match: + But the type annotation on `hash` says it must match: - Id -> U64 - "# - ) + Id -> U64 + "# ); test_report!( @@ -9697,37 +9654,35 @@ All branches in an `if` must have the same type! } "# ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + @r#" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - This expression has a type that does not implement the abilities it's expected to: + This expression has a type that does not implement the abilities it's expected to: - 15│ notYet: hash (A 1), - ^^^ + 15│ notYet: hash (A 1), + ^^^ - Roc can't generate an implementation of the `#UserApp.Hash` ability for + Roc can't generate an implementation of the `#UserApp.Hash` ability for - [A (Num a)]b + [A (Num a)]b - Only builtin abilities can have generated implementations! + Only builtin abilities can have generated implementations! - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - This expression has a type that does not implement the abilities it's expected to: + This expression has a type that does not implement the abilities it's expected to: - 14│ nope: hash (@User {}), - ^^^^^^^^ + 14│ nope: hash (@User {}), + ^^^^^^^^ - The type `User` does not fully implement the ability `Hash`. The following - specializations are missing: + The type `User` does not fully implement the ability `Hash`. The following + specializations are missing: - A specialization for `hash`, which is defined here: + A specialization for `hash`, which is defined here: - 4│ hash : a -> U64 | a has Hash - ^^^^ - "# - ) + 4│ hash : a -> U64 | a has Hash + ^^^^ + "# ); test_report!( @@ -9743,18 +9698,16 @@ All branches in an `if` must have the same type! 123 "# ), - indoc!( - r#" - ── ABILITY NOT ON TOP-LEVEL ────────────────────────────── /code/proj/Main.roc ─ + @r#" + ── ABILITY NOT ON TOP-LEVEL ────────────────────────────── /code/proj/Main.roc ─ - This ability definition is not on the top-level of a module: + This ability definition is not on the top-level of a module: - 4│> Hash has - 5│> hash : a -> U64 | a has Hash + 4│> Hash has + 5│> hash : a -> U64 | a has Hash - Abilities can only be defined on the top-level of a Roc module. - "# - ) + Abilities can only be defined on the top-level of a Roc module. + "# ); test_report!( @@ -9773,31 +9726,29 @@ All branches in an `if` must have the same type! hashable = @Id 15 "# ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + @r#" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - Something is off with the body of the `hashable` definition: + Something is off with the body of the `hashable` definition: - 9│ hashable : a | a has Hash - 10│ hashable = @Id 15 - ^^^^^^ + 9│ hashable : a | a has Hash + 10│ hashable = @Id 15 + ^^^^^^ - This Id opaque wrapping has the type: + This Id opaque wrapping has the type: - Id + Id - But the type annotation on `hashable` says it should be: + But the type annotation on `hashable` says it should be: - a | a has Hash + a | a has Hash - Tip: The type annotation uses the type variable `a` to say that this - definition can produce any value implementing the `Hash` ability. But in - the body I see that it will only produce a `Id` value of a single - specific type. Maybe change the type annotation to be more specific? - Maybe change the code to be more general? - "# - ) + Tip: The type annotation uses the type variable `a` to say that this + definition can produce any value implementing the `Hash` ability. But in + the body I see that it will only produce a `Id` value of a single + specific type. Maybe change the type annotation to be more specific? + Maybe change the code to be more general? + "# ); test_report!( @@ -9821,37 +9772,35 @@ All branches in an `if` must have the same type! result = mulHashes (@Id 100) (@Three {}) "# ), - indoc!( - r#" - ── ABILITY USED AS TYPE ────────────────────────────────── /code/proj/Main.roc ─ + @r#" + ── ABILITY USED AS TYPE ────────────────────────────────── /code/proj/Main.roc ─ - You are attempting to use the ability `Hash` as a type directly: + You are attempting to use the ability `Hash` as a type directly: - 6│ mulHashes : Hash, Hash -> U64 - ^^^^ + 6│ mulHashes : Hash, Hash -> U64 + ^^^^ - Abilities can only be used in type annotations to constrain type - variables. + Abilities can only be used in type annotations to constrain type + variables. - Hint: Perhaps you meant to include a `has` annotation, like + Hint: Perhaps you meant to include a `has` annotation, like - a has Hash + a has Hash - ── ABILITY USED AS TYPE ────────────────────────────────── /code/proj/Main.roc ─ + ── ABILITY USED AS TYPE ────────────────────────────────── /code/proj/Main.roc ─ - You are attempting to use the ability `Hash` as a type directly: + You are attempting to use the ability `Hash` as a type directly: - 6│ mulHashes : Hash, Hash -> U64 - ^^^^ + 6│ mulHashes : Hash, Hash -> U64 + ^^^^ - Abilities can only be used in type annotations to constrain type - variables. + Abilities can only be used in type annotations to constrain type + variables. - Hint: Perhaps you meant to include a `has` annotation, like + Hint: Perhaps you meant to include a `has` annotation, like - b has Hash - "# - ) + b has Hash + "# ); test_report!( @@ -9867,28 +9816,26 @@ All branches in an `if` must have the same type! foo "# ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + @r#" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - The branches of this `when` expression don't match the condition: + The branches of this `when` expression don't match the condition: - 6│> when bool is - 7│ True -> "true" - 8│ False -> "false" - 9│ Wat -> "surprise!" + 6│> when bool is + 7│ True -> "true" + 8│ False -> "false" + 9│ Wat -> "surprise!" - This `bool` value is a: + This `bool` value is a: - Bool + Bool - But the branch patterns have type: + But the branch patterns have type: - [False, True, Wat] + [False, True, Wat] - The branches must be cases of the `when` condition's type! - "# - ) + The branches must be cases of the `when` condition's type! + "# ); #[test] @@ -9920,20 +9867,18 @@ All branches in an `if` must have the same type! provides [main, @Foo] to pf "# ), - indoc!( - r#" - ── WEIRD IMPORTS ────────────────────────── tmp/imports_missing_comma/Test.roc ─ + @r#" + ── WEIRD IMPORTS ────────────────────────── tmp/imports_missing_comma/Test.roc ─ - I am partway through parsing a imports list, but I got stuck here: + I am partway through parsing a imports list, but I got stuck here: - 2│ packages { pf: "platform/main.roc" } - 3│ imports [pf.Task Base64] - ^ + 2│ packages { pf: "platform/main.roc" } + 3│ imports [pf.Task Base64] + ^ - I am expecting a comma or end of list, like + I am expecting a comma or end of list, like - imports [Shape, Vector]"# - ) + imports [Shape, Vector]"# ); test_report!( @@ -9947,23 +9892,21 @@ All branches in an `if` must have the same type! foo "# ), - indoc!( - r#" - ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ + @r#" + ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ - This `when` does not cover all the possibilities: + This `when` does not cover all the possibilities: - 6│> when it is - 7│> A -> "" + 6│> when it is + 7│> A -> "" - Other possibilities include: + Other possibilities include: - B - _ + B + _ - I would have to crash if I saw one of those! Add branches for them! - "# - ) + I would have to crash if I saw one of those! Add branches for them! + "# ); test_report!( @@ -9979,7 +9922,7 @@ All branches in an `if` must have the same type! Red |> formatColor |> Str.concat (formatColor Orange) "# ), - "" // no problem + @"" // no problem ); test_report!( @@ -9996,19 +9939,17 @@ All branches in an `if` must have the same type! default {} "# ), - indoc!( - r#" - ── SPECIALIZATION NOT ON TOP-LEVEL ─────────────────────── /code/proj/Main.roc ─ + @r#" + ── SPECIALIZATION NOT ON TOP-LEVEL ─────────────────────── /code/proj/Main.roc ─ - This specialization of the `default` ability member is in a nested - scope: + This specialization of the `default` ability member is in a nested + scope: - 7│ default = \{} -> @A {} - ^^^^^^^ + 7│ default = \{} -> @A {} + ^^^^^^^ - Specializations can only be defined on the top-level of a module. - "# - ) + Specializations can only be defined on the top-level of a module. + "# ); test_report!( @@ -10023,24 +9964,22 @@ All branches in an `if` must have the same type! Job lst -> lst == "" "# ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + @r#" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - The 2nd argument to `isEq` is not what I expect: + The 2nd argument to `isEq` is not what I expect: - 9│ Job lst -> lst == "" - ^^ + 9│ Job lst -> lst == "" + ^^ - This argument is a string of type: + This argument is a string of type: - Str + Str - But `isEq` needs the 2nd argument to be: + But `isEq` needs the 2nd argument to be: - List [Job ∞] as ∞ - "# - ) + List [Job ∞] as ∞ + "# ); test_report!( @@ -10060,56 +9999,54 @@ All branches in an `if` must have the same type! go goal new "# ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + @r#" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - The 1st argument to `remove` is not what I expect: + The 1st argument to `remove` is not what I expect: - 10│ new = { model & set : Set.remove goal model.set } - ^^^^ + 10│ new = { model & set : Set.remove goal model.set } + ^^^^ - This `goal` value is a: + This `goal` value is a: - a + a - But `remove` needs the 1st argument to be: + But `remove` needs the 1st argument to be: - Set a + Set a - Tip: The type annotation uses the type variable `a` to say that this - definition can produce any type of value. But in the body I see that - it will only produce a `Set` value of a single specific type. Maybe - change the type annotation to be more specific? Maybe change the code - to be more general? + Tip: The type annotation uses the type variable `a` to say that this + definition can produce any type of value. But in the body I see that + it will only produce a `Set` value of a single specific type. Maybe + change the type annotation to be more specific? Maybe change the code + to be more general? - ── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─ + ── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─ - I'm inferring a weird self-referential type for `new`: + I'm inferring a weird self-referential type for `new`: - 10│ new = { model & set : Set.remove goal model.set } - ^^^ + 10│ new = { model & set : Set.remove goal model.set } + ^^^ - Here is my best effort at writing down the type. You will see ∞ for - parts of the type that repeat something already printed out - infinitely. + Here is my best effort at writing down the type. You will see ∞ for + parts of the type that repeat something already printed out + infinitely. - { set : Set ∞ } + { set : Set ∞ } - ── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─ + ── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─ - I'm inferring a weird self-referential type for `goal`: + I'm inferring a weird self-referential type for `goal`: - 6│ go = \goal, model -> - ^^^^ + 6│ go = \goal, model -> + ^^^^ - Here is my best effort at writing down the type. You will see ∞ for - parts of the type that repeat something already printed out - infinitely. + Here is my best effort at writing down the type. You will see ∞ for + parts of the type that repeat something already printed out + infinitely. - Set ∞ - "# - ) + Set ∞ + "# ); test_report!( @@ -10126,25 +10063,23 @@ All branches in an `if` must have the same type! t2 "# ), - indoc!( - r#" - ── CIRCULAR DEFINITION ─────────────────────────────────── /code/proj/Main.roc ─ + @r#" + ── CIRCULAR DEFINITION ─────────────────────────────────── /code/proj/Main.roc ─ - The `t1` definition is causing a very tricky infinite loop: + The `t1` definition is causing a very tricky infinite loop: - 7│ t1 = \_ -> force (\_ -> t2) - ^^ + 7│ t1 = \_ -> force (\_ -> t2) + ^^ - The `t1` value depends on itself through the following chain of - definitions: + The `t1` value depends on itself through the following chain of + definitions: - ┌─────┐ - │ t1 - │ ↓ - │ t2 - └─────┘ - "# - ) + ┌─────┐ + │ t1 + │ ↓ + │ t2 + └─────┘ + "# ); test_report!( @@ -10156,23 +10091,21 @@ All branches in an `if` must have the same type! main = Encode.toEncoder \x -> x "# ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + @r#" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - This expression has a type that does not implement the abilities it's expected to: + This expression has a type that does not implement the abilities it's expected to: - 3│ main = Encode.toEncoder \x -> x - ^^^^^^^ + 3│ main = Encode.toEncoder \x -> x + ^^^^^^^ - Roc can't generate an implementation of the `Encode.Encoding` ability - for + Roc can't generate an implementation of the `Encode.Encoding` ability + for - a -> a + a -> a - Note: `Encoding` cannot be generated for functions. - "# - ) + Note: `Encoding` cannot be generated for functions. + "# ); test_report!( @@ -10184,30 +10117,28 @@ All branches in an `if` must have the same type! main = \x -> Encode.toEncoder { x: x } "# ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + @r#" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - This expression has a type that does not implement the abilities it's expected to: + This expression has a type that does not implement the abilities it's expected to: - 3│ main = \x -> Encode.toEncoder { x: x } - ^^^^^^^^ + 3│ main = \x -> Encode.toEncoder { x: x } + ^^^^^^^^ - Roc can't generate an implementation of the `Encode.Encoding` ability - for + Roc can't generate an implementation of the `Encode.Encoding` ability + for - { x : a } + { x : a } - In particular, an implementation for + In particular, an implementation for - a + a - cannot be generated. + cannot be generated. - Tip: This type variable is not bound to `Encoding`. Consider adding a - `has` clause to bind the type variable, like `| a has Encode.Encoding` - "# - ) + Tip: This type variable is not bound to `Encoding`. Consider adding a + `has` clause to bind the type variable, like `| a has Encode.Encoding` + "# ); test_report!( @@ -10220,30 +10151,28 @@ All branches in an `if` must have the same type! main = Encode.toEncoder { x: @A {} } "# ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + @r#" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - This expression has a type that does not implement the abilities it's expected to: + This expression has a type that does not implement the abilities it's expected to: - 4│ main = Encode.toEncoder { x: @A {} } - ^^^^^^^^^^^^ + 4│ main = Encode.toEncoder { x: @A {} } + ^^^^^^^^^^^^ - Roc can't generate an implementation of the `Encode.Encoding` ability - for + Roc can't generate an implementation of the `Encode.Encoding` ability + for - { x : A } + { x : A } - In particular, an implementation for + In particular, an implementation for - A + A - cannot be generated. + cannot be generated. - Tip: `A` does not implement `Encoding`. Consider adding a custom - implementation or `has Encode.Encoding` to the definition of `A`. - "# - ) + Tip: `A` does not implement `Encoding`. Consider adding a custom + implementation or `has Encode.Encoding` to the definition of `A`. + "# ); test_report!( @@ -10260,25 +10189,23 @@ All branches in an `if` must have the same type! t2 = t1 {} "# ), - indoc!( - r#" - ── CIRCULAR DEFINITION ─────────────────────────────────── /code/proj/Main.roc ─ + @r#" + ── CIRCULAR DEFINITION ─────────────────────────────────── /code/proj/Main.roc ─ - The `t1` definition is causing a very tricky infinite loop: + The `t1` definition is causing a very tricky infinite loop: - 6│ t1 = \_ -> force (\_ -> t2) - ^^ + 6│ t1 = \_ -> force (\_ -> t2) + ^^ - The `t1` value depends on itself through the following chain of - definitions: + The `t1` value depends on itself through the following chain of + definitions: - ┌─────┐ - │ t1 - │ ↓ - │ t2 - └─────┘ - "# - ) + ┌─────┐ + │ t1 + │ ↓ + │ t2 + └─────┘ + "# ); test_report!( @@ -10292,20 +10219,18 @@ All branches in an `if` must have the same type! A := {} has [Ab] "# ), - indoc!( - r#" - ── ILLEGAL DERIVE ──────────────────────────────────────── /code/proj/Main.roc ─ + @r#" + ── ILLEGAL DERIVE ──────────────────────────────────────── /code/proj/Main.roc ─ - This ability cannot be derived: + This ability cannot be derived: - 5│ A := {} has [Ab] - ^^ + 5│ A := {} has [Ab] + ^^ - Only builtin abilities can be derived. + Only builtin abilities can be derived. - Note: The builtin abilities are `Encode.Encoding` - "# - ) + Note: The builtin abilities are `Encode.Encoding` + "# ); test_report!( @@ -10317,20 +10242,18 @@ All branches in an `if` must have the same type! A a := a -> a has [Encode.Encoding] "# ), - indoc!( - r#" - ── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─ + @r#" + ── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─ - Roc can't derive an implementation of the `Encode.Encoding` for `A`: + Roc can't derive an implementation of the `Encode.Encoding` for `A`: - 3│ A a := a -> a has [Encode.Encoding] - ^^^^^^^^^^^^^^^ + 3│ A a := a -> a has [Encode.Encoding] + ^^^^^^^^^^^^^^^ - Note: `Encoding` cannot be generated for functions. + Note: `Encoding` cannot be generated for functions. - Tip: You can define a custom implementation of `Encode.Encoding` for `A`. - "# - ) + Tip: You can define a custom implementation of `Encode.Encoding` for `A`. + "# ); test_report!( @@ -10344,21 +10267,19 @@ All branches in an `if` must have the same type! B := {} "# ), - indoc!( - r#" - ── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─ + @r#" + ── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─ - Roc can't derive an implementation of the `Encode.Encoding` for `A`: + Roc can't derive an implementation of the `Encode.Encoding` for `A`: - 3│ A := B has [Encode.Encoding] - ^^^^^^^^^^^^^^^ + 3│ A := B has [Encode.Encoding] + ^^^^^^^^^^^^^^^ - Tip: `B` does not implement `Encoding`. Consider adding a custom - implementation or `has Encode.Encoding` to the definition of `B`. + Tip: `B` does not implement `Encoding`. Consider adding a custom + implementation or `has Encode.Encoding` to the definition of `B`. - Tip: You can define a custom implementation of `Encode.Encoding` for `A`. - "# - ) + Tip: You can define a custom implementation of `Encode.Encoding` for `A`. + "# ); test_report!( @@ -10372,7 +10293,7 @@ All branches in an `if` must have the same type! B := {} has [Encode.Encoding] "# ), - indoc!("") // no error + @"" // no error ); test_report!( @@ -10384,7 +10305,7 @@ All branches in an `if` must have the same type! MyNat := [S MyNat, Z] has [Encode.Encoding] "# ), - indoc!("") // no error + @"" // no error ); test_report!( @@ -10398,24 +10319,22 @@ All branches in an `if` must have the same type! main = \n -> n + 2 "# ), - indoc!( - r#" - ── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─ - - The `main` name is first defined here: - - 3│ main = 1 - ^^^^ - - But then it's defined a second time here: - - 5│ main = \n -> n + 2 - ^^^^ - - Since these variables have the same name, it's easy to use the wrong - one on accident. Give one of them a new name. - "# - ) + @r#" + ── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─ + + The `main` name is first defined here: + + 3│ main = 1 + ^^^^ + + But then it's defined a second time here: + + 5│ main = \n -> n + 2 + ^^^^ + + Since these variables have the same name, it's easy to use the wrong + one on accident. Give one of them a new name. + "# ); test_report!( @@ -10429,29 +10348,27 @@ All branches in an `if` must have the same type! toEncoder = \@A {} -> custom \l, _ -> l "# ), - indoc!( - r#" - ── CONFLICTING DERIVE AND IMPLEMENTATION ───────────────── /code/proj/Main.roc ─ + @r#" + ── CONFLICTING DERIVE AND IMPLEMENTATION ───────────────── /code/proj/Main.roc ─ - `A` both derives and custom-implements `Encode.Encoding`. We found the - derive here: + `A` both derives and custom-implements `Encode.Encoding`. We found the + derive here: - 3│ A := {} has [Encode.Encoding] - ^^^^^^^^^^^^^^^ + 3│ A := {} has [Encode.Encoding] + ^^^^^^^^^^^^^^^ - and one custom implementation of `Encode.Encoding` here: + and one custom implementation of `Encode.Encoding` here: - 5│ toEncoder = \@A {} -> custom \l, _ -> l - ^^^^^^^^^ + 5│ toEncoder = \@A {} -> custom \l, _ -> l + ^^^^^^^^^ - Derived and custom implementations can conflict, so one of them needs - to be removed! + Derived and custom implementations can conflict, so one of them needs + to be removed! - Note: We'll try to compile your program using the custom - implementation first, and fall-back on the derived implementation if - needed. Make sure to disambiguate which one you want! - "# - ) + Note: We'll try to compile your program using the custom + implementation first, and fall-back on the derived implementation if + needed. Make sure to disambiguate which one you want! + "# ); test_report!( @@ -10473,31 +10390,29 @@ All branches in an `if` must have the same type! withOpen "# ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + @r#" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - Something is off with the body of the `withOpen` definition: + Something is off with the body of the `withOpen` definition: - 10│ withOpen : (Handle -> Result {} *) -> Result {} * - 11│ withOpen = \callback -> - 12│> handle <- await (open {}) - 13│> {} <- await (callback handle) - 14│> close handle + 10│ withOpen : (Handle -> Result {} *) -> Result {} * + 11│ withOpen = \callback -> + 12│> handle <- await (open {}) + 13│> {} <- await (callback handle) + 14│> close handle - The type annotation on `withOpen` says this `await` call should have the - type: + The type annotation on `withOpen` says this `await` call should have the + type: - Result {} * + Result {} * - However, the type of this `await` call is connected to another type in a - way that isn't reflected in this annotation. + However, the type of this `await` call is connected to another type in a + way that isn't reflected in this annotation. - Tip: Any connection between types must use a named type variable, not - a `*`! Maybe the annotation on `withOpen` should have a named type - variable in place of the `*`? - "# - ) + Tip: Any connection between types must use a named type variable, not + a `*`! Maybe the annotation on `withOpen` should have a named type + variable in place of the `*`? + "# ); test_report!( @@ -10510,23 +10425,21 @@ All branches in an `if` must have the same type! f "# ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - This expression is used in an unexpected way: + This expression is used in an unexpected way: - 5│ f = \_ -> if True then {} else f {} - ^^^^^^^^^^^^^^^^^^^^^^^^^ + 5│ f = \_ -> if True then {} else f {} + ^^^^^^^^^^^^^^^^^^^^^^^^^ - It is a value of type: + It is a value of type: - {} + {} - But you are trying to use it as: + But you are trying to use it as: - a -> Str - "# - ) + a -> Str + "### ); } From 13a1a8f49d5e30f4f61e5576e64c5ce9e20f5954 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sun, 3 Jul 2022 10:20:52 -0700 Subject: [PATCH 41/66] update the wasm example so that it compiles and the readme is correct --- examples/platform-switching/web-assembly-platform/README.md | 6 +++--- .../platform-switching/web-assembly-platform/host.test.js | 2 +- .../platform-switching/web-assembly-platform/index.html | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/platform-switching/web-assembly-platform/README.md b/examples/platform-switching/web-assembly-platform/README.md index 47b65fbe64..2e77e5a0f0 100644 --- a/examples/platform-switching/web-assembly-platform/README.md +++ b/examples/platform-switching/web-assembly-platform/README.md @@ -3,12 +3,12 @@ To run this website, first compile either of these identical apps: ```bash -# Option A: Compile examples/platform-switching/web-assembly-platform/rocLovesWeb.roc -cargo run -- build --target=wasm32 examples/platform-switching/web-assembly-platform/rocLovesWeb.roc +# Option A: Compile examples/platform-switching/web-assembly-platform/rocLovesWebAssembly.roc +cargo run -- build --target=wasm32 examples/platform-switching/web-assembly-platform/rocLovesWebAssembly.roc # Option B: Compile examples/platform-switching/main.roc with `pf: "web-assembly-platform/main.roc"` and move the result cargo run -- build --target=wasm32 examples/platform-switching/main.roc -(cd examples/platform-switching && mv rocLovesPlatforms.wasm web-assembly-platform/rocLovesWeb.wasm) +(cd examples/platform-switching && mv rocLovesPlatforms.wasm web-assembly-platform/rocLovesWebAssembly.wasm) ``` Then `cd` into the website directory diff --git a/examples/platform-switching/web-assembly-platform/host.test.js b/examples/platform-switching/web-assembly-platform/host.test.js index ac0b1f5dc7..438203b29e 100644 --- a/examples/platform-switching/web-assembly-platform/host.test.js +++ b/examples/platform-switching/web-assembly-platform/host.test.js @@ -15,7 +15,7 @@ global.fetch = (filename) => const { roc_web_platform_run } = require("./host"); -roc_web_platform_run("../helloWeb.wasm", (string_from_roc) => { +roc_web_platform_run("./rocLovesWebAssembly.wasm", (string_from_roc) => { const expected = "Hello, World!\n"; if (string_from_roc !== expected) { console.error(`Expected "${expected}", but got "${string_from_roc}"`); diff --git a/examples/platform-switching/web-assembly-platform/index.html b/examples/platform-switching/web-assembly-platform/index.html index 0397305c5a..a664e3469c 100644 --- a/examples/platform-switching/web-assembly-platform/index.html +++ b/examples/platform-switching/web-assembly-platform/index.html @@ -4,7 +4,7 @@ From b1a17b5a6f710dd0454a48d81b826461d7227113 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sun, 3 Jul 2022 10:21:15 -0700 Subject: [PATCH 42/66] add fd_write so that works --- examples/platform-switching/web-assembly-platform/host.js | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/platform-switching/web-assembly-platform/host.js b/examples/platform-switching/web-assembly-platform/host.js index 77f2bbed78..e8cdfab3c5 100644 --- a/examples/platform-switching/web-assembly-platform/host.js +++ b/examples/platform-switching/web-assembly-platform/host.js @@ -17,6 +17,7 @@ async function roc_web_platform_run(wasm_filename, callback) { } exit_code = code; }, + fd_write: (x) => { console.error(`fd_write not supported: ${x}`); } }, env: { js_display_roc_string, From 91bf84719ff92b94370c104d91d10da2e6d52cec Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sun, 3 Jul 2022 10:28:56 -0700 Subject: [PATCH 43/66] correct host.test.js --- examples/platform-switching/web-assembly-platform/host.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/platform-switching/web-assembly-platform/host.test.js b/examples/platform-switching/web-assembly-platform/host.test.js index 438203b29e..0152e80a8d 100644 --- a/examples/platform-switching/web-assembly-platform/host.test.js +++ b/examples/platform-switching/web-assembly-platform/host.test.js @@ -16,7 +16,7 @@ global.fetch = (filename) => const { roc_web_platform_run } = require("./host"); roc_web_platform_run("./rocLovesWebAssembly.wasm", (string_from_roc) => { - const expected = "Hello, World!\n"; + const expected = "Roc <3 Web Assembly!\n"; if (string_from_roc !== expected) { console.error(`Expected "${expected}", but got "${string_from_roc}"`); process.exit(1); From 7c7e4507564ec43816c4804dffeeec0df12d4d82 Mon Sep 17 00:00:00 2001 From: Brian Carroll Date: Sun, 3 Jul 2022 18:42:35 +0100 Subject: [PATCH 44/66] mono: generate Eq functions for Boxed layout --- crates/compiler/gen_wasm/src/low_level.rs | 5 +-- .../mono/src/code_gen_help/equality.rs | 41 ++++++++++++++++--- crates/compiler/mono/src/code_gen_help/mod.rs | 8 ++-- crates/compiler/test_gen/src/gen_compare.rs | 22 ++++++++++ 4 files changed, 65 insertions(+), 11 deletions(-) diff --git a/crates/compiler/gen_wasm/src/low_level.rs b/crates/compiler/gen_wasm/src/low_level.rs index 6ef9106620..c3e96a23d9 100644 --- a/crates/compiler/gen_wasm/src/low_level.rs +++ b/crates/compiler/gen_wasm/src/low_level.rs @@ -1671,7 +1671,8 @@ impl<'a> LowLevelCall<'a> { Layout::Builtin(Builtin::Dict(_, _) | Builtin::Set(_) | Builtin::List(_)) | Layout::Struct { .. } | Layout::Union(_) - | Layout::LambdaSet(_) => { + | Layout::LambdaSet(_) + | Layout::Boxed(_) => { // Don't want Zig calling convention here, we're calling internal Roc functions backend .storage @@ -1689,8 +1690,6 @@ impl<'a> LowLevelCall<'a> { } } - Layout::Boxed(_) => todo!(), - Layout::RecursivePointer => { internal_error!( "Tried to apply `==` to RecursivePointer values {:?}", diff --git a/crates/compiler/mono/src/code_gen_help/equality.rs b/crates/compiler/mono/src/code_gen_help/equality.rs index 374daeb6d2..887ecfb947 100644 --- a/crates/compiler/mono/src/code_gen_help/equality.rs +++ b/crates/compiler/mono/src/code_gen_help/equality.rs @@ -530,12 +530,43 @@ fn eq_tag_fields<'a>( } fn eq_boxed<'a>( - _root: &mut CodeGenHelp<'a>, - _ident_ids: &mut IdentIds, - _ctx: &mut Context<'a>, - _inner_layout: &'a Layout<'a>, + root: &mut CodeGenHelp<'a>, + ident_ids: &mut IdentIds, + ctx: &mut Context<'a>, + inner_layout: &'a Layout<'a>, ) -> Stmt<'a> { - todo!() + let a = root.create_symbol(ident_ids, "a"); + let b = root.create_symbol(ident_ids, "b"); + let result = root.create_symbol(ident_ids, "result"); + + let a_expr = Expr::ExprUnbox { symbol: ARG_1 }; + let b_expr = Expr::ExprUnbox { symbol: ARG_2 }; + let eq_call_expr = root + .call_specialized_op(ident_ids, ctx, *inner_layout, root.arena.alloc([a, b])) + .unwrap(); + + Stmt::Let( + a, + a_expr, + *inner_layout, + root.arena.alloc( + // + Stmt::Let( + b, + b_expr, + *inner_layout, + root.arena.alloc( + // + Stmt::Let( + result, + eq_call_expr, + LAYOUT_BOOL, + root.arena.alloc(Stmt::Ret(result)), + ), + ), + ), + ), + ) } /// List equality diff --git a/crates/compiler/mono/src/code_gen_help/mod.rs b/crates/compiler/mono/src/code_gen_help/mod.rs index dadaafd9f1..3d675a2416 100644 --- a/crates/compiler/mono/src/code_gen_help/mod.rs +++ b/crates/compiler/mono/src/code_gen_help/mod.rs @@ -286,11 +286,11 @@ impl<'a> CodeGenHelp<'a> { &mut self, ident_ids: &mut IdentIds, ctx: &mut Context<'a>, - layout: Layout<'a>, + orig_layout: Layout<'a>, ) -> Symbol { use HelperOp::*; - let layout = self.replace_rec_ptr(ctx, layout); + let layout = self.replace_rec_ptr(ctx, orig_layout); let found = self .specializations @@ -450,7 +450,9 @@ impl<'a> CodeGenHelp<'a> { layout } - Layout::Boxed(inner) => self.replace_rec_ptr(ctx, *inner), + Layout::Boxed(inner) => { + Layout::Boxed(self.arena.alloc(self.replace_rec_ptr(ctx, *inner))) + } Layout::LambdaSet(lambda_set) => { self.replace_rec_ptr(ctx, lambda_set.runtime_representation()) diff --git a/crates/compiler/test_gen/src/gen_compare.rs b/crates/compiler/test_gen/src/gen_compare.rs index efc062eb5c..495ddee3c0 100644 --- a/crates/compiler/test_gen/src/gen_compare.rs +++ b/crates/compiler/test_gen/src/gen_compare.rs @@ -672,3 +672,25 @@ fn compare_nullable_recursive_union_same_content() { bool ); } + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +fn boxed_eq_int() { + assert_evals_to!("Box.box 1 == Box.box 1", true, bool); + assert_evals_to!("Box.box 2 == Box.box 1", false, bool); +} + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +fn boxed_eq_str() { + assert_evals_to!( + "Box.box \"Hello, world\" == Box.box \"Hello, world\"", + true, + bool + ); + assert_evals_to!( + "Box.box \"Hello, world\" == Box.box \"Hello, stranger\"", + false, + bool + ); +} From b490a1fdf8426cc0b9fd6e66975d4a686a670892 Mon Sep 17 00:00:00 2001 From: ayazhafiz Date: Sun, 3 Jul 2022 14:19:57 -0400 Subject: [PATCH 45/66] Replace other test_reporting tests with snapshots --- crates/reporting/src/report.rs | 2 +- crates/reporting/tests/test_reporting.rs | 14235 ++++++++++----------- 2 files changed, 6582 insertions(+), 7655 deletions(-) diff --git a/crates/reporting/src/report.rs b/crates/reporting/src/report.rs index 010c900f29..201285c528 100644 --- a/crates/reporting/src/report.rs +++ b/crates/reporting/src/report.rs @@ -650,7 +650,7 @@ impl<'a> RocDocAllocator<'a> { let line_number = line_number_string; let this_line_number_length = line_number.len(); - let line = self.src_lines[i as usize]; + let line: &str = self.src_lines.get(i as usize).unwrap_or(&""); let rest_of_line = if !line.trim().is_empty() { self.text(line) diff --git a/crates/reporting/tests/test_reporting.rs b/crates/reporting/tests/test_reporting.rs index edba4c7c93..af5c751023 100644 --- a/crates/reporting/tests/test_reporting.rs +++ b/crates/reporting/tests/test_reporting.rs @@ -22,7 +22,6 @@ mod test_reporting { }; use roc_reporting::report::{RocDocAllocator, RocDocBuilder}; use roc_solve::solve; - use roc_test_utils::assert_multiline_str_eq; use roc_types::subs::Subs; use std::path::PathBuf; @@ -333,28 +332,6 @@ mod test_reporting { } } - fn report_problem_as(src: &str, expected_rendering: &str) { - let mut buf: String = String::new(); - let arena = Bump::new(); - - let callback = |doc: RocDocBuilder<'_>, buf: &mut String| { - doc.1 - .render_raw(70, &mut roc_reporting::report::CiWrite::new(buf)) - .expect("list_reports") - }; - - list_reports(&arena, src, &mut buf, callback); - - // convenient to copy-paste the generated message - if buf != expected_rendering { - for line in buf.split('\n') { - println!(" {}", line); - } - } - - assert_multiline_str_eq!(expected_rendering, buf.as_str()); - } - fn report_header_problem_as(src: &str, expected_rendering: &str) { let mut buf: String = String::new(); let arena = Bump::new(); @@ -416,14 +393,16 @@ mod test_reporting { } macro_rules! test_report { - ($test_name:ident, $program:expr, @$output:literal) => { - #[test] - fn $test_name() { - __new_report_problem_as(std::stringify!($test_name), $program, |golden| { - insta::assert_snapshot!(golden, @$output) - }) - } + ($(#[$meta:meta])* $test_name:ident, $program:expr, @$output:literal) => { + test_report!($(#[$meta])* $test_name, $program, |golden| insta::assert_snapshot!(golden, @$output) ); }; + ($(#[$meta:meta])* $test_name: ident, $program:expr, $expecting:expr) => { + #[test] + $(#[$meta])* + fn $test_name() { + __new_report_problem_as(std::stringify!($test_name), $program, $expecting) + } + } } fn human_readable(str: &str) -> String { @@ -439,364 +418,265 @@ mod test_reporting { .replace(ANSI_STYLE_CODES.underline, "") } - #[test] - fn value_not_exposed() { - report_problem_as( - indoc!( - r#" - List.isempty 1 2 - "# - ), - indoc!( - r#" - ── NOT EXPOSED ─────────────────────────────────────────── /code/proj/Main.roc ─ + test_report!( + value_not_exposed, + indoc!( + r#" + List.isempty 1 2 + "# + ), + @r###" + ── NOT EXPOSED ─────────────────────────────────────────── /code/proj/Main.roc ─ - The List module does not expose `isempty`: + The List module does not expose `isempty`: - 1│ List.isempty 1 2 - ^^^^^^^^^^^^ + 4│ List.isempty 1 2 + ^^^^^^^^^^^^ - Did you mean one of these? + Did you mean one of these? - List.isEmpty - List.set - List.iterate - List.get - "# - ), - ) - } + List.isEmpty + List.set + List.iterate + List.get + "### + ); - #[test] - fn report_unused_def() { - report_problem_as( - indoc!( - r#" - x = 1 - y = 2 + test_report!( + report_unused_def, + indoc!( + r#" + x = 1 + y = 2 - x - "# - ), - indoc!( - r#" - ── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─ + x + "# + ), + @r###" + ── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─ - `y` is not used anywhere in your code. + `y` is not used anywhere in your code. - 2│ y = 2 - ^ + 5│ y = 2 + ^ - If you didn't intend on using `y` then remove it so future readers of - your code don't wonder why it is there. - "# - ), - ) - } + If you didn't intend on using `y` then remove it so future readers of + your code don't wonder why it is there. + "### + ); - #[test] - fn report_shadowing() { - report_problem_as( - indoc!( - r#" - i = 1 + test_report!( + report_shadowing, + indoc!( + r#" + i = 1 - s = \i -> - i + 1 + s = \i -> + i + 1 - s i - "# - ), - indoc!( - r#" - ── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─ + s i + "# + ), + @r###" + ── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─ - The `i` name is first defined here: + The `i` name is first defined here: - 1│ i = 1 - ^ + 4│ i = 1 + ^ - But then it's defined a second time here: + But then it's defined a second time here: - 3│ s = \i -> - ^ + 6│ s = \i -> + ^ - Since these variables have the same name, it's easy to use the wrong - one on accident. Give one of them a new name. - "# - ), - ) - } + Since these variables have the same name, it's easy to use the wrong + one on accident. Give one of them a new name. + "### + ); - #[test] - fn report_shadowing_in_annotation() { - report_problem_as( - indoc!( - r#" - Booly : [Yes, No] + test_report!( + report_shadowing_in_annotation, + indoc!( + r#" + Booly : [Yes, No] - Booly : [Yes, No, Maybe] + Booly : [Yes, No, Maybe] - x : List Booly - x = [] + x : List Booly + x = [] - x - "# - ), - indoc!( - r#" - ── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─ + x + "# + ), + @r###" + ── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─ - The `Booly` name is first defined here: + The `Booly` name is first defined here: - 1│ Booly : [Yes, No] - ^^^^^^^^^^^^^^^^^ + 4│ Booly : [Yes, No] + ^^^^^^^^^^^^^^^^^ - But then it's defined a second time here: + But then it's defined a second time here: - 3│ Booly : [Yes, No, Maybe] - ^^^^^^^^^^^^^^^^^^^^^^^^ + 6│ Booly : [Yes, No, Maybe] + ^^^^^^^^^^^^^^^^^^^^^^^^ - Since these aliases have the same name, it's easy to use the wrong one - on accident. Give one of them a new name. - "# - ), - ) - } + Since these aliases have the same name, it's easy to use the wrong one + on accident. Give one of them a new name. + "### + ); - // #[test] - // fn report_multi_line_shadowing_in_annotation() { - // report_problem_as( - // indoc!( - // r#" - // Booly : - // [ - // Yes, - // No - // ] - // - // Booly : - // [ - // Yes, - // No, - // Maybe - // ] - // - // x = - // No - // - // x - // "# - // ), - // indoc!( - // r#" - // Booly is first defined here: - // - // 1│> Booly : - // 2│> [ - // 3│> Yes, - // 4│> No - // 5│> ] - // - // But then it's defined a second time here: - // - // 7 │> Booly : - // 8 │> [ - // 9 │> Yes, - // 10│> No, - // 11│> Maybe - // 12│> ] - // - // Since these variables have the same name, it's easy to use the wrong one on accident. Give one of them a new name."# - // ), - // ) - // } - - // #[test] - // fn report_unsupported_top_level_def() { - // report_problem_as( - // indoc!( - // r#" - // x = 1 - // - // 5 = 2 + 1 - // - // x - // "# - // ), - // indoc!(r#" "#), - // ) - // } - - #[test] - fn report_precedence_problem_single_line() { - report_problem_as( - indoc!( - r#"x = 1 - y = - if selectedId != thisId == adminsId then - 4 - - else - 5 - - { x, y } - "# - ), - indoc!( - r#" - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - - Using != and == together requires parentheses, to clarify how they - should be grouped. - - 3│ if selectedId != thisId == adminsId then - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - "# - ), - ) - } - - #[test] - fn unrecognized_name() { - report_problem_as( - indoc!( - r#" - foo = { x: 1 == 1, y: 0x4 } - - baz = 3 - - main : Str - main = - when foo.y is - 4 -> bar baz "yay" - _ -> "nay" - - main - "# - ), - indoc!( - r#" - ── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─ - - Nothing is named `bar` in this scope. - - 8│ 4 -> bar baz "yay" - ^^^ - - Did you mean one of these? - - baz - Str - Err - main - "# - ), - ) - } - - #[test] - fn lowercase_primitive_tag_bool() { - report_problem_as( - indoc!( - r#" - if true then 1 else 2 - "# - ), - indoc!( - r#" - ── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─ - - Nothing is named `true` in this scope. - - 1│ if true then 1 else 2 - ^^^^ - - Did you mean one of these? - - True - Str - Err - List - "# - ), - ) - } - - #[test] - fn report_precedence_problem_multiline() { - report_problem_as( - indoc!( - r#" - if - 1 - == 2 - == 3 - then - 2 + test_report!( + report_precedence_problem_single_line, + indoc!( + r#"x = 1 + y = + if selectedId != thisId == adminsId then + 4 else - 3 - "# - ), - indoc!( - r#" - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + 5 - Using more than one == like this requires parentheses, to clarify how - things should be grouped. + { x, y } + "# + ), + @r###" + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - 2│> 1 - 3│> == 2 - 4│> == 3 - "# - ), - ) - } + Using != and == together requires parentheses, to clarify how they + should be grouped. - #[test] - fn unused_arg_and_unused_def() { - report_problem_as( - indoc!( - r#" - y = 9 + 6│ if selectedId != thisId == adminsId then + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + "### + ); - box = \class, htmlChildren -> - div [class] [] + test_report!( + #[ignore = "Blocked on https://github.com/rtfeldman/roc/issues/3385"] + unrecognized_name, + indoc!( + r#" + foo = { x: 1 == 1, y: 0x4 } - div = \_, _ -> 4 + baz = 3 - box "wizard" [] - "# - ), - indoc!( - r#" - ── UNUSED ARGUMENT ─────────────────────────────────────── /code/proj/Main.roc ─ + main : Str + main = + when foo.y is + 4 -> bar baz "yay" + _ -> "nay" - `box` doesn't use `htmlChildren`. + main + "# + ), + @r#" + ── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─ - 3│ box = \class, htmlChildren -> - ^^^^^^^^^^^^ + Nothing is named `bar` in this scope. - If you don't need `htmlChildren`, then you can just remove it. However, - if you really do need `htmlChildren` as an argument of `box`, prefix it - with an underscore, like this: "_`htmlChildren`". Adding an underscore - at the start of a variable name is a way of saying that the variable - is not used. + 8│ 4 -> bar baz "yay" + ^^^ - ── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─ + Did you mean one of these? - `y` is not used anywhere in your code. + baz + Str + Err + main + "# + ); - 1│ y = 9 - ^ + test_report!( + lowercase_primitive_tag_bool, + indoc!( + r#" + if true then 1 else 2 + "# + ), + @r###" + ── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─ - If you didn't intend on using `y` then remove it so future readers of - your code don't wonder why it is there. - "# - ), - ); - } + Nothing is named `true` in this scope. + + 4│ if true then 1 else 2 + ^^^^ + + Did you mean one of these? + + True + Str + Frac + Num + "### + ); + + test_report!( + report_precedence_problem_multiline, + indoc!( + r#" + if + 1 + == 2 + == 3 + then + 2 + + else + 3 + "# + ), + @r###" + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + + Using more than one == like this requires parentheses, to clarify how + things should be grouped. + + 5│> 1 + 6│> == 2 + 7│> == 3 + "### + ); + + test_report!( + unused_arg_and_unused_def, + indoc!( + r#" + y = 9 + + box = \class, htmlChildren -> + div [class] [] + + div = \_, _ -> 4 + + box "wizard" [] + "# + ), + @r###" + ── UNUSED ARGUMENT ─────────────────────────────────────── /code/proj/Main.roc ─ + + `box` doesn't use `htmlChildren`. + + 6│ box = \class, htmlChildren -> + ^^^^^^^^^^^^ + + If you don't need `htmlChildren`, then you can just remove it. However, + if you really do need `htmlChildren` as an argument of `box`, prefix it + with an underscore, like this: "_`htmlChildren`". Adding an underscore + at the start of a variable name is a way of saying that the variable + is not used. + + ── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─ + + `y` is not used anywhere in your code. + + 4│ y = 9 + ^ + + If you didn't intend on using `y` then remove it so future readers of + your code don't wonder why it is there. + "### + ); #[test] fn report_value_color() { @@ -888,488 +768,390 @@ mod test_reporting { ); } - // #[test] - // fn shadowing_type_alias() { - // report_problem_as( - // indoc!( - // r#" - // foo : I64 as I64 - // foo = 42 + test_report!( + if_condition_not_bool, + indoc!( + r#" + if "foo" then 2 else 3 + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - // foo - // "# - // ), - // indoc!( - // r#" - // You cannot mix (!=) and (==) without parentheses + This `if` condition needs to be a Bool: - // 3│ if selectedId != thisId == adminsId then - // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 4│ if "foo" then 2 else 3 + ^^^^^ - // "# - // ), - // ) - // } + Right now it’s a string of type: - // #[test] - // fn invalid_as_type_alias() { - // report_problem_as( - // indoc!( - // r#" - // foo : I64 as a - // foo = 42 + Str - // foo - // "# - // ), - // indoc!( - // r#" - // You cannot mix (!=) and (==) without parentheses + But I need every `if` condition to evaluate to a Bool—either `True` or + `False`. + "### + ); - // 3│ if selectedId != thisId == adminsId then - // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + test_report!( + when_if_guard, + indoc!( + r#" + when 1 is + 2 if 1 -> 0x0 + _ -> 0x1 + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - // "# - // ), - // ) - // } + This `if` guard condition needs to be a Bool: - #[test] - fn if_condition_not_bool() { - report_problem_as( - indoc!( - r#" - if "foo" then 2 else 3 - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + 4│ when 1 is + 5│> 2 if 1 -> 0x0 + 6│ _ -> 0x1 - This `if` condition needs to be a Bool: + Right now it’s a number of type: - 1│ if "foo" then 2 else 3 + Num a + + But I need every `if` guard condition to evaluate to a Bool—either + `True` or `False`. + "### + ); + + test_report!( + if_2_branch_mismatch, + indoc!( + r#" + if True then 2 else "foo" + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + This `if` has an `else` branch with a different type from its `then` branch: + + 4│ if True then 2 else "foo" + ^^^^^ + + The `else` branch is a string of type: + + Str + + but the `then` branch has the type: + + Num a + + All branches in an `if` must have the same type! + "### + ); + + test_report!( + if_3_branch_mismatch, + indoc!( + r#" + if True then 2 else if False then 2 else "foo" + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + The 3rd branch of this `if` does not match all the previous branches: + + 4│ if True then 2 else if False then 2 else "foo" + ^^^^^ + + The 3rd branch is a string of type: + + Str + + But all the previous branches have type: + + Num a + + All branches in an `if` must have the same type! + "### + ); + + test_report!( + when_branch_mismatch, + indoc!( + r#" + when 1 is + 2 -> "foo" + 3 -> {} + _ -> "" + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + The 2nd branch of this `when` does not match all the previous branches: + + 4│ when 1 is + 5│ 2 -> "foo" + 6│> 3 -> {} + 7│ _ -> "" + + The 2nd branch is a record of type: + + {} + + But all the previous branches have type: + + Str + + All branches of a `when` must have the same type! + "### + ); + + test_report!( + elem_in_list, + indoc!( + r#" + [1, 3, "foo"] + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + This list contains elements with different types: + + 4│ [1, 3, "foo"] + ^^^^^ + + Its 3rd element is a string of type: + + Str + + However, the preceding elements in the list all have the type: + + Num a + + Every element in a list must have the same type! + "### + ); + + test_report!( + unwrap_num_elem_in_list, + indoc!( + r#" + [1, 2.2, 0x3] + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + This list contains elements with different types: + + 4│ [1, 2.2, 0x3] + ^^^ + + Its 3rd element is an integer of type: + + Int a + + However, the preceding elements in the list all have the type: + + Frac a + + Every element in a list must have the same type! + + Tip: You can convert between Int and Frac using functions like + `Num.toFrac` and `Num.round`. + "### + ); + + test_report!( + record_update_value, + indoc!( + r#" + x : { foo : {} } + x = { foo: {} } + + { x & foo: "bar" } + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + I cannot update the `.foo` field like this: + + 7│ { x & foo: "bar" } ^^^^^ - Right now it’s a string of type: - - Str - - But I need every `if` condition to evaluate to a Bool—either `True` or - `False`. - "# - ), - ) - } - - #[test] - fn when_if_guard() { - report_problem_as( - indoc!( - r#" - when 1 is - 2 if 1 -> 0x0 - _ -> 0x1 - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - This `if` guard condition needs to be a Bool: - - 1│ when 1 is - 2│> 2 if 1 -> 0x0 - 3│ _ -> 0x1 - - Right now it’s a number of type: - - Num a - - But I need every `if` guard condition to evaluate to a Bool—either - `True` or `False`. - "# - ), - ) - } - - #[test] - fn if_2_branch_mismatch() { - report_problem_as( - indoc!( - r#" - if True then 2 else "foo" - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - This `if` has an `else` branch with a different type from its `then` branch: - - 1│ if True then 2 else "foo" - ^^^^^ - - The `else` branch is a string of type: - - Str - - but the `then` branch has the type: - - Num a - - All branches in an `if` must have the same type! - "# - ), - ) - } - - #[test] - fn if_3_branch_mismatch() { - report_problem_as( - indoc!( - r#" - if True then 2 else if False then 2 else "foo" - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - The 3rd branch of this `if` does not match all the previous branches: - - 1│ if True then 2 else if False then 2 else "foo" - ^^^^^ - - The 3rd branch is a string of type: - - Str - - But all the previous branches have type: - - Num a + You are trying to update `.foo` to be a string of type: - All branches in an `if` must have the same type! - "# - ), - ) - } - - #[test] - fn when_branch_mismatch() { - report_problem_as( - indoc!( - r#" - when 1 is - 2 -> "foo" - 3 -> {} - _ -> "" - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - The 2nd branch of this `when` does not match all the previous branches: - - 1│ when 1 is - 2│ 2 -> "foo" - 3│> 3 -> {} - 4│ _ -> "" + Str - The 2nd branch is a record of type: + But it should be: - {} + {} - But all the previous branches have type: + Record update syntax does not allow you to change the type of fields. + You can achieve that with record literal syntax. + "### + ); - Str + test_report!( + circular_type, + indoc!( + r#" + f = \g -> g g - All branches of a `when` must have the same type! - "# - ), - ) - } + f + "# + ), + @r###" + ── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn elem_in_list() { - report_problem_as( - indoc!( - r#" - [1, 3, "foo"] - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + I'm inferring a weird self-referential type for `g`: - This list contains elements with different types: + 4│ f = \g -> g g + ^ - 1│ [1, 3, "foo"] - ^^^^^ + Here is my best effort at writing down the type. You will see ∞ for + parts of the type that repeat something already printed out + infinitely. - Its 3rd element is a string of type: + ∞ -> a + "### + ); - Str + test_report!( + polymorphic_recursion, + indoc!( + r#" + f = \x -> f [x] - However, the preceding elements in the list all have the type: + f + "# + ), + @r###" + ── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─ - Num a + I'm inferring a weird self-referential type for `f`: - Every element in a list must have the same type! - "# - ), - ) - } - - #[test] - fn unwrap_num_elem_in_list() { - report_problem_as( - indoc!( - r#" - [1, 2.2, 0x3] - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + 4│ f = \x -> f [x] + ^ - This list contains elements with different types: + Here is my best effort at writing down the type. You will see ∞ for + parts of the type that repeat something already printed out + infinitely. - 1│ [1, 2.2, 0x3] - ^^^ + List ∞ -> a + "### + ); - Its 3rd element is an integer of type: + test_report!( + polymorphic_mutual_recursion, + indoc!( + r#" + f = \x -> g x + g = \x -> f [x] - Int a + f + "# + ), + @r###" + ── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─ - However, the preceding elements in the list all have the type: - - Frac a - - Every element in a list must have the same type! - - Tip: You can convert between Int and Frac using functions like - `Num.toFrac` and `Num.round`. - "# - ), - ) - } - - #[test] - fn record_update_value() { - report_problem_as( - indoc!( - r#" - x : { foo : {} } - x = { foo: {} } - - { x & foo: "bar" } - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - I cannot update the `.foo` field like this: - - 4│ { x & foo: "bar" } - ^^^^^ + I'm inferring a weird self-referential type for `f`: - You are trying to update `.foo` to be a string of type: + 4│ f = \x -> g x + ^ - Str + Here is my best effort at writing down the type. You will see ∞ for + parts of the type that repeat something already printed out + infinitely. - But it should be: + List ∞ -> a - {} - - Record update syntax does not allow you to change the type of fields. - You can achieve that with record literal syntax. - "# - ), - ) - } - - #[test] - fn circular_type() { - report_problem_as( - indoc!( - r#" - f = \g -> g g - - f - "# - ), - indoc!( - r#" - ── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─ - - I'm inferring a weird self-referential type for `g`: - - 1│ f = \g -> g g - ^ - - Here is my best effort at writing down the type. You will see ∞ for - parts of the type that repeat something already printed out - infinitely. - - ∞ -> a - "# - ), - ) - } - - #[test] - fn polymorphic_recursion() { - report_problem_as( - indoc!( - r#" - f = \x -> f [x] - - f - "# - ), - indoc!( - r#" - ── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─ + ── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─ - I'm inferring a weird self-referential type for `f`: - - 1│ f = \x -> f [x] - ^ - - Here is my best effort at writing down the type. You will see ∞ for - parts of the type that repeat something already printed out - infinitely. - - List ∞ -> a - "# - ), - ) - } - - #[test] - fn polymorphic_mutual_recursion() { - report_problem_as( - indoc!( - r#" - f = \x -> g x - g = \x -> f [x] - - f - "# - ), - indoc!( - r#" - ── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─ - - I'm inferring a weird self-referential type for `f`: - - 1│ f = \x -> g x - ^ - - Here is my best effort at writing down the type. You will see ∞ for - parts of the type that repeat something already printed out - infinitely. - - List ∞ -> a - - ── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─ - - I'm inferring a weird self-referential type for `g`: - - 2│ g = \x -> f [x] - ^ - - Here is my best effort at writing down the type. You will see ∞ for - parts of the type that repeat something already printed out - infinitely. - - List ∞ -> a - "# - ), - ) - } - - #[test] - fn polymorphic_mutual_recursion_annotated() { - report_problem_as( - indoc!( - r#" - f : a -> List a - f = \x -> g x - g = \x -> f [x] - - f - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - This expression is used in an unexpected way: - - 2│ f = \x -> g x - ^^^ - - This `g` call produces: - - List List a - - But you are trying to use it as: - - List a - - Tip: The type annotation uses the type variable `a` to say that this - definition can produce any type of value. But in the body I see that - it will only produce a `List` value of a single specific type. Maybe - change the type annotation to be more specific? Maybe change the code - to be more general? - "# - ), - ) - } - - #[test] - fn polymorphic_mutual_recursion_dually_annotated_lie() { - report_problem_as( - indoc!( - r#" - f : a -> List a - f = \x -> g x - g : b -> List b - g = \x -> f [x] - - f - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - This expression is used in an unexpected way: - - 4│ g = \x -> f [x] - ^^^^^ - - This `f` call produces: - - List List b + I'm inferring a weird self-referential type for `g`: - But you are trying to use it as: - - List b + 5│ g = \x -> f [x] + ^ - Tip: The type annotation uses the type variable `b` to say that this - definition can produce any type of value. But in the body I see that - it will only produce a `List` value of a single specific type. Maybe - change the type annotation to be more specific? Maybe change the code - to be more general? - "# - ), - ) - } + Here is my best effort at writing down the type. You will see ∞ for + parts of the type that repeat something already printed out + infinitely. + + List ∞ -> a + "### + ); + + test_report!( + polymorphic_mutual_recursion_annotated, + indoc!( + r#" + f : a -> List a + f = \x -> g x + g = \x -> f [x] + + f + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + This expression is used in an unexpected way: + + 5│ f = \x -> g x + ^^^ + + This `g` call produces: + + List List a + + But you are trying to use it as: + + List a + + Tip: The type annotation uses the type variable `a` to say that this + definition can produce any type of value. But in the body I see that + it will only produce a `List` value of a single specific type. Maybe + change the type annotation to be more specific? Maybe change the code + to be more general? + "### + ); + + test_report!( + polymorphic_mutual_recursion_dually_annotated_lie, + indoc!( + r#" + f : a -> List a + f = \x -> g x + g : b -> List b + g = \x -> f [x] + + f + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + This expression is used in an unexpected way: + + 7│ g = \x -> f [x] + ^^^^^ + + This `f` call produces: + + List List b + + But you are trying to use it as: + + List b + + Tip: The type annotation uses the type variable `b` to say that this + definition can produce any type of value. But in the body I see that + it will only produce a `List` value of a single specific type. Maybe + change the type annotation to be more specific? Maybe change the code + to be more general? + "### + ); test_report!( polymorphic_recursion_inference_var, @@ -1514,4503 +1296,3950 @@ mod test_reporting { "# ); - #[test] - fn record_field_mismatch() { - report_problem_as( - indoc!( - r#" - bar = { bar : 0x3 } + test_report!( + record_field_mismatch, + indoc!( + r#" + bar = { bar : 0x3 } - f : { foo : Num.Int * } -> [Yes, No] - f = \_ -> Yes + f : { foo : Num.Int * } -> [Yes, No] + f = \_ -> Yes - f bar - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + f bar + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - The 1st argument to `f` is not what I expect: + The 1st argument to `f` is not what I expect: - 6│ f bar - ^^^ + 9│ f bar + ^^^ - This `bar` value is a: + This `bar` value is a: - { bar : Int a } + { bar : Int a } - But `f` needs the 1st argument to be: + But `f` needs the 1st argument to be: - { foo : Int * } + { foo : Int * } - Tip: Seems like a record field typo. Maybe `bar` should be `foo`? + Tip: Seems like a record field typo. Maybe `bar` should be `foo`? - Tip: Can more type annotations be added? Type annotations always help - me give more specific messages, and I think they could help a lot in - this case - "# - ), - ) - } + Tip: Can more type annotations be added? Type annotations always help + me give more specific messages, and I think they could help a lot in + this case + "### + ); - #[test] - fn tag_mismatch() { - report_problem_as( - indoc!( - r#" - f : [Red, Green] -> [Yes, No] - f = \_ -> Yes + test_report!( + tag_mismatch, + indoc!( + r#" + f : [Red, Green] -> [Yes, No] + f = \_ -> Yes - f Blue - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + f Blue + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - The 1st argument to `f` is not what I expect: + The 1st argument to `f` is not what I expect: - 4│ f Blue + 7│ f Blue + ^^^^ + + This `Blue` tag has the type: + + [Blue]a + + But `f` needs the 1st argument to be: + + [Green, Red] + + Tip: Seems like a tag typo. Maybe `Blue` should be `Red`? + + Tip: Can more type annotations be added? Type annotations always help + me give more specific messages, and I think they could help a lot in + this case + "### + ); + + test_report!( + tag_with_arguments_mismatch, + indoc!( + r#" + f : [Red (Num.Int *), Green Str] -> Str + f = \_ -> "yes" + + f (Blue 3.14) + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + The 1st argument to `f` is not what I expect: + + 7│ f (Blue 3.14) + ^^^^^^^^^ + + This `Blue` tag application has the type: + + [Blue (Frac a)]b + + But `f` needs the 1st argument to be: + + [Green Str, Red (Int *)] + + Tip: Seems like a tag typo. Maybe `Blue` should be `Red`? + + Tip: Can more type annotations be added? Type annotations always help + me give more specific messages, and I think they could help a lot in + this case + "### + ); + + test_report!( + from_annotation_if, + indoc!( + r#" + x : Num.Int * + x = if True then 3.14 else 4 + + x + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + Something is off with the `then` branch of this `if` expression: + + 4│ x : Num.Int * + 5│ x = if True then 3.14 else 4 + ^^^^ + + The 1st branch is a frac of type: + + Frac a + + But the type annotation on `x` says it should be: + + Int * + + Tip: You can convert between Int and Frac using functions like + `Num.toFrac` and `Num.round`. + "### + ); + + test_report!( + from_annotation_when, + indoc!( + r#" + x : Num.Int * + x = + when True is + _ -> 3.14 + + x + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + Something is off with the body of the `x` definition: + + 4│ x : Num.Int * + 5│ x = + 6│> when True is + 7│> _ -> 3.14 + + This `when` expression produces: + + Frac a + + But the type annotation on `x` says it should be: + + Int * + + Tip: You can convert between Int and Frac using functions like + `Num.toFrac` and `Num.round`. + "### + ); + + test_report!( + from_annotation_function, + indoc!( + r#" + x : Num.Int * -> Num.Int * + x = \_ -> 3.14 + + x + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + Something is off with the body of the `x` definition: + + 4│ x : Num.Int * -> Num.Int * + 5│ x = \_ -> 3.14 ^^^^ - This `Blue` tag has the type: + The body is a frac of type: - [Blue]a + Frac a - But `f` needs the 1st argument to be: + But the type annotation on `x` says it should be: - [Green, Red] - - Tip: Seems like a tag typo. Maybe `Blue` should be `Red`? + Int * - Tip: Can more type annotations be added? Type annotations always help - me give more specific messages, and I think they could help a lot in - this case - "# - ), - ) - } + Tip: You can convert between Int and Frac using functions like + `Num.toFrac` and `Num.round`. + "### + ); - #[test] - fn tag_with_arguments_mismatch() { - report_problem_as( - indoc!( - r#" - f : [Red (Num.Int *), Green Str] -> Str - f = \_ -> "yes" + test_report!( + fncall_value, + indoc!( + r#" + x : Num.I64 + x = 42 + + x 3 + "# + ), + @r###" + ── TOO MANY ARGS ───────────────────────────────────────── /code/proj/Main.roc ─ - f (Blue 3.14) - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + The `x` value is not a function, but it was given 1 argument: - The 1st argument to `f` is not what I expect: + 7│ x 3 + ^ - 4│ f (Blue 3.14) - ^^^^^^^^^ + Are there any missing commas? Or missing parentheses? + "### + ); - This `Blue` tag application has the type: + test_report!( + fncall_overapplied, + indoc!( + r#" + f : Num.I64 -> Num.I64 + f = \_ -> 42 - [Blue (Frac a)]b + f 1 2 + "# + ), + @r###" + ── TOO MANY ARGS ───────────────────────────────────────── /code/proj/Main.roc ─ - But `f` needs the 1st argument to be: - - [Green Str, Red (Int *)] + The `f` function expects 1 argument, but it got 2 instead: - Tip: Seems like a tag typo. Maybe `Blue` should be `Red`? + 7│ f 1 2 + ^ - Tip: Can more type annotations be added? Type annotations always help - me give more specific messages, and I think they could help a lot in - this case - "# - ), - ) - } + Are there any missing commas? Or missing parentheses? + "### + ); - #[test] - fn from_annotation_if() { - report_problem_as( - indoc!( - r#" - x : Num.Int * - x = if True then 3.14 else 4 + test_report!( + fncall_underapplied, + indoc!( + r#" + f : Num.I64, Num.I64 -> Num.I64 + f = \_, _ -> 42 - x - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + f 1 + "# + ), + @r###" + ── TOO FEW ARGS ────────────────────────────────────────── /code/proj/Main.roc ─ - Something is off with the `then` branch of this `if` expression: + The `f` function expects 2 arguments, but it got only 1: - 1│ x : Num.Int * - 2│ x = if True then 3.14 else 4 - ^^^^ + 7│ f 1 + ^ - The 1st branch is a frac of type: + Roc does not allow functions to be partially applied. Use a closure to + make partial application explicit. + "### + ); - Frac a - - But the type annotation on `x` says it should be: + test_report!( + pattern_when_condition, + indoc!( + r#" + when 1 is + {} -> 42 + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - Int * + The branches of this `when` expression don't match the condition: - Tip: You can convert between Int and Frac using functions like - `Num.toFrac` and `Num.round`. - "# - ), - ) - } - - #[test] - fn from_annotation_when() { - report_problem_as( - indoc!( - r#" - x : Num.Int * - x = - when True is - _ -> 3.14 - - x - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - Something is off with the body of the `x` definition: - - 1│ x : Num.Int * - 2│ x = - 3│> when True is - 4│> _ -> 3.14 - - This `when` expression produces: - - Frac a - - But the type annotation on `x` says it should be: - - Int * - - Tip: You can convert between Int and Frac using functions like - `Num.toFrac` and `Num.round`. - "# - ), - ) - } - - #[test] - fn from_annotation_function() { - report_problem_as( - indoc!( - r#" - x : Num.Int * -> Num.Int * - x = \_ -> 3.14 - - x - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - Something is off with the body of the `x` definition: - - 1│ x : Num.Int * -> Num.Int * - 2│ x = \_ -> 3.14 - ^^^^ - - The body is a frac of type: - - Frac a - - But the type annotation on `x` says it should be: - - Int * - - Tip: You can convert between Int and Frac using functions like - `Num.toFrac` and `Num.round`. - "# - ), - ) - } - - #[test] - fn fncall_value() { - report_problem_as( - indoc!( - r#" - x : Num.I64 - x = 42 - - x 3 - "# - ), - indoc!( - r#" - ── TOO MANY ARGS ───────────────────────────────────────── /code/proj/Main.roc ─ - - The `x` value is not a function, but it was given 1 argument: - - 4│ x 3 - ^ - - Are there any missing commas? Or missing parentheses? - "# - ), - ) - } - - #[test] - fn fncall_overapplied() { - report_problem_as( - indoc!( - r#" - f : Num.I64 -> Num.I64 - f = \_ -> 42 - - f 1 2 - "# - ), - indoc!( - r#" - ── TOO MANY ARGS ───────────────────────────────────────── /code/proj/Main.roc ─ - - The `f` function expects 1 argument, but it got 2 instead: - - 4│ f 1 2 - ^ - - Are there any missing commas? Or missing parentheses? - "# - ), - ) - } - - #[test] - fn fncall_underapplied() { - report_problem_as( - indoc!( - r#" - f : Num.I64, Num.I64 -> Num.I64 - f = \_, _ -> 42 - - f 1 - "# - ), - indoc!( - r#" - ── TOO FEW ARGS ────────────────────────────────────────── /code/proj/Main.roc ─ - - The `f` function expects 2 arguments, but it got only 1: - - 4│ f 1 - ^ - - Roc does not allow functions to be partially applied. Use a closure to - make partial application explicit. - "# - ), - ) - } - - #[test] - fn pattern_when_condition() { - report_problem_as( - indoc!( - r#" - when 1 is - {} -> 42 - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - The branches of this `when` expression don't match the condition: - - 1│> when 1 is - 2│ {} -> 42 - - The `when` condition is a number of type: - - Num a - - But the branch patterns have type: - - {}a - - The branches must be cases of the `when` condition's type! - "# - ), - ) - } - - #[test] - fn pattern_when_pattern() { - report_problem_as( - indoc!( - r#" - when 1 is - 2 -> 3 - {} -> 42 - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - The 2nd pattern in this `when` does not match the previous ones: - - 3│ {} -> 42 - ^^ - - The 2nd pattern is trying to match record values of type: - - {}a - - But all the previous branches match: - - Num a - "# - ), - ) - } - - #[test] - fn pattern_guard_mismatch_alias() { - report_problem_as( - indoc!( - r#" - when { foo: 1 } is - { foo: True } -> 42 - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - The branches of this `when` expression don't match the condition: - - 1│> when { foo: 1 } is - 2│ { foo: True } -> 42 - - The `when` condition is a record of type: - - { foo : Num a } - - But the branch patterns have type: - - { foo : [True] } - - The branches must be cases of the `when` condition's type! - "# - ), - ) - } - - #[test] - fn pattern_guard_mismatch() { - report_problem_as( - indoc!( - r#" - when { foo: "" } is - { foo: True } -> 42 - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - The branches of this `when` expression don't match the condition: - - 1│> when { foo: "" } is - 2│ { foo: True } -> 42 - - The `when` condition is a record of type: - - { foo : Str } - - But the branch patterns have type: - - { foo : [True] } - - The branches must be cases of the `when` condition's type! - "# - ), - ) - } - - #[test] - fn pattern_guard_does_not_bind_label() { - // needs some improvement, but the principle works - report_problem_as( - indoc!( - r#" - when { foo: 1 } is - { foo: _ } -> foo - "# - ), - indoc!( - r#" - ── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─ - - Nothing is named `foo` in this scope. - - 2│ { foo: _ } -> foo - ^^^ - - Did you mean one of these? - - Box - Set - Str - Ok - "# - ), - ) - } - - #[test] - fn pattern_guard_can_be_shadowed_above() { - report_problem_as( - indoc!( - r#" - foo = 3 - - when { foo: 1 } is - { foo: 2 } -> foo - _ -> foo - "# - ), - // should give no error - "", - ) - } - - #[test] - fn pattern_guard_can_be_shadowed_below() { - report_problem_as( - indoc!( - r#" - when { foo: 1 } is - { foo: 2 } -> - foo = 3 - - foo - _ -> 3 - "# - ), - // should give no error - "", - ) - } - - #[test] - fn pattern_or_pattern_mismatch() { - report_problem_as( - indoc!( - r#" - when { foo: 1 } is - {} | 1 -> 3 - "# - ), - // Just putting this here. We should probably handle or-patterns better - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - The 2nd pattern in this branch does not match the previous ones: - - 2│ {} | 1 -> 3 - ^ - - The 2nd pattern is trying to match numbers: - - Num a - - But all the previous branches match: - - {}a - "# - ), - ) - } - - #[test] - fn pattern_let_mismatch() { - report_problem_as( - indoc!( - r#" - (Foo x) = 42 - - x - "# - ), - // Maybe this should specifically say the pattern doesn't work? - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - This expression is used in an unexpected way: - - 1│ (Foo x) = 42 - ^^ - - It is a number of type: - - Num a - - But you are trying to use it as: - - [Foo a] - "# - ), - ) - } - - #[test] - fn from_annotation_complex_pattern() { - report_problem_as( - indoc!( - r#" - { x } : { x : Num.Int * } - { x } = { x: 4.0 } - - x - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - Something is off with the body of this definition: - - 1│ { x } : { x : Num.Int * } - 2│ { x } = { x: 4.0 } - ^^^^^^^^^^ - - The body is a record of type: - - { x : Frac a } - - But the type annotation says it should be: - - { x : Int * } - - Tip: You can convert between Int and Frac using functions like - `Num.toFrac` and `Num.round`. - "# - ), - ) - } - - #[test] - fn malformed_int_pattern() { - report_problem_as( - indoc!( - r#" - when 1 is - 100A -> 3 - _ -> 4 - "# - ), - indoc!( - r#" - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - - This integer pattern is malformed: - - 2│ 100A -> 3 - ^^^^ - - Tip: Learn more about number literals at TODO - "# - ), - ) - } - - #[test] - fn malformed_float_pattern() { - report_problem_as( - indoc!( - r#" - when 1 is - 2.X -> 3 - _ -> 4 - "# - ), - indoc!( - r#" - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - - This float pattern is malformed: - - 2│ 2.X -> 3 - ^^^ - - Tip: Learn more about number literals at TODO - "# - ), - ) - } - - #[test] - fn malformed_hex_pattern() { - report_problem_as( - indoc!( - r#" - when 1 is - 0xZ -> 3 - _ -> 4 - "# - ), - indoc!( - r#" - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - - This hex integer pattern is malformed: - - 2│ 0xZ -> 3 - ^^^ - - Tip: Learn more about number literals at TODO - "# - ), - ) - } - - #[test] - fn malformed_oct_pattern() { - report_problem_as( - indoc!( - r#" - when 1 is - 0o9 -> 3 - _ -> 4 - "# - ), - indoc!( - r#" - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - - This octal integer pattern is malformed: - - 2│ 0o9 -> 3 - ^^^ - - Tip: Learn more about number literals at TODO - "# - ), - ) - } - - #[test] - fn malformed_bin_pattern() { - report_problem_as( - indoc!( - r#" - when 1 is - 0b4 -> 3 - _ -> 4 - "# - ), - indoc!( - r#" - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - - This binary integer pattern is malformed: - - 2│ 0b4 -> 3 - ^^^ - - Tip: Learn more about number literals at TODO - "# - ), - ) - } - - #[test] - fn missing_fields() { - report_problem_as( - indoc!( - r#" - x : { a : Num.Int *, b : Num.Frac *, c : Str } - x = { b: 4.0 } - - x - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - Something is off with the body of the `x` definition: - - 1│ x : { a : Num.Int *, b : Num.Frac *, c : Str } - 2│ x = { b: 4.0 } - ^^^^^^^^^^ - - The body is a record of type: - - { b : Frac a } - - But the type annotation on `x` says it should be: - - { a : Int *, b : Frac *, c : Str } - - Tip: Looks like the c and a fields are missing. - "# - ), - ) - } - - #[test] - fn bad_double_rigid() { - // this previously reported the message below, not sure which is better - // - // Something is off with the body of the `f` definition: - // - // 1│ f : a, b -> a - // 2│ f = \x, y -> if True then x else y - // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - // - // The body is an anonymous function of type: - // - // a, a -> a - // - // But the type annotation on `f` says it should be: - // - // a, b -> a - report_problem_as( - indoc!( - r#" - f : a, b -> a - f = \x, y -> if True then x else y - - f - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - Something is off with the `else` branch of this `if` expression: - - 1│ f : a, b -> a - 2│ f = \x, y -> if True then x else y - ^ - - This `y` value is a: - - b - - But the type annotation on `f` says it should be: - - a - - Tip: Your type annotation uses `b` and `a` as separate type variables. - Your code seems to be saying they are the same though. Maybe they - should be the same in your type annotation? Maybe your code uses them - in a weird way? - "# - ), - ) - } - - #[test] - fn bad_rigid_function() { - report_problem_as( - indoc!( - r#" - f : Str -> msg - f = \_ -> Foo - - f - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - Something is off with the body of the `f` definition: - - 1│ f : Str -> msg - 2│ f = \_ -> Foo + 4│> when 1 is + 5│ {} -> 42 + + The `when` condition is a number of type: + + Num a + + But the branch patterns have type: + + {}a + + The branches must be cases of the `when` condition's type! + "### + ); + + test_report!( + pattern_when_pattern, + indoc!( + r#" + when 1 is + 2 -> 3 + {} -> 42 + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + The 2nd pattern in this `when` does not match the previous ones: + + 6│ {} -> 42 + ^^ + + The 2nd pattern is trying to match record values of type: + + {}a + + But all the previous branches match: + + Num a + "### + ); + + test_report!( + pattern_guard_mismatch_alias, + indoc!( + r#" + when { foo: 1 } is + { foo: True } -> 42 + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + The branches of this `when` expression don't match the condition: + + 4│> when { foo: 1 } is + 5│ { foo: True } -> 42 + + The `when` condition is a record of type: + + { foo : Num a } + + But the branch patterns have type: + + { foo : [True] } + + The branches must be cases of the `when` condition's type! + "### + ); + + test_report!( + pattern_guard_mismatch, + indoc!( + r#" + when { foo: "" } is + { foo: True } -> 42 + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + The branches of this `when` expression don't match the condition: + + 4│> when { foo: "" } is + 5│ { foo: True } -> 42 + + The `when` condition is a record of type: + + { foo : Str } + + But the branch patterns have type: + + { foo : [True] } + + The branches must be cases of the `when` condition's type! + "### + ); + + // needs some improvement, but the principle works + test_report!( + pattern_guard_does_not_bind_label, + indoc!( + r#" + when { foo: 1 } is + { foo: _ } -> foo + "# + ), + @r###" + ── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─ + + Nothing is named `foo` in this scope. + + 5│ { foo: _ } -> foo ^^^ - This `Foo` tag has the type: + Did you mean one of these? - [Foo]a + Box + Bool + U8 + F64 + "### + ); - But the type annotation on `f` says it should be: + test_report! { + pattern_guard_can_be_shadowed_above, + indoc!( + r#" + foo = 3 - msg - - Tip: The type annotation uses the type variable `msg` to say that this - definition can produce any type of value. But in the body I see that - it will only produce a tag value of a single specific type. Maybe - change the type annotation to be more specific? Maybe change the code - to be more general? - "# - ), - ) + when { foo: 1 } is + { foo: 2 } -> foo + _ -> foo + "# + ), + @"" // should give no error } - #[test] - fn bad_rigid_value() { - report_problem_as( - indoc!( - r#" - f : msg - f = 0x3 + test_report! { + pattern_guard_can_be_shadowed_below, + indoc!( + r#" + when { foo: 1 } is + { foo: 2 } -> + foo = 3 - f - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - Something is off with the body of the `f` definition: - - 1│ f : msg - 2│ f = 0x3 - ^^^ - - The body is an integer of type: - - Int a - - But the type annotation on `f` says it should be: - - msg - - Tip: The type annotation uses the type variable `msg` to say that this - definition can produce any type of value. But in the body I see that - it will only produce a `Int` value of a single specific type. Maybe - change the type annotation to be more specific? Maybe change the code - to be more general? - "# - ), - ) + foo + _ -> 3 + "# + ), + // should give no error + @"" } - #[test] - fn typo_lowercase_ok() { - // TODO improve tag suggestions - report_problem_as( - indoc!( - r#" - f : Str -> [Ok Num.I64, InvalidFoo] - f = \_ -> ok 4 - - f - "# - ), - indoc!( - r#" - ── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─ - - Nothing is named `ok` in this scope. - - 2│ f = \_ -> ok 4 - ^^ - - Did you mean one of these? - - Ok - f - Box - Set - "# - ), - ) - } - - #[test] - fn typo_uppercase_ok() { - // these error messages seem pretty helpful - report_problem_as( - indoc!( - r#" - f : Str -> Num.I64 - f = \_ -> - ok = 3 - - Ok - - f - "# - ), - indoc!( - r#" - ── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─ - - `ok` is not used anywhere in your code. - - 3│ ok = 3 - ^^ - - If you didn't intend on using `ok` then remove it so future readers of - your code don't wonder why it is there. - - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - Something is off with the body of the `f` definition: - - 1│ f : Str -> Num.I64 - 2│ f = \_ -> - 3│ ok = 3 - 4│ - 5│ Ok - ^^ - - This `Ok` tag has the type: - - [Ok]a - - But the type annotation on `f` says it should be: - - I64 - "# - ), - ) - } - - #[test] - fn circular_definition_self() { - // invalid recursion - report_problem_as( - indoc!( - r#" - f = f - - f - "# - ), - indoc!( - r#" - ── CIRCULAR DEFINITION ─────────────────────────────────── /code/proj/Main.roc ─ - - The `f` value is defined directly in terms of itself, causing an - infinite loop. - "# - ), - ) - } - - #[test] - fn circular_definition() { - // invalid mutual recursion - report_problem_as( - indoc!( - r#" - foo = bar - - bar = foo - - foo - "# - ), - indoc!( - r#" - ── CIRCULAR DEFINITION ─────────────────────────────────── /code/proj/Main.roc ─ - - The `foo` definition is causing a very tricky infinite loop: - - 1│ foo = bar - ^^^ - - The `foo` value depends on itself through the following chain of - definitions: - - ┌─────┐ - │ foo - │ ↓ - │ bar - └─────┘ - "# - ), - ) - } - - #[test] - fn update_empty_record() { - report_problem_as( - indoc!( - r#" - x = {} - - { x & foo: 3 } - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - This `x` record doesn’t have a `foo` field: - - 3│ { x & foo: 3 } - ^^^^^^ - - In fact, `x` is a record with no fields at all! - "# - ), - ) - } - - #[test] - fn update_record() { - report_problem_as( - indoc!( - r#" - x = { fo: 3, bar: 4 } - - { x & foo: 3 } - "# - ), - // TODO also suggest fields with the correct type - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - This `x` record doesn’t have a `foo` field: - - 3│ { x & foo: 3 } - ^^^^^^ - - There may be a typo. These `x` fields are the most similar: - - { - fo : Num b, - bar : Num a, - } - - Maybe `foo:` should be `fo:` instead? - "# - ), - ) - } - - #[test] - fn update_record_ext() { - report_problem_as( - indoc!( - r#" - f : { fo: Num.I64 }ext -> Num.I64 - f = \r -> - r2 = { r & foo: r.fo } - - r2.fo - - f - "# - ), - // TODO also suggest fields with the correct type - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - This `r` record doesn’t have a `foo` field: - - 3│ r2 = { r & foo: r.fo } - ^^^^^^^^^ - - There may be a typo. These `r` fields are the most similar: - - { - fo : I64, - }ext - - Maybe `foo:` should be `fo:` instead? - "# - ), - ) - } - - #[test] - fn update_record_snippet() { - report_problem_as( - indoc!( - r#" - x = { fo: 3, bar: 4, baz: 3, spam: 42, foobar: 3 } - - { x & foo: 3 } - "# - ), - // TODO also suggest fields with the correct type - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - This `x` record doesn’t have a `foo` field: - - 3│ { x & foo: 3 } - ^^^^^^ - - There may be a typo. These `x` fields are the most similar: - - { - fo : Num c, - foobar : Num d, - bar : Num a, - baz : Num b, - … - } - - Maybe `foo:` should be `fo:` instead? - "# - ), - ) - } - - #[test] - fn plus_on_str() { - report_problem_as( - indoc!( - r#" - 0x4 + "foo" - "# - ), - // TODO also suggest fields with the correct type - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - The 2nd argument to `add` is not what I expect: - - 1│ 0x4 + "foo" - ^^^^^ - - This argument is a string of type: - - Str - - But `add` needs the 2nd argument to be: - - Int a - "# - ), - ) - } - - #[test] - fn int_frac() { - report_problem_as( - indoc!( - r#" - 0x4 + 3.14 - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - The 2nd argument to `add` is not what I expect: - - 1│ 0x4 + 3.14 + test_report!( + pattern_or_pattern_mismatch, + indoc!( + r#" + when { foo: 1 } is + {} | 1 -> 3 + "# + ), + // Just putting this here. We should probably handle or-patterns better + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + The 2nd pattern in this branch does not match the previous ones: + + 5│ {} | 1 -> 3 + ^ + + The 2nd pattern is trying to match numbers: + + Num a + + But all the previous branches match: + + {}a + "### + ); + + test_report!( + pattern_let_mismatch, + indoc!( + r#" + (Foo x) = 42 + + x + "# + ), + // Maybe this should specifically say the pattern doesn't work? + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + This expression is used in an unexpected way: + + 4│ (Foo x) = 42 + ^^ + + It is a number of type: + + Num a + + But you are trying to use it as: + + [Foo a] + "### + ); + + test_report!( + from_annotation_complex_pattern, + indoc!( + r#" + { x } : { x : Num.Int * } + { x } = { x: 4.0 } + + x + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + Something is off with the body of this definition: + + 4│ { x } : { x : Num.Int * } + 5│ { x } = { x: 4.0 } + ^^^^^^^^^^ + + The body is a record of type: + + { x : Frac a } + + But the type annotation says it should be: + + { x : Int * } + + Tip: You can convert between Int and Frac using functions like + `Num.toFrac` and `Num.round`. + "### + ); + + test_report!( + malformed_int_pattern, + indoc!( + r#" + when 1 is + 100A -> 3 + _ -> 4 + "# + ), + @r###" + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + + This integer pattern is malformed: + + 5│ 100A -> 3 + ^^^^ + + Tip: Learn more about number literals at TODO + "### + ); + + test_report!( + malformed_float_pattern, + indoc!( + r#" + when 1 is + 2.X -> 3 + _ -> 4 + "# + ), + @r###" + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + + This float pattern is malformed: + + 5│ 2.X -> 3 + ^^^ + + Tip: Learn more about number literals at TODO + "### + ); + + test_report!( + malformed_hex_pattern, + indoc!( + r#" + when 1 is + 0xZ -> 3 + _ -> 4 + "# + ), + @r###" + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + + This hex integer pattern is malformed: + + 5│ 0xZ -> 3 + ^^^ + + Tip: Learn more about number literals at TODO + "### + ); + + test_report!( + malformed_oct_pattern, + indoc!( + r#" + when 1 is + 0o9 -> 3 + _ -> 4 + "# + ), + @r###" + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + + This octal integer pattern is malformed: + + 5│ 0o9 -> 3 + ^^^ + + Tip: Learn more about number literals at TODO + "### + ); + + test_report!( + malformed_bin_pattern, + indoc!( + r#" + when 1 is + 0b4 -> 3 + _ -> 4 + "# + ), + @r###" + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + + This binary integer pattern is malformed: + + 5│ 0b4 -> 3 + ^^^ + + Tip: Learn more about number literals at TODO + "### + ); + + test_report!( + missing_fields, + indoc!( + r#" + x : { a : Num.Int *, b : Num.Frac *, c : Str } + x = { b: 4.0 } + + x + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + Something is off with the body of the `x` definition: + + 4│ x : { a : Num.Int *, b : Num.Frac *, c : Str } + 5│ x = { b: 4.0 } + ^^^^^^^^^^ + + The body is a record of type: + + { b : Frac a } + + But the type annotation on `x` says it should be: + + { a : Int *, b : Frac *, c : Str } + + Tip: Looks like the c and a fields are missing. + "### + ); + + // this previously reported the message below, not sure which is better + // + // Something is off with the body of the `f` definition: + // + // 1│ f : a, b -> a + // 2│ f = \x, y -> if True then x else y + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + // + // The body is an anonymous function of type: + // + // a, a -> a + // + // But the type annotation on `f` says it should be: + // + // a, b -> a + test_report!( + bad_double_rigid, + indoc!( + r#" + f : a, b -> a + f = \x, y -> if True then x else y + + f + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + Something is off with the `else` branch of this `if` expression: + + 4│ f : a, b -> a + 5│ f = \x, y -> if True then x else y + ^ + + This `y` value is a: + + b + + But the type annotation on `f` says it should be: + + a + + Tip: Your type annotation uses `b` and `a` as separate type variables. + Your code seems to be saying they are the same though. Maybe they + should be the same in your type annotation? Maybe your code uses them + in a weird way? + "### + ); + + test_report!( + bad_rigid_function, + indoc!( + r#" + f : Str -> msg + f = \_ -> Foo + + f + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + Something is off with the body of the `f` definition: + + 4│ f : Str -> msg + 5│ f = \_ -> Foo + ^^^ + + This `Foo` tag has the type: + + [Foo]a + + But the type annotation on `f` says it should be: + + msg + + Tip: The type annotation uses the type variable `msg` to say that this + definition can produce any type of value. But in the body I see that + it will only produce a tag value of a single specific type. Maybe + change the type annotation to be more specific? Maybe change the code + to be more general? + "### + ); + + test_report!( + bad_rigid_value, + indoc!( + r#" + f : msg + f = 0x3 + + f + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + Something is off with the body of the `f` definition: + + 4│ f : msg + 5│ f = 0x3 + ^^^ + + The body is an integer of type: + + Int a + + But the type annotation on `f` says it should be: + + msg + + Tip: The type annotation uses the type variable `msg` to say that this + definition can produce any type of value. But in the body I see that + it will only produce a `Int` value of a single specific type. Maybe + change the type annotation to be more specific? Maybe change the code + to be more general? + "### + ); + + // TODO improve tag suggestions + test_report!( + typo_lowercase_ok, + indoc!( + r#" + f : Str -> [Ok Num.I64, InvalidFoo] + f = \_ -> ok 4 + + f + "# + ), + @r###" + ── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─ + + Nothing is named `ok` in this scope. + + 5│ f = \_ -> ok 4 + ^^ + + Did you mean one of these? + + Ok + U8 + Box + f + "### + ); + + // these error messages seem pretty helpful + test_report!( + typo_uppercase_ok, + indoc!( + r#" + f : Str -> Num.I64 + f = \_ -> + ok = 3 + + Ok + + f + "# + ), + @r###" + ── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─ + + `ok` is not used anywhere in your code. + + 6│ ok = 3 + ^^ + + If you didn't intend on using `ok` then remove it so future readers of + your code don't wonder why it is there. + + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + Something is off with the body of the `f` definition: + + 4│ f : Str -> Num.I64 + 5│ f = \_ -> + 6│ ok = 3 + 7│ + 8│ Ok + ^^ + + This `Ok` tag has the type: + + [Ok]a + + But the type annotation on `f` says it should be: + + I64 + "### + ); + + // invalid recursion + test_report!( + circular_definition_self, + indoc!( + r#" + f = f + + f + "# + ), + @r#" + ── CIRCULAR DEFINITION ─────────────────────────────────── /code/proj/Main.roc ─ + + The `f` value is defined directly in terms of itself, causing an + infinite loop. + "# + ); + + // invalid mutual recursion + test_report!( + circular_definition, + indoc!( + r#" + foo = bar + + bar = foo + + foo + "# + ), + @r###" + ── CIRCULAR DEFINITION ─────────────────────────────────── /code/proj/Main.roc ─ + + The `foo` definition is causing a very tricky infinite loop: + + 4│ foo = bar + ^^^ + + The `foo` value depends on itself through the following chain of + definitions: + + ┌─────┐ + │ foo + │ ↓ + │ bar + └─────┘ + "### + ); + + test_report!( + update_empty_record, + indoc!( + r#" + x = {} + + { x & foo: 3 } + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + This `x` record doesn’t have a `foo` field: + + 6│ { x & foo: 3 } + ^^^^^^ + + In fact, `x` is a record with no fields at all! + "### + ); + + test_report!( + update_record, + indoc!( + r#" + x = { fo: 3, bar: 4 } + + { x & foo: 3 } + "# + ), + // TODO also suggest fields with the correct type + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + This `x` record doesn’t have a `foo` field: + + 6│ { x & foo: 3 } + ^^^^^^ + + There may be a typo. These `x` fields are the most similar: + + { + fo : Num b, + bar : Num a, + } + + Maybe `foo:` should be `fo:` instead? + "### + ); + + test_report!( + update_record_ext, + indoc!( + r#" + f : { fo: Num.I64 }ext -> Num.I64 + f = \r -> + r2 = { r & foo: r.fo } + + r2.fo + + f + "# + ), + // TODO also suggest fields with the correct type + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + This `r` record doesn’t have a `foo` field: + + 6│ r2 = { r & foo: r.fo } + ^^^^^^^^^ + + There may be a typo. These `r` fields are the most similar: + + { + fo : I64, + }ext + + Maybe `foo:` should be `fo:` instead? + "### + ); + + test_report!( + update_record_snippet, + indoc!( + r#" + x = { fo: 3, bar: 4, baz: 3, spam: 42, foobar: 3 } + + { x & foo: 3 } + "# + ), + // TODO also suggest fields with the correct type + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + This `x` record doesn’t have a `foo` field: + + 6│ { x & foo: 3 } + ^^^^^^ + + There may be a typo. These `x` fields are the most similar: + + { + fo : Num c, + foobar : Num d, + bar : Num a, + baz : Num b, + … + } + + Maybe `foo:` should be `fo:` instead? + "### + ); + + test_report!( + plus_on_str, + indoc!( + r#" + 0x4 + "foo" + "# + ), + // TODO also suggest fields with the correct type + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + The 2nd argument to `add` is not what I expect: + + 4│ 0x4 + "foo" + ^^^^^ + + This argument is a string of type: + + Str + + But `add` needs the 2nd argument to be: + + Int a + "### + ); + + test_report!( + int_frac, + indoc!( + r#" + 0x4 + 3.14 + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + The 2nd argument to `add` is not what I expect: + + 4│ 0x4 + 3.14 + ^^^^ + + This argument is a frac of type: + + Frac a + + But `add` needs the 2nd argument to be: + + Int a + + Tip: You can convert between Int and Frac using functions like + `Num.toFrac` and `Num.round`. + "### + ); + + test_report!( + boolean_tag, + indoc!( + r#" + 42 + True + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + The 2nd argument to `add` is not what I expect: + + 4│ 42 + True + ^^^^ + + This `True` boolean has the type: + + [True]a + + But `add` needs the 2nd argument to be: + + Num a + "### + ); + + test_report!( + tag_missing, + indoc!( + r#" + f : [A] -> [A, B] + f = \a -> a + + f + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + Something is off with the body of the `f` definition: + + 4│ f : [A] -> [A, B] + 5│ f = \a -> a + ^ + + This `a` value is a: + + [A] + + But the type annotation on `f` says it should be: + + [A, B] + + Tip: Looks like a closed tag union does not have the `B` tag. + + Tip: Closed tag unions can't grow, because that might change the size + in memory. Can you use an open tag union? + "### + ); + + test_report!( + tags_missing, + indoc!( + r#" + f : [A] -> [A, B, C] + f = \a -> a + + f + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + Something is off with the body of the `f` definition: + + 4│ f : [A] -> [A, B, C] + 5│ f = \a -> a + ^ + + This `a` value is a: + + [A] + + But the type annotation on `f` says it should be: + + [A, B, C] + + Tip: Looks like a closed tag union does not have the `B` and `C` tags. + + Tip: Closed tag unions can't grow, because that might change the size + in memory. Can you use an open tag union? + "### + ); + + test_report!( + patterns_fn_not_exhaustive, + indoc!( + r#" + Either : [Left {}, Right Str] + + x : Either + x = Left {} + + f : Either -> {} + f = \Left v -> v + + f x + "# + ), + @r###" + ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ + + This pattern does not cover all the possibilities: + + 10│ f = \Left v -> v + ^^^^^^ + + Other possibilities include: + + Right _ + + I would have to crash if I saw one of those! So rather than pattern + matching in function arguments, put a `when` in the function body to + account for all possibilities. + "### + ); + + test_report!( + patterns_let_not_exhaustive, + indoc!( + r#" + x : [Left {}, Right Str] + x = Left {} + + + (Left y) = x + + y + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + This expression is used in an unexpected way: + + 8│ (Left y) = x + ^ + + This `x` value is a: + + [Left {}, Right Str] + + But you are trying to use it as: + + [Left a] + + Tip: Looks like a closed tag union does not have the `Right` tag. + + Tip: Closed tag unions can't grow, because that might change the size + in memory. Can you use an open tag union? + "### + ); + + test_report!( + patterns_when_not_exhaustive, + indoc!( + r#" + when 0x1 is + 2 -> 0x3 + "# + ), + @r###" + ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ + + This `when` does not cover all the possibilities: + + 4│> when 0x1 is + 5│> 2 -> 0x3 + + Other possibilities include: + + _ + + I would have to crash if I saw one of those! Add branches for them! + "### + ); + + test_report!( + patterns_bool_not_exhaustive, + indoc!( + r#" + x : [Red, Green] + x = Green + + when x is + Red -> 3 + "# + ), + @r###" + ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ + + This `when` does not cover all the possibilities: + + 7│> when x is + 8│> Red -> 3 + + Other possibilities include: + + Green + + I would have to crash if I saw one of those! Add branches for them! + "### + ); + + test_report!( + patterns_enum_not_exhaustive, + indoc!( + r#" + x : [Red, Green, Blue] + x = Red + + when x is + Red -> 0 + Green -> 1 + "# + ), + @r###" + ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ + + This `when` does not cover all the possibilities: + + 7│> when x is + 8│> Red -> 0 + 9│> Green -> 1 + + Other possibilities include: + + Blue + + I would have to crash if I saw one of those! Add branches for them! + "### + ); + + test_report!( + patterns_remote_data_not_exhaustive, + indoc!( + r#" + RemoteData e a : [NotAsked, Loading, Failure e, Success a] + + x : RemoteData Num.I64 Str + + when x is + NotAsked -> 3 + "# + ), + @r###" + ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ + + This `when` does not cover all the possibilities: + + 8│> when x is + 9│> NotAsked -> 3 + + Other possibilities include: + + Failure _ + Loading + Success _ + + I would have to crash if I saw one of those! Add branches for them! + "### + ); + + test_report!( + patterns_record_not_exhaustive, + indoc!( + r#" + x = { a: 3 } + + when x is + { a: 4 } -> 4 + "# + ), + // Tip: Looks like a record field guard is not exhaustive. Learn more about record pattern matches at TODO. + @r###" + ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ + + This `when` does not cover all the possibilities: + + 6│> when x is + 7│> { a: 4 } -> 4 + + Other possibilities include: + + { a } + + I would have to crash if I saw one of those! Add branches for them! + "### + ); + + test_report!( + patterns_record_guard_not_exhaustive, + indoc!( + r#" + y : [Nothing, Just Num.I64] + y = Just 4 + x = { a: y, b: 42} + + when x is + { a: Nothing } -> 4 + { a: Just 3 } -> 4 + "# + ), + @r###" + ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ + + This `when` does not cover all the possibilities: + + 8│> when x is + 9│> { a: Nothing } -> 4 + 10│> { a: Just 3 } -> 4 + + Other possibilities include: + + { a: Just _ } + + I would have to crash if I saw one of those! Add branches for them! + "### + ); + + test_report!( + patterns_nested_tag_not_exhaustive, + indoc!( + r#" + when Record Nothing 1 is + Record (Nothing) b -> b + Record (Just 3) b -> b + "# + ), + @r###" + ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ + + This `when` does not cover all the possibilities: + + 4│> when Record Nothing 1 is + 5│> Record (Nothing) b -> b + 6│> Record (Just 3) b -> b + + Other possibilities include: + + Record (Just _) _ + + I would have to crash if I saw one of those! Add branches for them! + "### + ); + + test_report!( + patterns_int_redundant, + indoc!( + r#" + when 0x1 is + 2 -> 3 + 2 -> 4 + _ -> 5 + "# + ), + @r###" + ── REDUNDANT PATTERN ───────────────────────────────────── /code/proj/Main.roc ─ + + The 2nd pattern is redundant: + + 4│ when 0x1 is + 5│ 2 -> 3 + 6│> 2 -> 4 + 7│ _ -> 5 + + Any value of this shape will be handled by a previous pattern, so this + one should be removed. + "### + ); + + test_report!( + unify_alias_other, + indoc!( + r#" + Foo a : { x : Num.Int a } + + f : Foo a -> Num.Int a + f = \r -> r.x + + f { y: 3.14 } + "# + ), + // de-aliases the alias to give a better error message + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + The 1st argument to `f` is not what I expect: + + 9│ f { y: 3.14 } + ^^^^^^^^^^^ + + This argument is a record of type: + + { y : Frac a } + + But `f` needs the 1st argument to be: + + { x : Int a } + + Tip: Seems like a record field typo. Maybe `y` should be `x`? + + Tip: Can more type annotations be added? Type annotations always help + me give more specific messages, and I think they could help a lot in + this case + "### + ); + + test_report!( + #[ignore] + cyclic_alias, + indoc!( + r#" + Foo : { x : Bar } + Bar : { y : Foo } + + f : Foo + + f + "# + ), + // should not report Bar as unused! + @r###" + ── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─ + + The `Foo` alias is self-recursive in an invalid way: + + 4│ Foo : { x : Bar } + ^^^ + + Recursion in aliases is only allowed if recursion happens behind a + tagged union, at least one variant of which is not recursive. + "### + ); + + test_report!( + self_recursive_alias, + indoc!( + r#" + Foo : { x : Foo } + + f : Foo + f = 3 + + f + "# + ), + // should not report Bar as unused! + @r###" + ── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─ + + The `Foo` alias is self-recursive in an invalid way: + + 4│ Foo : { x : Foo } + ^^^ + + Recursion in aliases is only allowed if recursion happens behind a + tagged union, at least one variant of which is not recursive. + "### + ); + + test_report!( + record_duplicate_field_same_type, + indoc!( + r#" + { x: 4, y: 3, x: 4 } + "# + ), + @r###" + ── DUPLICATE FIELD NAME ────────────────────────────────── /code/proj/Main.roc ─ + + This record defines the `.x` field twice! + + 4│ { x: 4, y: 3, x: 4 } + ^^^^ ^^^^ + + In the rest of the program, I will only use the latter definition: + + 4│ { x: 4, y: 3, x: 4 } ^^^^ - This argument is a frac of type: - - Frac a - - But `add` needs the 2nd argument to be: - - Int a - - Tip: You can convert between Int and Frac using functions like - `Num.toFrac` and `Num.round`. - "# - ), - ) - } - - #[test] - fn boolean_tag() { - report_problem_as( - indoc!( - r#" - 42 + True - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - The 2nd argument to `add` is not what I expect: - - 1│ 42 + True - ^^^^ - - This `True` boolean has the type: - - [True]a - - But `add` needs the 2nd argument to be: - - Num a - "# - ), - ) - } - - #[test] - fn tag_missing() { - report_problem_as( - indoc!( - r#" - f : [A] -> [A, B] - f = \a -> a - - f - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - Something is off with the body of the `f` definition: - - 1│ f : [A] -> [A, B] - 2│ f = \a -> a - ^ - - This `a` value is a: - - [A] - - But the type annotation on `f` says it should be: - - [A, B] - - Tip: Looks like a closed tag union does not have the `B` tag. - - Tip: Closed tag unions can't grow, because that might change the size - in memory. Can you use an open tag union? - "# - ), - ) - } - - #[test] - fn tags_missing() { - report_problem_as( - indoc!( - r#" - f : [A] -> [A, B, C] - f = \a -> a - - f - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - Something is off with the body of the `f` definition: - - 1│ f : [A] -> [A, B, C] - 2│ f = \a -> a - ^ - - This `a` value is a: - - [A] - - But the type annotation on `f` says it should be: - - [A, B, C] - - Tip: Looks like a closed tag union does not have the `B` and `C` tags. - - Tip: Closed tag unions can't grow, because that might change the size - in memory. Can you use an open tag union? - "# - ), - ) - } - - #[test] - fn patterns_fn_not_exhaustive() { - report_problem_as( - indoc!( - r#" - Either : [Left {}, Right Str] - - x : Either - x = Left {} - - f : Either -> {} - f = \Left v -> v - - f x - "# - ), - indoc!( - r#" - ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ - - This pattern does not cover all the possibilities: - - 7│ f = \Left v -> v - ^^^^^^ - - Other possibilities include: - - Right _ - - I would have to crash if I saw one of those! So rather than pattern - matching in function arguments, put a `when` in the function body to - account for all possibilities. - "# - ), - ) - } - - #[test] - fn patterns_let_not_exhaustive() { - report_problem_as( - indoc!( - r#" - x : [Left {}, Right Str] - x = Left {} - - - (Left y) = x - - y - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - This expression is used in an unexpected way: - - 5│ (Left y) = x - ^ - - This `x` value is a: - - [Left {}, Right Str] - - But you are trying to use it as: - - [Left a] - - Tip: Looks like a closed tag union does not have the `Right` tag. - - Tip: Closed tag unions can't grow, because that might change the size - in memory. Can you use an open tag union? - "# - ), - ) - } - - #[test] - fn patterns_when_not_exhaustive() { - report_problem_as( - indoc!( - r#" - when 0x1 is - 2 -> 0x3 - "# - ), - indoc!( - r#" - ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ - - This `when` does not cover all the possibilities: - - 1│> when 0x1 is - 2│> 2 -> 0x3 - - Other possibilities include: - - _ - - I would have to crash if I saw one of those! Add branches for them! - "# - ), - ) - } - - #[test] - fn patterns_bool_not_exhaustive() { - report_problem_as( - indoc!( - r#" - x : [Red, Green] - x = Green - - when x is - Red -> 3 - "# - ), - indoc!( - r#" - ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ - - This `when` does not cover all the possibilities: - - 4│> when x is - 5│> Red -> 3 - - Other possibilities include: - - Green - - I would have to crash if I saw one of those! Add branches for them! - "# - ), - ) - } - - #[test] - fn patterns_enum_not_exhaustive() { - report_problem_as( - indoc!( - r#" - x : [Red, Green, Blue] - x = Red - - when x is - Red -> 0 - Green -> 1 - "# - ), - indoc!( - r#" - ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ - - This `when` does not cover all the possibilities: - - 4│> when x is - 5│> Red -> 0 - 6│> Green -> 1 - - Other possibilities include: - - Blue - - I would have to crash if I saw one of those! Add branches for them! - "# - ), - ) - } - - #[test] - fn patterns_remote_data_not_exhaustive() { - report_problem_as( - indoc!( - r#" - RemoteData e a : [NotAsked, Loading, Failure e, Success a] - - x : RemoteData Num.I64 Str - - when x is - NotAsked -> 3 - "# - ), - indoc!( - r#" - ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ - - This `when` does not cover all the possibilities: - - 5│> when x is - 6│> NotAsked -> 3 - - Other possibilities include: - - Failure _ - Loading - Success _ - - I would have to crash if I saw one of those! Add branches for them! - "# - ), - ) - } - - #[test] - fn patterns_record_not_exhaustive() { - report_problem_as( - indoc!( - r#" - x = { a: 3 } - - when x is - { a: 4 } -> 4 - "# - ), - // Tip: Looks like a record field guard is not exhaustive. Learn more about record pattern matches at TODO. - indoc!( - r#" - ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ - - This `when` does not cover all the possibilities: - - 3│> when x is - 4│> { a: 4 } -> 4 - - Other possibilities include: - - { a } - - I would have to crash if I saw one of those! Add branches for them! - "# - ), - ) - } - - #[test] - fn patterns_record_guard_not_exhaustive() { - report_problem_as( - indoc!( - r#" - y : [Nothing, Just Num.I64] - y = Just 4 - x = { a: y, b: 42} - - when x is - { a: Nothing } -> 4 - { a: Just 3 } -> 4 - "# - ), - indoc!( - r#" - ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ - - This `when` does not cover all the possibilities: - - 5│> when x is - 6│> { a: Nothing } -> 4 - 7│> { a: Just 3 } -> 4 - - Other possibilities include: - - { a: Just _ } - - I would have to crash if I saw one of those! Add branches for them! - "# - ), - ) - } - - #[test] - fn patterns_nested_tag_not_exhaustive() { - report_problem_as( - indoc!( - r#" - when Record Nothing 1 is - Record (Nothing) b -> b - Record (Just 3) b -> b - "# - ), - indoc!( - r#" - ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ - - This `when` does not cover all the possibilities: - - 1│> when Record Nothing 1 is - 2│> Record (Nothing) b -> b - 3│> Record (Just 3) b -> b - - Other possibilities include: - - Record (Just _) _ - - I would have to crash if I saw one of those! Add branches for them! - "# - ), - ) - } - - #[test] - fn patterns_int_redundant() { - report_problem_as( - indoc!( - r#" - when 0x1 is - 2 -> 3 - 2 -> 4 - _ -> 5 - "# - ), - indoc!( - r#" - ── REDUNDANT PATTERN ───────────────────────────────────── /code/proj/Main.roc ─ - - The 2nd pattern is redundant: - - 1│ when 0x1 is - 2│ 2 -> 3 - 3│> 2 -> 4 - 4│ _ -> 5 - - Any value of this shape will be handled by a previous pattern, so this - one should be removed. - "# - ), - ) - } - - #[test] - fn unify_alias_other() { - report_problem_as( - indoc!( - r#" - Foo a : { x : Num.Int a } - - f : Foo a -> Num.Int a - f = \r -> r.x - - f { y: 3.14 } - "# - ), - // de-aliases the alias to give a better error message - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - The 1st argument to `f` is not what I expect: - - 6│ f { y: 3.14 } - ^^^^^^^^^^^ - - This argument is a record of type: - - { y : Frac a } - - But `f` needs the 1st argument to be: - - { x : Int a } - - Tip: Seems like a record field typo. Maybe `y` should be `x`? - - Tip: Can more type annotations be added? Type annotations always help - me give more specific messages, and I think they could help a lot in - this case - "# - ), - ) - } - - #[test] - #[ignore] - fn cyclic_alias() { - report_problem_as( - indoc!( - r#" - Foo : { x : Bar } - Bar : { y : Foo } - - f : Foo - - f - "# - ), - // should not report Bar as unused! - indoc!( - r#" - ── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─ - - The `Foo` alias is recursive in an invalid way: - - 1│ Foo : { x : Bar } - ^^^^^^^^^^^ - - The `Foo` alias depends on itself through the following chain of - definitions: - - ┌─────┐ - │ Foo - │ ↓ - │ Bar - └─────┘ - - Recursion in aliases is only allowed if recursion happens behind a - tag. - - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - - `Bar` is not used anywhere in your code. - - 2│ Bar : { y : Foo } - ^^^^^^^^^^^^^^^^^ - - If you didn't intend on using `Bar` then remove it so future readers of - your code don't wonder why it is there. - "# - ), - ) - } - - #[test] - fn self_recursive_alias() { - report_problem_as( - indoc!( - r#" - Foo : { x : Foo } - - f : Foo - f = 3 - - f - "# - ), - // should not report Bar as unused! - indoc!( - r#" - ── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─ - - The `Foo` alias is self-recursive in an invalid way: - - 1│ Foo : { x : Foo } - ^^^ - - Recursion in aliases is only allowed if recursion happens behind a - tagged union, at least one variant of which is not recursive. - "# - ), - ) - } - - #[test] - fn record_duplicate_field_same_type() { - report_problem_as( - indoc!( - r#" - { x: 4, y: 3, x: 4 } - "# - ), - indoc!( - r#" - ── DUPLICATE FIELD NAME ────────────────────────────────── /code/proj/Main.roc ─ - - This record defines the `.x` field twice! - - 1│ { x: 4, y: 3, x: 4 } - ^^^^ ^^^^ - - In the rest of the program, I will only use the latter definition: - - 1│ { x: 4, y: 3, x: 4 } - ^^^^ - - For clarity, remove the previous `.x` definitions from this record. - "# - ), - ) - } - - #[test] - fn record_duplicate_field_different_types() { - report_problem_as( - indoc!( - r#" - { x: 4, y: 3, x: "foo" } - "# - ), - indoc!( - r#" - ── DUPLICATE FIELD NAME ────────────────────────────────── /code/proj/Main.roc ─ - - This record defines the `.x` field twice! - - 1│ { x: 4, y: 3, x: "foo" } - ^^^^ ^^^^^^^^ - - In the rest of the program, I will only use the latter definition: - - 1│ { x: 4, y: 3, x: "foo" } - ^^^^^^^^ - - For clarity, remove the previous `.x` definitions from this record. - "# - ), - ) - } - - #[test] - fn record_duplicate_field_multiline() { - report_problem_as( - indoc!( - r#" - { + For clarity, remove the previous `.x` definitions from this record. + "### + ); + + test_report!( + record_duplicate_field_different_types, + indoc!( + r#" + { x: 4, y: 3, x: "foo" } + "# + ), + @r###" + ── DUPLICATE FIELD NAME ────────────────────────────────── /code/proj/Main.roc ─ + + This record defines the `.x` field twice! + + 4│ { x: 4, y: 3, x: "foo" } + ^^^^ ^^^^^^^^ + + In the rest of the program, I will only use the latter definition: + + 4│ { x: 4, y: 3, x: "foo" } + ^^^^^^^^ + + For clarity, remove the previous `.x` definitions from this record. + "### + ); + + test_report!( + record_duplicate_field_multiline, + indoc!( + r#" + { + x: 4, + y: 3, + x: "foo" + } + "# + ), + @r###" + ── DUPLICATE FIELD NAME ────────────────────────────────── /code/proj/Main.roc ─ + + This record defines the `.x` field twice! + + 4│ { + 5│> x: 4, + 6│ y: 3, + 7│> x: "foo" + 8│ } + + In the rest of the program, I will only use the latter definition: + + 4│ { + 5│ x: 4, + 6│ y: 3, + 7│> x: "foo" + 8│ } + + For clarity, remove the previous `.x` definitions from this record. + "### + ); + + test_report!( + record_update_duplicate_field_multiline, + indoc!( + r#" + \r -> + { r & x: 4, y: 3, x: "foo" } - "# - ), - indoc!( - r#" - ── DUPLICATE FIELD NAME ────────────────────────────────── /code/proj/Main.roc ─ - - This record defines the `.x` field twice! - - 1│ { - 2│> x: 4, - 3│ y: 3, - 4│> x: "foo" - 5│ } - - In the rest of the program, I will only use the latter definition: - - 1│ { - 2│ x: 4, - 3│ y: 3, - 4│> x: "foo" - 5│ } - - For clarity, remove the previous `.x` definitions from this record. - "# - ), - ) - } - - #[test] - fn record_update_duplicate_field_multiline() { - report_problem_as( - indoc!( - r#" - \r -> - { r & - x: 4, - y: 3, - x: "foo" - } - "# - ), - indoc!( - r#" - ── DUPLICATE FIELD NAME ────────────────────────────────── /code/proj/Main.roc ─ - - This record defines the `.x` field twice! - - 2│ { r & - 3│> x: 4, - 4│ y: 3, - 5│> x: "foo" - 6│ } - - In the rest of the program, I will only use the latter definition: - - 2│ { r & - 3│ x: 4, - 4│ y: 3, - 5│> x: "foo" - 6│ } - - For clarity, remove the previous `.x` definitions from this record. - "# - ), - ) - } - - #[test] - fn record_type_duplicate_field() { - report_problem_as( - indoc!( - r#" - a : { foo : Num.I64, bar : {}, foo : Str } - a = { bar: {}, foo: "foo" } - - a - "# - ), - indoc!( - r#" - ── DUPLICATE FIELD NAME ────────────────────────────────── /code/proj/Main.roc ─ - - This record type defines the `.foo` field twice! - - 1│ a : { foo : Num.I64, bar : {}, foo : Str } - ^^^^^^^^^^^^^ ^^^^^^^^^ - - In the rest of the program, I will only use the latter definition: - - 1│ a : { foo : Num.I64, bar : {}, foo : Str } - ^^^^^^^^^ - - For clarity, remove the previous `.foo` definitions from this record - type. - "# - ), - ) - } - - #[test] - fn tag_union_duplicate_tag() { - report_problem_as( - indoc!( - r#" - a : [Foo Num.I64, Bar {}, Foo Str] - a = Foo "foo" - - a - "# - ), - indoc!( - r#" - ── DUPLICATE TAG NAME ──────────────────────────────────── /code/proj/Main.roc ─ - - This tag union type defines the `Foo` tag twice! - - 1│ a : [Foo Num.I64, Bar {}, Foo Str] - ^^^^^^^^^^^ ^^^^^^^ - - In the rest of the program, I will only use the latter definition: - - 1│ a : [Foo Num.I64, Bar {}, Foo Str] - ^^^^^^^ - - For clarity, remove the previous `Foo` definitions from this tag union - type. - "# - ), - ) - } - - #[test] - fn annotation_definition_mismatch() { - report_problem_as( - indoc!( - r#" - bar : Num.I64 - foo = \x -> x - - # NOTE: neither bar or foo are defined at this point - 4 - "# - ), - indoc!( - r#" - ── NAMING PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - - This annotation does not match the definition immediately following - it: - - 1│> bar : Num.I64 - 2│> foo = \x -> x - - Is it a typo? If not, put either a newline or comment between them. - "# - ), - ) - } - - #[test] - fn annotation_newline_body_is_fine() { - report_problem_as( - indoc!( - r#" - bar : Num.I64 - - foo = \x -> x - - foo bar - "# - ), - indoc!(""), - ) - } - - #[test] - fn invalid_alias_rigid_var_pattern() { - report_problem_as( - indoc!( - r#" - MyAlias 1 : Num.I64 - - 4 - "# - ), - indoc!( - r#" - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - - This pattern in the definition of `MyAlias` is not what I expect: - - 1│ MyAlias 1 : Num.I64 - ^ - - Only type variables like `a` or `value` can occur in this position. - - ── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─ - - `MyAlias` is not used anywhere in your code. - - 1│ MyAlias 1 : Num.I64 - ^^^^^^^^^^^^^^^^^^^ - - If you didn't intend on using `MyAlias` then remove it so future readers - of your code don't wonder why it is there. - "# - ), - ) - } - - #[test] - fn invalid_opaque_rigid_var_pattern() { - report_problem_as( - indoc!( - r#" - Age 1 := Num.I64 - - a : Age - a - "# - ), - indoc!( - r#" - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - - This pattern in the definition of `Age` is not what I expect: - - 1│ Age 1 := Num.I64 - ^ - - Only type variables like `a` or `value` can occur in this position. - "# - ), - ) - } - - #[test] - fn invalid_num() { - report_problem_as( - indoc!( - r#" - a : Num.Num Num.I64 Num.F64 - a = 3 - - a - "# - ), - indoc!( - r#" - ── TOO MANY TYPE ARGUMENTS ─────────────────────────────── /code/proj/Main.roc ─ - - The `Num` alias expects 1 type argument, but it got 2 instead: - - 1│ a : Num.Num Num.I64 Num.F64 - ^^^^^^^^^^^^^^^^^^^^^^^ - - Are there missing parentheses? - "# - ), - ) - } - - #[test] - fn invalid_num_fn() { - report_problem_as( - indoc!( - r#" - f : Str -> Num.Num Num.I64 Num.F64 - f = \_ -> 3 - - f - "# - ), - indoc!( - r#" - ── TOO MANY TYPE ARGUMENTS ─────────────────────────────── /code/proj/Main.roc ─ - - The `Num` alias expects 1 type argument, but it got 2 instead: - - 1│ f : Str -> Num.Num Num.I64 Num.F64 - ^^^^^^^^^^^^^^^^^^^^^^^ - - Are there missing parentheses? - "# - ), - ) - } - - #[test] - fn too_few_type_arguments() { - report_problem_as( - indoc!( - r#" - Pair a b : [Pair a b] - - x : Pair Num.I64 - x = Pair 2 3 - - x - "# - ), - indoc!( - r#" - ── TOO FEW TYPE ARGUMENTS ──────────────────────────────── /code/proj/Main.roc ─ - - The `Pair` alias expects 2 type arguments, but it got 1 instead: - - 3│ x : Pair Num.I64 - ^^^^^^^^^^^^ - - Are there missing parentheses? - "# - ), - ) - } - - #[test] - fn too_many_type_arguments() { - report_problem_as( - indoc!( - r#" - Pair a b : [Pair a b] - - x : Pair Num.I64 Num.I64 Num.I64 - x = 3 - - x - "# - ), - indoc!( - r#" - ── TOO MANY TYPE ARGUMENTS ─────────────────────────────── /code/proj/Main.roc ─ - - The `Pair` alias expects 2 type arguments, but it got 3 instead: - - 3│ x : Pair Num.I64 Num.I64 Num.I64 - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - Are there missing parentheses? - "# - ), - ) - } - - #[test] - fn phantom_type_variable() { - report_problem_as( - indoc!( - r#" - Foo a : [Foo] - - f : Foo Num.I64 - - f - "# - ), - indoc!( - r#" - ── UNUSED TYPE ALIAS PARAMETER ─────────────────────────── /code/proj/Main.roc ─ - - The `a` type parameter is not used in the `Foo` alias definition: - - 1│ Foo a : [Foo] - ^ - - Roc does not allow unused type alias parameters! - - Tip: If you want an unused type parameter (a so-called "phantom - type"), read the guide section on phantom values. - "# - ), - ) - } - - #[test] - fn elm_function_syntax() { - report_problem_as( - indoc!( - r#" - f x y = x - "# - ), - indoc!( - r#" - ── ARGUMENTS BEFORE EQUALS ─────────────────────────────── /code/proj/Main.roc ─ - - I am partway through parsing a definition, but I got stuck here: - - 1│ f x y = x - ^^^ - - Looks like you are trying to define a function. In roc, functions are - always written as a lambda, like increment = \n -> n + 1. - "# - ), - ) - } - - #[test] - fn two_different_cons() { - report_problem_as( - indoc!( - r#" - ConsList a : [Cons a (ConsList a), Nil] - - x : ConsList {} - x = Cons {} (Cons "foo" Nil) - - x - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - Something is off with the body of the `x` definition: - - 3│ x : ConsList {} - 4│ x = Cons {} (Cons "foo" Nil) - ^^^^^^^^^^^^^^^^^^^^^^^^ - - This `Cons` tag application has the type: - - [Cons {} [Cons Str [Cons {} a, Nil] as a, Nil], Nil] - - But the type annotation on `x` says it should be: - - [Cons {} a, Nil] as a - "# - ), - ) - } - - #[test] - fn mutually_recursive_types_with_type_error() { - report_problem_as( - indoc!( - r#" - AList a b : [ACons a (BList a b), ANil] - BList a b : [BCons a (AList a b), BNil] - - x : AList Num.I64 Num.I64 - x = ACons 0 (BCons 1 (ACons "foo" BNil )) - - y : BList a a - y = BNil - - { x, y } - "# - ), - // TODO render tag unions across multiple lines - // TODO do not show recursion var if the recursion var does not render on the surface of a type - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - Something is off with the body of the `x` definition: - - 4│ x : AList Num.I64 Num.I64 - 5│ x = ACons 0 (BCons 1 (ACons "foo" BNil )) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - This `ACons` tag application has the type: - - [ACons (Int Signed64) [BCons (Int Signed64) [ACons Str [BCons I64 [ACons I64 (BList I64 I64), - ANil] as ∞, BNil], ANil], BNil], ANil] - - But the type annotation on `x` says it should be: - - [ACons I64 (BList I64 I64), ANil] as a - "# - ), - ) - } - - #[test] - fn integer_out_of_range() { - report_problem_as( - indoc!( - r#" - x = 170_141_183_460_469_231_731_687_303_715_884_105_728_000 - - y = -170_141_183_460_469_231_731_687_303_715_884_105_728_000 - - h = 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF - l = -0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF - - minlit = -170_141_183_460_469_231_731_687_303_715_884_105_728 - maxlit = 340_282_366_920_938_463_463_374_607_431_768_211_455 - - x + y + h + l + minlit + maxlit - "# - ), - indoc!( - r#" - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - - This integer literal is too big: - - 1│ x = 170_141_183_460_469_231_731_687_303_715_884_105_728_000 - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - The largest number representable in Roc is the maximum U128 value, - 340_282_366_920_938_463_463_374_607_431_768_211_455. - - Tip: Learn more about number literals at TODO - - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - - This integer literal is too small: - - 3│ y = -170_141_183_460_469_231_731_687_303_715_884_105_728_000 - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - The smallest number representable in Roc is the minimum I128 value, - -170_141_183_460_469_231_731_687_303_715_884_105_728. - - Tip: Learn more about number literals at TODO - - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - - This integer literal is too big: - - 5│ h = 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - The largest number representable in Roc is the maximum U128 value, - 340_282_366_920_938_463_463_374_607_431_768_211_455. - - Tip: Learn more about number literals at TODO - - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - - This integer literal is too small: - - 6│ l = -0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - The smallest number representable in Roc is the minimum I128 value, - -170_141_183_460_469_231_731_687_303_715_884_105_728. - - Tip: Learn more about number literals at TODO - "# - ), - ) - } - - #[test] - fn float_out_of_range() { - // have to deal with some whitespace issues because of the format! macro - report_problem_as( - indoc!( - r#" - overflow = 11.7976931348623157e308 - underflow = -11.7976931348623157e308 - - overflow + underflow - "# - ), - indoc!( - r#" - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - - This float literal is too big: - - 1│ overflow = 11.7976931348623157e308 - ^^^^^^^^^^^^^^^^^^^^^^^ - - Roc uses signed 64-bit floating points, allowing values between - -1.7976931348623157e308 and 1.7976931348623157e308 - - Tip: Learn more about number literals at TODO - - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - - This float literal is too small: - - 2│ underflow = -11.7976931348623157e308 - ^^^^^^^^^^^^^^^^^^^^^^^^ - - Roc uses signed 64-bit floating points, allowing values between - -1.7976931348623157e308 and 1.7976931348623157e308 - - Tip: Learn more about number literals at TODO - "# - ), - ) - } - - #[test] - fn integer_malformed() { - // the generated messages here are incorrect. Waiting for a rust nightly feature to land, - // see https://github.com/rust-lang/rust/issues/22639 - // this test is here to spot regressions in error reporting - report_problem_as( - indoc!( - r#" - dec = 100A - - hex = 0xZZZ - - oct = 0o9 - - bin = 0b2 - - dec + hex + oct + bin - "# - ), - indoc!( - r#" - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - - This integer literal contains an invalid digit: - - 1│ dec = 100A - ^^^^ - - Integer literals can only contain the digits - 0-9, or have an integer suffix. - - Tip: Learn more about number literals at TODO - - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - - This hex integer literal contains an invalid digit: - - 3│ hex = 0xZZZ - ^^^^^ - - Hexadecimal (base-16) integer literals can only contain the digits - 0-9, a-f and A-F, or have an integer suffix. - - Tip: Learn more about number literals at TODO - - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - - This octal integer literal contains an invalid digit: - - 5│ oct = 0o9 - ^^^ - - Octal (base-8) integer literals can only contain the digits - 0-7, or have an integer suffix. - - Tip: Learn more about number literals at TODO - - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - - This binary integer literal contains an invalid digit: - - 7│ bin = 0b2 - ^^^ - - Binary (base-2) integer literals can only contain the digits - 0 and 1, or have an integer suffix. - - Tip: Learn more about number literals at TODO - "# - ), - ) - } - - #[test] - fn integer_empty() { - report_problem_as( - indoc!( - r#" - dec = 20 - - hex = 0x - - oct = 0o - - bin = 0b - - dec + hex + oct + bin - "# - ), - indoc!( - r#" - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - - This hex integer literal contains no digits: - - 3│ hex = 0x - ^^ - - Hexadecimal (base-16) integer literals must contain at least one of - the digits 0-9, a-f and A-F, or have an integer suffix. - - Tip: Learn more about number literals at TODO - - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - - This octal integer literal contains no digits: - - 5│ oct = 0o - ^^ - - Octal (base-8) integer literals must contain at least one of the - digits 0-7, or have an integer suffix. - - Tip: Learn more about number literals at TODO - - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - - This binary integer literal contains no digits: - - 7│ bin = 0b - ^^ - - Binary (base-2) integer literals must contain at least one of the - digits 0 and 1, or have an integer suffix. - - Tip: Learn more about number literals at TODO - "# - ), - ) - } - - #[test] - fn float_malformed() { - report_problem_as( - indoc!( - r#" - x = 3.0A - - x - "# - ), - indoc!( - r#" - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - - This float literal contains an invalid digit: - - 1│ x = 3.0A - ^^^^ - - Floating point literals can only contain the digits 0-9, or use - scientific notation 10e4, or have a float suffix. - - Tip: Learn more about number literals at TODO - "# - ), - ) - } - - #[test] - fn invalid_record_update() { - report_problem_as( - indoc!( - r#" - foo = { bar: 3 } - updateNestedRecord = { foo.bar & x: 4 } - - example = { age: 42 } - - # these should work - y = { Test.example & age: 3 } - x = { example & age: 4 } - - { updateNestedRecord, foo, x, y } - "# - ), - indoc!( - r#" - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - - This expression cannot be updated: - - 2│ updateNestedRecord = { foo.bar & x: 4 } - ^^^^^^^ - - Only variables can be updated with record update syntax. - "# - ), - ) - } - - #[test] - fn module_not_imported() { - report_problem_as( - indoc!( - r#" - Foo.test - "# - ), - indoc!( - r#" - ── MODULE NOT IMPORTED ─────────────────────────────────── /code/proj/Main.roc ─ - - The `Foo` module is not imported: - - 1│ Foo.test - ^^^^^^^^ - - Is there an import missing? Perhaps there is a typo. Did you mean one - of these? - - Box - Bool - Num - Set - "# - ), - ) - } - - #[test] - fn optional_record_default_type_error() { - report_problem_as( - indoc!( - r#" - \{ x, y ? True } -> x + y - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - The 2nd argument to `add` is not what I expect: - - 1│ \{ x, y ? True } -> x + y - ^ - - This `y` value is a: - - [True]a - - But `add` needs the 2nd argument to be: - - Num a - "# - ), - ) - } - - #[test] - fn optional_record_default_with_signature() { - report_problem_as( - indoc!( - r#" - f : { x : Num.I64, y ? Num.I64 } -> Num.I64 - f = \{ x, y ? "foo" } -> (\g, _ -> g) x y - - f - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - The 1st argument to `f` is weird: - - 2│ f = \{ x, y ? "foo" } -> (\g, _ -> g) x y - ^^^^^^^^^^^^^^^^ - - The argument is a pattern that matches record values of type: - - { x : I64, y ? Str } - - But the annotation on `f` says the 1st argument should be: - - { x : I64, y ? I64 } - "# - ), - ) - } - - #[test] - fn optional_record_invalid_let_binding() { - report_problem_as( - indoc!( - r#" - \rec -> - { x, y } : { x : Num.I64, y ? Str } - { x, y } = rec - - { x, y } - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - Something is off with the body of this definition: - - 2│> { x, y } : { x : Num.I64, y ? Str } - 3│> { x, y } = rec - - The body is a value of type: - - { x : I64, y : Str } - - But the type annotation says it should be: - - { x : I64, y ? Str } - - Tip: To extract the `.y` field it must be non-optional, but the type - says this field is optional. Learn more about optional fields at TODO. - "# - ), - ) - } - - #[test] - fn optional_record_invalid_function() { - report_problem_as( - indoc!( - r#" - f : { x : Num.I64, y ? Num.I64 } -> Num.I64 - f = \{ x, y } -> x + y - - f - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - The 1st argument to `f` is weird: - - 2│ f = \{ x, y } -> x + y - ^^^^^^^^ - - The argument is a pattern that matches record values of type: - - { x : I64, y : I64 } - - But the annotation on `f` says the 1st argument should be: - - { x : I64, y ? I64 } - - Tip: To extract the `.y` field it must be non-optional, but the type - says this field is optional. Learn more about optional fields at TODO. - "# - ), - ) - } - - #[test] - fn optional_record_invalid_when() { - report_problem_as( - indoc!( - r#" - f : { x : Num.I64, y ? Num.I64 } -> Num.I64 - f = \r -> - when r is - { x, y } -> x + y - - f - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - The branches of this `when` expression don't match the condition: - - 3│> when r is - 4│ { x, y } -> x + y - - This `r` value is a: - - { x : I64, y ? I64 } - - But the branch patterns have type: - - { x : I64, y : I64 } - - The branches must be cases of the `when` condition's type! - - Tip: To extract the `.y` field it must be non-optional, but the type - says this field is optional. Learn more about optional fields at TODO. - "# - ), - ) - } - - #[test] - fn optional_record_invalid_access() { - report_problem_as( - indoc!( - r#" - f : { x : Num.I64, y ? Num.I64 } -> Num.I64 - f = \r -> r.y - - f - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - This expression is used in an unexpected way: - - 2│ f = \r -> r.y - ^^^ - - This `r` value is a: - - { x : I64, y ? I64 } - - But you are trying to use it as: - - { x : I64, y : I64 } - - Tip: To extract the `.y` field it must be non-optional, but the type - says this field is optional. Learn more about optional fields at TODO. - "# - ), - ) - } - - #[test] - fn optional_record_invalid_accessor() { - report_problem_as( - indoc!( - r#" - f : { x : Num.I64, y ? Num.I64 } -> Num.I64 - f = \r -> .y r - - f - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - The 1st argument to this function is not what I expect: - - 2│ f = \r -> .y r - ^ - - This `r` value is a: - - { x : I64, y ? I64 } - - But this function needs the 1st argument to be: - - { x : I64, y : I64 } - - Tip: To extract the `.y` field it must be non-optional, but the type - says this field is optional. Learn more about optional fields at TODO. - "# - ), - ) - } - - #[test] - fn guard_mismatch_with_annotation() { - report_problem_as( - indoc!( - r#" - f : { x : Num.I64, y : Num.I64 } -> Num.I64 - f = \r -> - when r is - { x, y : "foo" } -> x + 0 - _ -> 0 - - f - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - The branches of this `when` expression don't match the condition: - - 3│> when r is - 4│ { x, y : "foo" } -> x + 0 - 5│ _ -> 0 - - This `r` value is a: - - { x : I64, y : I64 } - - But the branch patterns have type: - - { x : I64, y : Str } - - The branches must be cases of the `when` condition's type! - "# - ), - ) - } - - #[test] - fn optional_field_mismatch_with_annotation() { - report_problem_as( - indoc!( - r#" - f : { x : Num.I64, y ? Num.I64 } -> Num.I64 - f = \r -> - when r is - { x, y ? "foo" } -> (\g, _ -> g) x y - _ -> 0 - - f - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - The branches of this `when` expression don't match the condition: - - 3│> when r is - 4│ { x, y ? "foo" } -> (\g, _ -> g) x y - 5│ _ -> 0 - - This `r` value is a: - - { x : I64, y ? I64 } - - But the branch patterns have type: - - { x : I64, y ? Str } - - The branches must be cases of the `when` condition's type! - "# - ), - ) - } - - #[test] - fn incorrect_optional_field() { - report_problem_as( - indoc!( - r#" - { x: 5, y ? 42 } - "# - ), - indoc!( - r#" - ── BAD OPTIONAL VALUE ──────────────────────────────────── /code/proj/Main.roc ─ - - This record uses an optional value for the `.y` field in an incorrect - context! - - 1│ { x: 5, y ? 42 } - ^^^^^^ - - You can only use optional values in record destructuring, like: - - { answer ? 42, otherField } = myRecord - "# - ), - ) - } - #[test] - fn first_wildcard_is_required() { - report_problem_as( - indoc!( - r#" - when Foo 1 2 3 is - Foo _ 1 _ -> 1 - _ -> 2 - "# - ), - "", - ) - } - - #[test] - fn second_wildcard_is_redundant() { - report_problem_as( - indoc!( - r#" - when Foo 1 2 3 is - Foo _ 1 _ -> 1 - _ -> 2 - _ -> 3 - "# - ), - indoc!( - r#" - ── REDUNDANT PATTERN ───────────────────────────────────── /code/proj/Main.roc ─ - - The 3rd pattern is redundant: - - 1│ when Foo 1 2 3 is - 2│ Foo _ 1 _ -> 1 - 3│ _ -> 2 - 4│ _ -> 3 + "# + ), + @r###" + ── DUPLICATE FIELD NAME ────────────────────────────────── /code/proj/Main.roc ─ + + This record defines the `.x` field twice! + + 5│ { r & + 6│> x: 4, + 7│ y: 3, + 8│> x: "foo" + 9│ } + + In the rest of the program, I will only use the latter definition: + + 5│ { r & + 6│ x: 4, + 7│ y: 3, + 8│> x: "foo" + 9│ } + + For clarity, remove the previous `.x` definitions from this record. + "### + ); + + test_report!( + record_type_duplicate_field, + indoc!( + r#" + a : { foo : Num.I64, bar : {}, foo : Str } + a = { bar: {}, foo: "foo" } + + a + "# + ), + @r###" + ── DUPLICATE FIELD NAME ────────────────────────────────── /code/proj/Main.roc ─ + + This record type defines the `.foo` field twice! + + 4│ a : { foo : Num.I64, bar : {}, foo : Str } + ^^^^^^^^^^^^^ ^^^^^^^^^ + + In the rest of the program, I will only use the latter definition: + + 4│ a : { foo : Num.I64, bar : {}, foo : Str } + ^^^^^^^^^ + + For clarity, remove the previous `.foo` definitions from this record + type. + "### + ); + + test_report!( + tag_union_duplicate_tag, + indoc!( + r#" + a : [Foo Num.I64, Bar {}, Foo Str] + a = Foo "foo" + + a + "# + ), + @r###" + ── DUPLICATE TAG NAME ──────────────────────────────────── /code/proj/Main.roc ─ + + This tag union type defines the `Foo` tag twice! + + 4│ a : [Foo Num.I64, Bar {}, Foo Str] + ^^^^^^^^^^^ ^^^^^^^ + + In the rest of the program, I will only use the latter definition: + + 4│ a : [Foo Num.I64, Bar {}, Foo Str] + ^^^^^^^ + + For clarity, remove the previous `Foo` definitions from this tag union + type. + "### + ); + + test_report!( + annotation_definition_mismatch, + indoc!( + r#" + bar : Num.I64 + foo = \x -> x + + # NOTE: neither bar or foo are defined at this point + 4 + "# + ), + @r###" + ── NAMING PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + + This annotation does not match the definition immediately following + it: + + 4│> bar : Num.I64 + 5│> foo = \x -> x + + Is it a typo? If not, put either a newline or comment between them. + "### + ); + + test_report!( + annotation_newline_body_is_fine, + indoc!( + r#" + bar : Num.I64 + + foo = \x -> x + + foo bar + "# + ), + @"" + ); + + test_report!( + invalid_alias_rigid_var_pattern, + indoc!( + r#" + MyAlias 1 : Num.I64 + + 4 + "# + ), + @r###" + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + + This pattern in the definition of `MyAlias` is not what I expect: + + 4│ MyAlias 1 : Num.I64 ^ - Any value of this shape will be handled by a previous pattern, so this - one should be removed. + Only type variables like `a` or `value` can occur in this position. + + ── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─ + + `MyAlias` is not used anywhere in your code. + + 4│ MyAlias 1 : Num.I64 + ^^^^^^^^^^^^^^^^^^^ + + If you didn't intend on using `MyAlias` then remove it so future readers + of your code don't wonder why it is there. + "### + ); + + test_report!( + invalid_opaque_rigid_var_pattern, + indoc!( + r#" + Age 1 := Num.I64 + + a : Age + a "# - ), - ) - } + ), + @r###" + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn alias_using_alias() { - report_problem_as( - indoc!( - r#" - # The color of a node. Leaves are considered Black. - NodeColor : [Red, Black] + This pattern in the definition of `Age` is not what I expect: - RBTree k v : [Node NodeColor k v (RBTree k v) (RBTree k v), Empty] + 4│ Age 1 := Num.I64 + ^ - # Create an empty dictionary. - empty : RBTree k v - empty = - Empty + Only type variables like `a` or `value` can occur in this position. + "### + ); - empty - "# - ), - "", - ) - } + test_report!( + invalid_num, + indoc!( + r#" + a : Num.Num Num.I64 Num.F64 + a = 3 - #[test] - fn unused_argument() { - report_problem_as( - indoc!( - r#" - f = \foo -> 1 - - f - "# - ), - indoc!( - r#" - ── UNUSED ARGUMENT ─────────────────────────────────────── /code/proj/Main.roc ─ - - `f` doesn't use `foo`. - - 1│ f = \foo -> 1 - ^^^ - - If you don't need `foo`, then you can just remove it. However, if you - really do need `foo` as an argument of `f`, prefix it with an underscore, - like this: "_`foo`". Adding an underscore at the start of a variable - name is a way of saying that the variable is not used. + a "# - ), - ) - } + ), + @r###" + ── TOO MANY TYPE ARGUMENTS ─────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn qualified_tag() { - report_problem_as( - indoc!( - r#" - Foo.Bar - "# - ), - indoc!( - r#" - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + The `Num` alias expects 1 type argument, but it got 2 instead: - I am trying to parse a qualified name here: + 4│ a : Num.Num Num.I64 Num.F64 + ^^^^^^^^^^^^^^^^^^^^^^^ - 1│ Foo.Bar - ^ + Are there missing parentheses? + "### + ); - This looks like a qualified tag name to me, but tags cannot be - qualified! Maybe you wanted a qualified name, something like - Json.Decode.string? + test_report!( + invalid_num_fn, + indoc!( + r#" + f : Str -> Num.Num Num.I64 Num.F64 + f = \_ -> 3 + + f "# - ), - ) - } + ), + @r###" + ── TOO MANY TYPE ARGUMENTS ─────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn module_ident_ends_with_dot() { - report_problem_as( - indoc!( - r#" - Foo.Bar. - "# - ), - indoc!( - r#" - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + The `Num` alias expects 1 type argument, but it got 2 instead: - I am trying to parse a qualified name here: + 4│ f : Str -> Num.Num Num.I64 Num.F64 + ^^^^^^^^^^^^^^^^^^^^^^^ - 1│ Foo.Bar. - ^ + Are there missing parentheses? + "### + ); - I was expecting to see an identifier next, like height. A complete - qualified name looks something like Json.Decode.string. + test_report!( + too_few_type_arguments, + indoc!( + r#" + Pair a b : [Pair a b] + + x : Pair Num.I64 + x = Pair 2 3 + + x "# - ), - ) - } + ), + @r###" + ── TOO FEW TYPE ARGUMENTS ──────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn record_access_ends_with_dot() { - report_problem_as( - indoc!( - r#" - foo.bar. - "# - ), - indoc!( - r#" - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + The `Pair` alias expects 2 type arguments, but it got 1 instead: - I trying to parse a record field access here: + 6│ x : Pair Num.I64 + ^^^^^^^^^^^^ - 1│ foo.bar. - ^ + Are there missing parentheses? + "### + ); - So I expect to see a lowercase letter next, like .name or .height. + test_report!( + too_many_type_arguments, + indoc!( + r#" + Pair a b : [Pair a b] + + x : Pair Num.I64 Num.I64 Num.I64 + x = 3 + + x "# - ), - ) - } + ), + @r###" + ── TOO MANY TYPE ARGUMENTS ─────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn type_annotation_double_colon() { - report_problem_as( - indoc!( - r#" - f :: I64 - f = 42 + The `Pair` alias expects 2 type arguments, but it got 3 instead: - f - "# - ), - indoc!( - r#" - ── UNKNOWN OPERATOR ────────────────────────────────────── /code/proj/Main.roc ─ + 6│ x : Pair Num.I64 Num.I64 Num.I64 + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - This looks like an operator, but it's not one I recognize! + Are there missing parentheses? + "### + ); - 1│ f :: I64 - ^^ + test_report!( + phantom_type_variable, + indoc!( + r#" + Foo a : [Foo] - I have no specific suggestion for this operator, see TODO for the full - list of operators in Roc. + f : Foo Num.I64 + + f "# - ), - ) - } + ), + @r###" + ── UNUSED TYPE ALIAS PARAMETER ─────────────────────────── /code/proj/Main.roc ─ - #[test] - fn double_equals_in_def() { - // NOTE: VERY BAD ERROR MESSAGE - // - // looks like `x y` are considered argument to the add, even though they are - // on a lower indentation level - report_problem_as( - indoc!( - r#" - x = 3 - y = - x == 5 - Num.add 1 2 + The `a` type parameter is not used in the `Foo` alias definition: - { x, y } - "# - ), - indoc!( - r#" - ── TOO MANY ARGS ───────────────────────────────────────── /code/proj/Main.roc ─ + 4│ Foo a : [Foo] + ^ - This value is not a function, but it was given 3 arguments: + Roc does not allow unused type alias parameters! - 3│ x == 5 - ^ + Tip: If you want an unused type parameter (a so-called "phantom + type"), read the guide section on phantom values. + "### + ); - Are there any missing commas? Or missing parentheses? + test_report!( + elm_function_syntax, + indoc!( + r#" + f x y = x "# - ), - ) - } + ), + @r###" + ── ARGUMENTS BEFORE EQUALS ────────────────── tmp/elm_function_syntax/Test.roc ─ - #[test] - fn tag_union_open() { - report_problem_as( - indoc!( - r#" - f : [ - "# - ), - indoc!( - r#" - ── UNFINISHED TAG UNION TYPE ───────────────────────────── /code/proj/Main.roc ─ + I am partway through parsing a definition, but I got stuck here: - I just started parsing a tag union type, but I got stuck here: + 1│ app "test" provides [main] to "./platform" + 2│ + 3│ main = + 4│ f x y = x + ^^^ - 1│ f : [ - ^ + Looks like you are trying to define a function. In roc, functions are + always written as a lambda, like increment = \n -> n + 1. + "### + ); - Tag unions look like [Many I64, None], so I was expecting to see a tag - name next. + test_report!( + two_different_cons, + indoc!( + r#" + ConsList a : [Cons a (ConsList a), Nil] + + x : ConsList {} + x = Cons {} (Cons "foo" Nil) + + x "# - ), - ) - } + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn tag_union_end() { - report_problem_as( - indoc!( - r#" - f : [Yes, - "# - ), - indoc!( - r#" - ── UNFINISHED TAG UNION TYPE ───────────────────────────── /code/proj/Main.roc ─ + Something is off with the body of the `x` definition: - I am partway through parsing a tag union type, but I got stuck here: + 6│ x : ConsList {} + 7│ x = Cons {} (Cons "foo" Nil) + ^^^^^^^^^^^^^^^^^^^^^^^^ - 1│ f : [Yes, - ^ + This `Cons` tag application has the type: - I was expecting to see a closing square bracket before this, so try - adding a ] and see if that helps? + [Cons {} [Cons Str [Cons {} a, Nil] as a, Nil], Nil] + + But the type annotation on `x` says it should be: + + [Cons {} a, Nil] as a + "### + ); + + test_report!( + mutually_recursive_types_with_type_error, + indoc!( + r#" + AList a b : [ACons a (BList a b), ANil] + BList a b : [BCons a (AList a b), BNil] + + x : AList Num.I64 Num.I64 + x = ACons 0 (BCons 1 (ACons "foo" BNil )) + + y : BList a a + y = BNil + + { x, y } "# - ), - ) - } + ), + // TODO render tag unions across multiple lines + // TODO do not show recursion var if the recursion var does not render on the surface of a type + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn tag_union_lowercase_tag_name() { - report_problem_as( - indoc!( - r#" - f : [lowercase] - "# - ), - indoc!( - r#" - ── WEIRD TAG NAME ──────────────────────────────────────── /code/proj/Main.roc ─ + Something is off with the body of the `x` definition: - I am partway through parsing a tag union type, but I got stuck here: + 7│ x : AList Num.I64 Num.I64 + 8│ x = ACons 0 (BCons 1 (ACons "foo" BNil )) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - 1│ f : [lowercase] - ^ + This `ACons` tag application has the type: - I was expecting to see a tag name. + [ACons (Int Signed64) [BCons (Int Signed64) [ACons Str [BCons I64 [ACons I64 (BList I64 I64), + ANil] as ∞, BNil], ANil], BNil], ANil] - Hint: Tag names start with an uppercase letter, like Err or Green. + But the type annotation on `x` says it should be: + + [ACons I64 (BList I64 I64), ANil] as a + "### + ); + + test_report!( + integer_out_of_range, + indoc!( + r#" + x = 170_141_183_460_469_231_731_687_303_715_884_105_728_000 + + y = -170_141_183_460_469_231_731_687_303_715_884_105_728_000 + + h = 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF + l = -0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF + + minlit = -170_141_183_460_469_231_731_687_303_715_884_105_728 + maxlit = 340_282_366_920_938_463_463_374_607_431_768_211_455 + + x + y + h + l + minlit + maxlit "# - ), - ) - } + ), + @r###" + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn tag_union_second_lowercase_tag_name() { - report_problem_as( - indoc!( - r#" - f : [Good, bad] - "# - ), - indoc!( - r#" - ── WEIRD TAG NAME ──────────────────────────────────────── /code/proj/Main.roc ─ + This integer literal is too big: - I am partway through parsing a tag union type, but I got stuck here: + 4│ x = 170_141_183_460_469_231_731_687_303_715_884_105_728_000 + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - 1│ f : [Good, bad] - ^ + The largest number representable in Roc is the maximum U128 value, + 340_282_366_920_938_463_463_374_607_431_768_211_455. - I was expecting to see a tag name. + Tip: Learn more about number literals at TODO - Hint: Tag names start with an uppercase letter, like Err or Green. + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + + This integer literal is too small: + + 6│ y = -170_141_183_460_469_231_731_687_303_715_884_105_728_000 + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + The smallest number representable in Roc is the minimum I128 value, + -170_141_183_460_469_231_731_687_303_715_884_105_728. + + Tip: Learn more about number literals at TODO + + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + + This integer literal is too big: + + 8│ h = 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + The largest number representable in Roc is the maximum U128 value, + 340_282_366_920_938_463_463_374_607_431_768_211_455. + + Tip: Learn more about number literals at TODO + + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + + This integer literal is too small: + + 9│ l = -0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + The smallest number representable in Roc is the minimum I128 value, + -170_141_183_460_469_231_731_687_303_715_884_105_728. + + Tip: Learn more about number literals at TODO + "### + ); + + // have to deal with some whitespace issues because of the format! macro + test_report!( + float_out_of_range, + indoc!( + r#" + overflow = 11.7976931348623157e308 + underflow = -11.7976931348623157e308 + + overflow + underflow "# - ), - ) - } + ), + @r###" + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn record_type_open() { - report_problem_as( - indoc!( - r#" - f : { - "# - ), - indoc!( - r#" - ── UNFINISHED RECORD TYPE ──────────────────────────────── /code/proj/Main.roc ─ + This float literal is too big: - I just started parsing a record type, but I got stuck here: + 4│ overflow = 11.7976931348623157e308 + ^^^^^^^^^^^^^^^^^^^^^^^ - 1│ f : { - ^ + Roc uses signed 64-bit floating points, allowing values between + -1.7976931348623157e308 and 1.7976931348623157e308 - Record types look like { name : String, age : Int }, so I was - expecting to see a field name next. + Tip: Learn more about number literals at TODO + + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + + This float literal is too small: + + 5│ underflow = -11.7976931348623157e308 + ^^^^^^^^^^^^^^^^^^^^^^^^ + + Roc uses signed 64-bit floating points, allowing values between + -1.7976931348623157e308 and 1.7976931348623157e308 + + Tip: Learn more about number literals at TODO + "### + ); + + // the generated messages here are incorrect. Waiting for a rust nightly feature to land, + // see https://github.com/rust-lang/rust/issues/22639 + // this test is here to spot regressions in error reporting + test_report!( + integer_malformed, + indoc!( + r#" + dec = 100A + + hex = 0xZZZ + + oct = 0o9 + + bin = 0b2 + + dec + hex + oct + bin "# - ), - ) - } + ), + @r###" + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn record_type_open_indent() { - report_problem_as( - indoc!( - r#" - f : { - foo : I64, - "# - ), - indoc!( - r#" - ── UNFINISHED RECORD TYPE ──────────────────────────────── /code/proj/Main.roc ─ + This integer literal contains an invalid digit: - I am partway through parsing a record type, but I got stuck here: + 4│ dec = 100A + ^^^^ - 1│ f : { - ^ + Integer literals can only contain the digits + 0-9, or have an integer suffix. - I was expecting to see a closing curly brace before this, so try - adding a } and see if that helps? + Tip: Learn more about number literals at TODO - Note: I may be confused by indentation + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + + This hex integer literal contains an invalid digit: + + 6│ hex = 0xZZZ + ^^^^^ + + Hexadecimal (base-16) integer literals can only contain the digits + 0-9, a-f and A-F, or have an integer suffix. + + Tip: Learn more about number literals at TODO + + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + + This octal integer literal contains an invalid digit: + + 8│ oct = 0o9 + ^^^ + + Octal (base-8) integer literals can only contain the digits + 0-7, or have an integer suffix. + + Tip: Learn more about number literals at TODO + + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + + This binary integer literal contains an invalid digit: + + 10│ bin = 0b2 + ^^^ + + Binary (base-2) integer literals can only contain the digits + 0 and 1, or have an integer suffix. + + Tip: Learn more about number literals at TODO + "### + ); + + test_report!( + integer_empty, + indoc!( + r#" + dec = 20 + + hex = 0x + + oct = 0o + + bin = 0b + + dec + hex + oct + bin "# - ), - ) - } + ), + @r###" + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn record_type_end() { - report_problem_as( - indoc!( - r#" - f : { a: Int, - "# - ), - indoc!( - r#" - ── UNFINISHED RECORD TYPE ──────────────────────────────── /code/proj/Main.roc ─ + This hex integer literal contains no digits: - I am partway through parsing a record type, but I got stuck here: + 6│ hex = 0x + ^^ - 1│ f : { a: Int, - ^ + Hexadecimal (base-16) integer literals must contain at least one of + the digits 0-9, a-f and A-F, or have an integer suffix. - I was expecting to see a closing curly brace before this, so try - adding a } and see if that helps? + Tip: Learn more about number literals at TODO + + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + + This octal integer literal contains no digits: + + 8│ oct = 0o + ^^ + + Octal (base-8) integer literals must contain at least one of the + digits 0-7, or have an integer suffix. + + Tip: Learn more about number literals at TODO + + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + + This binary integer literal contains no digits: + + 10│ bin = 0b + ^^ + + Binary (base-2) integer literals must contain at least one of the + digits 0 and 1, or have an integer suffix. + + Tip: Learn more about number literals at TODO + "### + ); + + test_report!( + float_malformed, + indoc!( + r#" + x = 3.0A + + x "# - ), - ) - } + ), + @r###" + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn record_type_keyword_field_name() { - report_problem_as( - indoc!( - r#" - f : { if : I64 } - "# - ), - indoc!( - r#" - ── UNFINISHED RECORD TYPE ──────────────────────────────── /code/proj/Main.roc ─ + This float literal contains an invalid digit: - I just started parsing a record type, but I got stuck on this field - name: + 4│ x = 3.0A + ^^^^ - 1│ f : { if : I64 } - ^^ + Floating point literals can only contain the digits 0-9, or use + scientific notation 10e4, or have a float suffix. - Looks like you are trying to use `if` as a field name, but that is a - reserved word. Try using a different name! + Tip: Learn more about number literals at TODO + "### + ); + + test_report!( + invalid_record_update, + indoc!( + r#" + foo = { bar: 3 } + updateNestedRecord = { foo.bar & x: 4 } + + example = { age: 42 } + + # these should work + y = { Test.example & age: 3 } + x = { example & age: 4 } + + { updateNestedRecord, foo, x, y } "# - ), - ) - } + ), + @r###" + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn record_type_missing_comma() { - // a case where the message cannot be as good as elm's - report_problem_as( - indoc!( - r#" - f : { foo bar } - "# - ), - indoc!( - r#" - ── UNFINISHED RECORD TYPE ──────────────────────────────── /code/proj/Main.roc ─ + This expression cannot be updated: - I am partway through parsing a record type, but I got stuck here: + 5│ updateNestedRecord = { foo.bar & x: 4 } + ^^^^^^^ - 1│ f : { foo bar } - ^ + Only variables can be updated with record update syntax. - I was expecting to see a colon, question mark, comma or closing curly - brace. + ── MODULE NOT IMPORTED ─────────────────────────────────── /code/proj/Main.roc ─ + + The `Test` module is not imported: + + 10│ y = { Test.example & age: 3 } + ^^^^^^^^^^^^ + + Is there an import missing? Perhaps there is a typo. Did you mean one + of these? + + List + Set + Dict + Result + + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + + This expression cannot be updated: + + 10│ y = { Test.example & age: 3 } + ^^^^^^^^^^^^ + + Only variables can be updated with record update syntax. + "### + ); + + test_report!( + module_not_imported, + indoc!( + r#" + Foo.test "# - ), - ) - } + ), + @r###" + ── MODULE NOT IMPORTED ─────────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn record_type_tab() { - // a case where the message cannot be as good as elm's - report_problem_as( - "f : { foo \t }", - indoc!( - r#" - ── TAB CHARACTER ───────────────────────────────────────── /code/proj/Main.roc ─ + The `Foo` module is not imported: - I encountered a tab character + 4│ Foo.test + ^^^^^^^^ - 1│ f : { foo } - ^ + Is there an import missing? Perhaps there is a typo. Did you mean one + of these? - Tab characters are not allowed. + Box + Bool + Num + Set + "### + ); + + test_report!( + optional_record_default_type_error, + indoc!( + r#" + \{ x, y ? True } -> x + y "# - ), - ) - } + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn comment_with_tab() { - report_problem_as( - "# comment with a \t\n4", - indoc!( - " - ── TAB CHARACTER ───────────────────────────────────────── /code/proj/Main.roc ─ + The 2nd argument to `add` is not what I expect: - I encountered a tab character - - 1│ # comment with a \t - ^ - - Tab characters are not allowed. - " - ), - ) - } - - #[test] - fn type_in_parens_start() { - // TODO bad error message - report_problem_as( - indoc!( - r#" - f : ( - "# - ), - indoc!( - r#" - ── UNFINISHED TYPE ─────────────────────────────────────── /code/proj/Main.roc ─ - - I just started parsing a type, but I got stuck here: - - 1│ f : ( - ^ - - I am expecting a type next, like Bool or List a. - "# - ), - ) - } - - #[test] - fn type_in_parens_end() { - report_problem_as( - indoc!( - r#" - f : ( I64 - "# - ), - indoc!( - r#" - ── UNFINISHED PARENTHESES ──────────────────────────────── /code/proj/Main.roc ─ - - I am partway through parsing a type in parentheses, but I got stuck - here: - - 1│ f : ( I64 - ^ - - I was expecting to see a parenthesis before this, so try adding a ) - and see if that helps? - - Note: I may be confused by indentation - "# - ), - ) - } - - #[test] - fn type_apply_double_dot() { - report_problem_as( - indoc!( - r#" - f : Foo..Bar - - f - "# - ), - indoc!( - r#" - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - - I am confused by this type name: - - 1│ f : Foo..Bar - ^^^^^^^^ - - Type names start with an uppercase letter, and can optionally be - qualified by a module name, like Bool or Http.Request.Request. - "# - ), - ) - - // ── DOUBLE DOT ────────────────────────────────────────────────────────────────── - // - // I encountered two dots in a row: - // - // 1│ f : Foo..Bar - // ^ - // - // Try removing one of them. - } - - #[test] - fn type_apply_trailing_dot() { - report_problem_as( - indoc!( - r#" - f : Foo.Bar. - - f - "# - ), - indoc!( - r#" - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - - I am confused by this type name: - - 1│ f : Foo.Bar. - ^^^^^^^^ - - Type names start with an uppercase letter, and can optionally be - qualified by a module name, like Bool or Http.Request.Request. - "# - ), - ) - - // ── 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. - } - - #[test] - fn type_apply_stray_dot() { - report_problem_as( - indoc!( - r#" - f : . - "# - ), - indoc!( - r#" - ── UNFINISHED TYPE ─────────────────────────────────────── /code/proj/Main.roc ─ - - I just started parsing a type, but I got stuck here: - - 1│ f : . - ^ - - I am expecting a type next, like Bool or List a. - "# - ), - ) - } - - #[test] - fn type_apply_start_with_number() { - report_problem_as( - indoc!( - r#" - f : Foo.1 - - f - "# - ), - indoc!( - r#" - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - - I am confused by this type name: - - 1│ f : Foo.1 - ^^^^^ - - Type names start with an uppercase letter, and can optionally be - qualified by a module name, like Bool or Http.Request.Request. - "# - ), - ) - - // ── 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. - } - - #[test] - fn type_apply_start_with_lowercase() { - report_problem_as( - indoc!( - r#" - f : Foo.foo - - f - "# - ), - indoc!( - r#" - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - - I am confused by this type name: - - 1│ f : Foo.foo - ^^^^^^^ - - Type names start with an uppercase letter, and can optionally be - qualified by a module name, like Bool or Http.Request.Request. - "# - ), - ) - } - - #[test] - fn def_missing_final_expression() { - report_problem_as( - indoc!( - r#" - f : Foo.foo - "# - ), - indoc!( - r#" - ── MISSING FINAL EXPRESSION ────────────────────────────── /code/proj/Main.roc ─ - - I am partway through parsing a definition's final expression, but I - got stuck here: - - 1│ f : Foo.foo - ^ - - This definition is missing a final expression. A nested definition - must be followed by either another definition, or an expression - - x = 4 - y = 2 - - x + y - "# - ), - ) - } - - #[test] - fn type_inline_alias() { - report_problem_as( - indoc!( - r#" - f : I64 as - f = 0 - - f - "# - ), - indoc!( - r#" - ── UNFINISHED INLINE ALIAS ─────────────────────────────── /code/proj/Main.roc ─ - - I just started parsing an inline type alias, but I got stuck here: - - 1│ f : I64 as - ^ - - Note: I may be confused by indentation - "# - ), - ) - } - - #[test] - fn type_double_comma() { - report_problem_as( - indoc!( - r#" - f : I64,,I64 -> I64 - f = 0 - - f - "# - ), - indoc!( - r#" - ── DOUBLE COMMA ────────────────────────────────────────── /code/proj/Main.roc ─ - - I just started parsing a function argument type, but I encountered two - commas in a row: - - 1│ f : I64,,I64 -> I64 - ^ - - Try removing one of them. - "# - ), - ) - } - - #[test] - fn type_argument_no_arrow() { - report_problem_as( - indoc!( - r#" - f : I64, I64 - f = 0 - - f - "# - ), - indoc!( - r#" - ── UNFINISHED TYPE ─────────────────────────────────────── /code/proj/Main.roc ─ - - I am partway through parsing a type, but I got stuck here: - - 1│ f : I64, I64 - ^ - - Note: I may be confused by indentation - "# - ), - ) - } - - #[test] - fn type_argument_arrow_then_nothing() { - // TODO could do better by pointing out we're parsing a function type - report_problem_as( - indoc!( - r#" - f : I64, I64 -> - f = 0 - - f - "# - ), - indoc!( - r#" - ── UNFINISHED TYPE ─────────────────────────────────────── /code/proj/Main.roc ─ - - I just started parsing a type, but I got stuck here: - - 1│ f : I64, I64 -> - ^ - - Note: I may be confused by indentation - "# - ), - ) - } - - #[test] - fn dict_type_formatting() { - // TODO could do better by pointing out we're parsing a function type - report_problem_as( - indoc!( - r#" - myDict : Dict Num.I64 Str - myDict = Dict.insert Dict.empty "foo" 42 - - myDict - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - Something is off with the body of the `myDict` definition: - - 1│ myDict : Dict Num.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 ───────────────────────────────────────── /code/proj/Main.roc ─ - - 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 - "# - ), - ) - } - - #[test] - fn if_guard_without_condition() { - // this should get better with time - report_problem_as( - indoc!( - r#" - when Just 4 is - Just if -> - 4 - - _ -> - 2 - "# - ), - indoc!( - r#" - ── IF GUARD NO CONDITION ───────────────────────────────── /code/proj/Main.roc ─ - - I just started parsing an if guard, but there is no guard condition: - - 1│ when Just 4 is - 2│ Just if -> - ^ - - Try adding an expression before the arrow! - "# - ), - ) - } - - #[test] - fn empty_or_pattern() { - report_problem_as( - indoc!( - r#" - when Just 4 is - Just 4 | -> - 4 - - _ -> - 2 - "# - ), - indoc!( - r#" - ── UNFINISHED PATTERN ──────────────────────────────────── /code/proj/Main.roc ─ - - I just started parsing a pattern, but I got stuck here: - - 2│ Just 4 | -> - ^ - - Note: I may be confused by indentation - "# - ), - ) - } - - #[test] - fn pattern_binds_keyword() { - // TODO check if "what_is_next" is a keyword - report_problem_as( - indoc!( - r#" - when Just 4 is - Just when -> - 4 - - _ -> - 2 - "# - ), - indoc!( - r#" - ── MISSING EXPRESSION ──────────────────────────────────── /code/proj/Main.roc ─ - - I am partway through parsing a definition, but I got stuck here: - - 1│ when Just 4 is - 2│ Just when -> - ^ - - I was expecting to see an expression like 42 or "hello". - "# - ), - ) - } - - #[test] - fn when_missing_arrow() { - // this should get better with time - report_problem_as( - indoc!( - r#" - when 5 is - 1 -> 2 - _ - "# - ), - indoc!( - r#" - ── MISSING ARROW ───────────────────────────────────────── /code/proj/Main.roc ─ - - I am partway through parsing a `when` expression, but got stuck here: - - 2│ 1 -> 2 - 3│ _ - ^ - - I was expecting to see an arrow next. - - Note: Sometimes I get confused by indentation, so try to make your `when` - look something like this: - - when List.first plants is - Ok n -> - n - - Err _ -> - 200 - - Notice the indentation. All patterns are aligned, and each branch is - indented a bit more than the corresponding pattern. That is important! - "# - ), - ) - } - - #[test] - fn lambda_double_comma() { - report_problem_as( - indoc!( - r#" - \a,,b -> 1 - "# - ), - indoc!( - r#" - ── UNFINISHED ARGUMENT LIST ────────────────────────────── /code/proj/Main.roc ─ - - I am partway through parsing a function argument list, but I got stuck - at this comma: - - 1│ \a,,b -> 1 - ^ - - I was expecting an argument pattern before this, so try adding an - argument before the comma and see if that helps? - "# - ), - ) - } - - #[test] - fn lambda_leading_comma() { - report_problem_as( - indoc!( - r#" - \,b -> 1 - "# - ), - indoc!( - r#" - ── UNFINISHED ARGUMENT LIST ────────────────────────────── /code/proj/Main.roc ─ - - I am partway through parsing a function argument list, but I got stuck - at this comma: - - 1│ \,b -> 1 - ^ - - I was expecting an argument pattern before this, so try adding an - argument before the comma and see if that helps? - "# - ), - ) - } - - #[test] - fn when_outdented_branch() { - // this should get better with time - report_problem_as( - indoc!( - r#" - when 4 is - 5 -> 2 - 2 -> 2 - "# - ), - indoc!( - r#" - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - - I got stuck here: - - 1│ when 4 is - 2│ 5 -> 2 - ^ - - Whatever I am running into is confusing me a lot! Normally I can give - fairly specific hints, but something is really tripping me up this - time. - "# - ), - // TODO this formerly gave - // - // ── UNFINISHED WHEN ───────────────────────────────────────────────────────────── - // - // I was partway through parsing a `when` expression, but I got stuck here: - // - // 3│ _ -> 2 - // ^ - // - // I suspect this is a pattern that is not indented enough? (by 2 spaces) - // - // but that requires parsing the next pattern blindly, irrespective of indentation. Can - // we find an efficient solution that doesn't require parsing an extra pattern for - // every `when`, i.e. we want a good error message for the test case above, but for - // a valid `when`, we don't want to do extra work, e.g. here - // - // x - // when x is - // n -> n - // - // 4 - // - // We don't want to parse the `4` and say it's an outdented pattern! - ) - } - - #[test] - fn when_over_indented_underscore() { - report_problem_as( - indoc!( - r#" - when 4 is - 5 -> 2 - _ -> 2 - "# - ), - indoc!( - r#" - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - - I got stuck here: - - 1│ when 4 is - 2│ 5 -> 2 - ^ - - Whatever I am running into is confusing me a lot! Normally I can give - fairly specific hints, but something is really tripping me up this - time. - "# - ), - ) - } - - #[test] - fn when_over_indented_int() { - report_problem_as( - indoc!( - r#" - when 4 is - 5 -> Num.neg - 2 -> 2 - "# - ), - indoc!( - r#" - ── UNEXPECTED ARROW ────────────────────────────────────── /code/proj/Main.roc ─ - - I am parsing a `when` expression right now, but this arrow is confusing - me: - - 3│ 2 -> 2 - ^^ - - It makes sense to see arrows around here, so I suspect it is something - earlier.Maybe this pattern is indented a bit farther from the previous - patterns? - - Note: Here is an example of a valid `when` expression for reference. - - when List.first plants is - Ok n -> - n - - Err _ -> - 200 - - Notice the indentation. All patterns are aligned, and each branch is - indented a bit more than the corresponding pattern. That is important! - "# - ), - ) - } - - #[test] - fn if_outdented_then() { - // TODO I think we can do better here - report_problem_as( - indoc!( - r#" - x = - if 5 == 5 - then 2 else 3 - - x - "# - ), - indoc!( - r#" - ── UNFINISHED IF ───────────────────────────────────────── /code/proj/Main.roc ─ - - I was partway through parsing an `if` expression, but I got stuck here: - - 2│ if 5 == 5 - ^ - - I was expecting to see the `then` keyword next. - "# - ), - ) - } - - #[test] - fn if_missing_else() { - // this should get better with time - report_problem_as( - indoc!( - r#" - if 5 == 5 then 2 - "# - ), - indoc!( - r#" - ── UNFINISHED IF ───────────────────────────────────────── /code/proj/Main.roc ─ - - I was partway through parsing an `if` expression, but I got stuck here: - - 1│ if 5 == 5 then 2 + 4│ \{ x, y ? True } -> x + y ^ - I was expecting to see the `else` keyword next. - "# - ), - ) - } + This `y` value is a: - #[test] - fn list_double_comma() { - report_problem_as( - indoc!( - r#" - [1, 2, , 3] + [True]a + + But `add` needs the 2nd argument to be: + + Num a + "### + ); + + test_report!( + optional_record_default_with_signature, + indoc!( + r#" + f : { x : Num.I64, y ? Num.I64 } -> Num.I64 + f = \{ x, y ? "foo" } -> (\g, _ -> g) x y + + f + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + The 1st argument to `f` is weird: + + 5│ f = \{ x, y ? "foo" } -> (\g, _ -> g) x y + ^^^^^^^^^^^^^^^^ + + The argument is a pattern that matches record values of type: + + { x : I64, y ? Str } + + But the annotation on `f` says the 1st argument should be: + + { x : I64, y ? I64 } + "### + ); + + test_report!( + optional_record_invalid_let_binding, + indoc!( + r#" + \rec -> + { x, y } : { x : Num.I64, y ? Str } + { x, y } = rec + + { x, y } + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + Something is off with the body of this definition: + + 5│> { x, y } : { x : Num.I64, y ? Str } + 6│> { x, y } = rec + + The body is a value of type: + + { x : I64, y : Str } + + But the type annotation says it should be: + + { x : I64, y ? Str } + + Tip: To extract the `.y` field it must be non-optional, but the type + says this field is optional. Learn more about optional fields at TODO. + "### + ); + + test_report!( + optional_record_invalid_function, + indoc!( + r#" + f : { x : Num.I64, y ? Num.I64 } -> Num.I64 + f = \{ x, y } -> x + y + + f + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + The 1st argument to `f` is weird: + + 5│ f = \{ x, y } -> x + y + ^^^^^^^^ + + The argument is a pattern that matches record values of type: + + { x : I64, y : I64 } + + But the annotation on `f` says the 1st argument should be: + + { x : I64, y ? I64 } + + Tip: To extract the `.y` field it must be non-optional, but the type + says this field is optional. Learn more about optional fields at TODO. + "### + ); + + test_report!( + optional_record_invalid_when, + indoc!( + r#" + f : { x : Num.I64, y ? Num.I64 } -> Num.I64 + f = \r -> + when r is + { x, y } -> x + y + + f + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + The branches of this `when` expression don't match the condition: + + 6│> when r is + 7│ { x, y } -> x + y + + This `r` value is a: + + { x : I64, y ? I64 } + + But the branch patterns have type: + + { x : I64, y : I64 } + + The branches must be cases of the `when` condition's type! + + Tip: To extract the `.y` field it must be non-optional, but the type + says this field is optional. Learn more about optional fields at TODO. + "### + ); + + test_report!( + optional_record_invalid_access, + indoc!( + r#" + f : { x : Num.I64, y ? Num.I64 } -> Num.I64 + f = \r -> r.y + + f + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + This expression is used in an unexpected way: + + 5│ f = \r -> r.y + ^^^ + + This `r` value is a: + + { x : I64, y ? I64 } + + But you are trying to use it as: + + { x : I64, y : I64 } + + Tip: To extract the `.y` field it must be non-optional, but the type + says this field is optional. Learn more about optional fields at TODO. + "### + ); + + test_report!( + optional_record_invalid_accessor, + indoc!( + r#" + f : { x : Num.I64, y ? Num.I64 } -> Num.I64 + f = \r -> .y r + + f "# - ), - indoc!( - r#" - ── UNFINISHED LIST ─────────────────────────────────────── /code/proj/Main.roc ─ + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - I am partway through started parsing a list, but I got stuck here: + The 1st argument to this function is not what I expect: - 1│ [1, 2, , 3] - ^ + 5│ f = \r -> .y r + ^ - I was expecting to see a list entry before this comma, so try adding a - list entry and see if that helps? + This `r` value is a: + + { x : I64, y ? I64 } + + But this function needs the 1st argument to be: + + { x : I64, y : I64 } + + Tip: To extract the `.y` field it must be non-optional, but the type + says this field is optional. Learn more about optional fields at TODO. + "### + ); + + test_report!( + guard_mismatch_with_annotation, + indoc!( + r#" + f : { x : Num.I64, y : Num.I64 } -> Num.I64 + f = \r -> + when r is + { x, y : "foo" } -> x + 0 + _ -> 0 + + f "# - ), - ) - } + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn list_without_end() { - report_problem_as( - indoc!( - r#" - [1, 2, - "# - ), - indoc!( - r#" - ── UNFINISHED LIST ─────────────────────────────────────── /code/proj/Main.roc ─ + The branches of this `when` expression don't match the condition: - I am partway through started parsing a list, but I got stuck here: + 6│> when r is + 7│ { x, y : "foo" } -> x + 0 + 8│ _ -> 0 - 1│ [1, 2, - ^ + This `r` value is a: - I was expecting to see a closing square bracket before this, so try - adding a ] and see if that helps? + { x : I64, y : I64 } - Note: When I get stuck like this, it usually means that there is a - missing parenthesis or bracket somewhere earlier. It could also be a - stray keyword or operator. + But the branch patterns have type: + + { x : I64, y : Str } + + The branches must be cases of the `when` condition's type! + "### + ); + + test_report!( + optional_field_mismatch_with_annotation, + indoc!( + r#" + f : { x : Num.I64, y ? Num.I64 } -> Num.I64 + f = \r -> + when r is + { x, y ? "foo" } -> (\g, _ -> g) x y + _ -> 0 + + f "# - ), - ) - } + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn number_double_dot() { - report_problem_as( - indoc!( - r#" - 1.1.1 - "# - ), - indoc!( - r#" - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + The branches of this `when` expression don't match the condition: - This float literal contains an invalid digit: + 6│> when r is + 7│ { x, y ? "foo" } -> (\g, _ -> g) x y + 8│ _ -> 0 - 1│ 1.1.1 - ^^^^^ + This `r` value is a: - Floating point literals can only contain the digits 0-9, or use - scientific notation 10e4, or have a float suffix. + { x : I64, y ? I64 } - Tip: Learn more about number literals at TODO + But the branch patterns have type: + + { x : I64, y ? Str } + + The branches must be cases of the `when` condition's type! + "### + ); + + test_report!( + incorrect_optional_field, + indoc!( + r#" + { x: 5, y ? 42 } "# - ), - ) - } + ), + @r###" + ── BAD OPTIONAL VALUE ──────────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn unicode_not_hex() { - report_problem_as( - r#""abc\u(zzzz)def""#, - indoc!( - r#" - ── WEIRD CODE POINT ────────────────────────────────────── /code/proj/Main.roc ─ + This record uses an optional value for the `.y` field in an incorrect + context! - I am partway through parsing a unicode code point, but I got stuck - here: + 4│ { x: 5, y ? 42 } + ^^^^^^ - 1│ "abc\u(zzzz)def" - ^ + You can only use optional values in record destructuring, like: - I was expecting a hexadecimal number, like \u(1100) or \u(00FF). + { answer ? 42, otherField } = myRecord + "### + ); - Learn more about working with unicode in roc at TODO + test_report!( + first_wildcard_is_required, + indoc!( + r#" + when Foo 1 2 3 is + Foo _ 1 _ -> 1 + _ -> 2 "# - ), - ) - } + ), + @"" + ); - #[test] - fn interpolate_not_identifier() { - report_problem_as( - r#""abc\(32)def""#, - indoc!( - r#" - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - - This string interpolation is invalid: - - 1│ "abc\(32)def" - ^^ - - I was expecting an identifier, like \u(message) or - \u(LoremIpsum.text). - - Learn more about string interpolation at TODO + test_report!( + second_wildcard_is_redundant, + indoc!( + r#" + when Foo 1 2 3 is + Foo _ 1 _ -> 1 + _ -> 2 + _ -> 3 "# - ), - ) - } + ), + @r###" + ── REDUNDANT PATTERN ───────────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn unicode_too_large() { - report_problem_as( - r#""abc\u(110000)def""#, - indoc!( - r#" - ── INVALID UNICODE ─────────────────────────────────────── /code/proj/Main.roc ─ + The 3rd pattern is redundant: - This unicode code point is invalid: + 4│ when Foo 1 2 3 is + 5│ Foo _ 1 _ -> 1 + 6│ _ -> 2 + 7│ _ -> 3 + ^ - 1│ "abc\u(110000)def" - ^^^^^^ + Any value of this shape will be handled by a previous pattern, so this + one should be removed. + "### + ); - Learn more about working with unicode in roc at TODO + test_report!( + alias_using_alias, + indoc!( + r#" + # The color of a node. Leaves are considered Black. + NodeColor : [Red, Black] + + RBTree k v : [Node NodeColor k v (RBTree k v) (RBTree k v), Empty] + + # Create an empty dictionary. + empty : RBTree k v + empty = + Empty + + empty "# - ), - ) - } + ), + @"" + ); - #[test] - fn weird_escape() { - report_problem_as( - r#""abc\qdef""#, - indoc!( - r#" - ── WEIRD ESCAPE ────────────────────────────────────────── /code/proj/Main.roc ─ + test_report!( + unused_argument, + indoc!( + r#" + f = \foo -> 1 - I was partway through parsing a string literal, but I got stuck here: - - 1│ "abc\qdef" - ^^ - - This is not an escape sequence I recognize. After a backslash, I am - looking for one of these: - - - A newline: \n - - A caret return: \r - - A tab: \t - - An escaped quote: \" - - An escaped backslash: \\ - - A unicode code point: \u(00FF) - - An interpolated string: \(myVariable) + f "# - ), - ) - } + ), + @r###" + ── UNUSED ARGUMENT ─────────────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn single_no_end() { - report_problem_as( - r#""there is no end"#, - indoc!( - r#" - ── ENDLESS STRING ──────────────────────────────────────── /code/proj/Main.roc ─ + `f` doesn't use `foo`. - I cannot find the end of this string: + 4│ f = \foo -> 1 + ^^^ - 1│ "there is no end + If you don't need `foo`, then you can just remove it. However, if you + really do need `foo` as an argument of `f`, prefix it with an underscore, + like this: "_`foo`". Adding an underscore at the start of a variable + name is a way of saying that the variable is not used. + "### + ); + + test_report!( + qualified_tag, + indoc!( + r#" + Foo.Bar + "# + ), + @r###" + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + + I am trying to parse a qualified name here: + + 4│ Foo.Bar + ^ + + This looks like a qualified tag name to me, but tags cannot be + qualified! Maybe you wanted a qualified name, something like + Json.Decode.string? + "### + ); + + test_report!( + module_ident_ends_with_dot, + indoc!( + r#" + Foo.Bar. + "# + ), + @r###" + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + + I am trying to parse a qualified name here: + + 4│ Foo.Bar. + ^ + + I was expecting to see an identifier next, like height. A complete + qualified name looks something like Json.Decode.string. + "### + ); + + test_report!( + record_access_ends_with_dot, + indoc!( + r#" + foo.bar. + "# + ), + @r###" + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + + I trying to parse a record field access here: + + 4│ foo.bar. + ^ + + So I expect to see a lowercase letter next, like .name or .height. + "### + ); + + test_report!( + type_annotation_double_colon, + indoc!( + r#" + f :: I64 + f = 42 + + f + "# + ), + @r###" + ── UNKNOWN OPERATOR ──────────────── tmp/type_annotation_double_colon/Test.roc ─ + + This looks like an operator, but it's not one I recognize! + + 1│ app "test" provides [main] to "./platform" + 2│ + 3│ main = + 4│ f :: I64 + ^^ + + I have no specific suggestion for this operator, see TODO for the full + list of operators in Roc. + "### + ); + + // NOTE: VERY BAD ERROR MESSAGE + // + // looks like `x y` are considered argument to the add, even though they are + // on a lower indentation level + test_report!( + double_equals_in_def, + indoc!( + r#" + x = 3 + y = + x == 5 + Num.add 1 2 + + { x, y } + "# + ), + @r###" + ── TOO MANY ARGS ───────────────────────────────────────── /code/proj/Main.roc ─ + + This value is not a function, but it was given 3 arguments: + + 6│ x == 5 ^ - You could change it to something like "to be or not to be" or even - just "". + Are there any missing commas? Or missing parentheses? + "### + ); + + test_report!( + tag_union_open, + indoc!( + r#" + f : [ "# - ), - ) - } + ), + @r###" + ── UNFINISHED TAG UNION TYPE ───────────────────── tmp/tag_union_open/Test.roc ─ - #[test] - fn multi_no_end() { - report_problem_as( - r#""""there is no end"#, - indoc!( - r#" - ── ENDLESS STRING ──────────────────────────────────────── /code/proj/Main.roc ─ + I am partway through parsing a tag union type, but I got stuck here: - I cannot find the end of this block string: + 4│ f : [ + ^ - 1│ """there is no end + I was expecting to see a closing square bracket before this, so try + adding a ] and see if that helps? + + Note: I may be confused by indentation + "### + ); + + test_report!( + tag_union_end, + indoc!( + r#" + f : [Yes, + "# + ), + @r###" + ── UNFINISHED TAG UNION TYPE ────────────────────── tmp/tag_union_end/Test.roc ─ + + I am partway through parsing a tag union type, but I got stuck here: + + 4│ f : [Yes, + 5│ + 6│ + ^ + + I was expecting to see a closing square bracket before this, so try + adding a ] and see if that helps? + "### + ); + + test_report!( + tag_union_lowercase_tag_name, + indoc!( + r#" + f : [lowercase] + "# + ), + @r###" + ── WEIRD TAG NAME ────────────────── tmp/tag_union_lowercase_tag_name/Test.roc ─ + + I am partway through parsing a tag union type, but I got stuck here: + + 4│ f : [lowercase] + ^ + + I was expecting to see a tag name. + + Hint: Tag names start with an uppercase letter, like Err or Green. + "### + ); + + test_report!( + tag_union_second_lowercase_tag_name, + indoc!( + r#" + f : [Good, bad] + "# + ), + @r###" + ── WEIRD TAG NAME ─────────── tmp/tag_union_second_lowercase_tag_name/Test.roc ─ + + I am partway through parsing a tag union type, but I got stuck here: + + 4│ f : [Good, bad] ^ - You could change it to something like """to be or not to be""" or even - just """""". + I was expecting to see a tag name. + + Hint: Tag names start with an uppercase letter, like Err or Green. + "### + ); + + test_report!( + record_type_open, + indoc!( + r#" + f : { "# - ), - ) - } + ), + @r###" + ── UNFINISHED RECORD TYPE ────────────────────── tmp/record_type_open/Test.roc ─ + + I am partway through parsing a record type, but I got stuck here: + + 4│ 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 + "### + ); + + test_report!( + record_type_open_indent, + indoc!( + r#" + f : { + foo : I64, + "# + ), + @r###" + ── UNFINISHED RECORD TYPE ─────────────── tmp/record_type_open_indent/Test.roc ─ + + I am partway through parsing a record type, but I got stuck here: + + 4│ 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 + "### + ); + + test_report!( + record_type_end, + indoc!( + r#" + f : { a: Int, + "# + ), + @r###" + ── UNFINISHED RECORD TYPE ─────────────────────── tmp/record_type_end/Test.roc ─ + + I am partway through parsing a record type, but I got stuck here: + + 4│ f : { a: Int, + 5│ + 6│ + ^ + + I was expecting to see a closing curly brace before this, so try + adding a } and see if that helps? + "### + ); + + test_report!( + record_type_keyword_field_name, + indoc!( + r#" + f : { if : I64 } + "# + ), + @r###" + ── UNFINISHED RECORD TYPE ──────── tmp/record_type_keyword_field_name/Test.roc ─ + + I just started parsing a record type, but I got stuck on this field + name: + + 4│ 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! + "### + ); + + // a case where the message cannot be as good as elm's + test_report!( + record_type_missing_comma, + indoc!( + r#" + f : { foo bar } + "# + ), + @r###" + ── UNFINISHED RECORD TYPE ───────────── tmp/record_type_missing_comma/Test.roc ─ + + I am partway through parsing a record type, but I got stuck here: + + 4│ f : { foo bar } + ^ + + I was expecting to see a colon, question mark, comma or closing curly + brace. + "### + ); + + // a case where the message cannot be as good as elm's + test_report!( + record_type_tab, + "f : { foo \t }", + @r###" + ── TAB CHARACTER ──────────────────────────────── tmp/record_type_tab/Test.roc ─ + + I encountered a tab character + + 4│ f : { foo } + ^ + + Tab characters are not allowed. + "### + ); + + test_report!( + comment_with_tab, + "# comment with a \t\n4", + @r###" + ── TAB CHARACTER ─────────────────────────────── tmp/comment_with_tab/Test.roc ─ + + I encountered a tab character + + 4│ # comment with a + ^ + + Tab characters are not allowed. + "### + ); + + // TODO bad error message + test_report!( + type_in_parens_start, + indoc!( + r#" + f : ( + "# + ), + @r###" + ── UNFINISHED PARENTHESES ────────────────── tmp/type_in_parens_start/Test.roc ─ + + I just started parsing a type in parentheses, but I got stuck here: + + 4│ f : ( + ^ + + Tag unions look like [Many I64, None], so I was expecting to see a tag + name next. + + Note: I may be confused by indentation + "### + ); + + test_report!( + type_in_parens_end, + indoc!( + r#" + f : ( I64 + "# + ), + @r###" + ── UNFINISHED PARENTHESES ──────────────────── tmp/type_in_parens_end/Test.roc ─ + + I am partway through parsing a type in parentheses, but I got stuck + here: + + 4│ f : ( I64 + ^ + + I was expecting to see a parenthesis before this, so try adding a ) + and see if that helps? + + Note: I may be confused by indentation + "### + ); + + test_report!( + type_apply_double_dot, + indoc!( + r#" + f : Foo..Bar + + f + "# + ), + @r###" + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + + I am confused by this type name: + + 4│ f : Foo..Bar + ^^^^^^^^ + + Type names start with an uppercase letter, and can optionally be + qualified by a module name, like Bool or Http.Request.Request. + "### + ); + // ── DOUBLE DOT ────────────────────────────────────────────────────────────────── + // + // I encountered two dots in a row: + // + // 1│ f : Foo..Bar + // ^ + // + // Try removing one of them. + + test_report!( + type_apply_trailing_dot, + indoc!( + r#" + f : Foo.Bar. + + f + "# + ), + @r###" + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + + I am confused by this type name: + + 4│ f : Foo.Bar. + ^^^^^^^^ + + Type names start with an uppercase letter, and can optionally be + qualified by a module name, like Bool or Http.Request.Request. + "### + ); + // ── 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. + + test_report!( + type_apply_stray_dot, + indoc!( + r#" + f : . + "# + ), + @r###" + ── UNFINISHED TYPE ───────────────────────── tmp/type_apply_stray_dot/Test.roc ─ + + I just started parsing a type, but I got stuck here: + + 4│ f : . + ^ + + I am expecting a type next, like Bool or List a. + "### + ); + + test_report!( + type_apply_start_with_number, + indoc!( + r#" + f : Foo.1 + + f + "# + ), + @r###" + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + + I am confused by this type name: + + 4│ f : Foo.1 + ^^^^^ + + Type names start with an uppercase letter, and can optionally be + qualified by a module name, like Bool or Http.Request.Request. + "### + ); + // ── 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. + + test_report!( + type_apply_start_with_lowercase, + indoc!( + r#" + f : Foo.foo + + f + "# + ), + @r###" + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + + I am confused by this type name: + + 4│ f : Foo.foo + ^^^^^^^ + + Type names start with an uppercase letter, and can optionally be + qualified by a module name, like Bool or Http.Request.Request. + "### + ); + + test_report!( + def_missing_final_expression, + indoc!( + r#" + f : Foo.foo + "# + ), + @r###" + ── MISSING FINAL EXPRESSION ──────── tmp/def_missing_final_expression/Test.roc ─ + + I am partway through parsing a definition, but I got stuck here: + + 1│ app "test" provides [main] to "./platform" + 2│ + 3│ main = + 4│ f : Foo.foo + ^ + + This definition is missing a final expression. A nested definition + must be followed by either another definition, or an expression + + x = 4 + y = 2 + + x + y + "### + ); + + test_report!( + type_inline_alias, + indoc!( + r#" + f : I64 as + f = 0 + + f + "# + ), + @r###" + ── UNFINISHED INLINE ALIAS ──────────────────── tmp/type_inline_alias/Test.roc ─ + + I just started parsing an inline type alias, but I got stuck here: + + 4│ f : I64 as + ^ + + Note: I may be confused by indentation + "### + ); + + test_report!( + type_double_comma, + indoc!( + r#" + f : I64,,I64 -> I64 + f = 0 + + f + "# + ), + @r###" + ── DOUBLE COMMA ─────────────────────────────── tmp/type_double_comma/Test.roc ─ + + I just started parsing a function argument type, but I encountered two + commas in a row: + + 4│ f : I64,,I64 -> I64 + ^ + + Try removing one of them. + "### + ); + + test_report!( + type_argument_no_arrow, + indoc!( + r#" + f : I64, I64 + f = 0 + + f + "# + ), + @r###" + ── UNFINISHED TYPE ─────────────────────── tmp/type_argument_no_arrow/Test.roc ─ + + I am partway through parsing a type, but I got stuck here: + + 4│ f : I64, I64 + ^ + + Note: I may be confused by indentation + "### + ); + + // TODO could do better by pointing out we're parsing a function type + test_report!( + type_argument_arrow_then_nothing, + indoc!( + r#" + f : I64, I64 -> + f = 0 + + f + "# + ), + @r###" + ── UNFINISHED TYPE ───────────── tmp/type_argument_arrow_then_nothing/Test.roc ─ + + I just started parsing a type, but I got stuck here: + + 4│ f : I64, I64 -> + ^ + + Note: I may be confused by indentation + "### + ); + + // TODO could do better by pointing out we're parsing a function type + test_report!( + dict_type_formatting, + indoc!( + r#" + myDict : Dict Num.I64 Str + myDict = Dict.insert Dict.empty "foo" 42 + + myDict + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + Something is off with the body of the `myDict` definition: + + 4│ myDict : Dict Num.I64 Str + 5│ 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_report!( + alias_type_diff, + indoc!( + r#" + HSet a : Set a + + foo : Str -> HSet {} + + myDict : HSet Str + myDict = foo "bar" + + myDict + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + Something is off with the body of the `myDict` definition: + + 8│ myDict : HSet Str + 9│ myDict = foo "bar" + ^^^^^^^^^ + + This `foo` call produces: + + HSet {} + + But the type annotation on `myDict` says it should be: + + HSet Str + "### + ); + + // this should get better with time + test_report!( + if_guard_without_condition, + indoc!( + r#" + when Just 4 is + Just if -> + 4 + + _ -> + 2 + "# + ), + @r###" + ── IF GUARD NO CONDITION ───────────── tmp/if_guard_without_condition/Test.roc ─ + + I just started parsing an if guard, but there is no guard condition: + + 4│ when Just 4 is + 5│ Just if -> + ^ + + Try adding an expression before the arrow! + "### + ); + + test_report!( + empty_or_pattern, + indoc!( + r#" + when Just 4 is + Just 4 | -> + 4 + + _ -> + 2 + "# + ), + @r###" + ── UNFINISHED PATTERN ────────────────────────── tmp/empty_or_pattern/Test.roc ─ + + I just started parsing a pattern, but I got stuck here: + + 5│ Just 4 | -> + ^ + + Note: I may be confused by indentation + "### + ); + + // TODO check if "what_is_next" is a keyword + test_report!( + pattern_binds_keyword, + indoc!( + r#" + when Just 4 is + Just when -> + 4 + + _ -> + 2 + "# + ), + @r###" + ── MISSING EXPRESSION ───────────────────── tmp/pattern_binds_keyword/Test.roc ─ + + I am partway through parsing a definition, but I got stuck here: + + 1│ app "test" provides [main] to "./platform" + 2│ + 3│ main = + 4│ when Just 4 is + 5│ Just when -> + ^ + + I was expecting to see an expression like 42 or "hello". + "### + ); + + // this should get better with time + test_report!( + when_missing_arrow, + indoc!( + r#" + when 5 is + 1 -> 2 + _ + "# + ), + @r###" + ── UNFINISHED WHEN ─────────────────────────── tmp/when_missing_arrow/Test.roc ─ + + I was partway through parsing a `when` expression, but I got stuck here: + + 6│ _ + ^ + + I was expecting to see a pattern next + + Note: Here is an example of a valid `when` expression for reference. + + when List.first plants is + Ok n -> + n + + Err _ -> + 200 + + Notice the indentation. All patterns are aligned, and each branch is + indented a bit more than the corresponding pattern. That is important! + "### + ); + + test_report!( + lambda_double_comma, + indoc!( + r#" + \a,,b -> 1 + "# + ), + @r###" + ── UNFINISHED ARGUMENT LIST ───────────────── tmp/lambda_double_comma/Test.roc ─ + + I am partway through parsing a function argument list, but I got stuck + at this comma: + + 4│ \a,,b -> 1 + ^ + + I was expecting an argument pattern before this, so try adding an + argument before the comma and see if that helps? + "### + ); + + test_report!( + lambda_leading_comma, + indoc!( + r#" + \,b -> 1 + "# + ), + @r###" + ── UNFINISHED ARGUMENT LIST ──────────────── tmp/lambda_leading_comma/Test.roc ─ + + I am partway through parsing a function argument list, but I got stuck + at this comma: + + 4│ \,b -> 1 + ^ + + I was expecting an argument pattern before this, so try adding an + argument before the comma and see if that helps? + "### + ); + + // this should get better with time + // TODO this formerly gave + // + // ── UNFINISHED WHEN ───────────────────────────────────────────────────────────── + // + // I was partway through parsing a `when` expression, but I got stuck here: + // + // 3│ _ -> 2 + // ^ + // + // I suspect this is a pattern that is not indented enough? (by 2 spaces) + // + // but that requires parsing the next pattern blindly, irrespective of indentation. Can + // we find an efficient solution that doesn't require parsing an extra pattern for + // every `when`, i.e. we want a good error message for the test case above, but for + // a valid `when`, we don't want to do extra work, e.g. here + // + // x + // when x is + // n -> n + // + // 4 + // + // We don't want to parse the `4` and say it's an outdented pattern! + test_report!( + when_outdented_branch, + indoc!( + r#" + when 4 is + 5 -> 2 + 2 -> 2 + "# + ), + @r###" + ── NOT END OF FILE ──────────────────────── tmp/when_outdented_branch/Test.roc ─ + + I expected to reach the end of the file, but got stuck here: + + 6│ 2 -> 2 + ^ + "### + ); + + test_report!( + when_over_indented_underscore, + indoc!( + r#" + when 4 is + 5 -> 2 + _ -> 2 + "# + ), + @r###" + ── NOT END OF FILE ──────────────── tmp/when_over_indented_underscore/Test.roc ─ + + I expected to reach the end of the file, but got stuck here: + + 6│ _ -> 2 + ^ + "### + ); + + test_report!( + when_over_indented_int, + indoc!( + r#" + when 4 is + 5 -> Num.neg + 2 -> 2 + "# + ), + @r###" + ── UNEXPECTED ARROW ────────────────────── tmp/when_over_indented_int/Test.roc ─ + + I am parsing a `when` expression right now, but this arrow is confusing + me: + + 6│ 2 -> 2 + ^^ + + It makes sense to see arrows around here, so I suspect it is something + earlier.Maybe this pattern is indented a bit farther from the previous + patterns? + + Note: Here is an example of a valid `when` expression for reference. + + when List.first plants is + Ok n -> + n + + Err _ -> + 200 + + Notice the indentation. All patterns are aligned, and each branch is + indented a bit more than the corresponding pattern. That is important! + "### + ); + + // TODO I think we can do better here + test_report!( + if_outdented_then, + indoc!( + r#" + x = + if 5 == 5 + then 2 else 3 + + x + "# + ), + @r###" + ── UNFINISHED IF ────────────────────────────── tmp/if_outdented_then/Test.roc ─ + + I was partway through parsing an `if` expression, but I got stuck here: + + 5│ if 5 == 5 + ^ + + I was expecting to see the `then` keyword next. + "### + ); + + // this should get better with time + test_report!( + if_missing_else, + indoc!( + r#" + if 5 == 5 then 2 + "# + ), + @r###" + ── UNFINISHED IF ──────────────────────────────── tmp/if_missing_else/Test.roc ─ + + I was partway through parsing an `if` expression, but I got stuck here: + + 4│ if 5 == 5 then 2 + ^ + + I was expecting to see the `else` keyword next. + "### + ); + + test_report!( + list_double_comma, + indoc!( + r#" + [1, 2, , 3] + "# + ), + @r###" + ── UNFINISHED LIST ──────────────────────────── tmp/list_double_comma/Test.roc ─ + + I am partway through started parsing a list, but I got stuck here: + + 4│ [1, 2, , 3] + ^ + + I was expecting to see a list entry before this comma, so try adding a + list entry and see if that helps? + "### + ); + + test_report!( + list_without_end, + indoc!( + r#" + [1, 2, + "# + ), + @r###" + ── UNFINISHED LIST ───────────────────────────── tmp/list_without_end/Test.roc ─ + + I am partway through started parsing a list, but I got stuck here: + + 4│ [1, 2, + 5│ + 6│ + ^ + + I was expecting to see a closing square bracket before this, so try + adding a ] and see if that helps? + + Note: When I get stuck like this, it usually means that there is a + missing parenthesis or bracket somewhere earlier. It could also be a + stray keyword or operator. + "### + ); + + test_report!( + number_double_dot, + indoc!( + r#" + 1.1.1 + "# + ), + @r###" + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + + This float literal contains an invalid digit: + + 4│ 1.1.1 + ^^^^^ + + Floating point literals can only contain the digits 0-9, or use + scientific notation 10e4, or have a float suffix. + + Tip: Learn more about number literals at TODO + "### + ); + + test_report!( + unicode_not_hex, + r#""abc\u(zzzz)def""#, + @r###" + ── WEIRD CODE POINT ───────────────────────────── tmp/unicode_not_hex/Test.roc ─ + + I am partway through parsing a unicode code point, but I got stuck + here: + + 4│ "abc\u(zzzz)def" + ^ + + I was expecting a hexadecimal number, like \u(1100) or \u(00FF). + + Learn more about working with unicode in roc at TODO + "### + ); + + test_report!( + interpolate_not_identifier, + r#""abc\(32)def""#, + @r###" + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + + This string interpolation is invalid: + + 4│ "abc\(32)def" + ^^ + + I was expecting an identifier, like \u(message) or + \u(LoremIpsum.text). + + Learn more about string interpolation at TODO + "### + ); + + test_report!( + unicode_too_large, + r#""abc\u(110000)def""#, + @r###" + ── INVALID UNICODE ─────────────────────────────────────── /code/proj/Main.roc ─ + + This unicode code point is invalid: + + 4│ "abc\u(110000)def" + ^^^^^^ + + Learn more about working with unicode in roc at TODO + "### + ); + + test_report!( + weird_escape, + r#""abc\qdef""#, + @r###" + ── WEIRD ESCAPE ──────────────────────────────────── tmp/weird_escape/Test.roc ─ + + I was partway through parsing a string literal, but I got stuck here: + + 4│ "abc\qdef" + ^^ + + This is not an escape sequence I recognize. After a backslash, I am + looking for one of these: + + - A newline: \n + - A caret return: \r + - A tab: \t + - An escaped quote: \" + - An escaped backslash: \\ + - A unicode code point: \u(00FF) + - An interpolated string: \(myVariable) + "### + ); + + test_report!( + single_no_end, + r#""there is no end"#, + @r###" + ── ENDLESS STRING ───────────────────────────────── tmp/single_no_end/Test.roc ─ + + I cannot find the end of this string: + + 4│ "there is no end + ^ + + You could change it to something like "to be or not to be" or even + just "". + "### + ); + + test_report!( + multi_no_end, + r#""""there is no end"#, + @r###" + ── ENDLESS STRING ────────────────────────────────── tmp/multi_no_end/Test.roc ─ + + I cannot find the end of this block string: + + 4│ """there is no end + ^ + + You could change it to something like """to be or not to be""" or even + just """""". + "### + ); - #[test] // https://github.com/rtfeldman/roc/issues/1714 - fn interpolate_concat_is_transparent_1714() { - report_problem_as( + test_report!( + interpolate_concat_is_transparent_1714, indoc!( r#" - greeting = "Privet" + greeting = "Privet" - if True then 1 else "\(greeting), World!" - "#, + if True then 1 else "\(greeting), World!" + "#, ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - This `if` has an `else` branch with a different type from its `then` branch: + This `if` has an `else` branch with a different type from its `then` branch: - 3│ if True then 1 else "\(greeting), World!" - ^^^^^^^^^^^^^^^^^^^^^ + 6│ if True then 1 else "\(greeting), World!" + ^^^^^^^^^^^^^^^^^^^^^ - The `else` branch is a string of type: + The `else` branch is a string of type: - Str + Str - but the `then` branch has the type: + but the `then` branch has the type: - Num a + Num a - All branches in an `if` must have the same type! - "# - ), - ) - } + All branches in an `if` must have the same type! + "### + ); macro_rules! comparison_binop_transparency_tests { ($($op:expr, $name:ident),* $(,)?) => { $( - #[test] - fn $name() { - report_problem_as( - &format!(r#"if True then "abc" else 1 {} 2"#, $op), - &format!( + test_report!( + $name, + &format!(r#"if True then "abc" else 1 {} 2"#, $op), + |golden| assert_eq!(golden, format!( r#"── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This `if` has an `else` branch with a different type from its `then` branch: -1│ if True then "abc" else 1 {} 2 - ^^{}^^ +4│ if True then "abc" else 1 {} 2 + ^^{}^^ This comparison produces: @@ -6022,10 +5251,9 @@ but the `then` branch has the type: All branches in an `if` must have the same type! "#, - $op, "^".repeat($op.len()) - ), - ) - } + $op, "^".repeat($op.len()) + )) + ); )* } } @@ -6039,349 +5267,321 @@ All branches in an `if` must have the same type! ">=", geq_binop_is_transparent, } - #[test] - fn keyword_record_field_access() { - report_problem_as( - indoc!( - r#" - foo = {} + test_report!( + keyword_record_field_access, + indoc!( + r#" + foo = {} - foo.if - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - This `foo` record doesn’t have a `if` field: - - 3│ foo.if - ^^^^^^ - - In fact, `foo` is a record with no fields at all! + foo.if "# - ), - ) - } + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn keyword_qualified_import() { - report_problem_as( - indoc!( - r#" - Num.if - "# - ), - indoc!( - r#" - ── NOT EXPOSED ─────────────────────────────────────────── /code/proj/Main.roc ─ + This `foo` record doesn’t have a `if` field: - The Num module does not expose `if`: + 6│ foo.if + ^^^^^^ - 1│ Num.if - ^^^^^^ + In fact, `foo` is a record with no fields at all! + "### + ); - Did you mean one of these? - - Num.sin - Num.div - Num.abs - Num.neg + test_report!( + keyword_qualified_import, + indoc!( + r#" + Num.if "# - ), - ) - } + ), + @r###" + ── NOT EXPOSED ─────────────────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn stray_dot_expr() { - report_problem_as( - indoc!( - r#" - Num.add . 23 - "# - ), - indoc!( - r#" - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + The Num module does not expose `if`: - I trying to parse a record field access here: + 4│ Num.if + ^^^^^^ - 1│ Num.add . 23 - ^ + Did you mean one of these? - So I expect to see a lowercase letter next, like .name or .height. + Num.sin + Num.div + Num.abs + Num.neg + "### + ); + + test_report!( + stray_dot_expr, + indoc!( + r#" + Num.add . 23 "# - ), - ) - } + ), + @r###" + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn opaque_ref_field_access() { - report_problem_as( - indoc!( - r#" - @UUID.bar - "# - ), - indoc!( - r#" - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + I trying to parse a record field access here: - I am very confused by this field access: + 4│ Num.add . 23 + ^ - 1│ @UUID.bar - ^^^^ + So I expect to see a lowercase letter next, like .name or .height. + "### + ); - It looks like a record field access on an opaque reference. + test_report!( + opaque_ref_field_access, + indoc!( + r#" + @UUID.bar "# - ), - ) - } + ), + @r###" + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn weird_accessor() { - report_problem_as( - indoc!( - r#" - .foo.bar - "# - ), - indoc!( - r#" - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + I am very confused by this field access: - I am very confused by this field access + 4│ @UUID.bar + ^^^^ - 1│ .foo.bar - ^^^^^^^^ + It looks like a record field access on an opaque reference. + "### + ); - It looks like a field access on an accessor. I parse.client.name as - (.client).name. Maybe use an anonymous function like - (\r -> r.client.name) instead? + test_report!( + weird_accessor, + indoc!( + r#" + .foo.bar "# - ), - ) - } + ), + @r###" + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn part_starts_with_number() { - report_problem_as( - indoc!( - r#" - foo.100 - "# - ), - indoc!( - r#" - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + I am very confused by this field access - I trying to parse a record field access here: + 4│ .foo.bar + ^^^^^^^^ - 1│ foo.100 - ^ + It looks like a field access on an accessor. I parse.client.name as + (.client).name. Maybe use an anonymous function like + (\r -> r.client.name) instead? + "### + ); - So I expect to see a lowercase letter next, like .name or .height. + test_report!( + part_starts_with_number, + indoc!( + r#" + foo.100 "# - ), - ) - } + ), + @r###" + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn closure_underscore_ident() { - report_problem_as( - indoc!( - r#" - \the_answer -> 100 - "# - ), - indoc!( - r#" - ── NAMING PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + I trying to parse a record field access here: - I am trying to parse an identifier here: + 4│ foo.100 + ^ - 1│ \the_answer -> 100 - ^ + So I expect to see a lowercase letter next, like .name or .height. + "### + ); - Underscores are not allowed in identifiers. Use camelCase instead! + test_report!( + closure_underscore_ident, + indoc!( + r#" + \the_answer -> 100 "# - ), - ) - } + ), + @r###" + ── NAMING PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - #[test] - #[ignore] - fn double_binop() { - report_problem_as( - indoc!( - r#" - key >= 97 && <= 122 - "# - ), - indoc!( - r#" - "# - ), - ) - } + I am trying to parse an identifier here: - #[test] - #[ignore] - fn case_of() { - report_problem_as( - indoc!( - r#" - case 1 of - 1 -> True - _ -> False - "# - ), - indoc!( - r#" - "# - ), - ) - } + 4│ \the_answer -> 100 + ^ - #[test] - fn argument_without_space() { - report_problem_as( - indoc!( - r#" - ["foo", bar("")] - "# - ), - indoc!( - r#" - ── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─ + Underscores are not allowed in identifiers. Use camelCase instead! + "### + ); - Nothing is named `bar` in this scope. - - 1│ ["foo", bar("")] - ^^^ - - Did you mean one of these? - - Str - Err - Box - Set - "# - ), - ) - } - - #[test] - fn invalid_operator() { - report_problem_as( - indoc!( - r#" - main = - 5 ** 3 - "# - ), - indoc!( - r#" - ── UNKNOWN OPERATOR ────────────────────────────────────── /code/proj/Main.roc ─ - - This looks like an operator, but it's not one I recognize! - - 1│ main = - 2│ 5 ** 3 - ^^ - - I have no specific suggestion for this operator, see TODO for the full - list of operators in Roc. + test_report!( + #[ignore] + double_binop, + indoc!( + r#" + key >= 97 && <= 122 "# - ), - ) - } + ), + @r#" + "# + ); - #[test] - fn double_plus() { - report_problem_as( - indoc!( - r#" - main = - [] ++ [] - "# - ), - indoc!( - r#" - ── UNKNOWN OPERATOR ────────────────────────────────────── /code/proj/Main.roc ─ - - This looks like an operator, but it's not one I recognize! - - 1│ main = - 2│ [] ++ [] - ^^ - - To concatenate two lists or strings, try using List.concat or - Str.concat instead. + test_report!( + #[ignore] + case_of, + indoc!( + r#" + case 1 of + 1 -> True + _ -> False "# - ), - ) - } + ), + @r###" + ── UNKNOWN OPERATOR ───────────────────────────────────── tmp/case_of/Test.roc ─ - #[test] - fn inline_hastype() { - report_problem_as( - indoc!( - r#" - main = - (\x -> x) : I64 + This looks like an operator, but it's not one I recognize! - 3 - "# - ), - indoc!( - r#" - ── UNKNOWN OPERATOR ────────────────────────────────────── /code/proj/Main.roc ─ + 1│ app "test" provides [main] to "./platform" + 2│ + 3│ main = + 4│ case 1 of + 5│ 1 -> True + ^^ - This looks like an operator, but it's not one I recognize! + The arrow -> is only used to define cases in a `when`. - 1│ main = - 2│ (\x -> x) : I64 - ^ + when color is + Red -> "stop!" + Green -> "go!" + "### + ); - The has-type operator : can only occur in a definition's type - signature, like - - increment : I64 -> I64 - increment = \x -> x + 1 + test_report!( + argument_without_space, + indoc!( + r#" + ["foo", bar("")] "# - ), - ) - } + ), + @r###" + ── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn wild_case_arrow() { - // this is still bad, but changing the order and progress of other parsers should improve it - // down the line - report_problem_as( - indoc!( - r#" - main = 5 -> 3 - "# - ), - indoc!( - r#" - ── UNKNOWN OPERATOR ────────────────────────────────────── /code/proj/Main.roc ─ + Nothing is named `bar` in this scope. - This looks like an operator, but it's not one I recognize! + 4│ ["foo", bar("")] + ^^^ - 1│ main = 5 -> 3 - ^^ + Did you mean one of these? - The arrow -> is only used to define cases in a `when`. + Nat + Str + Err + U8 + "### + ); - when color is - Red -> "stop!" - Green -> "go!" + test_report!( + invalid_operator, + indoc!( + r#" + main = + 5 ** 3 "# - ), - ) - } + ), + @r###" + ── UNKNOWN OPERATOR ──────────────────────────── tmp/invalid_operator/Test.roc ─ + + This looks like an operator, but it's not one I recognize! + + 1│ app "test" provides [main] to "./platform" + 2│ + 3│ main = + 4│ main = + 5│ 5 ** 3 + ^^ + + I have no specific suggestion for this operator, see TODO for the full + list of operators in Roc. + "### + ); + + test_report!( + double_plus, + indoc!( + r#" + main = + [] ++ [] + "# + ), + @r###" + ── UNKNOWN OPERATOR ───────────────────────────────── tmp/double_plus/Test.roc ─ + + This looks like an operator, but it's not one I recognize! + + 1│ app "test" provides [main] to "./platform" + 2│ + 3│ main = + 4│ main = + 5│ [] ++ [] + ^^ + + To concatenate two lists or strings, try using List.concat or + Str.concat instead. + "### + ); + + test_report!( + inline_hastype, + indoc!( + r#" + main = + (\x -> x) : I64 + + 3 + "# + ), + @r###" + ── UNKNOWN OPERATOR ────────────────────────────── tmp/inline_hastype/Test.roc ─ + + This looks like an operator, but it's not one I recognize! + + 1│ app "test" provides [main] to "./platform" + 2│ + 3│ main = + 4│ main = + 5│ (\x -> x) : I64 + ^ + + The has-type operator : can only occur in a definition's type + signature, like + + increment : I64 -> I64 + increment = \x -> x + 1 + "### + ); + + // this is still bad, but changing the order and progress of other parsers should improve it + // down the line + test_report!( + wild_case_arrow, + indoc!( + r#" + main = 5 -> 3 + "# + ), + @r###" + ── UNKNOWN OPERATOR ───────────────────────────── tmp/wild_case_arrow/Test.roc ─ + + This looks like an operator, but it's not one I recognize! + + 1│ app "test" provides [main] to "./platform" + 2│ + 3│ main = + 4│ main = 5 -> 3 + ^^ + + The arrow -> is only used to define cases in a `when`. + + when color is + Red -> "stop!" + Green -> "go!" + "### + ); #[test] fn provides_to_identifier() { @@ -6556,1040 +5756,956 @@ All branches in an `if` must have the same type! ) } - #[test] - fn apply_unary_negative() { - report_problem_as( - indoc!( - r#" - foo = 3 + test_report!( + apply_unary_negative, + indoc!( + r#" + foo = 3 - -foo 1 2 - "# - ), - indoc!( - r#" - ── TOO MANY ARGS ───────────────────────────────────────── /code/proj/Main.roc ─ - - This value is not a function, but it was given 2 arguments: - - 3│ -foo 1 2 - ^^^^ - - Are there any missing commas? Or missing parentheses? + -foo 1 2 "# - ), - ) - } + ), + @r###" + ── TOO MANY ARGS ───────────────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn apply_unary_not() { - report_problem_as( - indoc!( - r#" - foo = True + This value is not a function, but it was given 2 arguments: - !foo 1 2 - "# - ), - indoc!( - r#" - ── TOO MANY ARGS ───────────────────────────────────────── /code/proj/Main.roc ─ + 6│ -foo 1 2 + ^^^^ - This value is not a function, but it was given 2 arguments: + Are there any missing commas? Or missing parentheses? + "### + ); - 3│ !foo 1 2 - ^^^^ + test_report!( + apply_unary_not, + indoc!( + r#" + foo = True - Are there any missing commas? Or missing parentheses? + !foo 1 2 "# - ), - ) - } + ), + @r###" + ── TOO MANY ARGS ───────────────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn applied_tag_function() { - report_problem_as( - indoc!( - r#" - x : List [Foo Str] - x = List.map [1, 2] Foo + This value is not a function, but it was given 2 arguments: - x - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + 6│ !foo 1 2 + ^^^^ - Something is off with the body of the `x` definition: + Are there any missing commas? Or missing parentheses? + "### + ); - 1│ x : List [Foo Str] - 2│ x = List.map [1, 2] Foo - ^^^^^^^^^^^^^^^^^^^ + test_report!( + applied_tag_function, + indoc!( + r#" + x : List [Foo Str] + x = List.map [1, 2] Foo - This `map` call produces: - - List [Foo Num a] - - But the type annotation on `x` says it should be: - - List [Foo Str] - "# - ), - ) - } - - #[test] - fn pattern_in_parens_open() { - report_problem_as( - indoc!( - r#" - \( a - "# - ), - indoc!( - r#" - ── UNFINISHED PARENTHESES ──────────────────────────────── /code/proj/Main.roc ─ - - I am partway through parsing a pattern in parentheses, but I got stuck - here: - - 1│ \( a - ^ - - I was expecting to see a closing parenthesis before this, so try - adding a ) and see if that helps? + x "# - ), - ) - } + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn pattern_in_parens_end_comma() { - report_problem_as( - indoc!( - r#" - \( a, - "# - ), - indoc!( - r#" - ── UNFINISHED PARENTHESES ──────────────────────────────── /code/proj/Main.roc ─ + Something is off with the body of the `x` definition: - I am partway through parsing a pattern in parentheses, but I got stuck - here: + 4│ x : List [Foo Str] + 5│ x = List.map [1, 2] Foo + ^^^^^^^^^^^^^^^^^^^ - 1│ \( a, - ^ + This `map` call produces: - I was expecting to see a closing parenthesis before this, so try - adding a ) and see if that helps? + List [Foo Num a] + + But the type annotation on `x` says it should be: + + List [Foo Str] + "### + ); + + test_report!( + pattern_in_parens_open, + indoc!( + r#" + \( a "# - ), - ) - } + ), + @r###" + ── UNFINISHED PARENTHESES ──────────────── tmp/pattern_in_parens_open/Test.roc ─ - #[test] - fn pattern_in_parens_end() { - report_problem_as( - indoc!( - r#" - \( a - "# - ), - indoc!( - r#" - ── UNFINISHED PARENTHESES ──────────────────────────────── /code/proj/Main.roc ─ + I am partway through parsing a pattern in parentheses, but I got stuck + here: - I am partway through parsing a pattern in parentheses, but I got stuck - here: + 4│ \( a + ^ - 1│ \( a - ^ + I was expecting to see a closing parenthesis before this, so try + adding a ) and see if that helps? - I was expecting to see a closing parenthesis before this, so try - adding a ) and see if that helps? + Note: I may be confused by indentation + "### + ); + + test_report!( + pattern_in_parens_end_comma, + indoc!( + r#" + \( a, "# - ), - ) - } + ), + @r###" + ── UNFINISHED PARENTHESES ─────────── tmp/pattern_in_parens_end_comma/Test.roc ─ - #[test] - fn pattern_in_parens_indent_end() { - report_problem_as( - indoc!( - r#" - x = \( a - ) - "# - ), - indoc!( - r#" - ── NEED MORE INDENTATION ───────────────────────────────── /code/proj/Main.roc ─ + I am partway through parsing a pattern in parentheses, but I got stuck + here: - I am partway through parsing a pattern in parentheses, but I got stuck - here: + 4│ \( a, + ^ - 1│ x = \( a - 2│ ) - ^ + I was expecting to see a closing parenthesis before this, so try + adding a ) and see if that helps? + "### + ); - I need this parenthesis to be indented more. Try adding more spaces - before it! + test_report!( + pattern_in_parens_end, + indoc!( + r#" + \( a "# - ), - ) - } + ), + @r###" + ── UNFINISHED PARENTHESES ───────────────── tmp/pattern_in_parens_end/Test.roc ─ - #[test] - fn pattern_in_parens_indent_open() { - report_problem_as( - indoc!( - r#" - \( - "# - ), - indoc!( - r#" - ── UNFINISHED PATTERN ──────────────────────────────────── /code/proj/Main.roc ─ + I am partway through parsing a pattern in parentheses, but I got stuck + here: - I just started parsing a pattern, but I got stuck here: + 4│ \( a + ^ - 1│ \( - ^ + I was expecting to see a closing parenthesis before this, so try + adding a ) and see if that helps? - Note: I may be confused by indentation + Note: I may be confused by indentation + "### + ); + + test_report!( + pattern_in_parens_indent_end, + indoc!( + r#" + x = \( a + ) "# - ), - ) - } + ), + @r###" + ── NEED MORE INDENTATION ─────────── tmp/pattern_in_parens_indent_end/Test.roc ─ - #[test] - fn outdented_in_parens() { - report_problem_as( - indoc!( - r#" - Box : ( - Str - ) + I am partway through parsing a pattern in parentheses, but I got stuck + here: - 4 - "# - ), - indoc!( - r#" - ── NEED MORE INDENTATION ───────────────────────────────── /code/proj/Main.roc ─ + 4│ x = \( a + 5│ ) + ^ - I am partway through parsing a type in parentheses, but I got stuck - here: + I need this parenthesis to be indented more. Try adding more spaces + before it! + "### + ); - 1│ Box : ( - 2│ Str - 3│ ) - ^ - - I need this parenthesis to be indented more. Try adding more spaces - before it! + test_report!( + pattern_in_parens_indent_open, + indoc!( + r#" + \( "# - ), - ) - } + ), + @r###" + ── UNFINISHED PARENTHESES ───────── tmp/pattern_in_parens_indent_open/Test.roc ─ - #[test] - fn backpassing_type_error() { - report_problem_as( - indoc!( - r#" - x <- List.map ["a", "b"] + I just started parsing a pattern in parentheses, but I got stuck here: - x + 1 - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + 4│ \( + ^ - The 2nd argument to `map` is not what I expect: + Record pattern look like { name, age: currentAge }, so I was expecting + to see a field name next. - 1│> x <- List.map ["a", "b"] - 2│> - 3│> x + 1 + Note: I may be confused by indentation + "### + ); - This argument is an anonymous function of type: + test_report!( + outdented_in_parens, + indoc!( + r#" + Box : ( + Str + ) - Num a -> Num a - - But `map` needs the 2nd argument to be: - - Str -> Num a + 4 "# - ), - ) - } + ), + @r###" + ── NEED MORE INDENTATION ──────────────────── tmp/outdented_in_parens/Test.roc ─ - #[test] - fn underscore_let() { - report_problem_as( - indoc!( - r#" - _ = 3 + I am partway through parsing a type in parentheses, but I got stuck + here: - 4 - "# - ), - indoc!( - r#" - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + 4│ Box : ( + 5│ Str + 6│ ) + ^ - Underscore patterns are not allowed in definitions + I need this parenthesis to be indented more. Try adding more spaces + before it! + "### + ); - 1│ _ = 3 - ^ + test_report!( + backpassing_type_error, + indoc!( + r#" + x <- List.map ["a", "b"] + + x + 1 "# - ), - ) - } + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn expect_expr_type_error() { - report_problem_as( - indoc!( - r#" - expect "foobar" + The 2nd argument to `map` is not what I expect: - 4 - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + 4│> x <- List.map ["a", "b"] + 5│> + 6│> x + 1 - This `expect` condition needs to be a Bool: + This argument is an anonymous function of type: - 1│ expect "foobar" - ^^^^^^^^ + Num a -> Num a - Right now it’s a string of type: + But `map` needs the 2nd argument to be: - Str + Str -> Num a + "### + ); - But I need every `expect` condition to evaluate to a Bool—either `True` - or `False`. + test_report!( + underscore_let, + indoc!( + r#" + _ = 3 + + 4 "# - ), - ) - } + ), + @r###" + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn num_too_general_wildcard() { - report_problem_as( - indoc!( - r#" - mult : Num.Num *, Num.F64 -> Num.F64 - mult = \a, b -> a * b + Underscore patterns are not allowed in definitions - mult 0 0 - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + 4│ _ = 3 + ^ + "### + ); - The 2nd argument to `mul` is not what I expect: + test_report!( + expect_expr_type_error, + indoc!( + r#" + expect "foobar" - 2│ mult = \a, b -> a * b - ^ - - This `b` value is a: - - F64 - - But `mul` needs the 2nd argument to be: - - Num * - - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - Something is off with the body of the `mult` definition: - - 1│ mult : Num.Num *, Num.F64 -> Num.F64 - 2│ mult = \a, b -> a * b - ^^^^^ - - This `mul` call produces: - - Num * - - But the type annotation on `mult` says it should be: - - F64 + 4 "# - ), - ) - } + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn num_too_general_named() { - report_problem_as( - indoc!( - r#" - mult : Num.Num a, Num.F64 -> Num.F64 - mult = \a, b -> a * b + This `expect` condition needs to be a Bool: - mult 0 0 - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + 4│ expect "foobar" + ^^^^^^^^ - The 2nd argument to `mul` is not what I expect: + Right now it’s a string of type: - 2│ mult = \a, b -> a * b - ^ + Str - This `b` value is a: + But I need every `expect` condition to evaluate to a Bool—either `True` + or `False`. + "### + ); - F64 + test_report!( + num_too_general_wildcard, + indoc!( + r#" + mult : Num.Num *, Num.F64 -> Num.F64 + mult = \a, b -> a * b - But `mul` needs the 2nd argument to be: - - Num a - - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - Something is off with the body of the `mult` definition: - - 1│ mult : Num.Num a, Num.F64 -> Num.F64 - 2│ mult = \a, b -> a * b - ^^^^^ - - This `mul` call produces: - - Num a - - But the type annotation on `mult` says it should be: - - F64 + mult 0 0 "# - ), - ) - } + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn inference_var_not_enough_in_alias() { - report_problem_as( - indoc!( - r#" - Result a b : [Ok a, Err b] + The 2nd argument to `mul` is not what I expect: - canIGo : _ -> Result _ - canIGo = \color -> - when color is - "green" -> Ok "go!" - "yellow" -> Err (SlowIt "whoa, let's slow down!") - "red" -> Err (StopIt "absolutely not") - _ -> Err (UnknownColor "this is a weird stoplight") - canIGo - "# - ), - indoc!( - r#" - ── TOO FEW TYPE ARGUMENTS ──────────────────────────────── /code/proj/Main.roc ─ - - The `Result` alias expects 2 type arguments, but it got 1 instead: - - 3│ canIGo : _ -> Result _ - ^^^^^^^^ - - Are there missing parentheses? - "# - ), - ) - } - - #[test] - fn inference_var_too_many_in_alias() { - report_problem_as( - indoc!( - r#" - Result a b : [Ok a, Err b] - - canIGo : _ -> Result _ _ _ - canIGo = \color -> - when color is - "green" -> Ok "go!" - "yellow" -> Err (SlowIt "whoa, let's slow down!") - "red" -> Err (StopIt "absolutely not") - _ -> Err (UnknownColor "this is a weird stoplight") - canIGo - "# - ), - indoc!( - r#" - ── TOO MANY TYPE ARGUMENTS ─────────────────────────────── /code/proj/Main.roc ─ - - The `Result` alias expects 2 type arguments, but it got 3 instead: - - 3│ canIGo : _ -> Result _ _ _ - ^^^^^^^^^^^^ - - Are there missing parentheses? - "# - ), - ) - } - - #[test] - fn inference_var_conflict_in_rigid_links() { - report_problem_as( - indoc!( - r#" - f : a -> (_ -> b) - f = \x -> \y -> if x == y then x else y - f - "# - ), - // TODO: We should tell the user that we inferred `_` as `a` - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - Something is off with the body of the `f` definition: - - 1│ f : a -> (_ -> b) - 2│ f = \x -> \y -> if x == y then x else y - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - The body is an anonymous function of type: - - a -> a - - But the type annotation on `f` says it should be: - - a -> b - - Tip: Your type annotation uses `a` and `b` as separate type variables. - Your code seems to be saying they are the same though. Maybe they - should be the same in your type annotation? Maybe your code uses them - in a weird way? - "# - ), - ) - } - - #[test] - fn error_wildcards_are_related() { - report_problem_as( - indoc!( - r#" - f : * -> * - f = \x -> x - - f - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - Something is off with the body of the `f` definition: - - 1│ f : * -> * - 2│ f = \x -> x - ^ - - The type annotation on `f` says this `x` value should have the type: - - * - - However, the type of this `x` value is connected to another type in a - way that isn't reflected in this annotation. - - Tip: Any connection between types must use a named type variable, not - a `*`! Maybe the annotation on `f` should have a named type variable in - place of the `*`? - "# - ), - ) - } - - #[test] - fn error_nested_wildcards_are_related() { - report_problem_as( - indoc!( - r#" - f : a, b, * -> {x: a, y: b, z: *} - f = \x, y, z -> {x, y, z} - - f - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - Something is off with the body of the `f` definition: - - 1│ f : a, b, * -> {x: a, y: b, z: *} - 2│ f = \x, y, z -> {x, y, z} - ^^^^^^^^^ - - The type annotation on `f` says the body is a record should have the - type: - - { x : a, y : b, z : * } - - However, the type of the body is a record is connected to another type - in a way that isn't reflected in this annotation. - - Tip: Any connection between types must use a named type variable, not - a `*`! Maybe the annotation on `f` should have a named type variable in - place of the `*`? - "# - ), - ) - } - - #[test] - fn error_wildcards_are_related_in_nested_defs() { - report_problem_as( - indoc!( - r#" - f : a, b, * -> * - f = \_, _, x2 -> - inner : * -> * - inner = \y -> y - inner x2 - - f - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - Something is off with the body of the `inner` definition: - - 3│ inner : * -> * - 4│ inner = \y -> y - ^ - - The type annotation on `inner` says this `y` value should have the type: - - * - - However, the type of this `y` value is connected to another type in a - way that isn't reflected in this annotation. - - Tip: Any connection between types must use a named type variable, not - a `*`! Maybe the annotation on `inner` should have a named type variable - in place of the `*`? - "# - ), - ) - } - - #[test] - fn error_inline_alias_not_an_alias() { - report_problem_as( - indoc!( - r#" - f : List elem -> [Nil, Cons elem a] as a - "# - ), - indoc!( - r#" - ── NOT AN INLINE ALIAS ─────────────────────────────────── /code/proj/Main.roc ─ - - The inline type after this `as` is not a type alias: - - 1│ f : List elem -> [Nil, Cons elem a] as a - ^ - - Inline alias types must start with an uppercase identifier and be - followed by zero or more type arguments, like Point or List a. - "# - ), - ) - } - - #[test] - fn error_inline_alias_qualified() { - report_problem_as( - indoc!( - r#" - f : List elem -> [Nil, Cons elem a] as Module.LinkedList a - "# - ), - indoc!( - r#" - ── QUALIFIED ALIAS NAME ────────────────────────────────── /code/proj/Main.roc ─ - - This type alias has a qualified name: - - 1│ f : List elem -> [Nil, Cons elem a] as Module.LinkedList a - ^ - - An alias introduces a new name to the current scope, so it must be - unqualified. - "# - ), - ) - } - - #[test] - fn error_inline_alias_argument_uppercase() { - report_problem_as( - indoc!( - r#" - f : List elem -> [Nil, Cons elem a] as LinkedList U - "# - ), - indoc!( - r#" - ── TYPE ARGUMENT NOT LOWERCASE ─────────────────────────── /code/proj/Main.roc ─ - - This alias type argument is not lowercase: - - 1│ f : List elem -> [Nil, Cons elem a] as LinkedList U - ^ - - All type arguments must be lowercase. - "# - ), - ) - } - - #[test] - fn mismatched_single_tag_arg() { - report_problem_as( - indoc!( - r#" - isEmpty = - \email -> - Email str = email - Str.isEmpty str - - isEmpty (Name "boo") - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - The 1st argument to `isEmpty` is not what I expect: - - 6│ isEmpty (Name "boo") - ^^^^^^^^^^ - - This `Name` tag application has the type: - - [Name Str]a - - But `isEmpty` needs the 1st argument to be: - - [Email Str] - - Tip: Seems like a tag typo. Maybe `Name` should be `Email`? - - Tip: Can more type annotations be added? Type annotations always help - me give more specific messages, and I think they could help a lot in - this case - "# - ), - ) - } - - #[test] - fn issue_2326() { - report_problem_as( - indoc!( - r#" - C a b : a -> D a b - D a b : { a, b } - - f : C a Num.Nat -> D a Num.Nat - f = \c -> c 6 - f - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - The 1st argument to `c` is not what I expect: - - 5│ f = \c -> c 6 + 5│ mult = \a, b -> a * b ^ - This argument is a number of type: + This `b` value is a: - Num a + F64 - But `c` needs the 1st argument to be: + But `mul` needs the 2nd argument to be: - a + Num * - Tip: The type annotation uses the type variable `a` to say that this - definition can produce any type of value. But in the body I see that - it will only produce a `Num` value of a single specific type. Maybe - change the type annotation to be more specific? Maybe change the code - to be more general? - "# - ), - ) - } + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn issue_2380_annotations_only() { - report_problem_as( - indoc!( - r#" - F : F - a : F - a - "# - ), - indoc!( - r#" - ── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─ + Something is off with the body of the `mult` definition: - The `F` alias is self-recursive in an invalid way: + 4│ mult : Num.Num *, Num.F64 -> Num.F64 + 5│ mult = \a, b -> a * b + ^^^^^ - 1│ F : F - ^ + This `mul` call produces: - Recursion in aliases is only allowed if recursion happens behind a - tagged union, at least one variant of which is not recursive. - "# - ), - ) - } + Num * - #[test] - fn issue_2380_typed_body() { - report_problem_as( - indoc!( - r#" - F : F - a : F - a = 1 - a - "# - ), - indoc!( - r#" - ── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─ + But the type annotation on `mult` says it should be: - The `F` alias is self-recursive in an invalid way: + F64 + "### + ); - 1│ F : F - ^ + test_report!( + num_too_general_named, + indoc!( + r#" + mult : Num.Num a, Num.F64 -> Num.F64 + mult = \a, b -> a * b - Recursion in aliases is only allowed if recursion happens behind a - tagged union, at least one variant of which is not recursive. - "# - ), - ) - } + mult 0 0 + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn issue_2380_alias_with_vars() { - report_problem_as( - indoc!( - r#" - F a b : F a b - a : F Str Str - a - "# - ), - indoc!( - r#" - ── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─ + The 2nd argument to `mul` is not what I expect: - The `F` alias is self-recursive in an invalid way: + 5│ mult = \a, b -> a * b + ^ - 1│ F a b : F a b - ^ + This `b` value is a: - Recursion in aliases is only allowed if recursion happens behind a - tagged union, at least one variant of which is not recursive. - "# - ), - ) - } + F64 - #[test] - fn issue_2167_record_field_optional_and_required_mismatch() { - report_problem_as( - indoc!( - r#" - Job : [Job { inputs : List Str }] - job : { inputs ? List Str } -> Job - job = \{ inputs } -> - Job { inputs } + But `mul` needs the 2nd argument to be: - job { inputs: ["build", "test"] } - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + Num a - The 1st argument to `job` is weird: + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - 3│ job = \{ inputs } -> - ^^^^^^^^^^ + Something is off with the body of the `mult` definition: - The argument is a pattern that matches record values of type: + 4│ mult : Num.Num a, Num.F64 -> Num.F64 + 5│ mult = \a, b -> a * b + ^^^^^ - { inputs : List Str } + This `mul` call produces: - But the annotation on `job` says the 1st argument should be: + Num a - { inputs ? List Str } + But the type annotation on `mult` says it should be: - Tip: To extract the `.inputs` field it must be non-optional, but the - type says this field is optional. Learn more about optional fields at - TODO. - "# - ), - ) - } + F64 + "### + ); - #[test] - fn unify_recursive_with_nonrecursive() { - report_problem_as( - indoc!( - r#" - Job : [Job { inputs : List Job }] + test_report!( + inference_var_not_enough_in_alias, + indoc!( + r#" + Result a b : [Ok a, Err b] - job : { inputs : List Str } -> Job - job = \{ inputs } -> - Job { inputs } + canIGo : _ -> Result _ + canIGo = \color -> + when color is + "green" -> Ok "go!" + "yellow" -> Err (SlowIt "whoa, let's slow down!") + "red" -> Err (StopIt "absolutely not") + _ -> Err (UnknownColor "this is a weird stoplight") + canIGo + "# + ), + @r###" + ── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─ - job { inputs: ["build", "test"] } - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + The `Result` name is first defined here: - Something is off with the body of the `job` definition: + 1│ app "test" provides [main] to "./platform" + - 3│ job : { inputs : List Str } -> Job - 4│ job = \{ inputs } -> - 5│ Job { inputs } - ^^^^^^^^^^^^^^ + But then it's defined a second time here: - This `Job` tag application has the type: + 4│ Result a b : [Ok a, Err b] + ^^^^^^^^^^^^^^^^^^^^^^^^^^ - [Job { inputs : List Str }] + Since these aliases have the same name, it's easy to use the wrong one + on accident. Give one of them a new name. - But the type annotation on `job` says it should be: + ── TOO FEW TYPE ARGUMENTS ──────────────────────────────── /code/proj/Main.roc ─ - [Job { inputs : List a }] as a - "# - ), - ) - } + The `Result` alias expects 2 type arguments, but it got 1 instead: - #[test] - fn nested_datatype() { - report_problem_as( - indoc!( - r#" - Nested a : [Chain a (Nested (List a)), Term] + 6│ canIGo : _ -> Result _ + ^^^^^^^^ - s : Nested Str + Are there missing parentheses? + "### + ); - s - "# - ), - indoc!( - r#" - ── NESTED DATATYPE ─────────────────────────────────────── /code/proj/Main.roc ─ + test_report!( + inference_var_too_many_in_alias, + indoc!( + r#" + Result a b : [Ok a, Err b] - `Nested` is a nested datatype. Here is one recursive usage of it: + canIGo : _ -> Result _ _ _ + canIGo = \color -> + when color is + "green" -> Ok "go!" + "yellow" -> Err (SlowIt "whoa, let's slow down!") + "red" -> Err (StopIt "absolutely not") + _ -> Err (UnknownColor "this is a weird stoplight") + canIGo + "# + ), + @r###" + ── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─ - 1│ Nested a : [Chain a (Nested (List a)), Term] - ^^^^^^^^^^^^^^^ + The `Result` name is first defined here: - But recursive usages of `Nested` must match its definition: + 1│ app "test" provides [main] to "./platform" + - 1│ Nested a : [Chain a (Nested (List a)), Term] - ^^^^^^^^ + But then it's defined a second time here: - Nested datatypes are not supported in Roc. + 4│ Result a b : [Ok a, Err b] + ^^^^^^^^^^^^^^^^^^^^^^^^^^ - Hint: Consider rewriting the definition of `Nested` to use the recursive type with the same arguments. - "# - ), - ) - } + Since these aliases have the same name, it's easy to use the wrong one + on accident. Give one of them a new name. - #[test] - fn nested_datatype_inline() { - report_problem_as( - indoc!( - r#" - f : {} -> [Chain a (Nested (List a)), Term] as Nested a + ── TOO MANY TYPE ARGUMENTS ─────────────────────────────── /code/proj/Main.roc ─ - f - "# - ), - indoc!( - r#" - ── NESTED DATATYPE ─────────────────────────────────────── /code/proj/Main.roc ─ + The `Result` alias expects 2 type arguments, but it got 3 instead: - `Nested` is a nested datatype. Here is one recursive usage of it: + 6│ canIGo : _ -> Result _ _ _ + ^^^^^^^^^^^^ - 1│ f : {} -> [Chain a (Nested (List a)), Term] as Nested a - ^^^^^^^^^^^^^^^ + Are there missing parentheses? + "### + ); - But recursive usages of `Nested` must match its definition: + test_report!( + inference_var_conflict_in_rigid_links, + indoc!( + r#" + f : a -> (_ -> b) + f = \x -> \y -> if x == y then x else y + f + "# + ), + // TODO: We should tell the user that we inferred `_` as `a` + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - 1│ f : {} -> [Chain a (Nested (List a)), Term] as Nested a - ^^^^^^^^ + Something is off with the body of the `f` definition: - Nested datatypes are not supported in Roc. + 4│ f : a -> (_ -> b) + 5│ f = \x -> \y -> if x == y then x else y + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Hint: Consider rewriting the definition of `Nested` to use the recursive type with the same arguments. - "# - ), - ) - } + The body is an anonymous function of type: + + a -> a + + But the type annotation on `f` says it should be: + + a -> b + + Tip: Your type annotation uses `a` and `b` as separate type variables. + Your code seems to be saying they are the same though. Maybe they + should be the same in your type annotation? Maybe your code uses them + in a weird way? + "### + ); + + test_report!( + error_wildcards_are_related, + indoc!( + r#" + f : * -> * + f = \x -> x + + f + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + Something is off with the body of the `f` definition: + + 4│ f : * -> * + 5│ f = \x -> x + ^ + + The type annotation on `f` says this `x` value should have the type: + + * + + However, the type of this `x` value is connected to another type in a + way that isn't reflected in this annotation. + + Tip: Any connection between types must use a named type variable, not + a `*`! Maybe the annotation on `f` should have a named type variable in + place of the `*`? + "### + ); + + test_report!( + error_nested_wildcards_are_related, + indoc!( + r#" + f : a, b, * -> {x: a, y: b, z: *} + f = \x, y, z -> {x, y, z} + + f + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + Something is off with the body of the `f` definition: + + 4│ f : a, b, * -> {x: a, y: b, z: *} + 5│ f = \x, y, z -> {x, y, z} + ^^^^^^^^^ + + The type annotation on `f` says the body is a record should have the + type: + + { x : a, y : b, z : * } + + However, the type of the body is a record is connected to another type + in a way that isn't reflected in this annotation. + + Tip: Any connection between types must use a named type variable, not + a `*`! Maybe the annotation on `f` should have a named type variable in + place of the `*`? + "### + ); + + test_report!( + error_wildcards_are_related_in_nested_defs, + indoc!( + r#" + f : a, b, * -> * + f = \_, _, x2 -> + inner : * -> * + inner = \y -> y + inner x2 + + f + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + Something is off with the body of the `inner` definition: + + 6│ inner : * -> * + 7│ inner = \y -> y + ^ + + The type annotation on `inner` says this `y` value should have the type: + + * + + However, the type of this `y` value is connected to another type in a + way that isn't reflected in this annotation. + + Tip: Any connection between types must use a named type variable, not + a `*`! Maybe the annotation on `inner` should have a named type variable + in place of the `*`? + "### + ); + + test_report!( + error_inline_alias_not_an_alias, + indoc!( + r#" + f : List elem -> [Nil, Cons elem a] as a + "# + ), + @r###" + ── NOT AN INLINE ALIAS ────────── tmp/error_inline_alias_not_an_alias/Test.roc ─ + + The inline type after this `as` is not a type alias: + + 4│ f : List elem -> [Nil, Cons elem a] as a + ^ + + Inline alias types must start with an uppercase identifier and be + followed by zero or more type arguments, like Point or List a. + "### + ); + + test_report!( + error_inline_alias_qualified, + indoc!( + r#" + f : List elem -> [Nil, Cons elem a] as Module.LinkedList a + "# + ), + @r###" + ── QUALIFIED ALIAS NAME ──────────── tmp/error_inline_alias_qualified/Test.roc ─ + + This type alias has a qualified name: + + 4│ f : List elem -> [Nil, Cons elem a] as Module.LinkedList a + ^ + + An alias introduces a new name to the current scope, so it must be + unqualified. + "### + ); + + test_report!( + error_inline_alias_argument_uppercase, + indoc!( + r#" + f : List elem -> [Nil, Cons elem a] as LinkedList U + "# + ), + @r###" + ── TYPE ARGUMENT NOT LOWERCASE ─ ...r_inline_alias_argument_uppercase/Test.roc ─ + + This alias type argument is not lowercase: + + 4│ f : List elem -> [Nil, Cons elem a] as LinkedList U + ^ + + All type arguments must be lowercase. + "### + ); + + test_report!( + mismatched_single_tag_arg, + indoc!( + r#" + isEmpty = + \email -> + Email str = email + Str.isEmpty str + + isEmpty (Name "boo") + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + The 1st argument to `isEmpty` is not what I expect: + + 9│ isEmpty (Name "boo") + ^^^^^^^^^^ + + This `Name` tag application has the type: + + [Name Str]a + + But `isEmpty` needs the 1st argument to be: + + [Email Str] + + Tip: Seems like a tag typo. Maybe `Name` should be `Email`? + + Tip: Can more type annotations be added? Type annotations always help + me give more specific messages, and I think they could help a lot in + this case + "### + ); + + test_report!( + issue_2326, + indoc!( + r#" + C a b : a -> D a b + D a b : { a, b } + + f : C a Num.Nat -> D a Num.Nat + f = \c -> c 6 + f + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + The 1st argument to `c` is not what I expect: + + 8│ f = \c -> c 6 + ^ + + This argument is a number of type: + + Num a + + But `c` needs the 1st argument to be: + + a + + Tip: The type annotation uses the type variable `a` to say that this + definition can produce any type of value. But in the body I see that + it will only produce a `Num` value of a single specific type. Maybe + change the type annotation to be more specific? Maybe change the code + to be more general? + "### + ); + + test_report!( + issue_2380_annotations_only, + indoc!( + r#" + F : F + a : F + a + "# + ), + @r###" + ── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─ + + The `F` alias is self-recursive in an invalid way: + + 4│ F : F + ^ + + Recursion in aliases is only allowed if recursion happens behind a + tagged union, at least one variant of which is not recursive. + "### + ); + + test_report!( + issue_2380_typed_body, + indoc!( + r#" + F : F + a : F + a = 1 + a + "# + ), + @r###" + ── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─ + + The `F` alias is self-recursive in an invalid way: + + 4│ F : F + ^ + + Recursion in aliases is only allowed if recursion happens behind a + tagged union, at least one variant of which is not recursive. + "### + ); + + test_report!( + issue_2380_alias_with_vars, + indoc!( + r#" + F a b : F a b + a : F Str Str + a + "# + ), + @r###" + ── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─ + + The `F` alias is self-recursive in an invalid way: + + 4│ F a b : F a b + ^ + + Recursion in aliases is only allowed if recursion happens behind a + tagged union, at least one variant of which is not recursive. + "### + ); + + test_report!( + issue_2167_record_field_optional_and_required_mismatch, + indoc!( + r#" + Job : [Job { inputs : List Str }] + job : { inputs ? List Str } -> Job + job = \{ inputs } -> + Job { inputs } + + job { inputs: ["build", "test"] } + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + The 1st argument to `job` is weird: + + 6│ job = \{ inputs } -> + ^^^^^^^^^^ + + The argument is a pattern that matches record values of type: + + { inputs : List Str } + + But the annotation on `job` says the 1st argument should be: + + { inputs ? List Str } + + Tip: To extract the `.inputs` field it must be non-optional, but the + type says this field is optional. Learn more about optional fields at + TODO. + "### + ); + + test_report!( + unify_recursive_with_nonrecursive, + indoc!( + r#" + Job : [Job { inputs : List Job }] + + job : { inputs : List Str } -> Job + job = \{ inputs } -> + Job { inputs } + + job { inputs: ["build", "test"] } + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + Something is off with the body of the `job` definition: + + 6│ job : { inputs : List Str } -> Job + 7│ job = \{ inputs } -> + 8│ Job { inputs } + ^^^^^^^^^^^^^^ + + This `Job` tag application has the type: + + [Job { inputs : List Str }] + + But the type annotation on `job` says it should be: + + [Job { inputs : List a }] as a + "### + ); + + test_report!( + nested_datatype, + indoc!( + r#" + Nested a : [Chain a (Nested (List a)), Term] + + s : Nested Str + + s + "# + ), + @r###" + ── NESTED DATATYPE ─────────────────────────────────────── /code/proj/Main.roc ─ + + `Nested` is a nested datatype. Here is one recursive usage of it: + + 4│ Nested a : [Chain a (Nested (List a)), Term] + ^^^^^^^^^^^^^^^ + + But recursive usages of `Nested` must match its definition: + + 4│ Nested a : [Chain a (Nested (List a)), Term] + ^^^^^^^^ + + Nested datatypes are not supported in Roc. + + Hint: Consider rewriting the definition of `Nested` to use the recursive type with the same arguments. + "### + ); + + test_report!( + nested_datatype_inline, + indoc!( + r#" + f : {} -> [Chain a (Nested (List a)), Term] as Nested a + + f + "# + ), + @r###" + ── NESTED DATATYPE ─────────────────────────────────────── /code/proj/Main.roc ─ + + `Nested` is a nested datatype. Here is one recursive usage of it: + + 4│ f : {} -> [Chain a (Nested (List a)), Term] as Nested a + ^^^^^^^^^^^^^^^ + + But recursive usages of `Nested` must match its definition: + + 4│ f : {} -> [Chain a (Nested (List a)), Term] as Nested a + ^^^^^^^^ + + Nested datatypes are not supported in Roc. + + Hint: Consider rewriting the definition of `Nested` to use the recursive type with the same arguments. + "### + ); macro_rules! mismatched_suffix_tests { ($($number:expr, $suffix:expr, $name:ident)*) => {$( - #[test] - fn $name() { - let number = $number.to_string(); - let mut typ = $suffix.to_string(); - typ.get_mut(0..1).unwrap().make_ascii_uppercase(); - let bad_type = if $suffix == "u8" { "I8" } else { "U8" }; - let carets = "^".repeat(number.len() + $suffix.len()); - let kind = match $suffix { - "dec"|"f32"|"f64" => "a frac", - _ => "an integer", - }; + test_report!( + $name, + &{ + let number = $number.to_string(); + let mut typ = $suffix.to_string(); + typ.get_mut(0..1).unwrap().make_ascii_uppercase(); + let bad_type = if $suffix == "u8" { "I8" } else { "U8" }; - report_problem_as( - &format!(indoc!( + format!(indoc!( r#" use : Num.{} -> Num.U8 use {}{} "# - ), bad_type, number, $suffix), - &format!(indoc!( + ), bad_type, number, $suffix) + }, + |golden| { + let number = $number.to_string(); + let mut typ = $suffix.to_string(); + typ.get_mut(0..1).unwrap().make_ascii_uppercase(); + let bad_type = if $suffix == "u8" { "I8" } else { "U8" }; + let carets = "^".repeat(number.len() + $suffix.len()); + let kind = match $suffix { + "dec"|"f32"|"f64" => "a frac", + _ => "an integer", + }; + + let real = format!(indoc!( r#" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ The 1st argument to `use` is not what I expect: - 2│ use {}{} - {} + 5│ use {}{} + {} This argument is {} of type: @@ -7599,9 +6715,11 @@ All branches in an `if` must have the same type! {} "# - ), number, $suffix, carets, kind, typ, bad_type), - ) - } + ), number, $suffix, carets, kind, typ, bad_type); + + assert_eq!(golden, real); + } + ); )*} } @@ -7624,31 +6742,38 @@ All branches in an `if` must have the same type! macro_rules! mismatched_suffix_tests_in_pattern { ($($number:expr, $suffix:expr, $name:ident)*) => {$( - #[test] - fn $name() { - let number = $number.to_string(); - let mut typ = $suffix.to_string(); - typ.get_mut(0..1).unwrap().make_ascii_uppercase(); - let bad_suffix = if $suffix == "u8" { "i8" } else { "u8" }; - let bad_type = if $suffix == "u8" { "I8" } else { "U8" }; + test_report!( + $name, + &{ + let number = $number.to_string(); + let mut typ = $suffix.to_string(); + typ.get_mut(0..1).unwrap().make_ascii_uppercase(); + let bad_suffix = if $suffix == "u8" { "i8" } else { "u8" }; - report_problem_as( - &format!(indoc!( + format!(indoc!( r#" when {}{} is {}{} -> 1 _ -> 1 "# - ), number, bad_suffix, number, $suffix), - &format!(indoc!( + ), number, bad_suffix, number, $suffix) + }, + |golden| { + let number = $number.to_string(); + let mut typ = $suffix.to_string(); + typ.get_mut(0..1).unwrap().make_ascii_uppercase(); + let bad_suffix = if $suffix == "u8" { "i8" } else { "u8" }; + let bad_type = if $suffix == "u8" { "I8" } else { "U8" }; + + let real = format!(indoc!( r#" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ The branches of this `when` expression don't match the condition: - 1│> when {}{} is - 2│ {}{} -> 1 - 3│ _ -> 1 + 4│> when {}{} is + 5│ {}{} -> 1 + 6│ _ -> 1 The `when` condition is an integer of type: @@ -7660,9 +6785,11 @@ All branches in an `if` must have the same type! The branches must be cases of the `when` condition's type! "# - ), number, bad_suffix, number, $suffix, bad_type, typ), - ) - } + ), number, bad_suffix, number, $suffix, bad_type, typ); + + assert_eq!(golden, real); + } + ); )*} } @@ -7683,1326 +6810,1152 @@ All branches in an `if` must have the same type! 1, "f64", mismatched_suffix_f64_pattern } - #[test] - fn bad_numeric_literal_suffix() { - report_problem_as( - indoc!( - r#" - 1u256 - "# - ), - // TODO: link to number suffixes - indoc!( - r#" - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - - This integer literal contains an invalid digit: - - 1│ 1u256 - ^^^^^ - - Integer literals can only contain the digits - 0-9, or have an integer suffix. - - Tip: Learn more about number literals at TODO - "# - ), - ) - } - - #[test] - fn numer_literal_multi_suffix() { - report_problem_as( - indoc!( - r#" - 1u8u8 - "# - ), - // TODO: link to number suffixes - indoc!( - r#" - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - - This integer literal contains an invalid digit: - - 1│ 1u8u8 - ^^^^^ - - Integer literals can only contain the digits - 0-9, or have an integer suffix. - - Tip: Learn more about number literals at TODO - "# - ), - ) - } - - #[test] - fn int_literal_has_float_suffix() { - report_problem_as( - indoc!( - r#" - 0b1f32 - "# - ), - indoc!( - r#" - ── CONFLICTING NUMBER SUFFIX ───────────────────────────── /code/proj/Main.roc ─ - - This number literal is an integer, but it has a float suffix: - - 1│ 0b1f32 - ^^^^^^ - "# - ), - ) - } - - #[test] - fn float_literal_has_int_suffix() { - report_problem_as( - indoc!( - r#" - 1.0u8 - "# - ), - indoc!( - r#" - ── CONFLICTING NUMBER SUFFIX ───────────────────────────── /code/proj/Main.roc ─ - - This number literal is a float, but it has an integer suffix: - - 1│ 1.0u8 - ^^^^^ - "# - ), - ) - } - - #[test] - fn u8_overflow() { - report_problem_as( - "256u8", - indoc!( - r#" - ── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─ - - This integer literal overflows the type indicated by its suffix: - - 1│ 256u8 - ^^^^^ - - Tip: The suffix indicates this integer is a U8, whose maximum value is - 255. - "# - ), - ) - } - - #[test] - fn negative_u8() { - report_problem_as( - "-1u8", - indoc!( - r#" - ── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─ - - This integer literal underflows the type indicated by its suffix: - - 1│ -1u8 - ^^^^ - - Tip: The suffix indicates this integer is a U8, whose minimum value is - 0. - "# - ), - ) - } - - #[test] - fn u16_overflow() { - report_problem_as( - "65536u16", - indoc!( - r#" - ── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─ - - This integer literal overflows the type indicated by its suffix: - - 1│ 65536u16 - ^^^^^^^^ - - Tip: The suffix indicates this integer is a U16, whose maximum value - is 65535. - "# - ), - ) - } - - #[test] - fn negative_u16() { - report_problem_as( - "-1u16", - indoc!( - r#" - ── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─ - - This integer literal underflows the type indicated by its suffix: - - 1│ -1u16 - ^^^^^ - - Tip: The suffix indicates this integer is a U16, whose minimum value - is 0. - "# - ), - ) - } - - #[test] - fn u32_overflow() { - report_problem_as( - "4_294_967_296u32", - indoc!( - r#" - ── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─ - - This integer literal overflows the type indicated by its suffix: - - 1│ 4_294_967_296u32 - ^^^^^^^^^^^^^^^^ - - Tip: The suffix indicates this integer is a U32, whose maximum value - is 4_294_967_295. - "# - ), - ) - } - - #[test] - fn negative_u32() { - report_problem_as( - "-1u32", - indoc!( - r#" - ── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─ - - This integer literal underflows the type indicated by its suffix: - - 1│ -1u32 - ^^^^^ - - Tip: The suffix indicates this integer is a U32, whose minimum value - is 0. - "# - ), - ) - } - - #[test] - fn u64_overflow() { - report_problem_as( - "18_446_744_073_709_551_616u64", - indoc!( - r#" - ── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─ - - This integer literal overflows the type indicated by its suffix: - - 1│ 18_446_744_073_709_551_616u64 - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - Tip: The suffix indicates this integer is a U64, whose maximum value - is 18_446_744_073_709_551_615. - "# - ), - ) - } - - #[test] - fn negative_u64() { - report_problem_as( - "-1u64", - indoc!( - r#" - ── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─ - - This integer literal underflows the type indicated by its suffix: - - 1│ -1u64 - ^^^^^ - - Tip: The suffix indicates this integer is a U64, whose minimum value - is 0. - "# - ), - ) - } - - #[test] - fn negative_u128() { - report_problem_as( - "-1u128", - indoc!( - r#" - ── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─ - - This integer literal underflows the type indicated by its suffix: - - 1│ -1u128 - ^^^^^^ - - Tip: The suffix indicates this integer is a U128, whose minimum value - is 0. - "# - ), - ) - } - - #[test] - fn i8_overflow() { - report_problem_as( - "128i8", - indoc!( - r#" - ── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─ - - This integer literal overflows the type indicated by its suffix: - - 1│ 128i8 - ^^^^^ - - Tip: The suffix indicates this integer is a I8, whose maximum value is - 127. - "# - ), - ) - } - - #[test] - fn i8_underflow() { - report_problem_as( - "-129i8", - indoc!( - r#" - ── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─ - - This integer literal underflows the type indicated by its suffix: - - 1│ -129i8 - ^^^^^^ - - Tip: The suffix indicates this integer is a I8, whose minimum value is - -128. - "# - ), - ) - } - - #[test] - fn i16_overflow() { - report_problem_as( - "32768i16", - indoc!( - r#" - ── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─ - - This integer literal overflows the type indicated by its suffix: - - 1│ 32768i16 - ^^^^^^^^ - - Tip: The suffix indicates this integer is a I16, whose maximum value - is 32767. - "# - ), - ) - } - - #[test] - fn i16_underflow() { - report_problem_as( - "-32769i16", - indoc!( - r#" - ── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─ - - This integer literal underflows the type indicated by its suffix: - - 1│ -32769i16 - ^^^^^^^^^ - - Tip: The suffix indicates this integer is a I16, whose minimum value - is -32768. - "# - ), - ) - } - - #[test] - fn i32_overflow() { - report_problem_as( - "2_147_483_648i32", - indoc!( - r#" - ── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─ - - This integer literal overflows the type indicated by its suffix: - - 1│ 2_147_483_648i32 - ^^^^^^^^^^^^^^^^ - - Tip: The suffix indicates this integer is a I32, whose maximum value - is 2_147_483_647. - "# - ), - ) - } - - #[test] - fn i32_underflow() { - report_problem_as( - "-2_147_483_649i32", - indoc!( - r#" - ── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─ - - This integer literal underflows the type indicated by its suffix: - - 1│ -2_147_483_649i32 - ^^^^^^^^^^^^^^^^^ - - Tip: The suffix indicates this integer is a I32, whose minimum value - is -2_147_483_648. - "# - ), - ) - } - - #[test] - fn i64_overflow() { - report_problem_as( - "9_223_372_036_854_775_808i64", - indoc!( - r#" - ── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─ - - This integer literal overflows the type indicated by its suffix: - - 1│ 9_223_372_036_854_775_808i64 - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - Tip: The suffix indicates this integer is a I64, whose maximum value - is 9_223_372_036_854_775_807. - "# - ), - ) - } - - #[test] - fn i64_underflow() { - report_problem_as( - "-9_223_372_036_854_775_809i64", - indoc!( - r#" - ── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─ - - This integer literal underflows the type indicated by its suffix: - - 1│ -9_223_372_036_854_775_809i64 - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - Tip: The suffix indicates this integer is a I64, whose minimum value - is -9_223_372_036_854_775_808. - "# - ), - ) - } - - #[test] - fn i128_overflow() { - report_problem_as( - "170_141_183_460_469_231_731_687_303_715_884_105_728i128", - indoc!( - r#" - ── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─ - - This integer literal overflows the type indicated by its suffix: - - 1│ 170_141_183_460_469_231_731_687_303_715_884_105_728i128 - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - Tip: The suffix indicates this integer is a I128, whose maximum value - is 170_141_183_460_469_231_731_687_303_715_884_105_727. - "# - ), - ) - } - - #[test] - fn list_get_negative_number() { - report_problem_as( - indoc!( - r#" - List.get [1,2,3] -1 - "# - ), - // TODO: this error message could be improved, e.g. something like "This argument can - // be used as ... because of its literal value" - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - The 2nd argument to `get` is not what I expect: - - 1│ List.get [1,2,3] -1 - ^^ - - This argument is a number of type: - - I8, I16, I32, I64, I128, F32, F64, or Dec - - But `get` needs the 2nd argument to be: - - Nat - "# - ), - ) - } - - #[test] - fn list_get_negative_number_indirect() { - report_problem_as( - indoc!( - r#" - a = -9_223_372_036_854 - List.get [1,2,3] a - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - The 2nd argument to `get` is not what I expect: - - 2│ List.get [1,2,3] a - ^ - - This `a` value is a: - - I64, I128, F32, F64, or Dec - - But `get` needs the 2nd argument to be: - - Nat - "# - ), - ) - } - - #[test] - fn list_get_negative_number_double_indirect() { - report_problem_as( - indoc!( - r#" - a = -9_223_372_036_854 - b = a - List.get [1,2,3] b - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - The 2nd argument to `get` is not what I expect: - - 3│ List.get [1,2,3] b - ^ - - This `b` value is a: - - I64, I128, F32, F64, or Dec - - But `get` needs the 2nd argument to be: - - Nat - "# - ), - ) - } - - #[test] - fn compare_unsigned_to_signed() { - report_problem_as( - indoc!( - r#" - when -1 is - 1u8 -> 1 - _ -> 1 - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - The branches of this `when` expression don't match the condition: - - 1│> when -1 is - 2│ 1u8 -> 1 - 3│ _ -> 1 - - The `when` condition is a number of type: - - I8, I16, I32, I64, I128, F32, F64, or Dec - - But the branch patterns have type: - - U8 - - The branches must be cases of the `when` condition's type! - "# - ), - ) - } - - #[test] - fn recursive_type_alias_is_newtype() { - report_problem_as( - indoc!( - r#" - R a : [Only (R a)] - - v : R Str - v - "# - ), - indoc!( - r#" - ── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─ - - The `R` alias is self-recursive in an invalid way: - - 1│ R a : [Only (R a)] - ^ - - Recursion in aliases is only allowed if recursion happens behind a - tagged union, at least one variant of which is not recursive. - "# - ), - ) - } - - #[test] - fn recursive_type_alias_is_newtype_deep() { - report_problem_as( - indoc!( - r#" - R a : [Only { very: [Deep (R a)] }] - - v : R Str - v - "# - ), - indoc!( - r#" - ── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─ - - The `R` alias is self-recursive in an invalid way: - - 1│ R a : [Only { very: [Deep (R a)] }] - ^ - - Recursion in aliases is only allowed if recursion happens behind a - tagged union, at least one variant of which is not recursive. - "# - ), - ) - } - - #[test] - fn recursive_type_alias_is_newtype_mutual() { - report_problem_as( - indoc!( - r#" - Foo a : [Thing (Bar a)] - Bar a : [Stuff (Foo a)] - - v : Bar Str - v - "# - ), - indoc!( - r#" - ── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─ - - The `Foo` alias is recursive in an invalid way: - - 1│ Foo a : [Thing (Bar a)] - ^^^ - - The `Foo` alias depends on itself through the following chain of - definitions: - - ┌─────┐ - │ Foo - │ ↓ - │ Bar - └─────┘ - - Recursion in aliases is only allowed if recursion happens behind a - tagged union, at least one variant of which is not recursive. - "# - ), - ) - } - - #[test] - fn issue_2458() { - report_problem_as( - indoc!( - r#" - Result a b : [Ok a, Err b] - - Foo a : [Blah (Result (Bar a) [])] - Bar a : Foo a - - v : Bar Str - v - "# - ), - "", - ) - } - - #[test] - fn opaque_type_not_in_scope() { - report_problem_as( - indoc!( - r#" - @Age 21 - "# - ), - indoc!( - r#" - ── OPAQUE TYPE NOT DEFINED ─────────────────────────────── /code/proj/Main.roc ─ - - The opaque type Age referenced here is not defined: - - 1│ @Age 21 - ^^^^ - - Note: It looks like there are no opaque types declared in this scope yet! - "# - ), - ) - } - - #[test] - fn opaque_reference_not_opaque_type() { - report_problem_as( - indoc!( - r#" - Age : Num.U8 - - @Age 21 - "# - ), - indoc!( - r#" - ── OPAQUE TYPE NOT DEFINED ─────────────────────────────── /code/proj/Main.roc ─ - - The opaque type Age referenced here is not defined: - - 3│ @Age 21 - ^^^^ - - Note: There is an alias of the same name: - - 1│ Age : Num.U8 - ^^^ - - Note: It looks like there are no opaque types declared in this scope yet! - - ── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─ - - `Age` is not used anywhere in your code. - - 1│ Age : Num.U8 - ^^^^^^^^^^^^ - - If you didn't intend on using `Age` then remove it so future readers of - your code don't wonder why it is there. - "# - ), - ) - } - - #[test] - fn qualified_opaque_reference() { - report_problem_as( - indoc!( - r#" - OtherModule.@Age 21 - "# - ), - // TODO: get rid of the first error. Consider parsing OtherModule.@Age to completion - // and checking it during can. The reason the error appears is because it is parsed as - // Apply(Error(OtherModule), [@Age, 21]) - indoc!( - r#" - ── OPAQUE TYPE NOT APPLIED ─────────────────────────────── /code/proj/Main.roc ─ - - This opaque type is not applied to an argument: - - 1│ OtherModule.@Age 21 - ^^^^ - - Note: Opaque types always wrap exactly one argument! - - ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ - - I am trying to parse a qualified name here: - - 1│ OtherModule.@Age 21 - ^ - - I was expecting to see an identifier next, like height. A complete - qualified name looks something like Json.Decode.string. - "# - ), - ) - } - - #[test] - fn opaque_used_outside_declaration_scope() { - report_problem_as( - indoc!( - r#" - age = - Age := Num.U8 - 21u8 - - @Age age - "# - ), - // TODO(opaques): there is a potential for a better error message here, if the usage of - // `@Age` can be linked to the declaration of `Age` inside `age`, and a suggestion to - // raise that declaration to the outer scope. - indoc!( - r#" - ── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─ - - `Age` is not used anywhere in your code. - - 2│ Age := Num.U8 - ^^^^^^^^^^^^^ - - If you didn't intend on using `Age` then remove it so future readers of - your code don't wonder why it is there. - - ── OPAQUE TYPE NOT DEFINED ─────────────────────────────── /code/proj/Main.roc ─ - - The opaque type Age referenced here is not defined: - - 5│ @Age age - ^^^^ - - Note: It looks like there are no opaque types declared in this scope yet! - "# - ), - ) - } - - #[test] - fn unimported_modules_reported() { - report_problem_as( - indoc!( - r#" - main : Task.Task {} [] - main = "whatever man you don't even know my type" - main - "# - ), - indoc!( - r#" - ── MODULE NOT IMPORTED ─────────────────────────────────── /code/proj/Main.roc ─ - - The `Task` module is not imported: - - 1│ main : Task.Task {} [] - ^^^^^^^^^^^^^^^ - - Is there an import missing? Perhaps there is a typo. Did you mean one - of these? - - Test - List - Num - Box - "# - ), - ) - } - - #[test] - fn opaque_mismatch_check() { - report_problem_as( - indoc!( - r#" - Age := Num.U8 - - n : Age - n = @Age "" - - n - "# - ), - // TODO(opaques): error could be improved by saying that the opaque definition demands - // that the argument be a U8, and linking to the definitin! - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - - This expression is used in an unexpected way: - - 4│ n = @Age "" + test_report!( + bad_numeric_literal_suffix, + indoc!( + r#" + 1u256 + "# + ), + // TODO: link to number suffixes + @r###" + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + + This integer literal contains an invalid digit: + + 4│ 1u256 + ^^^^^ + + Integer literals can only contain the digits + 0-9, or have an integer suffix. + + Tip: Learn more about number literals at TODO + "### + ); + + test_report!( + numer_literal_multi_suffix, + indoc!( + r#" + 1u8u8 + "# + ), + // TODO: link to number suffixes + @r###" + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + + This integer literal contains an invalid digit: + + 4│ 1u8u8 + ^^^^^ + + Integer literals can only contain the digits + 0-9, or have an integer suffix. + + Tip: Learn more about number literals at TODO + "### + ); + + test_report!( + int_literal_has_float_suffix, + indoc!( + r#" + 0b1f32 + "# + ), + @r###" + ── CONFLICTING NUMBER SUFFIX ───────────────────────────── /code/proj/Main.roc ─ + + This number literal is an integer, but it has a float suffix: + + 4│ 0b1f32 + ^^^^^^ + "### + ); + + test_report!( + float_literal_has_int_suffix, + indoc!( + r#" + 1.0u8 + "# + ), + @r###" + ── CONFLICTING NUMBER SUFFIX ───────────────────────────── /code/proj/Main.roc ─ + + This number literal is a float, but it has an integer suffix: + + 4│ 1.0u8 + ^^^^^ + "### + ); + + test_report!( + u8_overflow, + "256u8", + @r###" + ── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─ + + This integer literal overflows the type indicated by its suffix: + + 4│ 256u8 + ^^^^^ + + Tip: The suffix indicates this integer is a U8, whose maximum value is + 255. + "### + ); + + test_report!( + negative_u8, + "-1u8", + @r###" + ── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─ + + This integer literal underflows the type indicated by its suffix: + + 4│ -1u8 + ^^^^ + + Tip: The suffix indicates this integer is a U8, whose minimum value is + 0. + "### + ); + + test_report!( + u16_overflow, + "65536u16", + @r###" + ── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─ + + This integer literal overflows the type indicated by its suffix: + + 4│ 65536u16 + ^^^^^^^^ + + Tip: The suffix indicates this integer is a U16, whose maximum value + is 65535. + "### + ); + + test_report!( + negative_u16, + "-1u16", + @r###" + ── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─ + + This integer literal underflows the type indicated by its suffix: + + 4│ -1u16 + ^^^^^ + + Tip: The suffix indicates this integer is a U16, whose minimum value + is 0. + "### + ); + + test_report!( + u32_overflow, + "4_294_967_296u32", + @r###" + ── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─ + + This integer literal overflows the type indicated by its suffix: + + 4│ 4_294_967_296u32 + ^^^^^^^^^^^^^^^^ + + Tip: The suffix indicates this integer is a U32, whose maximum value + is 4_294_967_295. + "### + ); + + test_report!( + negative_u32, + "-1u32", + @r###" + ── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─ + + This integer literal underflows the type indicated by its suffix: + + 4│ -1u32 + ^^^^^ + + Tip: The suffix indicates this integer is a U32, whose minimum value + is 0. + "### + ); + + test_report!( + u64_overflow, + "18_446_744_073_709_551_616u64", + @r###" + ── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─ + + This integer literal overflows the type indicated by its suffix: + + 4│ 18_446_744_073_709_551_616u64 + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + Tip: The suffix indicates this integer is a U64, whose maximum value + is 18_446_744_073_709_551_615. + "### + ); + + test_report!( + negative_u64, + "-1u64", + @r###" + ── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─ + + This integer literal underflows the type indicated by its suffix: + + 4│ -1u64 + ^^^^^ + + Tip: The suffix indicates this integer is a U64, whose minimum value + is 0. + "### + ); + + test_report!( + negative_u128, + "-1u128", + @r###" + ── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─ + + This integer literal underflows the type indicated by its suffix: + + 4│ -1u128 + ^^^^^^ + + Tip: The suffix indicates this integer is a U128, whose minimum value + is 0. + "### + ); + + test_report!( + i8_overflow, + "128i8", + @r###" + ── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─ + + This integer literal overflows the type indicated by its suffix: + + 4│ 128i8 + ^^^^^ + + Tip: The suffix indicates this integer is a I8, whose maximum value is + 127. + "### + ); + + test_report!( + i8_underflow, + "-129i8", + @r###" + ── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─ + + This integer literal underflows the type indicated by its suffix: + + 4│ -129i8 + ^^^^^^ + + Tip: The suffix indicates this integer is a I8, whose minimum value is + -128. + "### + ); + + test_report!( + i16_overflow, + "32768i16", + @r###" + ── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─ + + This integer literal overflows the type indicated by its suffix: + + 4│ 32768i16 + ^^^^^^^^ + + Tip: The suffix indicates this integer is a I16, whose maximum value + is 32767. + "### + ); + + test_report!( + i16_underflow, + "-32769i16", + @r###" + ── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─ + + This integer literal underflows the type indicated by its suffix: + + 4│ -32769i16 + ^^^^^^^^^ + + Tip: The suffix indicates this integer is a I16, whose minimum value + is -32768. + "### + ); + + test_report!( + i32_overflow, + "2_147_483_648i32", + @r###" + ── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─ + + This integer literal overflows the type indicated by its suffix: + + 4│ 2_147_483_648i32 + ^^^^^^^^^^^^^^^^ + + Tip: The suffix indicates this integer is a I32, whose maximum value + is 2_147_483_647. + "### + ); + + test_report!( + i32_underflow, + "-2_147_483_649i32", + @r###" + ── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─ + + This integer literal underflows the type indicated by its suffix: + + 4│ -2_147_483_649i32 + ^^^^^^^^^^^^^^^^^ + + Tip: The suffix indicates this integer is a I32, whose minimum value + is -2_147_483_648. + "### + ); + + test_report!( + i64_overflow, + "9_223_372_036_854_775_808i64", + @r###" + ── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─ + + This integer literal overflows the type indicated by its suffix: + + 4│ 9_223_372_036_854_775_808i64 + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + Tip: The suffix indicates this integer is a I64, whose maximum value + is 9_223_372_036_854_775_807. + "### + ); + + test_report!( + i64_underflow, + "-9_223_372_036_854_775_809i64", + @r###" + ── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─ + + This integer literal underflows the type indicated by its suffix: + + 4│ -9_223_372_036_854_775_809i64 + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + Tip: The suffix indicates this integer is a I64, whose minimum value + is -9_223_372_036_854_775_808. + "### + ); + + test_report!( + i128_overflow, + "170_141_183_460_469_231_731_687_303_715_884_105_728i128", + @r###" + ── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─ + + This integer literal overflows the type indicated by its suffix: + + 4│ 170_141_183_460_469_231_731_687_303_715_884_105_728i128 + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + Tip: The suffix indicates this integer is a I128, whose maximum value + is 170_141_183_460_469_231_731_687_303_715_884_105_727. + "### + ); + + test_report!( + list_get_negative_number, + indoc!( + r#" + List.get [1,2,3] -1 + "# + ), + // TODO: this error message could be improved, e.g. something like "This argument can + // be used as ... because of its literal value" + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + The 2nd argument to `get` is not what I expect: + + 4│ List.get [1,2,3] -1 ^^ - This argument to an opaque type has type: + This argument is a number of type: - Str + I8, I16, I32, I64, I128, F32, F64, or Dec - But you are trying to use it as: + But `get` needs the 2nd argument to be: - U8 - "# - ), - ) - } + Nat + "### + ); - #[test] - fn opaque_mismatch_infer() { - report_problem_as( - indoc!( - r#" - F n := n + test_report!( + list_get_negative_number_indirect, + indoc!( + r#" + a = -9_223_372_036_854 + List.get [1,2,3] a + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - if True - then @F "" - else @F {} - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + The 2nd argument to `get` is not what I expect: - This expression is used in an unexpected way: + 5│ List.get [1,2,3] a + ^ - 5│ else @F {} - ^^ + This `a` value is a: - This argument to an opaque type has type: + I64, I128, F32, F64, or Dec - {} + But `get` needs the 2nd argument to be: - But you are trying to use it as: + Nat + "### + ); - Str - "# - ), - ) - } + test_report!( + list_get_negative_number_double_indirect, + indoc!( + r#" + a = -9_223_372_036_854 + b = a + List.get [1,2,3] b + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn opaque_creation_is_not_wrapped() { - report_problem_as( - indoc!( - r#" - F n := n + The 2nd argument to `get` is not what I expect: - v : F Str - v = "" + 6│ List.get [1,2,3] b + ^ - v - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + This `b` value is a: - Something is off with the body of the `v` definition: + I64, I128, F32, F64, or Dec - 3│ v : F Str - 4│ v = "" - ^^ + But `get` needs the 2nd argument to be: - The body is a string of type: + Nat + "### + ); - Str + test_report!( + compare_unsigned_to_signed, + indoc!( + r#" + when -1 is + 1u8 -> 1 + _ -> 1 + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - But the type annotation on `v` says it should be: + The branches of this `when` expression don't match the condition: - F Str + 4│> when -1 is + 5│ 1u8 -> 1 + 6│ _ -> 1 - Tip: Type comparisons between an opaque type are only ever equal if - both types are the same opaque type. Did you mean to create an opaque - type by wrapping it? If I have an opaque type Age := U32 I can create - an instance of this opaque type by doing @Age 23. - "# - ), - ) - } + The `when` condition is a number of type: - #[test] - fn opaque_mismatch_pattern_check() { - report_problem_as( - indoc!( - r#" + I8, I16, I32, I64, I128, F32, F64, or Dec + + But the branch patterns have type: + + U8 + + The branches must be cases of the `when` condition's type! + "### + ); + + test_report!( + recursive_type_alias_is_newtype, + indoc!( + r#" + R a : [Only (R a)] + + v : R Str + v + "# + ), + @r###" + ── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─ + + The `R` alias is self-recursive in an invalid way: + + 4│ R a : [Only (R a)] + ^ + + Recursion in aliases is only allowed if recursion happens behind a + tagged union, at least one variant of which is not recursive. + "### + ); + + test_report!( + recursive_type_alias_is_newtype_deep, + indoc!( + r#" + R a : [Only { very: [Deep (R a)] }] + + v : R Str + v + "# + ), + @r###" + ── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─ + + The `R` alias is self-recursive in an invalid way: + + 4│ R a : [Only { very: [Deep (R a)] }] + ^ + + Recursion in aliases is only allowed if recursion happens behind a + tagged union, at least one variant of which is not recursive. + "### + ); + + test_report!( + recursive_type_alias_is_newtype_mutual, + indoc!( + r#" + Foo a : [Thing (Bar a)] + Bar a : [Stuff (Foo a)] + + v : Bar Str + v + "# + ), + @r###" + ── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─ + + The `Foo` alias is recursive in an invalid way: + + 4│ Foo a : [Thing (Bar a)] + ^^^ + + The `Foo` alias depends on itself through the following chain of + definitions: + + ┌─────┐ + │ Foo + │ ↓ + │ Bar + └─────┘ + + Recursion in aliases is only allowed if recursion happens behind a + tagged union, at least one variant of which is not recursive. + "### + ); + + test_report!( + issue_2458, + indoc!( + r#" + Result a b : [Ok a, Err b] + + Foo a : [Blah (Result (Bar a) [])] + Bar a : Foo a + + v : Bar Str + v + "# + ), + @r###" + ── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─ + + The `Result` name is first defined here: + + 1│ app "test" provides [main] to "./platform" + + + But then it's defined a second time here: + + 4│ Result a b : [Ok a, Err b] + ^^^^^^^^^^^^^^^^^^^^^^^^^^ + + Since these aliases have the same name, it's easy to use the wrong one + on accident. Give one of them a new name. + "### + ); + + test_report!( + opaque_type_not_in_scope, + indoc!( + r#" + @Age 21 + "# + ), + @r###" + ── OPAQUE TYPE NOT DEFINED ─────────────────────────────── /code/proj/Main.roc ─ + + The opaque type Age referenced here is not defined: + + 4│ @Age 21 + ^^^^ + + Note: It looks like there are no opaque types declared in this scope yet! + "### + ); + + test_report!( + opaque_reference_not_opaque_type, + indoc!( + r#" + Age : Num.U8 + + @Age 21 + "# + ), + @r###" + ── OPAQUE TYPE NOT DEFINED ─────────────────────────────── /code/proj/Main.roc ─ + + The opaque type Age referenced here is not defined: + + 6│ @Age 21 + ^^^^ + + Note: There is an alias of the same name: + + 4│ Age : Num.U8 + ^^^ + + Note: It looks like there are no opaque types declared in this scope yet! + + ── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─ + + `Age` is not used anywhere in your code. + + 4│ Age : Num.U8 + ^^^^^^^^^^^^ + + If you didn't intend on using `Age` then remove it so future readers of + your code don't wonder why it is there. + "### + ); + + test_report!( + qualified_opaque_reference, + indoc!( + r#" + OtherModule.@Age 21 + "# + ), + // TODO: get rid of the first error. Consider parsing OtherModule.@Age to completion + // and checking it during can. The reason the error appears is because it is parsed as + // Apply(Error(OtherModule), [@Age, 21]) + @r###" + ── OPAQUE TYPE NOT APPLIED ─────────────────────────────── /code/proj/Main.roc ─ + + This opaque type is not applied to an argument: + + 4│ OtherModule.@Age 21 + ^^^^ + + Note: Opaque types always wrap exactly one argument! + + ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ + + I am trying to parse a qualified name here: + + 4│ OtherModule.@Age 21 + ^ + + I was expecting to see an identifier next, like height. A complete + qualified name looks something like Json.Decode.string. + "### + ); + + test_report!( + opaque_used_outside_declaration_scope, + indoc!( + r#" + age = Age := Num.U8 + 21u8 - f : Age -> Num.U8 - f = \Age n -> n + @Age age + "# + ), + // TODO(opaques): there is a potential for a better error message here, if the usage of + // `@Age` can be linked to the declaration of `Age` inside `age`, and a suggestion to + // raise that declaration to the outer scope. + @r###" + ── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─ - f - "# - ), - // TODO(opaques): error could be improved by saying that the user-provided pattern - // probably wants to change "Age" to "@Age"! - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + `Age` is not used anywhere in your code. - The 1st argument to `f` is weird: + 5│ Age := Num.U8 + ^^^^^^^^^^^^^ - 4│ f = \Age n -> n - ^^^^^ + If you didn't intend on using `Age` then remove it so future readers of + your code don't wonder why it is there. - The argument is a pattern that matches a `Age` tag of type: + ── OPAQUE TYPE NOT DEFINED ─────────────────────────────── /code/proj/Main.roc ─ - [Age a] + The opaque type Age referenced here is not defined: - But the annotation on `f` says the 1st argument should be: + 8│ @Age age + ^^^^ - Age + Note: It looks like there are no opaque types declared in this scope yet! + "### + ); - Tip: Type comparisons between an opaque type are only ever equal if - both types are the same opaque type. Did you mean to create an opaque - type by wrapping it? If I have an opaque type Age := U32 I can create - an instance of this opaque type by doing @Age 23. - "# - ), - ) - } + test_report!( + #[ignore = "Blocked on https://github.com/rtfeldman/roc/issues/3385"] + unimported_modules_reported, + indoc!( + r#" + main : Task.Task {} [] + main = "whatever man you don't even know my type" + main + "# + ), + @r#" + ── MODULE NOT IMPORTED ─────────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn opaque_mismatch_pattern_infer() { - report_problem_as( - indoc!( - r#" - F n := n + The `Task` module is not imported: - \x -> - when x is - @F A -> "" - @F {} -> "" - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + 1│ main : Task.Task {} [] + ^^^^^^^^^^^^^^^ - The 2nd pattern in this `when` does not match the previous ones: + Is there an import missing? Perhaps there is a typo. Did you mean one + of these? - 6│ @F {} -> "" - ^^^^^ + Test + List + Num + Box + "# + ); - The 2nd pattern is trying to matchF unwrappings of type: + test_report!( + opaque_mismatch_check, + indoc!( + r#" + Age := Num.U8 - F {}a + n : Age + n = @Age "" - But all the previous branches match: + n + "# + ), + // TODO(opaques): error could be improved by saying that the opaque definition demands + // that the argument be a U8, and linking to the definitin! + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - F [A]a - "# - ), - ) - } + This expression is used in an unexpected way: - #[test] - fn opaque_pattern_match_not_exhaustive_tag() { - report_problem_as( - indoc!( - r#" - F n := n + 7│ n = @Age "" + ^^ - v : F [A, B, C] + This argument to an opaque type has type: - when v is - @F A -> "" - @F B -> "" - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + Str - The branches of this `when` expression don't match the condition: + But you are trying to use it as: - 5│> when v is - 6│ @F A -> "" - 7│ @F B -> "" + U8 + "### + ); - This `v` value is a: + test_report!( + opaque_mismatch_infer, + indoc!( + r#" + F n := n - F [A, B, C] + if True + then @F "" + else @F {} + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - But the branch patterns have type: + This expression is used in an unexpected way: - F [A, B] + 8│ else @F {} + ^^ - The branches must be cases of the `when` condition's type! + This argument to an opaque type has type: - Tip: Looks like the branches are missing coverage of the `C` tag. + {} - Tip: Maybe you need to add a catch-all branch, like `_`? - "# - ), - ) - } + But you are trying to use it as: - #[test] - fn opaque_pattern_match_not_exhaustive_int() { - report_problem_as( - indoc!( - r#" - F n := n + Str + "### + ); - v : F Num.U8 + test_report!( + opaque_creation_is_not_wrapped, + indoc!( + r#" + F n := n - when v is - @F 1 -> "" - @F 2 -> "" - "# - ), - indoc!( - r#" - ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ + v : F Str + v = "" - This `when` does not cover all the possibilities: + v + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - 5│> when v is - 6│> @F 1 -> "" - 7│> @F 2 -> "" + Something is off with the body of the `v` definition: - Other possibilities include: + 6│ v : F Str + 7│ v = "" + ^^ - @F _ + The body is a string of type: - I would have to crash if I saw one of those! Add branches for them! - "# - ), - ) - } + Str - #[test] - fn let_polymorphism_with_scoped_type_variables() { - report_problem_as( - indoc!( - r#" - f : a -> a - f = \x -> - y : a -> a - y = \z -> z + But the type annotation on `v` says it should be: - n = y 1u8 - x1 = y x - (\_ -> x1) n + F Str - f - "# - ), - indoc!( - r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + Tip: Type comparisons between an opaque type are only ever equal if + both types are the same opaque type. Did you mean to create an opaque + type by wrapping it? If I have an opaque type Age := U32 I can create + an instance of this opaque type by doing @Age 23. + "### + ); - The 1st argument to `y` is not what I expect: + test_report!( + opaque_mismatch_pattern_check, + indoc!( + r#" + Age := Num.U8 - 6│ n = y 1u8 - ^^^ + f : Age -> Num.U8 + f = \Age n -> n - This argument is an integer of type: + f + "# + ), + // TODO(opaques): error could be improved by saying that the user-provided pattern + // probably wants to change "Age" to "@Age"! + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - U8 + The 1st argument to `f` is weird: - But `y` needs the 1st argument to be: + 7│ f = \Age n -> n + ^^^^^ - a + The argument is a pattern that matches a `Age` tag of type: - Tip: The type annotation uses the type variable `a` to say that this - definition can produce any type of value. But in the body I see that - it will only produce a `U8` value of a single specific type. Maybe - change the type annotation to be more specific? Maybe change the code - to be more general? - "# - ), - ) - } + [Age a] - #[test] - fn non_exhaustive_with_guard() { - report_problem_as( - indoc!( - r#" - x : [A] + But the annotation on `f` says the 1st argument should be: + + Age + + Tip: Type comparisons between an opaque type are only ever equal if + both types are the same opaque type. Did you mean to create an opaque + type by wrapping it? If I have an opaque type Age := U32 I can create + an instance of this opaque type by doing @Age 23. + "### + ); + + test_report!( + opaque_mismatch_pattern_infer, + indoc!( + r#" + F n := n + + \x -> when x is - A if True -> "" - "# - ), - indoc!( - r#" - ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ + @F A -> "" + @F {} -> "" + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - This `when` does not cover all the possibilities: + The 2nd pattern in this `when` does not match the previous ones: - 2│> when x is - 3│> A if True -> "" + 9│ @F {} -> "" + ^^^^^ - Other possibilities include: + The 2nd pattern is trying to matchF unwrappings of type: - A (note the lack of an if clause) + F {}a - I would have to crash if I saw one of those! Add branches for them! - "# - ), - ) - } + But all the previous branches match: - #[test] - fn invalid_record_extension_type() { - report_problem_as( - indoc!( - r#" - f : { x : Num.Nat }[] - f - "# - ), - indoc!( - r#" - ── INVALID_EXTENSION_TYPE ──────────────────────────────── /code/proj/Main.roc ─ + F [A]a + "### + ); - This record extension type is invalid: + test_report!( + opaque_pattern_match_not_exhaustive_tag, + indoc!( + r#" + F n := n - 1│ f : { x : Num.Nat }[] - ^^ + v : F [A, B, C] - Note: A record extension variable can only contain a type variable or - another record. - "# - ), - ) - } + when v is + @F A -> "" + @F B -> "" + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - #[test] - fn invalid_tag_extension_type() { - report_problem_as( - indoc!( - r#" - f : [A]Str - f - "# - ), - indoc!( - r#" - ── INVALID_EXTENSION_TYPE ──────────────────────────────── /code/proj/Main.roc ─ + The branches of this `when` expression don't match the condition: - This tag union extension type is invalid: + 8│> when v is + 9│ @F A -> "" + 10│ @F B -> "" - 1│ f : [A]Str - ^^^ + This `v` value is a: - Note: A tag union extension variable can only contain a type variable - or another tag union. - "# - ), - ) - } + F [A, B, C] - #[test] - fn unknown_type() { - report_problem_as( - indoc!( - r#" - Type : [Constructor UnknownType] + But the branch patterns have type: - insertHelper : UnknownType, Type -> Type - insertHelper = \h, m -> - when m is - Constructor _ -> Constructor h + F [A, B] - insertHelper - "# - ), - indoc!( - r#" - ── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─ + The branches must be cases of the `when` condition's type! - Nothing is named `UnknownType` in this scope. + Tip: Looks like the branches are missing coverage of the `C` tag. - 1│ Type : [Constructor UnknownType] - ^^^^^^^^^^^ + Tip: Maybe you need to add a catch-all branch, like `_`? + "### + ); - Did you mean one of these? + test_report!( + opaque_pattern_match_not_exhaustive_int, + indoc!( + r#" + F n := n - Type - True - Box - Ok + v : F Num.U8 - ── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─ + when v is + @F 1 -> "" + @F 2 -> "" + "# + ), + @r###" + ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ - Nothing is named `UnknownType` in this scope. + This `when` does not cover all the possibilities: - 3│ insertHelper : UnknownType, Type -> Type - ^^^^^^^^^^^ + 8│> when v is + 9│> @F 1 -> "" + 10│> @F 2 -> "" - Did you mean one of these? + Other possibilities include: - Type - True - insertHelper - Box - "# - ), - ) - } + @F _ - #[test] - fn ability_first_demand_not_indented_enough() { - report_problem_as( - indoc!( - r#" - Eq has - eq : a, a -> U64 | a has Eq + I would have to crash if I saw one of those! Add branches for them! + "### + ); - 1 - "# - ), - indoc!( - r#" - ── UNFINISHED ABILITY ──────────────────────────────────── /code/proj/Main.roc ─ + test_report!( + let_polymorphism_with_scoped_type_variables, + indoc!( + r#" + f : a -> a + f = \x -> + y : a -> a + y = \z -> z - I was partway through parsing an ability definition, but I got stuck - here: + n = y 1u8 + x1 = y x + (\_ -> x1) n - 1│ Eq has - 2│ eq : a, a -> U64 | a has Eq - ^ + f + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - I suspect this line is not indented enough (by 1 spaces) - "# - ), - ) - } + The 1st argument to `y` is not what I expect: + + 9│ n = y 1u8 + ^^^ + + This argument is an integer of type: + + U8 + + But `y` needs the 1st argument to be: + + a + + Tip: The type annotation uses the type variable `a` to say that this + definition can produce any type of value. But in the body I see that + it will only produce a `U8` value of a single specific type. Maybe + change the type annotation to be more specific? Maybe change the code + to be more general? + "### + ); + + test_report!( + non_exhaustive_with_guard, + indoc!( + r#" + x : [A] + when x is + A if True -> "" + "# + ), + @r###" + ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ + + This `when` does not cover all the possibilities: + + 5│> when x is + 6│> A if True -> "" + + Other possibilities include: + + A (note the lack of an if clause) + + I would have to crash if I saw one of those! Add branches for them! + "### + ); + + test_report!( + invalid_record_extension_type, + indoc!( + r#" + f : { x : Num.Nat }[] + f + "# + ), + @r###" + ── INVALID_EXTENSION_TYPE ──────────────────────────────── /code/proj/Main.roc ─ + + This record extension type is invalid: + + 4│ f : { x : Num.Nat }[] + ^^ + + Note: A record extension variable can only contain a type variable or + another record. + "### + ); + + test_report!( + invalid_tag_extension_type, + indoc!( + r#" + f : [A]Str + f + "# + ), + @r###" + ── INVALID_EXTENSION_TYPE ──────────────────────────────── /code/proj/Main.roc ─ + + This tag union extension type is invalid: + + 4│ f : [A]Str + ^^^ + + Note: A tag union extension variable can only contain a type variable + or another tag union. + "### + ); + + test_report!( + unknown_type, + indoc!( + r#" + Type : [Constructor UnknownType] + + insertHelper : UnknownType, Type -> Type + insertHelper = \h, m -> + when m is + Constructor _ -> Constructor h + + insertHelper + "# + ), + @r###" + ── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─ + + Nothing is named `UnknownType` in this scope. + + 4│ Type : [Constructor UnknownType] + ^^^^^^^^^^^ + + Did you mean one of these? + + Type + Unsigned8 + Unsigned32 + Unsigned16 + + ── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─ + + Nothing is named `UnknownType` in this scope. + + 6│ insertHelper : UnknownType, Type -> Type + ^^^^^^^^^^^ + + Did you mean one of these? + + Type + Unsigned8 + Unsigned32 + Unsigned16 + "### + ); + + test_report!( + ability_first_demand_not_indented_enough, + indoc!( + r#" + Eq has + eq : a, a -> U64 | a has Eq + + 1 + "# + ), + @r###" + ── UNFINISHED ABILITY ── tmp/ability_first_demand_not_indented_enough/Test.roc ─ + + I was partway through parsing an ability definition, but I got stuck + here: + + 4│ Eq has + 5│ eq : a, a -> U64 | a has Eq + ^ + + I suspect this line is not indented enough (by 1 spaces) + "### + ); test_report!( ability_demands_not_indented_with_first, @@ -9051,165 +8004,141 @@ All branches in an `if` must have the same type! next."# ); - #[test] - fn ability_non_signature_expression() { - report_problem_as( - indoc!( - r#" - Eq has - 123 + test_report!( + ability_non_signature_expression, + indoc!( + r#" + Eq has + 123 - 1 - "# - ), - indoc!( - r#" - ── UNFINISHED ABILITY ──────────────────────────────────── /code/proj/Main.roc ─ + 1 + "# + ), + @r###" + ── UNFINISHED ABILITY ────────── tmp/ability_non_signature_expression/Test.roc ─ - I was partway through parsing an ability definition, but I got stuck - here: + I was partway through parsing an ability definition, but I got stuck + here: - 1│ Eq has - 2│ 123 + 4│ Eq has + 5│ 123 + ^ + + I was expecting to see a value signature next. + "### + ); + + test_report!( + wildcard_in_alias, + indoc!( + r#" + I : Num.Int * + a : I + a + "# + ), + @r###" + ── UNBOUND TYPE VARIABLE ───────────────────────────────── /code/proj/Main.roc ─ + + The definition of `I` has an unbound type variable: + + 4│ I : Num.Int * ^ - I was expecting to see a value signature next. - "# - ), - ) - } + Tip: Type variables must be bound before the `:`. Perhaps you intended + to add a type parameter to this type? + "### + ); - #[test] - fn wildcard_in_alias() { - report_problem_as( - indoc!( - r#" - I : Num.Int * - a : I - a - "# - ), - indoc!( - r#" - ── UNBOUND TYPE VARIABLE ───────────────────────────────── /code/proj/Main.roc ─ + test_report!( + wildcard_in_opaque, + indoc!( + r#" + I := Num.Int * + a : I + a + "# + ), + @r###" + ── UNBOUND TYPE VARIABLE ───────────────────────────────── /code/proj/Main.roc ─ - The definition of `I` has an unbound type variable: + The definition of `I` has an unbound type variable: - 1│ I : Num.Int * - ^ + 4│ I := Num.Int * + ^ - Tip: Type variables must be bound before the `:`. Perhaps you intended - to add a type parameter to this type? - "# - ), - ) - } + Tip: Type variables must be bound before the `:=`. Perhaps you intended + to add a type parameter to this type? + "### + ); - #[test] - fn wildcard_in_opaque() { - report_problem_as( - indoc!( - r#" - I := Num.Int * - a : I - a - "# - ), - indoc!( - r#" - ── UNBOUND TYPE VARIABLE ───────────────────────────────── /code/proj/Main.roc ─ + test_report!( + multiple_wildcards_in_alias, + indoc!( + r#" + I : [A (Num.Int *), B (Num.Int *)] + a : I + a + "# + ), + @r###" + ── UNBOUND TYPE VARIABLE ───────────────────────────────── /code/proj/Main.roc ─ - The definition of `I` has an unbound type variable: + The definition of `I` has 2 unbound type variables. - 1│ I := Num.Int * - ^ + Here is one occurrence: - Tip: Type variables must be bound before the `:=`. Perhaps you intended - to add a type parameter to this type? - "# - ), - ) - } + 4│ I : [A (Num.Int *), B (Num.Int *)] + ^ - #[test] - fn multiple_wildcards_in_alias() { - report_problem_as( - indoc!( - r#" - I : [A (Num.Int *), B (Num.Int *)] - a : I - a - "# - ), - indoc!( - r#" - ── UNBOUND TYPE VARIABLE ───────────────────────────────── /code/proj/Main.roc ─ + Tip: Type variables must be bound before the `:`. Perhaps you intended + to add a type parameter to this type? + "### + ); - The definition of `I` has 2 unbound type variables. + test_report!( + inference_var_in_alias, + indoc!( + r#" + I : Num.Int _ + a : I + a + "# + ), + @r###" + ── UNBOUND TYPE VARIABLE ───────────────────────────────── /code/proj/Main.roc ─ - Here is one occurrence: + The definition of `I` has an unbound type variable: - 1│ I : [A (Num.Int *), B (Num.Int *)] - ^ + 4│ I : Num.Int _ + ^ - Tip: Type variables must be bound before the `:`. Perhaps you intended - to add a type parameter to this type? - "# - ), - ) - } + Tip: Type variables must be bound before the `:`. Perhaps you intended + to add a type parameter to this type? + "### + ); - #[test] - fn inference_var_in_alias() { - report_problem_as( - indoc!( - r#" - I : Num.Int _ - a : I - a - "# - ), - indoc!( - r#" - ── UNBOUND TYPE VARIABLE ───────────────────────────────── /code/proj/Main.roc ─ + test_report!( + unbound_var_in_alias, + indoc!( + r#" + I : Num.Int a + a : I + a + "# + ), + @r###" + ── UNBOUND TYPE VARIABLE ───────────────────────────────── /code/proj/Main.roc ─ - The definition of `I` has an unbound type variable: + The definition of `I` has an unbound type variable: - 1│ I : Num.Int _ - ^ + 4│ I : Num.Int a + ^ - Tip: Type variables must be bound before the `:`. Perhaps you intended - to add a type parameter to this type? - "# - ), - ) - } - - #[test] - fn unbound_var_in_alias() { - report_problem_as( - indoc!( - r#" - I : Num.Int a - a : I - a - "# - ), - indoc!( - r#" - ── UNBOUND TYPE VARIABLE ───────────────────────────────── /code/proj/Main.roc ─ - - The definition of `I` has an unbound type variable: - - 1│ I : Num.Int a - ^ - - Tip: Type variables must be bound before the `:`. Perhaps you intended - to add a type parameter to this type? - "# - ), - ) - } + Tip: Type variables must be bound before the `:`. Perhaps you intended + to add a type parameter to this type? + "### + ); test_report!( ability_bad_type_parameter, @@ -9838,24 +8767,22 @@ All branches in an `if` must have the same type! "# ); - #[test] - fn always_function() { - // from https://github.com/rtfeldman/roc/commit/1372737f5e53ee5bb96d7e1b9593985e5537023a - // There was a bug where this reported UnusedArgument("val") - // since it was used only in the returned function only. - // - // we want this to not give any warnings/errors! - report_problem_as( - indoc!( - r#" - always = \val -> \_ -> val + // from https://github.com/rtfeldman/roc/commit/1372737f5e53ee5bb96d7e1b9593985e5537023a + // There was a bug where this reported UnusedArgument("val") + // since it was used only in the returned function only. + // + // we want this to not give any warnings/errors! + test_report!( + always_function, + indoc!( + r#" + always = \val -> \_ -> val - always - "# - ), - "", - ) - } + always + "# + ), + @"" + ); test_report!( imports_missing_comma, From 2d169bf518733ab89b5df29502d90beefd773f77 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Sun, 3 Jul 2022 15:10:00 -0400 Subject: [PATCH 46/66] Remove tag_name from ResetReuse --- crates/compiler/alias_analysis/src/lib.rs | 1 - crates/compiler/gen_dev/src/lib.rs | 15 ++------------- crates/compiler/mono/src/ir.rs | 18 +++++++----------- crates/compiler/mono/src/reset_reuse.rs | 3 +-- 4 files changed, 10 insertions(+), 27 deletions(-) diff --git a/crates/compiler/alias_analysis/src/lib.rs b/crates/compiler/alias_analysis/src/lib.rs index a5c3026d81..0480617779 100644 --- a/crates/compiler/alias_analysis/src/lib.rs +++ b/crates/compiler/alias_analysis/src/lib.rs @@ -1226,7 +1226,6 @@ fn expr_spec<'a>( Call(call) => call_spec(builder, env, block, layout, call), Reuse { tag_layout, - tag_name: _, tag_id, arguments, .. diff --git a/crates/compiler/gen_dev/src/lib.rs b/crates/compiler/gen_dev/src/lib.rs index 960278588e..eef12f4cf8 100644 --- a/crates/compiler/gen_dev/src/lib.rs +++ b/crates/compiler/gen_dev/src/lib.rs @@ -14,9 +14,7 @@ use roc_mono::ir::{ BranchInfo, CallType, Expr, JoinPointId, ListLiteralElement, Literal, Param, Proc, ProcLayout, SelfRecursive, Stmt, }; -use roc_mono::layout::{ - Builtin, Layout, LayoutId, LayoutIds, TagIdIntType, TagOrClosure, UnionLayout, -}; +use roc_mono::layout::{Builtin, Layout, LayoutId, LayoutIds, TagIdIntType, UnionLayout}; mod generic64; mod object_builder; @@ -915,18 +913,9 @@ trait Backend<'a> { } } Expr::Reuse { - symbol, - arguments, - tag_name, - .. + symbol, arguments, .. } => { self.set_last_seen(*symbol, stmt); - match tag_name { - TagOrClosure::Closure(sym) => { - self.set_last_seen(*sym, stmt); - } - TagOrClosure::Tag(_) => {} - } for sym in *arguments { self.set_last_seen(*sym, stmt); } diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index 0b784c2c7e..fa6adbf63b 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -53,14 +53,14 @@ pub fn pretty_print_ir_symbols() -> bool { // if it went up, maybe check that the change is really required roc_error_macros::assert_sizeof_wasm!(Literal, 24); -roc_error_macros::assert_sizeof_wasm!(Expr, 48); +roc_error_macros::assert_sizeof_wasm!(Expr, 40); roc_error_macros::assert_sizeof_wasm!(Stmt, 120); roc_error_macros::assert_sizeof_wasm!(ProcLayout, 40); roc_error_macros::assert_sizeof_wasm!(Call, 44); roc_error_macros::assert_sizeof_wasm!(CallType, 36); roc_error_macros::assert_sizeof_non_wasm!(Literal, 3 * 8); -roc_error_macros::assert_sizeof_non_wasm!(Expr, 10 * 8); +roc_error_macros::assert_sizeof_non_wasm!(Expr, 9 * 8); roc_error_macros::assert_sizeof_non_wasm!(Stmt, 19 * 8); roc_error_macros::assert_sizeof_non_wasm!(ProcLayout, 8 * 8); roc_error_macros::assert_sizeof_non_wasm!(Call, 9 * 8); @@ -1757,7 +1757,6 @@ pub enum Expr<'a> { update_mode: UpdateModeId, // normal Tag fields tag_layout: UnionLayout<'a>, - tag_name: TagOrClosure, tag_id: TagIdIntType, arguments: &'a [Symbol], }, @@ -1860,18 +1859,15 @@ impl<'a> Expr<'a> { } Reuse { symbol, - tag_name, + tag_id, arguments, update_mode, .. } => { - let doc_tag = match tag_name { - TagOrClosure::Tag(TagName(s)) => alloc.text(s.as_str()), - TagOrClosure::Closure(s) => alloc - .text("ClosureTag(") - .append(symbol_to_doc(alloc, *s)) - .append(")"), - }; + let doc_tag = alloc + .text("TagId(") + .append(alloc.text(tag_id.to_string())) + .append(")"); let it = arguments.iter().map(|s| symbol_to_doc(alloc, *s)); diff --git a/crates/compiler/mono/src/reset_reuse.rs b/crates/compiler/mono/src/reset_reuse.rs index 189a1b8481..28ba763035 100644 --- a/crates/compiler/mono/src/reset_reuse.rs +++ b/crates/compiler/mono/src/reset_reuse.rs @@ -97,7 +97,7 @@ fn function_s<'a, 'i>( Expr::Tag { tag_layout, tag_id, - tag_name, + tag_name: _, arguments, } if may_reuse(*tag_layout, *tag_id, c) => { // for now, always overwrite the tag ID just to be sure @@ -109,7 +109,6 @@ fn function_s<'a, 'i>( update_tag_id, tag_layout: *tag_layout, tag_id: *tag_id, - tag_name: tag_name.clone(), arguments, }; let new_stmt = Let(*symbol, new_expr, *layout, continuation); From 2726a3506fff65e7a1ee0c62eeec51471d91cee5 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Sun, 3 Jul 2022 15:12:51 -0400 Subject: [PATCH 47/66] Remove tag_name from Expr::Tag --- crates/compiler/alias_analysis/src/lib.rs | 1 - crates/compiler/mono/src/ir.rs | 30 +++++-------------- crates/compiler/mono/src/reset_reuse.rs | 1 - .../generated/empty_list_of_function_type.txt | 4 +-- .../test_mono/generated/ir_when_idiv.txt | 4 +-- .../test_mono/generated/ir_when_just.txt | 2 +- .../test_mono/generated/ir_when_maybe.txt | 2 +- .../test_mono/generated/ir_when_these.txt | 2 +- .../compiler/test_mono/generated/is_nil.txt | 4 +-- ...cialize_errors_behind_unified_branches.txt | 12 ++++---- .../test_mono/generated/issue_2810.txt | 8 ++--- .../lambda_capture_niche_u8_vs_u64.txt | 4 +-- ...ches_have_captured_function_in_closure.txt | 4 +-- ...ure_niches_with_non_capturing_function.txt | 6 ++-- ...pture_niches_with_other_lambda_capture.txt | 6 ++-- .../compiler/test_mono/generated/list_get.txt | 4 +-- .../generated/list_map_closure_borrows.txt | 4 +-- .../generated/list_map_closure_owns.txt | 4 +-- .../generated/monomorphized_applied_tag.txt | 2 +- .../generated/nested_pattern_match.txt | 4 +-- .../generated/opaque_assign_to_symbol.txt | 2 +- crates/compiler/test_mono/generated/peano.txt | 8 ++--- .../compiler/test_mono/generated/peano1.txt | 8 ++--- .../compiler/test_mono/generated/peano2.txt | 8 ++--- .../test_mono/generated/quicksort_swap.txt | 4 +-- .../compiler/test_mono/generated/rigids.txt | 4 +-- .../generated/specialize_closures.txt | 4 +-- .../generated/specialize_lowlevel.txt | 4 +-- .../test_mono/generated/when_nested_maybe.txt | 4 +-- .../test_mono/generated/when_on_result.txt | 2 +- 30 files changed, 70 insertions(+), 86 deletions(-) diff --git a/crates/compiler/alias_analysis/src/lib.rs b/crates/compiler/alias_analysis/src/lib.rs index 0480617779..b1157d8059 100644 --- a/crates/compiler/alias_analysis/src/lib.rs +++ b/crates/compiler/alias_analysis/src/lib.rs @@ -1232,7 +1232,6 @@ fn expr_spec<'a>( } | Tag { tag_layout, - tag_name: _, tag_id, arguments, } => { diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index fa6adbf63b..5744488f1c 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -53,14 +53,14 @@ pub fn pretty_print_ir_symbols() -> bool { // if it went up, maybe check that the change is really required roc_error_macros::assert_sizeof_wasm!(Literal, 24); -roc_error_macros::assert_sizeof_wasm!(Expr, 40); +roc_error_macros::assert_sizeof_wasm!(Expr, 48); roc_error_macros::assert_sizeof_wasm!(Stmt, 120); roc_error_macros::assert_sizeof_wasm!(ProcLayout, 40); roc_error_macros::assert_sizeof_wasm!(Call, 44); roc_error_macros::assert_sizeof_wasm!(CallType, 36); roc_error_macros::assert_sizeof_non_wasm!(Literal, 3 * 8); -roc_error_macros::assert_sizeof_non_wasm!(Expr, 9 * 8); +roc_error_macros::assert_sizeof_non_wasm!(Expr, 10 * 8); roc_error_macros::assert_sizeof_non_wasm!(Stmt, 19 * 8); roc_error_macros::assert_sizeof_non_wasm!(ProcLayout, 8 * 8); roc_error_macros::assert_sizeof_non_wasm!(Call, 9 * 8); @@ -1713,7 +1713,6 @@ pub enum Expr<'a> { Tag { tag_layout: UnionLayout<'a>, - tag_name: TagOrClosure, tag_id: TagIdIntType, arguments: &'a [Symbol], }, @@ -1839,17 +1838,12 @@ impl<'a> Expr<'a> { Call(call) => call.to_doc(alloc), Tag { - tag_name, - arguments, - .. + tag_id, arguments, .. } => { - let doc_tag = match tag_name { - TagOrClosure::Tag(TagName(s)) => alloc.text(s.as_str()), - TagOrClosure::Closure(s) => alloc - .text("ClosureTag(") - .append(symbol_to_doc(alloc, *s)) - .append(")"), - }; + let doc_tag = alloc + .text("TagId(") + .append(alloc.text(tag_id.to_string())) + .append(")"); let it = arguments.iter().map(|s| symbol_to_doc(alloc, *s)); @@ -5281,8 +5275,8 @@ where ClosureRepresentation::Union { tag_id, alphabetic_order_fields: field_layouts, - closure_name: tag_name, union_layout, + closure_name: _, } => { // captured variables are in symbol-alphabetic order, but now we want // them ordered by their alignment requirements @@ -5306,7 +5300,6 @@ where let expr = Expr::Tag { tag_id, tag_layout: union_layout, - tag_name: tag_name.into(), arguments: symbols, }; @@ -5502,7 +5495,6 @@ fn convert_tag_union<'a>( let tag = Expr::Tag { tag_layout: union_layout, - tag_name: tag_name.into(), tag_id: tag_id as _, arguments: field_symbols, }; @@ -5525,7 +5517,6 @@ fn convert_tag_union<'a>( let tag = Expr::Tag { tag_layout: union_layout, - tag_name: tag_name.into(), tag_id: tag_id as _, arguments: field_symbols, }; @@ -5550,7 +5541,6 @@ fn convert_tag_union<'a>( let tag = Expr::Tag { tag_layout: union_layout, - tag_name: tag_name.into(), tag_id: tag_id as _, arguments: field_symbols, }; @@ -5577,7 +5567,6 @@ fn convert_tag_union<'a>( let tag = Expr::Tag { tag_layout: union_layout, - tag_name: tag_name.into(), tag_id: tag_id as _, arguments: field_symbols, }; @@ -5595,7 +5584,6 @@ fn convert_tag_union<'a>( let tag = Expr::Tag { tag_layout: union_layout, - tag_name: tag_name.into(), tag_id: tag_id as _, arguments: field_symbols, }; @@ -6445,7 +6433,6 @@ fn substitute_in_expr<'a>( Tag { tag_layout, - tag_name, tag_id, arguments: args, } => { @@ -6466,7 +6453,6 @@ fn substitute_in_expr<'a>( Some(Tag { tag_layout: *tag_layout, - tag_name: tag_name.clone(), tag_id: *tag_id, arguments, }) diff --git a/crates/compiler/mono/src/reset_reuse.rs b/crates/compiler/mono/src/reset_reuse.rs index 28ba763035..44c77190f7 100644 --- a/crates/compiler/mono/src/reset_reuse.rs +++ b/crates/compiler/mono/src/reset_reuse.rs @@ -97,7 +97,6 @@ fn function_s<'a, 'i>( Expr::Tag { tag_layout, tag_id, - tag_name: _, arguments, } if may_reuse(*tag_layout, *tag_id, c) => { // for now, always overwrite the tag ID just to be sure diff --git a/crates/compiler/test_mono/generated/empty_list_of_function_type.txt b/crates/compiler/test_mono/generated/empty_list_of_function_type.txt index 0694fc9cbc..f378649fab 100644 --- a/crates/compiler/test_mono/generated/empty_list_of_function_type.txt +++ b/crates/compiler/test_mono/generated/empty_list_of_function_type.txt @@ -3,11 +3,11 @@ procedure List.2 (List.74, List.75): let List.268 : Int1 = CallByName Num.22 List.75 List.272; if List.268 then let List.270 : {} = CallByName List.60 List.74 List.75; - let List.269 : [C {}, C {}] = Ok List.270; + let List.269 : [C {}, C {}] = TagId(1) List.270; ret List.269; else let List.267 : {} = Struct {}; - let List.266 : [C {}, C {}] = Err List.267; + let List.266 : [C {}, C {}] = TagId(0) List.267; ret List.266; procedure List.6 (#Attr.2): diff --git a/crates/compiler/test_mono/generated/ir_when_idiv.txt b/crates/compiler/test_mono/generated/ir_when_idiv.txt index 9706bd86fa..fc0e6e7ffb 100644 --- a/crates/compiler/test_mono/generated/ir_when_idiv.txt +++ b/crates/compiler/test_mono/generated/ir_when_idiv.txt @@ -3,11 +3,11 @@ procedure Num.40 (#Attr.2, #Attr.3): let Num.190 : Int1 = lowlevel NotEq #Attr.3 Num.193; if Num.190 then let Num.192 : I64 = lowlevel NumDivUnchecked #Attr.2 #Attr.3; - let Num.191 : [C {}, C I64] = Ok Num.192; + let Num.191 : [C {}, C I64] = TagId(1) Num.192; ret Num.191; else let Num.189 : {} = Struct {}; - let Num.188 : [C {}, C I64] = Err Num.189; + let Num.188 : [C {}, C I64] = TagId(0) Num.189; ret Num.188; procedure Test.0 (): diff --git a/crates/compiler/test_mono/generated/ir_when_just.txt b/crates/compiler/test_mono/generated/ir_when_just.txt index 8959b2d342..e6b67860ca 100644 --- a/crates/compiler/test_mono/generated/ir_when_just.txt +++ b/crates/compiler/test_mono/generated/ir_when_just.txt @@ -4,7 +4,7 @@ procedure Num.19 (#Attr.2, #Attr.3): procedure Test.0 (): let Test.10 : I64 = 41i64; - let Test.1 : [C I64, C ] = Just Test.10; + let Test.1 : [C I64, C ] = TagId(0) Test.10; let Test.7 : U8 = 0i64; let Test.8 : U8 = GetTagId Test.1; let Test.9 : Int1 = lowlevel Eq Test.7 Test.8; diff --git a/crates/compiler/test_mono/generated/ir_when_maybe.txt b/crates/compiler/test_mono/generated/ir_when_maybe.txt index e5a573c182..b038594df1 100644 --- a/crates/compiler/test_mono/generated/ir_when_maybe.txt +++ b/crates/compiler/test_mono/generated/ir_when_maybe.txt @@ -1,6 +1,6 @@ procedure Test.0 (): let Test.9 : I64 = 3i64; - let Test.3 : [C I64, C ] = Just Test.9; + let Test.3 : [C I64, C ] = TagId(0) Test.9; let Test.6 : U8 = 0i64; let Test.7 : U8 = GetTagId Test.3; let Test.8 : Int1 = lowlevel Eq Test.6 Test.7; diff --git a/crates/compiler/test_mono/generated/ir_when_these.txt b/crates/compiler/test_mono/generated/ir_when_these.txt index 0c7453e2b7..456c42f01c 100644 --- a/crates/compiler/test_mono/generated/ir_when_these.txt +++ b/crates/compiler/test_mono/generated/ir_when_these.txt @@ -1,7 +1,7 @@ procedure Test.0 (): let Test.10 : I64 = 1i64; let Test.11 : I64 = 2i64; - let Test.5 : [C I64, C I64 I64, C I64] = These Test.10 Test.11; + let Test.5 : [C I64, C I64 I64, C I64] = TagId(1) Test.10 Test.11; let Test.9 : U8 = GetTagId Test.5; switch Test.9: case 2: diff --git a/crates/compiler/test_mono/generated/is_nil.txt b/crates/compiler/test_mono/generated/is_nil.txt index 9b47d797ca..d12f69a017 100644 --- a/crates/compiler/test_mono/generated/is_nil.txt +++ b/crates/compiler/test_mono/generated/is_nil.txt @@ -11,8 +11,8 @@ procedure Test.3 (Test.4): procedure Test.0 (): let Test.16 : I64 = 2i64; - let Test.17 : [, C I64 *self] = Nil ; - let Test.10 : [, C I64 *self] = Cons Test.16 Test.17; + let Test.17 : [, C I64 *self] = TagId(1) ; + let Test.10 : [, C I64 *self] = TagId(0) Test.16 Test.17; let Test.9 : Int1 = CallByName Test.3 Test.10; dec Test.10; ret Test.9; diff --git a/crates/compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt b/crates/compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt index d80b9bb3c5..6c72fdda41 100644 --- a/crates/compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt +++ b/crates/compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt @@ -3,11 +3,11 @@ procedure List.2 (List.74, List.75): let List.277 : Int1 = CallByName Num.22 List.75 List.281; if List.277 then let List.279 : I64 = CallByName List.60 List.74 List.75; - let List.278 : [C {}, C I64] = Ok List.279; + let List.278 : [C {}, C I64] = TagId(1) List.279; ret List.278; else let List.276 : {} = Struct {}; - let List.275 : [C {}, C I64] = Err List.276; + let List.275 : [C {}, C I64] = TagId(0) List.276; ret List.275; procedure List.6 (#Attr.2): @@ -26,11 +26,11 @@ procedure List.9 (List.188): let List.272 : Int1 = lowlevel Eq List.270 List.271; if List.272 then let List.189 : I64 = UnionAtIndex (Id 1) (Index 0) List.266; - let List.267 : [C Int1, C I64] = Ok List.189; + let List.267 : [C Int1, C I64] = TagId(1) List.189; ret List.267; else let List.269 : Int1 = true; - let List.268 : [C Int1, C I64] = Err List.269; + let List.268 : [C Int1, C I64] = TagId(0) List.269; ret List.268; procedure Num.22 (#Attr.2, #Attr.3): @@ -44,11 +44,11 @@ procedure Str.27 (#Attr.2): let Str.39 : Int1 = lowlevel NumGt Str.42 Str.43; if Str.39 then let Str.41 : Int1 = false; - let Str.40 : [C Int1, C I64] = Err Str.41; + let Str.40 : [C Int1, C I64] = TagId(0) Str.41; ret Str.40; else let Str.38 : I64 = StructAtIndex 0 #Attr.3; - let Str.37 : [C Int1, C I64] = Ok Str.38; + let Str.37 : [C Int1, C I64] = TagId(1) Str.38; ret Str.37; procedure Test.0 (): diff --git a/crates/compiler/test_mono/generated/issue_2810.txt b/crates/compiler/test_mono/generated/issue_2810.txt index 31f6b7125e..f898de15a3 100644 --- a/crates/compiler/test_mono/generated/issue_2810.txt +++ b/crates/compiler/test_mono/generated/issue_2810.txt @@ -1,6 +1,6 @@ procedure Test.0 (): - let Test.19 : [C [C [C *self, C ]], C ] = SystemTool ; - let Test.17 : [C [C *self, C ]] = Job Test.19; - let Test.16 : [C [C [C *self, C ]], C ] = FromJob Test.17; - let Test.7 : [C [C *self, C ]] = Job Test.16; + let Test.19 : [C [C [C *self, C ]], C ] = TagId(1) ; + let Test.17 : [C [C *self, C ]] = TagId(0) Test.19; + let Test.16 : [C [C [C *self, C ]], C ] = TagId(0) Test.17; + let Test.7 : [C [C *self, C ]] = TagId(0) Test.16; ret Test.7; diff --git a/crates/compiler/test_mono/generated/lambda_capture_niche_u8_vs_u64.txt b/crates/compiler/test_mono/generated/lambda_capture_niche_u8_vs_u64.txt index b7cc9bd341..da0ea30610 100644 --- a/crates/compiler/test_mono/generated/lambda_capture_niche_u8_vs_u64.txt +++ b/crates/compiler/test_mono/generated/lambda_capture_niche_u8_vs_u64.txt @@ -7,11 +7,11 @@ procedure Num.94 (#Attr.2): ret Num.189; procedure Test.1 (Test.4): - let Test.16 : [C U8, C U64] = ClosureTag(Test.5) Test.4; + let Test.16 : [C U8, C U64] = TagId(1) Test.4; ret Test.16; procedure Test.1 (Test.4): - let Test.22 : [C U8, C U64] = ClosureTag(Test.5) Test.4; + let Test.22 : [C U8, C U64] = TagId(0) Test.4; ret Test.22; procedure Test.5 (Test.17, #Attr.12): diff --git a/crates/compiler/test_mono/generated/lambda_capture_niches_have_captured_function_in_closure.txt b/crates/compiler/test_mono/generated/lambda_capture_niches_have_captured_function_in_closure.txt index d7371b4421..5228441c5b 100644 --- a/crates/compiler/test_mono/generated/lambda_capture_niches_have_captured_function_in_closure.txt +++ b/crates/compiler/test_mono/generated/lambda_capture_niches_have_captured_function_in_closure.txt @@ -17,11 +17,11 @@ procedure Test.16 (Test.54): ret Test.56; procedure Test.2 (Test.7, Test.8): - let Test.9 : [C {} {}, C {} {}] = ClosureTag(Test.9) Test.7 Test.8; + let Test.9 : [C {} {}, C {} {}] = TagId(0) Test.7 Test.8; ret Test.9; procedure Test.2 (Test.7, Test.8): - let Test.9 : [C {} {}, C {} {}] = ClosureTag(Test.9) Test.7 Test.8; + let Test.9 : [C {} {}, C {} {}] = TagId(1) Test.7 Test.8; ret Test.9; procedure Test.3 (Test.17): diff --git a/crates/compiler/test_mono/generated/lambda_capture_niches_with_non_capturing_function.txt b/crates/compiler/test_mono/generated/lambda_capture_niches_with_non_capturing_function.txt index 6ef000047c..e6eb82b21b 100644 --- a/crates/compiler/test_mono/generated/lambda_capture_niches_with_non_capturing_function.txt +++ b/crates/compiler/test_mono/generated/lambda_capture_niches_with_non_capturing_function.txt @@ -1,9 +1,9 @@ procedure Test.1 (Test.5): - let Test.19 : [C , C U64, C {}] = ClosureTag(Test.6) Test.5; + let Test.19 : [C , C U64, C {}] = TagId(2) Test.5; ret Test.19; procedure Test.1 (Test.5): - let Test.27 : [C , C U64, C {}] = ClosureTag(Test.6) Test.5; + let Test.27 : [C , C U64, C {}] = TagId(1) Test.5; ret Test.27; procedure Test.2 (Test.8): @@ -49,7 +49,7 @@ procedure Test.0 (): jump Test.16 Test.17; case 1: - let Test.2 : [C , C U64, C {}] = ClosureTag(Test.2) ; + let Test.2 : [C , C U64, C {}] = TagId(0) ; jump Test.16 Test.2; default: diff --git a/crates/compiler/test_mono/generated/lambda_capture_niches_with_other_lambda_capture.txt b/crates/compiler/test_mono/generated/lambda_capture_niches_with_other_lambda_capture.txt index b553adfbc1..0ec50cc024 100644 --- a/crates/compiler/test_mono/generated/lambda_capture_niches_with_other_lambda_capture.txt +++ b/crates/compiler/test_mono/generated/lambda_capture_niches_with_other_lambda_capture.txt @@ -1,13 +1,13 @@ procedure Test.1 (Test.5): - let Test.20 : [C U64, C {}, C Str] = ClosureTag(Test.6) Test.5; + let Test.20 : [C U64, C {}, C Str] = TagId(1) Test.5; ret Test.20; procedure Test.1 (Test.5): - let Test.32 : [C U64, C {}, C Str] = ClosureTag(Test.6) Test.5; + let Test.32 : [C U64, C {}, C Str] = TagId(0) Test.5; ret Test.32; procedure Test.2 (Test.7): - let Test.26 : [C U64, C {}, C Str] = ClosureTag(Test.8) Test.7; + let Test.26 : [C U64, C {}, C Str] = TagId(2) Test.7; ret Test.26; procedure Test.6 (Test.21, #Attr.12): diff --git a/crates/compiler/test_mono/generated/list_get.txt b/crates/compiler/test_mono/generated/list_get.txt index 3bdeee4a87..e54a7a6995 100644 --- a/crates/compiler/test_mono/generated/list_get.txt +++ b/crates/compiler/test_mono/generated/list_get.txt @@ -3,11 +3,11 @@ procedure List.2 (List.74, List.75): let List.268 : Int1 = CallByName Num.22 List.75 List.272; if List.268 then let List.270 : I64 = CallByName List.60 List.74 List.75; - let List.269 : [C {}, C I64] = Ok List.270; + let List.269 : [C {}, C I64] = TagId(1) List.270; ret List.269; else let List.267 : {} = Struct {}; - let List.266 : [C {}, C I64] = Err List.267; + let List.266 : [C {}, C I64] = TagId(0) List.267; ret List.266; procedure List.6 (#Attr.2): diff --git a/crates/compiler/test_mono/generated/list_map_closure_borrows.txt b/crates/compiler/test_mono/generated/list_map_closure_borrows.txt index 0121e1773e..f01ce2d803 100644 --- a/crates/compiler/test_mono/generated/list_map_closure_borrows.txt +++ b/crates/compiler/test_mono/generated/list_map_closure_borrows.txt @@ -3,11 +3,11 @@ procedure List.2 (List.74, List.75): let List.268 : Int1 = CallByName Num.22 List.75 List.272; if List.268 then let List.270 : Str = CallByName List.60 List.74 List.75; - let List.269 : [C {}, C Str] = Ok List.270; + let List.269 : [C {}, C Str] = TagId(1) List.270; ret List.269; else let List.267 : {} = Struct {}; - let List.266 : [C {}, C Str] = Err List.267; + let List.266 : [C {}, C Str] = TagId(0) List.267; ret List.266; procedure List.5 (#Attr.2, #Attr.3): diff --git a/crates/compiler/test_mono/generated/list_map_closure_owns.txt b/crates/compiler/test_mono/generated/list_map_closure_owns.txt index e62826e3c2..6b251728f0 100644 --- a/crates/compiler/test_mono/generated/list_map_closure_owns.txt +++ b/crates/compiler/test_mono/generated/list_map_closure_owns.txt @@ -3,11 +3,11 @@ procedure List.2 (List.74, List.75): let List.268 : Int1 = CallByName Num.22 List.75 List.272; if List.268 then let List.270 : Str = CallByName List.60 List.74 List.75; - let List.269 : [C {}, C Str] = Ok List.270; + let List.269 : [C {}, C Str] = TagId(1) List.270; ret List.269; else let List.267 : {} = Struct {}; - let List.266 : [C {}, C Str] = Err List.267; + let List.266 : [C {}, C Str] = TagId(0) List.267; ret List.266; procedure List.5 (#Attr.2, #Attr.3): diff --git a/crates/compiler/test_mono/generated/monomorphized_applied_tag.txt b/crates/compiler/test_mono/generated/monomorphized_applied_tag.txt index 0d3b277cf4..eedf08c0d9 100644 --- a/crates/compiler/test_mono/generated/monomorphized_applied_tag.txt +++ b/crates/compiler/test_mono/generated/monomorphized_applied_tag.txt @@ -15,6 +15,6 @@ procedure Test.2 (Test.4): procedure Test.0 (): let Test.13 : Str = "A"; - let Test.1 : [C Str, C Str] = A Test.13; + let Test.1 : [C Str, C Str] = TagId(0) Test.13; let Test.7 : Str = CallByName Test.2 Test.1; ret Test.7; diff --git a/crates/compiler/test_mono/generated/nested_pattern_match.txt b/crates/compiler/test_mono/generated/nested_pattern_match.txt index 6673261ca3..42df0a49ee 100644 --- a/crates/compiler/test_mono/generated/nested_pattern_match.txt +++ b/crates/compiler/test_mono/generated/nested_pattern_match.txt @@ -4,8 +4,8 @@ procedure Num.19 (#Attr.2, #Attr.3): procedure Test.0 (): let Test.19 : I64 = 41i64; - let Test.18 : [C I64, C ] = Just Test.19; - let Test.2 : [C [C I64, C ], C ] = Just Test.18; + let Test.18 : [C I64, C ] = TagId(0) Test.19; + let Test.2 : [C [C I64, C ], C ] = TagId(0) Test.18; joinpoint Test.15: let Test.8 : I64 = 1i64; ret Test.8; diff --git a/crates/compiler/test_mono/generated/opaque_assign_to_symbol.txt b/crates/compiler/test_mono/generated/opaque_assign_to_symbol.txt index 23eee2ceea..f2e8378359 100644 --- a/crates/compiler/test_mono/generated/opaque_assign_to_symbol.txt +++ b/crates/compiler/test_mono/generated/opaque_assign_to_symbol.txt @@ -1,5 +1,5 @@ procedure Test.3 (Test.4): - let Test.8 : [C {}, C U8] = Ok Test.4; + let Test.8 : [C {}, C U8] = TagId(1) Test.4; ret Test.8; procedure Test.0 (): diff --git a/crates/compiler/test_mono/generated/peano.txt b/crates/compiler/test_mono/generated/peano.txt index 8b7370a838..ee5393219a 100644 --- a/crates/compiler/test_mono/generated/peano.txt +++ b/crates/compiler/test_mono/generated/peano.txt @@ -1,6 +1,6 @@ procedure Test.0 (): - let Test.11 : [, C *self] = Z ; - let Test.10 : [, C *self] = S Test.11; - let Test.9 : [, C *self] = S Test.10; - let Test.3 : [, C *self] = S Test.9; + let Test.11 : [, C *self] = TagId(1) ; + let Test.10 : [, C *self] = TagId(0) Test.11; + let Test.9 : [, C *self] = TagId(0) Test.10; + let Test.3 : [, C *self] = TagId(0) Test.9; ret Test.3; diff --git a/crates/compiler/test_mono/generated/peano1.txt b/crates/compiler/test_mono/generated/peano1.txt index 63db968efc..75c118328e 100644 --- a/crates/compiler/test_mono/generated/peano1.txt +++ b/crates/compiler/test_mono/generated/peano1.txt @@ -1,8 +1,8 @@ procedure Test.0 (): - let Test.15 : [, C *self] = Z ; - let Test.14 : [, C *self] = S Test.15; - let Test.13 : [, C *self] = S Test.14; - let Test.3 : [, C *self] = S Test.13; + let Test.15 : [, C *self] = TagId(1) ; + let Test.14 : [, C *self] = TagId(0) Test.15; + let Test.13 : [, C *self] = TagId(0) Test.14; + let Test.3 : [, C *self] = TagId(0) Test.13; let Test.10 : Int1 = 1i64; let Test.11 : Int1 = GetTagId Test.3; dec Test.3; diff --git a/crates/compiler/test_mono/generated/peano2.txt b/crates/compiler/test_mono/generated/peano2.txt index 73dd42687a..36a67b1b66 100644 --- a/crates/compiler/test_mono/generated/peano2.txt +++ b/crates/compiler/test_mono/generated/peano2.txt @@ -1,8 +1,8 @@ procedure Test.0 (): - let Test.21 : [, C *self] = Z ; - let Test.20 : [, C *self] = S Test.21; - let Test.19 : [, C *self] = S Test.20; - let Test.3 : [, C *self] = S Test.19; + let Test.21 : [, C *self] = TagId(1) ; + let Test.20 : [, C *self] = TagId(0) Test.21; + let Test.19 : [, C *self] = TagId(0) Test.20; + let Test.3 : [, C *self] = TagId(0) Test.19; let Test.16 : Int1 = 0i64; let Test.17 : Int1 = GetTagId Test.3; let Test.18 : Int1 = lowlevel Eq Test.16 Test.17; diff --git a/crates/compiler/test_mono/generated/quicksort_swap.txt b/crates/compiler/test_mono/generated/quicksort_swap.txt index 47d50ff7d9..e71e93a6b4 100644 --- a/crates/compiler/test_mono/generated/quicksort_swap.txt +++ b/crates/compiler/test_mono/generated/quicksort_swap.txt @@ -3,11 +3,11 @@ procedure List.2 (List.74, List.75): let List.282 : Int1 = CallByName Num.22 List.75 List.286; if List.282 then let List.284 : I64 = CallByName List.60 List.74 List.75; - let List.283 : [C {}, C I64] = Ok List.284; + let List.283 : [C {}, C I64] = TagId(1) List.284; ret List.283; else let List.281 : {} = Struct {}; - let List.280 : [C {}, C I64] = Err List.281; + let List.280 : [C {}, C I64] = TagId(0) List.281; ret List.280; procedure List.3 (List.82, List.83, List.84): diff --git a/crates/compiler/test_mono/generated/rigids.txt b/crates/compiler/test_mono/generated/rigids.txt index d5dcc8bf25..b9b09bca94 100644 --- a/crates/compiler/test_mono/generated/rigids.txt +++ b/crates/compiler/test_mono/generated/rigids.txt @@ -3,11 +3,11 @@ procedure List.2 (List.74, List.75): let List.282 : Int1 = CallByName Num.22 List.75 List.286; if List.282 then let List.284 : I64 = CallByName List.60 List.74 List.75; - let List.283 : [C {}, C I64] = Ok List.284; + let List.283 : [C {}, C I64] = TagId(1) List.284; ret List.283; else let List.281 : {} = Struct {}; - let List.280 : [C {}, C I64] = Err List.281; + let List.280 : [C {}, C I64] = TagId(0) List.281; ret List.280; procedure List.3 (List.82, List.83, List.84): diff --git a/crates/compiler/test_mono/generated/specialize_closures.txt b/crates/compiler/test_mono/generated/specialize_closures.txt index 30e609df83..9fc9ae5a24 100644 --- a/crates/compiler/test_mono/generated/specialize_closures.txt +++ b/crates/compiler/test_mono/generated/specialize_closures.txt @@ -46,8 +46,8 @@ procedure Test.0 (): in let Test.25 : Int1 = true; if Test.25 then - let Test.7 : [C I64, C I64 Int1] = ClosureTag(Test.7) Test.4; + let Test.7 : [C I64, C I64 Int1] = TagId(0) Test.4; jump Test.22 Test.7; else - let Test.8 : [C I64, C I64 Int1] = ClosureTag(Test.8) Test.5 Test.6; + let Test.8 : [C I64, C I64 Int1] = TagId(1) Test.5 Test.6; jump Test.22 Test.8; diff --git a/crates/compiler/test_mono/generated/specialize_lowlevel.txt b/crates/compiler/test_mono/generated/specialize_lowlevel.txt index e460b2b108..157c9b7fb7 100644 --- a/crates/compiler/test_mono/generated/specialize_lowlevel.txt +++ b/crates/compiler/test_mono/generated/specialize_lowlevel.txt @@ -37,8 +37,8 @@ procedure Test.0 (): in let Test.21 : Int1 = true; if Test.21 then - let Test.6 : [C I64, C I64] = ClosureTag(Test.6) Test.4; + let Test.6 : [C I64, C I64] = TagId(0) Test.4; jump Test.19 Test.6; else - let Test.7 : [C I64, C I64] = ClosureTag(Test.7) Test.5; + let Test.7 : [C I64, C I64] = TagId(1) Test.5; jump Test.19 Test.7; diff --git a/crates/compiler/test_mono/generated/when_nested_maybe.txt b/crates/compiler/test_mono/generated/when_nested_maybe.txt index 6673261ca3..42df0a49ee 100644 --- a/crates/compiler/test_mono/generated/when_nested_maybe.txt +++ b/crates/compiler/test_mono/generated/when_nested_maybe.txt @@ -4,8 +4,8 @@ procedure Num.19 (#Attr.2, #Attr.3): procedure Test.0 (): let Test.19 : I64 = 41i64; - let Test.18 : [C I64, C ] = Just Test.19; - let Test.2 : [C [C I64, C ], C ] = Just Test.18; + let Test.18 : [C I64, C ] = TagId(0) Test.19; + let Test.2 : [C [C I64, C ], C ] = TagId(0) Test.18; joinpoint Test.15: let Test.8 : I64 = 1i64; ret Test.8; diff --git a/crates/compiler/test_mono/generated/when_on_result.txt b/crates/compiler/test_mono/generated/when_on_result.txt index 6fbe2699d4..242c90438d 100644 --- a/crates/compiler/test_mono/generated/when_on_result.txt +++ b/crates/compiler/test_mono/generated/when_on_result.txt @@ -1,6 +1,6 @@ procedure Test.1 (Test.5): let Test.19 : I64 = 2i64; - let Test.2 : [C I64, C I64] = Ok Test.19; + let Test.2 : [C I64, C I64] = TagId(1) Test.19; joinpoint Test.9 Test.3: ret Test.3; in From ba1a3fa62e770553cceb834bfff27b75af5e5e3f Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Sun, 3 Jul 2022 15:15:50 -0400 Subject: [PATCH 48/66] Remove unnecessary import --- crates/compiler/mono/src/ir.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index 5744488f1c..f38e2c3082 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -2,7 +2,7 @@ use crate::layout::{ Builtin, CapturesNiche, ClosureRepresentation, LambdaName, LambdaSet, Layout, LayoutCache, - LayoutProblem, RawFunctionLayout, TagIdIntType, TagOrClosure, UnionLayout, WrappedVariant, + LayoutProblem, RawFunctionLayout, TagIdIntType, UnionLayout, WrappedVariant, }; use bumpalo::collections::{CollectIn, Vec}; use bumpalo::Bump; @@ -5505,7 +5505,7 @@ fn convert_tag_union<'a>( tag_name: wrapped_tag_name, .. } => { - debug_assert_eq!(TagOrClosure::Tag(tag_name.clone()), wrapped_tag_name); + debug_assert_eq!(wrapped_tag_name.expect_tag(), tag_name); field_symbols = { let mut temp = Vec::with_capacity_in(field_symbols_temp.len(), arena); From 35243e0efedda1fba907e0ead939bed53284580d Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Sun, 3 Jul 2022 16:53:06 -0400 Subject: [PATCH 49/66] Leftover file cleanup - Remove a toplevel directory accidentally re-added in #3352 - Remove mono tests renamed but not properly removed in #3352 --- ...cialize_errors_behind_unified_branches.txt | 65 -------------- compiler/test_mono/generated/list_get.txt | 35 -------- .../generated/list_pass_to_function.txt | 39 --------- .../multimorphic_lambda_set_capture.txt | 54 ------------ ...bdas_have_captured_function_in_closure.txt | 85 ------------------- ...ic_lambdas_with_non_capturing_function.txt | 59 ------------- ...phic_lambdas_with_other_lambda_capture.txt | 68 --------------- 7 files changed, 405 deletions(-) delete mode 100644 compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt delete mode 100644 compiler/test_mono/generated/list_get.txt delete mode 100644 compiler/test_mono/generated/list_pass_to_function.txt delete mode 100644 crates/compiler/test_mono/generated/multimorphic_lambda_set_capture.txt delete mode 100644 crates/compiler/test_mono/generated/multimorphic_lambdas_have_captured_function_in_closure.txt delete mode 100644 crates/compiler/test_mono/generated/multimorphic_lambdas_with_non_capturing_function.txt delete mode 100644 crates/compiler/test_mono/generated/multimorphic_lambdas_with_other_lambda_capture.txt diff --git a/compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt b/compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt deleted file mode 100644 index 23285418ff..0000000000 --- a/compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt +++ /dev/null @@ -1,65 +0,0 @@ -procedure List.2 (List.71, List.72): - let List.228 : U64 = CallByName List.6 List.71; - let List.224 : Int1 = CallByName Num.22 List.72 List.228; - if List.224 then - let List.226 : I64 = CallByName List.60 List.71 List.72; - let List.225 : [C {}, C I64] = Ok List.226; - ret List.225; - else - let List.223 : {} = Struct {}; - let List.222 : [C {}, C I64] = Err List.223; - ret List.222; - -procedure List.6 (#Attr.2): - let List.229 : U64 = lowlevel ListLen #Attr.2; - ret List.229; - -procedure List.60 (#Attr.2, #Attr.3): - let List.227 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.227; - -procedure List.9 (List.147): - let List.220 : U64 = 0i64; - let List.213 : [C {}, C I64] = CallByName List.2 List.147 List.220; - let List.217 : U8 = 1i64; - let List.218 : U8 = GetTagId List.213; - let List.219 : Int1 = lowlevel Eq List.217 List.218; - if List.219 then - let List.148 : I64 = UnionAtIndex (Id 1) (Index 0) List.213; - let List.214 : [C Int1, C I64] = Ok List.148; - ret List.214; - else - let List.216 : Int1 = true; - let List.215 : [C Int1, C I64] = Err List.216; - ret List.215; - -procedure Num.22 (#Attr.2, #Attr.3): - let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.188; - -procedure Str.27 (#Attr.2): - let #Attr.3 : {I64, U8} = lowlevel StrToNum #Attr.2; - let Str.41 : U8 = StructAtIndex 1 #Attr.3; - let Str.42 : U8 = 0i64; - let Str.38 : Int1 = lowlevel NumGt Str.41 Str.42; - if Str.38 then - let Str.40 : Int1 = false; - let Str.39 : [C Int1, C I64] = Err Str.40; - ret Str.39; - else - let Str.37 : I64 = StructAtIndex 0 #Attr.3; - let Str.36 : [C Int1, C I64] = Ok Str.37; - ret Str.36; - -procedure Test.0 (): - let Test.4 : Int1 = true; - if Test.4 then - let Test.6 : List I64 = Array []; - let Test.5 : [C Int1, C I64] = CallByName List.9 Test.6; - dec Test.6; - ret Test.5; - else - let Test.3 : Str = ""; - let Test.2 : [C Int1, C I64] = CallByName Str.27 Test.3; - dec Test.3; - ret Test.2; diff --git a/compiler/test_mono/generated/list_get.txt b/compiler/test_mono/generated/list_get.txt deleted file mode 100644 index 44e45be867..0000000000 --- a/compiler/test_mono/generated/list_get.txt +++ /dev/null @@ -1,35 +0,0 @@ -procedure List.2 (List.71, List.72): - let List.219 : U64 = CallByName List.6 List.71; - let List.215 : Int1 = CallByName Num.22 List.72 List.219; - if List.215 then - let List.217 : I64 = CallByName List.60 List.71 List.72; - let List.216 : [C {}, C I64] = Ok List.217; - ret List.216; - else - let List.214 : {} = Struct {}; - let List.213 : [C {}, C I64] = Err List.214; - ret List.213; - -procedure List.6 (#Attr.2): - let List.222 : U64 = lowlevel ListLen #Attr.2; - ret List.222; - -procedure List.60 (#Attr.2, #Attr.3): - let List.221 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.221; - -procedure Num.22 (#Attr.2, #Attr.3): - let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.188; - -procedure Test.1 (Test.2): - let Test.6 : List I64 = Array [1i64, 2i64, 3i64]; - let Test.7 : U64 = 0i64; - let Test.5 : [C {}, C I64] = CallByName List.2 Test.6 Test.7; - dec Test.6; - ret Test.5; - -procedure Test.0 (): - let Test.4 : {} = Struct {}; - let Test.3 : [C {}, C I64] = CallByName Test.1 Test.4; - ret Test.3; diff --git a/compiler/test_mono/generated/list_pass_to_function.txt b/compiler/test_mono/generated/list_pass_to_function.txt deleted file mode 100644 index 6d7eba5654..0000000000 --- a/compiler/test_mono/generated/list_pass_to_function.txt +++ /dev/null @@ -1,39 +0,0 @@ -procedure List.3 (List.79, List.80, List.81): - let List.214 : {List I64, I64} = CallByName List.57 List.79 List.80 List.81; - let List.213 : List I64 = StructAtIndex 0 List.214; - inc List.213; - dec List.214; - ret List.213; - -procedure List.57 (List.76, List.77, List.78): - let List.220 : U64 = CallByName List.6 List.76; - let List.217 : Int1 = CallByName Num.22 List.77 List.220; - if List.217 then - let List.218 : {List I64, I64} = CallByName List.61 List.76 List.77 List.78; - ret List.218; - else - let List.216 : {List I64, I64} = Struct {List.76, List.78}; - ret List.216; - -procedure List.6 (#Attr.2): - let List.221 : U64 = lowlevel ListLen #Attr.2; - ret List.221; - -procedure List.61 (#Attr.2, #Attr.3, #Attr.4): - let List.219 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; - ret List.219; - -procedure Num.22 (#Attr.2, #Attr.3): - let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.188; - -procedure Test.2 (Test.3): - let Test.6 : U64 = 0i64; - let Test.7 : I64 = 0i64; - let Test.5 : List I64 = CallByName List.3 Test.3 Test.6 Test.7; - ret Test.5; - -procedure Test.0 (): - let Test.1 : List I64 = Array [1i64, 2i64, 3i64]; - let Test.4 : List I64 = CallByName Test.2 Test.1; - ret Test.4; diff --git a/crates/compiler/test_mono/generated/multimorphic_lambda_set_capture.txt b/crates/compiler/test_mono/generated/multimorphic_lambda_set_capture.txt deleted file mode 100644 index b7cc9bd341..0000000000 --- a/crates/compiler/test_mono/generated/multimorphic_lambda_set_capture.txt +++ /dev/null @@ -1,54 +0,0 @@ -procedure Num.94 (#Attr.2): - let Num.188 : Str = lowlevel NumToStr #Attr.2; - ret Num.188; - -procedure Num.94 (#Attr.2): - let Num.189 : Str = lowlevel NumToStr #Attr.2; - ret Num.189; - -procedure Test.1 (Test.4): - let Test.16 : [C U8, C U64] = ClosureTag(Test.5) Test.4; - ret Test.16; - -procedure Test.1 (Test.4): - let Test.22 : [C U8, C U64] = ClosureTag(Test.5) Test.4; - ret Test.22; - -procedure Test.5 (Test.17, #Attr.12): - let Test.4 : U64 = UnionAtIndex (Id 1) (Index 0) #Attr.12; - let Test.19 : Str = CallByName Num.94 Test.4; - ret Test.19; - -procedure Test.5 (Test.17, #Attr.12): - let Test.4 : U8 = UnionAtIndex (Id 0) (Index 0) #Attr.12; - let Test.25 : Str = CallByName Num.94 Test.4; - ret Test.25; - -procedure Test.0 (): - let Test.2 : Int1 = true; - joinpoint Test.13 Test.3: - let Test.8 : {} = Struct {}; - let Test.9 : U8 = GetTagId Test.3; - joinpoint Test.10 Test.7: - ret Test.7; - in - switch Test.9: - case 0: - let Test.11 : Str = CallByName Test.5 Test.8 Test.3; - jump Test.10 Test.11; - - default: - let Test.12 : Str = CallByName Test.5 Test.8 Test.3; - jump Test.10 Test.12; - - in - let Test.26 : Int1 = true; - let Test.27 : Int1 = lowlevel Eq Test.26 Test.2; - if Test.27 then - let Test.15 : U64 = 123i64; - let Test.14 : [C U8, C U64] = CallByName Test.1 Test.15; - jump Test.13 Test.14; - else - let Test.21 : U8 = 18i64; - let Test.20 : [C U8, C U64] = CallByName Test.1 Test.21; - jump Test.13 Test.20; diff --git a/crates/compiler/test_mono/generated/multimorphic_lambdas_have_captured_function_in_closure.txt b/crates/compiler/test_mono/generated/multimorphic_lambdas_have_captured_function_in_closure.txt deleted file mode 100644 index d7371b4421..0000000000 --- a/crates/compiler/test_mono/generated/multimorphic_lambdas_have_captured_function_in_closure.txt +++ /dev/null @@ -1,85 +0,0 @@ -procedure Test.11 (Test.37): - let Test.38 : Str = ""; - ret Test.38; - -procedure Test.13 (Test.51, #Attr.12): - let Test.12 : Str = StructAtIndex 0 #Attr.12; - inc Test.12; - dec #Attr.12; - ret Test.12; - -procedure Test.15 (Test.39): - let Test.40 : Str = ""; - ret Test.40; - -procedure Test.16 (Test.54): - let Test.56 : Str = "s1"; - ret Test.56; - -procedure Test.2 (Test.7, Test.8): - let Test.9 : [C {} {}, C {} {}] = ClosureTag(Test.9) Test.7 Test.8; - ret Test.9; - -procedure Test.2 (Test.7, Test.8): - let Test.9 : [C {} {}, C {} {}] = ClosureTag(Test.9) Test.7 Test.8; - ret Test.9; - -procedure Test.3 (Test.17): - let Test.36 : {} = Struct {}; - ret Test.36; - -procedure Test.4 (Test.18): - let Test.50 : {Str} = Struct {Test.18}; - ret Test.50; - -procedure Test.9 (Test.29, #Attr.12): - let Test.8 : {} = UnionAtIndex (Id 0) (Index 1) #Attr.12; - let Test.7 : {} = UnionAtIndex (Id 0) (Index 0) #Attr.12; - let Test.35 : {} = Struct {}; - let Test.34 : Str = CallByName Test.15 Test.35; - let Test.31 : {} = CallByName Test.3 Test.34; - dec Test.34; - let Test.33 : {} = Struct {}; - let Test.32 : Str = CallByName Test.11 Test.33; - ret Test.32; - -procedure Test.9 (Test.29, #Attr.12): - let Test.8 : {} = UnionAtIndex (Id 1) (Index 1) #Attr.12; - let Test.7 : {} = UnionAtIndex (Id 1) (Index 0) #Attr.12; - let Test.49 : {} = Struct {}; - let Test.48 : Str = CallByName Test.16 Test.49; - let Test.45 : {Str} = CallByName Test.4 Test.48; - let Test.47 : {} = Struct {}; - let Test.46 : Str = CallByName Test.13 Test.47 Test.45; - ret Test.46; - -procedure Test.0 (): - let Test.5 : Int1 = true; - joinpoint Test.25 Test.6: - let Test.20 : {} = Struct {}; - let Test.21 : U8 = GetTagId Test.6; - joinpoint Test.22 Test.19: - ret Test.19; - in - switch Test.21: - case 0: - let Test.23 : Str = CallByName Test.9 Test.20 Test.6; - jump Test.22 Test.23; - - default: - let Test.24 : Str = CallByName Test.9 Test.20 Test.6; - jump Test.22 Test.24; - - in - let Test.57 : Int1 = true; - let Test.58 : Int1 = lowlevel Eq Test.57 Test.5; - if Test.58 then - let Test.27 : {} = Struct {}; - let Test.28 : {} = Struct {}; - let Test.26 : [C {} {}, C {} {}] = CallByName Test.2 Test.27 Test.28; - jump Test.25 Test.26; - else - let Test.42 : {} = Struct {}; - let Test.43 : {} = Struct {}; - let Test.41 : [C {} {}, C {} {}] = CallByName Test.2 Test.42 Test.43; - jump Test.25 Test.41; diff --git a/crates/compiler/test_mono/generated/multimorphic_lambdas_with_non_capturing_function.txt b/crates/compiler/test_mono/generated/multimorphic_lambdas_with_non_capturing_function.txt deleted file mode 100644 index 6ef000047c..0000000000 --- a/crates/compiler/test_mono/generated/multimorphic_lambdas_with_non_capturing_function.txt +++ /dev/null @@ -1,59 +0,0 @@ -procedure Test.1 (Test.5): - let Test.19 : [C , C U64, C {}] = ClosureTag(Test.6) Test.5; - ret Test.19; - -procedure Test.1 (Test.5): - let Test.27 : [C , C U64, C {}] = ClosureTag(Test.6) Test.5; - ret Test.27; - -procedure Test.2 (Test.8): - let Test.24 : Str = ""; - ret Test.24; - -procedure Test.6 (Test.20, #Attr.12): - let Test.5 : U64 = UnionAtIndex (Id 1) (Index 0) #Attr.12; - let Test.30 : Str = ""; - ret Test.30; - -procedure Test.6 (Test.20, #Attr.12): - let Test.5 : {} = UnionAtIndex (Id 2) (Index 0) #Attr.12; - let Test.22 : Str = ""; - ret Test.22; - -procedure Test.0 (): - let Test.3 : U8 = 0u8; - joinpoint Test.16 Test.4: - let Test.10 : {} = Struct {}; - let Test.11 : U8 = GetTagId Test.4; - joinpoint Test.12 Test.9: - ret Test.9; - in - switch Test.11: - case 0: - let Test.13 : Str = CallByName Test.2 Test.10; - jump Test.12 Test.13; - - case 1: - let Test.14 : Str = CallByName Test.6 Test.10 Test.4; - jump Test.12 Test.14; - - default: - let Test.15 : Str = CallByName Test.6 Test.10 Test.4; - jump Test.12 Test.15; - - in - switch Test.3: - case 0: - let Test.18 : {} = Struct {}; - let Test.17 : [C , C U64, C {}] = CallByName Test.1 Test.18; - jump Test.16 Test.17; - - case 1: - let Test.2 : [C , C U64, C {}] = ClosureTag(Test.2) ; - jump Test.16 Test.2; - - default: - let Test.26 : U64 = 1i64; - let Test.25 : [C , C U64, C {}] = CallByName Test.1 Test.26; - jump Test.16 Test.25; - diff --git a/crates/compiler/test_mono/generated/multimorphic_lambdas_with_other_lambda_capture.txt b/crates/compiler/test_mono/generated/multimorphic_lambdas_with_other_lambda_capture.txt deleted file mode 100644 index b553adfbc1..0000000000 --- a/crates/compiler/test_mono/generated/multimorphic_lambdas_with_other_lambda_capture.txt +++ /dev/null @@ -1,68 +0,0 @@ -procedure Test.1 (Test.5): - let Test.20 : [C U64, C {}, C Str] = ClosureTag(Test.6) Test.5; - ret Test.20; - -procedure Test.1 (Test.5): - let Test.32 : [C U64, C {}, C Str] = ClosureTag(Test.6) Test.5; - ret Test.32; - -procedure Test.2 (Test.7): - let Test.26 : [C U64, C {}, C Str] = ClosureTag(Test.8) Test.7; - ret Test.26; - -procedure Test.6 (Test.21, #Attr.12): - let Test.5 : U64 = UnionAtIndex (Id 0) (Index 0) #Attr.12; - dec #Attr.12; - let Test.35 : Str = ""; - ret Test.35; - -procedure Test.6 (Test.21, #Attr.12): - let Test.5 : {} = UnionAtIndex (Id 1) (Index 0) #Attr.12; - dec #Attr.12; - let Test.23 : Str = ""; - ret Test.23; - -procedure Test.8 (Test.27, #Attr.12): - let Test.7 : Str = UnionAtIndex (Id 2) (Index 0) #Attr.12; - inc Test.7; - dec #Attr.12; - ret Test.7; - -procedure Test.0 (): - let Test.3 : U8 = 0u8; - joinpoint Test.17 Test.4: - let Test.11 : {} = Struct {}; - let Test.12 : U8 = GetTagId Test.4; - joinpoint Test.13 Test.10: - ret Test.10; - in - switch Test.12: - case 0: - let Test.14 : Str = CallByName Test.6 Test.11 Test.4; - jump Test.13 Test.14; - - case 1: - let Test.15 : Str = CallByName Test.6 Test.11 Test.4; - jump Test.13 Test.15; - - default: - let Test.16 : Str = CallByName Test.8 Test.11 Test.4; - jump Test.13 Test.16; - - in - switch Test.3: - case 0: - let Test.19 : {} = Struct {}; - let Test.18 : [C U64, C {}, C Str] = CallByName Test.1 Test.19; - jump Test.17 Test.18; - - case 1: - let Test.25 : Str = "foo"; - let Test.24 : [C U64, C {}, C Str] = CallByName Test.2 Test.25; - jump Test.17 Test.24; - - default: - let Test.31 : U64 = 1i64; - let Test.30 : [C U64, C {}, C Str] = CallByName Test.1 Test.31; - jump Test.17 Test.30; - From 873142b473cc5ec71dfbe2ab295d422bc8eddeca Mon Sep 17 00:00:00 2001 From: Mfon Eti-mfon Date: Sun, 3 Jul 2022 23:04:57 +0100 Subject: [PATCH 50/66] docs-ui: Wrap module entry defs in section elements --- crates/docs/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/docs/src/lib.rs b/crates/docs/src/lib.rs index 98fb5be02f..c3995c8bc3 100644 --- a/crates/docs/src/lib.rs +++ b/crates/docs/src/lib.rs @@ -191,6 +191,8 @@ fn render_module_documentation( if should_render_entry { match entry { DocEntry::DocDef(doc_def) => { + buf.push_str("
"); + let mut href = String::new(); href.push('#'); href.push_str(doc_def.name.as_str()); @@ -239,6 +241,8 @@ fn render_module_documentation( .as_str(), ); } + + buf.push_str("
"); } DocEntry::DetachedDoc(docs) => { let markdown = markdown_to_html( From 6b1e9c75c8c2a5c3fa972bedf6985c4d2539a2dd Mon Sep 17 00:00:00 2001 From: Brian Carroll Date: Sun, 3 Jul 2022 23:31:49 +0100 Subject: [PATCH 51/66] mono: generate refcounting helper functions for Boxed layout --- crates/compiler/gen_wasm/src/wasm32_result.rs | 2 +- .../mono/src/code_gen_help/refcount.rs | 69 ++++++++++++++++++- crates/compiler/test_gen/src/gen_refcount.rs | 43 ++++++++++++ 3 files changed, 111 insertions(+), 3 deletions(-) diff --git a/crates/compiler/gen_wasm/src/wasm32_result.rs b/crates/compiler/gen_wasm/src/wasm32_result.rs index e9fd6358e0..1863bff7e6 100644 --- a/crates/compiler/gen_wasm/src/wasm32_result.rs +++ b/crates/compiler/gen_wasm/src/wasm32_result.rs @@ -76,7 +76,7 @@ pub fn insert_wrapper_for_layout<'a>( bool::insert_wrapper(arena, module, wrapper_name, main_fn_index); } Layout::Union(UnionLayout::NonRecursive(_)) => stack_data_structure(), - Layout::Union(_) => { + Layout::Union(_) | Layout::Boxed(_) => { i32::insert_wrapper(arena, module, wrapper_name, main_fn_index); } _ => stack_data_structure(), diff --git a/crates/compiler/mono/src/code_gen_help/refcount.rs b/crates/compiler/mono/src/code_gen_help/refcount.rs index e3a6282ce0..24f1b9c9ba 100644 --- a/crates/compiler/mono/src/code_gen_help/refcount.rs +++ b/crates/compiler/mono/src/code_gen_help/refcount.rs @@ -129,7 +129,9 @@ pub fn refcount_generic<'a>( Layout::RecursivePointer => unreachable!( "We should never call a refcounting helper on a RecursivePointer layout directly" ), - Layout::Boxed(_) => rc_todo(), + Layout::Boxed(inner_layout) => { + refcount_boxed(root, ident_ids, ctx, &layout, inner_layout, structure) + } } } @@ -343,7 +345,7 @@ pub fn is_rc_implemented_yet(layout: &Layout) -> bool { is_rc_implemented_yet(&lambda_set.runtime_representation()) } Layout::RecursivePointer => true, - Layout::Boxed(_) => false, + Layout::Boxed(_) => true, } } @@ -1465,3 +1467,66 @@ fn refcount_tag_fields<'a>( stmt } + +fn refcount_boxed<'a>( + root: &mut CodeGenHelp<'a>, + ident_ids: &mut IdentIds, + ctx: &mut Context<'a>, + layout: &Layout, + inner_layout: &'a Layout, + outer: Symbol, +) -> Stmt<'a> { + let arena = root.arena; + + // + // modify refcount of the inner and outer structures + // RC on inner first, to avoid use-after-free for Dec + // We're defining statements in reverse, so define outer first + // + + let rc_ptr = root.create_symbol(ident_ids, "rc_ptr"); + let alignment = layout.alignment_bytes(root.target_info); + let ret_stmt = rc_return_stmt(root, ident_ids, ctx); + let modify_outer = modify_refcount( + root, + ident_ids, + ctx, + rc_ptr, + alignment, + arena.alloc(ret_stmt), + ); + + let get_rc_and_modify_outer = rc_ptr_from_data_ptr( + root, + ident_ids, + outer, + rc_ptr, + false, + arena.alloc(modify_outer), + ); + + if inner_layout.is_refcounted() && !ctx.op.is_decref() { + let inner = root.create_symbol(ident_ids, "inner"); + let inner_expr = Expr::ExprUnbox { symbol: outer }; + + let mod_inner_unit = root.create_symbol(ident_ids, "mod_inner_unit"); + let mod_inner_args = refcount_args(root, ctx, inner); + let mod_inner_expr = root + .call_specialized_op(ident_ids, ctx, *inner_layout, mod_inner_args) + .unwrap(); + + Stmt::Let( + inner, + inner_expr, + *inner_layout, + arena.alloc(Stmt::Let( + mod_inner_unit, + mod_inner_expr, + LAYOUT_UNIT, + arena.alloc(get_rc_and_modify_outer), + )), + ) + } else { + get_rc_and_modify_outer + } +} diff --git a/crates/compiler/test_gen/src/gen_refcount.rs b/crates/compiler/test_gen/src/gen_refcount.rs index e707495919..9024d96a44 100644 --- a/crates/compiler/test_gen/src/gen_refcount.rs +++ b/crates/compiler/test_gen/src/gen_refcount.rs @@ -431,3 +431,46 @@ fn union_linked_list_long_dec() { &[Deallocated; 1_000] ); } + +#[test] +#[cfg(any(feature = "gen-wasm"))] +fn boxed_str_inc() { + assert_refcounts!( + indoc!( + r#" + s = Str.concat "A long enough string " "to be heap-allocated" + b = Box.box s + + Tuple b b + "# + ), + (Pointer, Pointer), + &[ + Live(2), // s + Live(2), // b + ] + ); +} + +#[test] +#[cfg(any(feature = "gen-wasm"))] +fn boxed_str_dec() { + assert_refcounts!( + indoc!( + r#" + s = Str.concat "A long enough string " "to be heap-allocated" + b = Box.box s + + if False then + ReturnTheBox b + else + DeallocateEverything + "# + ), + (i32, i32), + &[ + Deallocated, // s + Deallocated, // b + ] + ); +} From eee30fb58c08e12951ea8b87a91ce39466255daf Mon Sep 17 00:00:00 2001 From: Mfon Eti-mfon Date: Sun, 3 Jul 2022 23:31:28 +0100 Subject: [PATCH 52/66] docs-ui: Add border to and style wrapping section --- crates/docs/src/static/styles.css | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/crates/docs/src/static/styles.css b/crates/docs/src/static/styles.css index a00e19a2bd..8f14f37c72 100644 --- a/crates/docs/src/static/styles.css +++ b/crates/docs/src/static/styles.css @@ -30,6 +30,7 @@ --nav-link-hover-color: #000000; --type-signature-color: var(--purple-5); --type-signature-bg-color: var(--purple-1); + --module-entry-border-color: var(--gray-3); } a { @@ -75,6 +76,8 @@ a { color: var(--type-signature-color); background-color: var(--type-signature-bg-color); width: fit-content; + margin-top: 0; + margin-bottom: 24px; padding: 8px 16px; border-radius: 8px; } @@ -158,6 +161,17 @@ main { min-width: 0; /* necessary for text-overflow: ellipsis to work in descendants */ } +section { + border: 1px solid var(--module-entry-border-color); + border-radius: 8px; + padding: 32px; + margin: 32px 0; +} + +section > *:last-child { + margin-bottom: 0; +} + #sidebar-nav { grid-column-start: sidebar; grid-column-end: sidebar; @@ -447,6 +461,7 @@ pre { --top-bar-fg: #eeeeee; --type-signature-color: var(--grey-1); --type-signature-bg-color: var(--purple-4); + --module-entry-border-color: var(--purple-7); } html { From cda740a472c994be957f80b181a6001a8ecbe82c Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Sun, 3 Jul 2022 20:08:23 -0400 Subject: [PATCH 53/66] Correct LLVM box eq build --- crates/compiler/gen_llvm/src/llvm/compare.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/compiler/gen_llvm/src/llvm/compare.rs b/crates/compiler/gen_llvm/src/llvm/compare.rs index 8af0ea575c..be5e6116a9 100644 --- a/crates/compiler/gen_llvm/src/llvm/compare.rs +++ b/crates/compiler/gen_llvm/src/llvm/compare.rs @@ -1374,12 +1374,14 @@ fn build_box_eq_help<'a, 'ctx, 'env>( box1.set_name(Symbol::ARG_1.as_str(&env.interns)); box2.set_name(Symbol::ARG_2.as_str(&env.interns)); + let entry = ctx.append_basic_block(parent, "entry"); + env.builder.position_at_end(entry); + let return_true = ctx.append_basic_block(parent, "return_true"); env.builder.position_at_end(return_true); env.builder .build_return(Some(&env.context.bool_type().const_all_ones())); - let entry = ctx.append_basic_block(parent, "entry"); env.builder.position_at_end(entry); let ptr_equal = env.builder.build_int_compare( @@ -1402,8 +1404,8 @@ fn build_box_eq_help<'a, 'ctx, 'env>( let box1 = box1.into_pointer_value(); let box2 = box2.into_pointer_value(); - let value1 = env.builder.build_load(box1, "load_box1"); - let value2 = env.builder.build_load(box2, "load_box2"); + let value1 = load_roc_value(env, *inner_layout, box1, "load_box1"); + let value2 = load_roc_value(env, *inner_layout, box2, "load_box2"); let is_equal = build_eq( env, From e4d09e9e592f927a2fe2bb99bcd5df1d8d3baee4 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Sun, 3 Jul 2022 21:57:41 -0400 Subject: [PATCH 54/66] Drop obsolete comment --- crates/compiler/fmt/src/pattern.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/compiler/fmt/src/pattern.rs b/crates/compiler/fmt/src/pattern.rs index 4df623dd5c..a600aec1c8 100644 --- a/crates/compiler/fmt/src/pattern.rs +++ b/crates/compiler/fmt/src/pattern.rs @@ -1,7 +1,7 @@ use crate::annotation::{Formattable, Newlines, Parens}; use crate::spaces::{fmt_comments_only, fmt_spaces, NewlineAt}; use crate::Buf; -use roc_parse::ast::{Base, Pattern}; +use roc_parse::ast::{Base, CommentOrNewline, Pattern}; pub fn fmt_pattern<'a, 'buf>( buf: &mut Buf<'buf>, @@ -167,11 +167,11 @@ impl<'a> Formattable for Pattern<'a> { } else { fmt_spaces(buf, spaces.iter(), indent); } + sub_pattern.format_with_options(buf, parens, newlines, indent); } SpaceAfter(sub_pattern, spaces) => { sub_pattern.format_with_options(buf, parens, newlines, indent); - // if only_comments { if !sub_pattern.is_multiline() { fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent) } else { From 3e2f581db4fcb11f5f270c19d4805846b0951a9a Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Sun, 3 Jul 2022 21:58:38 -0400 Subject: [PATCH 55/66] Reproduce formatter bug --- crates/compiler/fmt/tests/test_fmt.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/crates/compiler/fmt/tests/test_fmt.rs b/crates/compiler/fmt/tests/test_fmt.rs index 504b646da7..0a71cbdc84 100644 --- a/crates/compiler/fmt/tests/test_fmt.rs +++ b/crates/compiler/fmt/tests/test_fmt.rs @@ -3373,6 +3373,18 @@ mod test_fmt { )); } + #[test] + fn when_with_integer_comments() { + expr_formats_same(indoc!( + r#" + when 0 is + 1 # comment + | 2 -> "a" + _ -> "b" + "# + )); + } + #[test] fn nested_when() { expr_formats_same(indoc!( From 872efa9724fc5735973e20e7904d732aaad4cb68 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Sun, 3 Jul 2022 21:58:20 -0400 Subject: [PATCH 56/66] Put a space before comments in patterns --- crates/compiler/fmt/src/pattern.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/crates/compiler/fmt/src/pattern.rs b/crates/compiler/fmt/src/pattern.rs index a600aec1c8..73553bce01 100644 --- a/crates/compiler/fmt/src/pattern.rs +++ b/crates/compiler/fmt/src/pattern.rs @@ -162,6 +162,10 @@ impl<'a> Formattable for Pattern<'a> { // Space SpaceBefore(sub_pattern, spaces) => { + if has_inline_comment(spaces.iter()) { + buf.spaces(1); + } + if !sub_pattern.is_multiline() { fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent) } else { @@ -172,6 +176,11 @@ impl<'a> Formattable for Pattern<'a> { } SpaceAfter(sub_pattern, spaces) => { sub_pattern.format_with_options(buf, parens, newlines, indent); + + if has_inline_comment(spaces.iter()) { + buf.spaces(1); + } + if !sub_pattern.is_multiline() { fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent) } else { @@ -196,3 +205,9 @@ impl<'a> Formattable for Pattern<'a> { } } } + +fn has_inline_comment<'a, I: IntoIterator>>(spaces: I) -> bool { + spaces + .into_iter() + .any(|space| matches!(space, CommentOrNewline::LineComment(_))) +} From 244a50143373d5a7543b55cfbda02199da379ef8 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Sun, 3 Jul 2022 22:17:13 -0400 Subject: [PATCH 57/66] Indent for multiline | patterns --- crates/compiler/fmt/src/expr.rs | 9 +++++++-- crates/compiler/fmt/src/pattern.rs | 17 ++++++++--------- crates/compiler/fmt/tests/test_fmt.rs | 12 ++++++------ 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/crates/compiler/fmt/src/expr.rs b/crates/compiler/fmt/src/expr.rs index 61b8f1e1a2..e45b15ace9 100644 --- a/crates/compiler/fmt/src/expr.rs +++ b/crates/compiler/fmt/src/expr.rs @@ -710,10 +710,15 @@ fn fmt_when<'a, 'buf>( if index != 0 { if is_multiline_patterns { buf.newline(); - buf.indent(indent + INDENT); + // Indent an extra level for the `|`; + // otherwise it'll be at the start of the line, + // and will be incorrectly parsed as a pattern + buf.indent(indent + INDENT + INDENT); + buf.push('|'); + } else { + buf.push_str(" |"); } - buf.push_str(" |"); buf.spaces(1); } diff --git a/crates/compiler/fmt/src/pattern.rs b/crates/compiler/fmt/src/pattern.rs index 73553bce01..4c5f3de9e7 100644 --- a/crates/compiler/fmt/src/pattern.rs +++ b/crates/compiler/fmt/src/pattern.rs @@ -162,10 +162,6 @@ impl<'a> Formattable for Pattern<'a> { // Space SpaceBefore(sub_pattern, spaces) => { - if has_inline_comment(spaces.iter()) { - buf.spaces(1); - } - if !sub_pattern.is_multiline() { fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent) } else { @@ -177,7 +173,7 @@ impl<'a> Formattable for Pattern<'a> { SpaceAfter(sub_pattern, spaces) => { sub_pattern.format_with_options(buf, parens, newlines, indent); - if has_inline_comment(spaces.iter()) { + if starts_with_inline_comment(spaces.iter()) { buf.spaces(1); } @@ -206,8 +202,11 @@ impl<'a> Formattable for Pattern<'a> { } } -fn has_inline_comment<'a, I: IntoIterator>>(spaces: I) -> bool { - spaces - .into_iter() - .any(|space| matches!(space, CommentOrNewline::LineComment(_))) +fn starts_with_inline_comment<'a, I: IntoIterator>>( + spaces: I, +) -> bool { + match spaces.into_iter().next() { + Some(space) => matches!(space, CommentOrNewline::LineComment(_)), + None => false, + } } diff --git a/crates/compiler/fmt/tests/test_fmt.rs b/crates/compiler/fmt/tests/test_fmt.rs index 0a71cbdc84..b647603f34 100644 --- a/crates/compiler/fmt/tests/test_fmt.rs +++ b/crates/compiler/fmt/tests/test_fmt.rs @@ -3379,7 +3379,7 @@ mod test_fmt { r#" when 0 is 1 # comment - | 2 -> "a" + | 2 -> "a" _ -> "b" "# )); @@ -3477,18 +3477,18 @@ mod test_fmt { r#" when b is 1 - | 2 - | 3 -> + | 2 + | 3 -> 4 5 | 6 | 7 -> 8 9 - | 10 -> 11 + | 10 -> 11 12 | 13 -> when c is 14 | 15 -> 16 17 - | 18 -> 19 + | 18 -> 19 20 -> 21 "# ), @@ -3510,7 +3510,7 @@ mod test_fmt { when b is 3 -> 4 9 - | 8 -> 9 + | 8 -> 9 "# ), ); From d1ba89f97fdb6112b4d985156884cbea019c2658 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Sun, 3 Jul 2022 22:38:26 -0400 Subject: [PATCH 58/66] Fix an extraneous newline in formatting --- crates/compiler/fmt/src/expr.rs | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/crates/compiler/fmt/src/expr.rs b/crates/compiler/fmt/src/expr.rs index e45b15ace9..499c0ad46c 100644 --- a/crates/compiler/fmt/src/expr.rs +++ b/crates/compiler/fmt/src/expr.rs @@ -699,15 +699,29 @@ fn fmt_when<'a, 'buf>( buf.push_str("is"); buf.newline(); - let mut it = branches.iter().peekable(); - while let Some(branch) = it.next() { + let mut it = branches.iter().enumerate().peekable(); + + while let Some((branch_index, branch)) = it.next() { let expr = &branch.value; let patterns = &branch.patterns; let is_multiline_expr = expr.is_multiline(); let is_multiline_patterns = is_when_patterns_multiline(branch); for (index, pattern) in patterns.iter().enumerate() { - if index != 0 { + if index == 0 { + match &pattern.value { + Pattern::SpaceBefore(sub_pattern, spaces) if branch_index == 0 => { + // Never include extra newlines before the first branch. + // Instead, write the comments and that's it. + fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent + INDENT); + + fmt_pattern(buf, sub_pattern, indent + INDENT, Parens::NotNeeded); + } + other => { + fmt_pattern(buf, other, indent + INDENT, Parens::NotNeeded); + } + } + } else { if is_multiline_patterns { buf.newline(); // Indent an extra level for the `|`; @@ -720,9 +734,9 @@ fn fmt_when<'a, 'buf>( } buf.spaces(1); - } - fmt_pattern(buf, &pattern.value, indent + INDENT, Parens::NotNeeded); + fmt_pattern(buf, &pattern.value, indent + INDENT, Parens::NotNeeded); + } } if let Some(guard_expr) = &branch.guard { From cab096b4e17dbefb5d4ab1751bad0bbf842972dc Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Sun, 3 Jul 2022 22:43:21 -0400 Subject: [PATCH 59/66] Don't print extra newline in multiline `|` patterns --- crates/compiler/fmt/src/expr.rs | 2 +- crates/compiler/fmt/src/lib.rs | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/crates/compiler/fmt/src/expr.rs b/crates/compiler/fmt/src/expr.rs index 499c0ad46c..468c1c2d8c 100644 --- a/crates/compiler/fmt/src/expr.rs +++ b/crates/compiler/fmt/src/expr.rs @@ -723,7 +723,7 @@ fn fmt_when<'a, 'buf>( } } else { if is_multiline_patterns { - buf.newline(); + buf.ensure_ends_in_newline(); // Indent an extra level for the `|`; // otherwise it'll be at the start of the line, // and will be incorrectly parsed as a pattern diff --git a/crates/compiler/fmt/src/lib.rs b/crates/compiler/fmt/src/lib.rs index 1e15f605b8..782f47b3f0 100644 --- a/crates/compiler/fmt/src/lib.rs +++ b/crates/compiler/fmt/src/lib.rs @@ -89,6 +89,14 @@ impl<'a> Buf<'a> { self.beginning_of_line = true; } + /// Ensures the current buffer ends in a newline, if it didn't already. + /// Doesn't add a newline if the buffer already ends in one. + pub fn ensure_ends_in_newline(&mut self) { + if !self.text.ends_with('\n') { + self.newline() + } + } + fn flush_spaces(&mut self) { if self.spaces_to_flush > 0 { for _ in 0..self.spaces_to_flush { From b9532cadaa3a37644e094df907f0831048d48e65 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Sun, 3 Jul 2022 22:47:41 -0400 Subject: [PATCH 60/66] clippy --- crates/compiler/fmt/src/pattern.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/compiler/fmt/src/pattern.rs b/crates/compiler/fmt/src/pattern.rs index 4c5f3de9e7..43bbc298b9 100644 --- a/crates/compiler/fmt/src/pattern.rs +++ b/crates/compiler/fmt/src/pattern.rs @@ -205,8 +205,8 @@ impl<'a> Formattable for Pattern<'a> { fn starts_with_inline_comment<'a, I: IntoIterator>>( spaces: I, ) -> bool { - match spaces.into_iter().next() { - Some(space) => matches!(space, CommentOrNewline::LineComment(_)), - None => false, - } + matches!( + spaces.into_iter().next(), + Some(CommentOrNewline::LineComment(_)) + ) } From 02ec30425c28383168027fa70bb6242ac10c6d60 Mon Sep 17 00:00:00 2001 From: Brian Carroll Date: Mon, 4 Jul 2022 09:29:36 +0100 Subject: [PATCH 61/66] wasm: Implement Str.split --- crates/compiler/builtins/bitcode/src/main.zig | 1 + crates/compiler/builtins/bitcode/src/str.zig | 15 + crates/compiler/builtins/src/bitcode.rs | 1 + crates/compiler/gen_wasm/src/lib.rs | 3 +- crates/compiler/gen_wasm/src/low_level.rs | 10 +- crates/compiler/test_gen/src/gen_str.rs | 10 +- crates/compiler/test_gen/src/wasm_str.rs | 486 +++++++++--------- 7 files changed, 267 insertions(+), 259 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/main.zig b/crates/compiler/builtins/bitcode/src/main.zig index 2346244cc4..1c6fd8e7d0 100644 --- a/crates/compiler/builtins/bitcode/src/main.zig +++ b/crates/compiler/builtins/bitcode/src/main.zig @@ -143,6 +143,7 @@ const str = @import("str.zig"); comptime { exportStrFn(str.init, "init"); exportStrFn(str.strToScalarsC, "to_scalars"); + exportStrFn(str.strSplit, "str_split"); exportStrFn(str.strSplitInPlaceC, "str_split_in_place"); exportStrFn(str.countSegments, "count_segments"); exportStrFn(str.countGraphemeClusters, "count_grapheme_clusters"); diff --git a/crates/compiler/builtins/bitcode/src/str.zig b/crates/compiler/builtins/bitcode/src/str.zig index 13503a1658..a8732aa2e1 100644 --- a/crates/compiler/builtins/bitcode/src/str.zig +++ b/crates/compiler/builtins/bitcode/src/str.zig @@ -744,6 +744,21 @@ fn strFromFloatHelp(comptime T: type, float: T) RocStr { } // Str.split + +// For dev backends +pub fn strSplit(string: RocStr, delimiter: RocStr) callconv(.C) RocList { + const segment_count = countSegments(string, delimiter); + const list = RocList.allocate(@alignOf(RocStr), segment_count, @sizeOf(RocStr)); + + if (list.bytes) |bytes| { + const strings = @ptrCast([*]RocStr, @alignCast(@alignOf(RocStr), bytes)); + strSplitInPlace(strings, string, delimiter); + } + + return list; +} + +// For LLVM backend pub fn strSplitInPlaceC(opt_array: ?[*]RocStr, string: RocStr, delimiter: RocStr) callconv(.C) void { if (opt_array) |array| { return @call(.{ .modifier = always_inline }, strSplitInPlace, .{ array, string, delimiter }); diff --git a/crates/compiler/builtins/src/bitcode.rs b/crates/compiler/builtins/src/bitcode.rs index 24a07a3f76..bef34989fa 100644 --- a/crates/compiler/builtins/src/bitcode.rs +++ b/crates/compiler/builtins/src/bitcode.rs @@ -310,6 +310,7 @@ pub const STR_INIT: &str = "roc_builtins.str.init"; pub const STR_COUNT_SEGMENTS: &str = "roc_builtins.str.count_segments"; pub const STR_CONCAT: &str = "roc_builtins.str.concat"; pub const STR_JOIN_WITH: &str = "roc_builtins.str.joinWith"; +pub const STR_STR_SPLIT: &str = "roc_builtins.str.str_split"; pub const STR_STR_SPLIT_IN_PLACE: &str = "roc_builtins.str.str_split_in_place"; pub const STR_TO_SCALARS: &str = "roc_builtins.str.to_scalars"; pub const STR_COUNT_GRAPEHEME_CLUSTERS: &str = "roc_builtins.str.count_grapheme_clusters"; diff --git a/crates/compiler/gen_wasm/src/lib.rs b/crates/compiler/gen_wasm/src/lib.rs index 4afc37c7fe..1d00e29f64 100644 --- a/crates/compiler/gen_wasm/src/lib.rs +++ b/crates/compiler/gen_wasm/src/lib.rs @@ -184,7 +184,8 @@ pub fn build_app_module<'a>( } let (module, called_preload_fns) = backend.finalize(); - let main_function_index = maybe_main_fn_index.unwrap(); + let main_function_index = + maybe_main_fn_index.expect("The app must expose at least one value to the host"); (module, called_preload_fns, main_function_index) } diff --git a/crates/compiler/gen_wasm/src/low_level.rs b/crates/compiler/gen_wasm/src/low_level.rs index 2c6e24cb7d..f06e6659be 100644 --- a/crates/compiler/gen_wasm/src/low_level.rs +++ b/crates/compiler/gen_wasm/src/low_level.rs @@ -235,15 +235,7 @@ impl<'a> LowLevelCall<'a> { self.load_args_and_call_zig(backend, bitcode::STR_STARTS_WITH_SCALAR) } StrEndsWith => self.load_args_and_call_zig(backend, bitcode::STR_ENDS_WITH), - StrSplit => { - // LLVM implementation (build_str.rs) does the following - // 1. Call bitcode::STR_COUNT_SEGMENTS - // 2. Allocate a `List Str` - // 3. Call bitcode::STR_STR_SPLIT_IN_PLACE - // 4. Write the elements and length of the List - // To do this here, we need full access to WasmBackend, or we could make a Zig wrapper - todo!("{:?}", self.lowlevel); - } + StrSplit => self.load_args_and_call_zig(backend, bitcode::STR_STR_SPLIT), StrCountGraphemes => { self.load_args_and_call_zig(backend, bitcode::STR_COUNT_GRAPEHEME_CLUSTERS) } diff --git a/crates/compiler/test_gen/src/gen_str.rs b/crates/compiler/test_gen/src/gen_str.rs index 20b0536499..d38efb4063 100644 --- a/crates/compiler/test_gen/src/gen_str.rs +++ b/crates/compiler/test_gen/src/gen_str.rs @@ -25,7 +25,7 @@ fn str_split_empty_delimiter() { "# ), 1, - i64 + usize ); assert_evals_to!( @@ -41,7 +41,7 @@ fn str_split_empty_delimiter() { "# ), 3, - i64 + usize ); } @@ -55,7 +55,7 @@ fn str_split_bigger_delimiter_small_str() { "# ), 1, - i64 + usize ); assert_evals_to!( @@ -71,7 +71,7 @@ fn str_split_bigger_delimiter_small_str() { "# ), 3, - i64 + usize ); } @@ -210,7 +210,7 @@ fn str_split_small_str_big_delimiter() { "# ), 3, - i64 + usize ); assert_evals_to!( diff --git a/crates/compiler/test_gen/src/wasm_str.rs b/crates/compiler/test_gen/src/wasm_str.rs index 52bcf50293..a28fc54d9a 100644 --- a/crates/compiler/test_gen/src/wasm_str.rs +++ b/crates/compiler/test_gen/src/wasm_str.rs @@ -14,257 +14,255 @@ use crate::helpers::wasm::assert_evals_to; use indoc::indoc; use roc_std::{RocList, RocStr}; -// #[test] -// fn str_split_empty_delimiter() { -// assert_evals_to!( -// indoc!( -// r#" -// List.len (Str.split "hello" "") -// "# -// ), -// 1, -// i64 -// ); +#[test] +fn str_split_empty_delimiter() { + assert_evals_to!( + indoc!( + r#" + List.len (Str.split "hello" "") + "# + ), + 1, + usize + ); +} -// assert_evals_to!( -// indoc!( -// r#" -// when List.first (Str.split "JJJ" "") is -// Ok str -> -// Str.countGraphemes str +// This test produces an app that exposes nothing to the host! +#[test] +#[ignore] +fn str_split_empty_delimiter_broken() { + assert_evals_to!( + indoc!( + r#" + when List.first (Str.split "JJJ" "") is + Ok str -> + Str.countGraphemes str -// _ -> -// -1 + _ -> + -1 -// "# -// ), -// 3, -// i64 -// ); -// } + "# + ), + 3, + usize + ); +} -// #[test] -// fn str_split_bigger_delimiter_small_str() { -// assert_evals_to!( -// indoc!( -// r#" -// List.len (Str.split "hello" "JJJJ there") -// "# -// ), -// 1, -// i64 -// ); +#[test] +fn str_split_bigger_delimiter_small_str() { + assert_evals_to!( + indoc!( + r#" + List.len (Str.split "hello" "JJJJ there") + "# + ), + 1, + usize + ); +} -// assert_evals_to!( -// indoc!( -// r#" -// when List.first (Str.split "JJJ" "JJJJ there") is -// Ok str -> -// Str.countGraphemes str +// This test produces an app that exposes nothing to the host! +#[test] +#[ignore] +fn str_split_bigger_delimiter_small_str_broken() { + assert_evals_to!( + indoc!( + r#" + when List.first (Str.split "JJJ" "JJJJ there") is + Ok str -> + Str.countGraphemes str -// _ -> -// -1 + _ -> + -1 -// "# -// ), -// 3, -// i64 -// ); -// } + "# + ), + 3, + usize + ); +} -// #[test] -// fn str_split_str_concat_repeated() { -// assert_evals_to!( -// indoc!( -// r#" -// when List.first (Str.split "JJJJJ" "JJJJ there") is -// Ok str -> -// str -// |> Str.concat str -// |> Str.concat str -// |> Str.concat str -// |> Str.concat str +#[test] +fn str_split_str_concat_repeated() { + assert_evals_to!( + indoc!( + r#" + when List.first (Str.split "JJJJJ" "JJJJ there") is + Ok str -> + str + |> Str.concat str + |> Str.concat str + |> Str.concat str + |> Str.concat str -// _ -> -// "Not Str!" + _ -> + "Not Str!" -// "# -// ), -// RocStr::from_slice_unchecked(b"JJJJJJJJJJJJJJJJJJJJJJJJJ"), -// RocStr -// ); -// } + "# + ), + RocStr::from("JJJJJJJJJJJJJJJJJJJJJJJJJ"), + RocStr + ); +} -// #[test] -// fn str_split_small_str_bigger_delimiter() { -// assert_evals_to!( -// indoc!( -// r#" -// when -// List.first -// (Str.split "JJJ" "0123456789abcdefghi") -// is -// Ok str -> str -// _ -> "" -// "# -// ), -// RocStr::from_slice_unchecked(b"JJJ"), -// RocStr -// ); -// } +#[test] +fn str_split_small_str_bigger_delimiter() { + assert_evals_to!( + indoc!( + r#" + when + List.first + (Str.split "JJJ" "0123456789abcdefghi") + is + Ok str -> str + _ -> "" + "# + ), + RocStr::from("JJJ"), + RocStr + ); +} -// #[test] -// fn str_split_big_str_small_delimiter() { -// assert_evals_to!( -// indoc!( -// r#" -// Str.split "01234567789abcdefghi?01234567789abcdefghi" "?" -// "# -// ), -// RocList::from_slice(&[ -// RocStr::from_slice_unchecked(b"01234567789abcdefghi"), -// RocStr::from_slice_unchecked(b"01234567789abcdefghi") -// ]), -// RocList -// ); +#[test] +fn str_split_big_str_small_delimiter() { + assert_evals_to!( + indoc!( + r#" + Str.split "01234567789abcdefghi?01234567789abcdefghi" "?" + "# + ), + RocList::from_slice(&[ + RocStr::from("01234567789abcdefghi"), + RocStr::from("01234567789abcdefghi") + ]), + RocList + ); -// assert_evals_to!( -// indoc!( -// r#" -// Str.split "01234567789abcdefghi 3ch 01234567789abcdefghi" "3ch" -// "# -// ), -// RocList::from_slice(&[ -// RocStr::from_slice_unchecked(b"01234567789abcdefghi "), -// RocStr::from_slice_unchecked(b" 01234567789abcdefghi") -// ]), -// RocList -// ); -// } + assert_evals_to!( + indoc!( + r#" + Str.split "01234567789abcdefghi 3ch 01234567789abcdefghi" "3ch" + "# + ), + RocList::from_slice(&[ + RocStr::from("01234567789abcdefghi "), + RocStr::from(" 01234567789abcdefghi") + ]), + RocList + ); +} -// #[test] -// fn str_split_small_str_small_delimiter() { -// assert_evals_to!( -// indoc!( -// r#" -// Str.split "J!J!J" "!" -// "# -// ), -// RocList::from_slice(&[ -// RocStr::from_slice_unchecked(b"J"), -// RocStr::from_slice_unchecked(b"J"), -// RocStr::from_slice_unchecked(b"J") -// ]), -// RocList -// ); -// } +#[test] +fn str_split_small_str_small_delimiter() { + assert_evals_to!( + indoc!( + r#" + Str.split "J!J!J" "!" + "# + ), + RocList::from_slice(&[RocStr::from("J"), RocStr::from("J"), RocStr::from("J")]), + RocList + ); +} -// #[test] -// fn str_split_bigger_delimiter_big_strs() { -// assert_evals_to!( -// indoc!( -// r#" -// Str.split -// "string to split is shorter" -// "than the delimiter which happens to be very very long" -// "# -// ), -// RocList::from_slice(&[RocStr::from_slice_unchecked(b"string to split is shorter")]), -// RocList -// ); -// } +#[test] +fn str_split_bigger_delimiter_big_strs() { + assert_evals_to!( + indoc!( + r#" + Str.split + "string to split is shorter" + "than the delimiter which happens to be very very long" + "# + ), + RocList::from_slice(&[RocStr::from("string to split is shorter")]), + RocList + ); +} -// #[test] -// fn str_split_empty_strs() { -// assert_evals_to!( -// indoc!( -// r#" -// Str.split "" "" -// "# -// ), -// RocList::from_slice(&[RocStr::from_slice_unchecked(b"")]), -// RocList -// ); -// } +#[test] +fn str_split_empty_strs() { + assert_evals_to!( + indoc!( + r#" + Str.split "" "" + "# + ), + RocList::from_slice(&[RocStr::from("")]), + RocList + ); +} -// #[test] -// fn str_split_minimal_example() { -// assert_evals_to!( -// indoc!( -// r#" -// Str.split "a," "," -// "# -// ), -// RocList::from_slice(&[RocStr::from_slice_unchecked(b"a"), RocStr::from_slice_unchecked(b"")]), -// RocList -// ) -// } +#[test] +fn str_split_minimal_example() { + assert_evals_to!( + indoc!( + r#" + Str.split "a," "," + "# + ), + RocList::from_slice(&[RocStr::from("a"), RocStr::from("")]), + RocList + ) +} -// #[test] -// fn str_split_small_str_big_delimiter() { -// assert_evals_to!( -// indoc!( -// r#" -// Str.split -// "1---- ---- ---- ---- ----2---- ---- ---- ---- ----" -// "---- ---- ---- ---- ----" -// |> List.len -// "# -// ), -// 3, -// i64 -// ); +#[test] +fn str_split_small_str_big_delimiter() { + assert_evals_to!( + indoc!( + r#" + Str.split + "1---- ---- ---- ---- ----2---- ---- ---- ---- ----" + "---- ---- ---- ---- ----" + |> List.len + "# + ), + 3, + usize + ); -// assert_evals_to!( -// indoc!( -// r#" -// Str.split -// "1---- ---- ---- ---- ----2---- ---- ---- ---- ----" -// "---- ---- ---- ---- ----" -// "# -// ), -// RocList::from_slice(&[ -// RocStr::from_slice_unchecked(b"1"), -// RocStr::from_slice_unchecked(b"2"), -// RocStr::from_slice_unchecked(b"") -// ]), -// RocList -// ); -// } + assert_evals_to!( + indoc!( + r#" + Str.split + "1---- ---- ---- ---- ----2---- ---- ---- ---- ----" + "---- ---- ---- ---- ----" + "# + ), + RocList::from_slice(&[RocStr::from("1"), RocStr::from("2"), RocStr::from("")]), + RocList + ); +} -// #[test] -// fn str_split_small_str_20_char_delimiter() { -// assert_evals_to!( -// indoc!( -// r#" -// Str.split -// "3|-- -- -- -- -- -- |4|-- -- -- -- -- -- |" -// "|-- -- -- -- -- -- |" -// "# -// ), -// RocList::from_slice(&[ -// RocStr::from_slice_unchecked(b"3"), -// RocStr::from_slice_unchecked(b"4"), -// RocStr::from_slice_unchecked(b"") -// ]), -// RocList -// ); -// } +#[test] +fn str_split_small_str_20_char_delimiter() { + assert_evals_to!( + indoc!( + r#" + Str.split + "3|-- -- -- -- -- -- |4|-- -- -- -- -- -- |" + "|-- -- -- -- -- -- |" + "# + ), + RocList::from_slice(&[RocStr::from("3"), RocStr::from("4"), RocStr::from("")]), + RocList + ); +} -// #[test] -// fn str_concat_big_to_big() { -// assert_evals_to!( -// indoc!( -// r#" -// Str.concat -// "First string that is fairly long. Longer strings make for different errors. " -// "Second string that is also fairly long. Two long strings test things that might not appear with short strings." -// "# -// ), -// RocStr::from_slice_unchecked(b"First string that is fairly long. Longer strings make for different errors. Second string that is also fairly long. Two long strings test things that might not appear with short strings."), -// RocStr -// ); -// } +#[test] +fn str_concat_big_to_big() { + assert_evals_to!( + indoc!( + r#" + Str.concat + "First string that is fairly long. Longer strings make for different errors. " + "Second string that is also fairly long. Two long strings test things that might not appear with short strings." + "# + ), + RocStr::from("First string that is fairly long. Longer strings make for different errors. Second string that is also fairly long. Two long strings test things that might not appear with short strings."), + RocStr + ); +} #[test] #[cfg(any(feature = "gen-wasm"))] @@ -498,7 +496,7 @@ fn str_starts_with_false_small_str() { // Err _ -> "" // "# // ), -// roc_std::RocStr::from_slice_unchecked("a".as_bytes()), +// roc_std::RocStr::from("a"), // roc_std::RocStr // ); // } @@ -513,7 +511,7 @@ fn str_starts_with_false_small_str() { // Err _ -> "" // "# // ), -// roc_std::RocStr::from_slice_unchecked("abc~".as_bytes()), +// roc_std::RocStr::from("abc~"), // roc_std::RocStr // ); // } @@ -528,7 +526,7 @@ fn str_starts_with_false_small_str() { // Err _ -> "" // "# // ), -// roc_std::RocStr::from_slice_unchecked("∆".as_bytes()), +// roc_std::RocStr::from("∆"), // roc_std::RocStr // ); // } @@ -543,7 +541,7 @@ fn str_starts_with_false_small_str() { // Err _ -> "" // "# // ), -// roc_std::RocStr::from_slice_unchecked("∆œ¬".as_bytes()), +// roc_std::RocStr::from("∆œ¬"), // roc_std::RocStr // ); // } @@ -558,7 +556,7 @@ fn str_starts_with_false_small_str() { // Err _ -> "" // "# // ), -// roc_std::RocStr::from_slice_unchecked("💖".as_bytes()), +// roc_std::RocStr::from("💖"), // roc_std::RocStr // ); // } @@ -573,7 +571,7 @@ fn str_starts_with_false_small_str() { // Err _ -> "" // "# // ), -// roc_std::RocStr::from_slice_unchecked("💖🤠🚀".as_bytes()), +// roc_std::RocStr::from("💖🤠🚀"), // roc_std::RocStr // ); // } @@ -588,7 +586,7 @@ fn str_starts_with_false_small_str() { // Err _ -> "" // "# // ), -// roc_std::RocStr::from_slice_unchecked("💖b∆".as_bytes()), +// roc_std::RocStr::from("💖b∆"), // roc_std::RocStr // ); // } @@ -607,7 +605,7 @@ fn str_starts_with_false_small_str() { // _ -> "" // "# // ), -// roc_std::RocStr::from_slice_unchecked("a".as_bytes()), +// roc_std::RocStr::from("a"), // roc_std::RocStr // ); // } @@ -626,7 +624,7 @@ fn str_starts_with_false_small_str() { // _ -> "" // "# // ), -// roc_std::RocStr::from_slice_unchecked("a".as_bytes()), +// roc_std::RocStr::from("a"), // roc_std::RocStr // ); // } @@ -645,7 +643,7 @@ fn str_starts_with_false_small_str() { // _ -> "" // "# // ), -// roc_std::RocStr::from_slice_unchecked("a".as_bytes()), +// roc_std::RocStr::from("a"), // roc_std::RocStr // ); // } @@ -664,7 +662,7 @@ fn str_starts_with_false_small_str() { // _ -> "" // "# // ), -// roc_std::RocStr::from_slice_unchecked("a".as_bytes()), +// roc_std::RocStr::from("a"), // roc_std::RocStr // ); // } @@ -683,7 +681,7 @@ fn str_starts_with_false_small_str() { // _ -> "" // "# // ), -// roc_std::RocStr::from_slice_unchecked("a".as_bytes()), +// roc_std::RocStr::from("a"), // roc_std::RocStr // ); // } @@ -702,7 +700,7 @@ fn str_starts_with_false_small_str() { // _ -> "" // "# // ), -// roc_std::RocStr::from_slice_unchecked("a".as_bytes()), +// roc_std::RocStr::from("a"), // roc_std::RocStr // ); // } @@ -744,7 +742,7 @@ fn str_equality() { // printExpr expr // "# // ), -// RocStr::from_slice_unchecked(b"Add (Add (Val 3) (Val 1)) (Add (Val 1) (Var 1))"), +// RocStr::from("Add (Add (Val 3) (Val 1)) (Add (Val 1) (Var 1))"), // RocStr // ); // } From a342f028241ee24bdd823c53f0cd4d5c93287d1b Mon Sep 17 00:00:00 2001 From: Brian Carroll Date: Mon, 4 Jul 2022 10:56:28 +0100 Subject: [PATCH 62/66] wasm: fix Str.fromUtf8 --- crates/compiler/can/src/builtins.rs | 2 +- crates/compiler/gen_wasm/src/lib.rs | 2 +- crates/compiler/gen_wasm/src/low_level.rs | 31 +- crates/compiler/test_gen/src/wasm_str.rs | 442 ++++++++++------------ 4 files changed, 238 insertions(+), 239 deletions(-) diff --git a/crates/compiler/can/src/builtins.rs b/crates/compiler/can/src/builtins.rs index 6d123a9a24..180b144e43 100644 --- a/crates/compiler/can/src/builtins.rs +++ b/crates/compiler/can/src/builtins.rs @@ -1803,7 +1803,7 @@ fn str_from_utf8(symbol: Symbol, var_store: &mut VarStore) -> Def { // Ok arg_2.str // else // # problem - // Err (BadUtf8 { byteIndex: arg_2.byteIndex, problem : arg_2.problem }) + // Err (BadUtf8 arg_2.problem arg_2.byteIndex) let def = crate::def::Def { loc_pattern: no_region(Pattern::Identifier(Symbol::ARG_2)), diff --git a/crates/compiler/gen_wasm/src/lib.rs b/crates/compiler/gen_wasm/src/lib.rs index 1d00e29f64..7b5972f371 100644 --- a/crates/compiler/gen_wasm/src/lib.rs +++ b/crates/compiler/gen_wasm/src/lib.rs @@ -270,6 +270,6 @@ pub const DEBUG_SETTINGS: WasmDebugSettings = WasmDebugSettings { let_stmt_ir: false && cfg!(debug_assertions), instructions: false && cfg!(debug_assertions), storage_map: false && cfg!(debug_assertions), - keep_test_binary: false && cfg!(debug_assertions), + keep_test_binary: true && cfg!(debug_assertions), skip_dead_code_elim: false && cfg!(debug_assertions), }; diff --git a/crates/compiler/gen_wasm/src/low_level.rs b/crates/compiler/gen_wasm/src/low_level.rs index f06e6659be..cd2ee66f64 100644 --- a/crates/compiler/gen_wasm/src/low_level.rs +++ b/crates/compiler/gen_wasm/src/low_level.rs @@ -260,7 +260,36 @@ impl<'a> LowLevelCall<'a> { } StrFromInt => self.num_to_str(backend), StrFromFloat => self.num_to_str(backend), - StrFromUtf8 => self.load_args_and_call_zig(backend, bitcode::STR_FROM_UTF8), + StrFromUtf8 => { + /* + Low-level op returns a struct with all the data for both Ok and Err. + Roc AST wrapper converts this to a tag union, with app-dependent tag IDs. + + fromUtf8C(arg: RocList, update_mode: UpdateMode, output: *FromUtf8Result) callconv(.C) void + arg: RocList i64, i32 + update_mode: UpdateMode i32 + output: *FromUtf8Result i32 + */ + + let (ret_ptr, ret_offset) = match &self.ret_storage { + StoredValue::StackMemory { location, .. } => { + location.local_and_offset(backend.storage.stack_frame_pointer) + } + _ => internal_error!("Low-level op StrFromUtf8 should return a struct"), + }; + + // Return pointer is the last arg rather than the first, so we can't use the usual helper. + backend + .storage + .load_symbol_zig(&mut backend.code_builder, self.arguments[0]); + backend.code_builder.i32_const(UPDATE_MODE_IMMUTABLE); + backend.code_builder.get_local(ret_ptr); + if ret_offset != 0 { + backend.code_builder.i32_const(ret_offset as i32); + backend.code_builder.i32_add(); + } + backend.call_host_fn_after_loading_args(bitcode::STR_FROM_UTF8, 4, false); + } StrTrimLeft => self.load_args_and_call_zig(backend, bitcode::STR_TRIM_LEFT), StrTrimRight => self.load_args_and_call_zig(backend, bitcode::STR_TRIM_RIGHT), StrFromUtf8Range => self.load_args_and_call_zig(backend, bitcode::STR_FROM_UTF8_RANGE), diff --git a/crates/compiler/test_gen/src/wasm_str.rs b/crates/compiler/test_gen/src/wasm_str.rs index a28fc54d9a..bbc76043ac 100644 --- a/crates/compiler/test_gen/src/wasm_str.rs +++ b/crates/compiler/test_gen/src/wasm_str.rs @@ -486,224 +486,224 @@ fn str_starts_with_false_small_str() { assert_evals_to!(r#"Str.startsWith "1234" "23""#, false, bool); } -// #[test] -// fn str_from_utf8_pass_single_ascii() { -// assert_evals_to!( -// indoc!( -// r#" -// when Str.fromUtf8 [97] is -// Ok val -> val -// Err _ -> "" -// "# -// ), -// roc_std::RocStr::from("a"), -// roc_std::RocStr -// ); -// } +#[test] +fn str_from_utf8_pass_single_ascii() { + assert_evals_to!( + indoc!( + r#" + when Str.fromUtf8 [97] is + Ok val -> val + Err _ -> "" + "# + ), + roc_std::RocStr::from("a"), + roc_std::RocStr + ); +} -// #[test] -// fn str_from_utf8_pass_many_ascii() { -// assert_evals_to!( -// indoc!( -// r#" -// when Str.fromUtf8 [97, 98, 99, 0x7E] is -// Ok val -> val -// Err _ -> "" -// "# -// ), -// roc_std::RocStr::from("abc~"), -// roc_std::RocStr -// ); -// } +#[test] +fn str_from_utf8_pass_many_ascii() { + assert_evals_to!( + indoc!( + r#" + when Str.fromUtf8 [97, 98, 99, 0x7E] is + Ok val -> val + Err _ -> "" + "# + ), + roc_std::RocStr::from("abc~"), + roc_std::RocStr + ); +} -// #[test] -// fn str_from_utf8_pass_single_unicode() { -// assert_evals_to!( -// indoc!( -// r#" -// when Str.fromUtf8 [0xE2, 0x88, 0x86] is -// Ok val -> val -// Err _ -> "" -// "# -// ), -// roc_std::RocStr::from("∆"), -// roc_std::RocStr -// ); -// } +#[test] +fn str_from_utf8_pass_single_unicode() { + assert_evals_to!( + indoc!( + r#" + when Str.fromUtf8 [0xE2, 0x88, 0x86] is + Ok val -> val + Err _ -> "" + "# + ), + roc_std::RocStr::from("∆"), + roc_std::RocStr + ); +} -// #[test] -// fn str_from_utf8_pass_many_unicode() { -// assert_evals_to!( -// indoc!( -// r#" -// when Str.fromUtf8 [0xE2, 0x88, 0x86, 0xC5, 0x93, 0xC2, 0xAC] is -// Ok val -> val -// Err _ -> "" -// "# -// ), -// roc_std::RocStr::from("∆œ¬"), -// roc_std::RocStr -// ); -// } +#[test] +fn str_from_utf8_pass_many_unicode() { + assert_evals_to!( + indoc!( + r#" + when Str.fromUtf8 [0xE2, 0x88, 0x86, 0xC5, 0x93, 0xC2, 0xAC] is + Ok val -> val + Err _ -> "" + "# + ), + roc_std::RocStr::from("∆œ¬"), + roc_std::RocStr + ); +} -// #[test] -// fn str_from_utf8_pass_single_grapheme() { -// assert_evals_to!( -// indoc!( -// r#" -// when Str.fromUtf8 [0xF0, 0x9F, 0x92, 0x96] is -// Ok val -> val -// Err _ -> "" -// "# -// ), -// roc_std::RocStr::from("💖"), -// roc_std::RocStr -// ); -// } +#[test] +fn str_from_utf8_pass_single_grapheme() { + assert_evals_to!( + indoc!( + r#" + when Str.fromUtf8 [0xF0, 0x9F, 0x92, 0x96] is + Ok val -> val + Err _ -> "" + "# + ), + roc_std::RocStr::from("💖"), + roc_std::RocStr + ); +} -// #[test] -// fn str_from_utf8_pass_many_grapheme() { -// assert_evals_to!( -// indoc!( -// r#" -// when Str.fromUtf8 [0xF0, 0x9F, 0x92, 0x96, 0xF0, 0x9F, 0xA4, 0xA0, 0xF0, 0x9F, 0x9A, 0x80] is -// Ok val -> val -// Err _ -> "" -// "# -// ), -// roc_std::RocStr::from("💖🤠🚀"), -// roc_std::RocStr -// ); -// } +#[test] +fn str_from_utf8_pass_many_grapheme() { + assert_evals_to!( + indoc!( + r#" + when Str.fromUtf8 [0xF0, 0x9F, 0x92, 0x96, 0xF0, 0x9F, 0xA4, 0xA0, 0xF0, 0x9F, 0x9A, 0x80] is + Ok val -> val + Err _ -> "" + "# + ), + roc_std::RocStr::from("💖🤠🚀"), + roc_std::RocStr + ); +} -// #[test] -// fn str_from_utf8_pass_all() { -// assert_evals_to!( -// indoc!( -// r#" -// when Str.fromUtf8 [0xF0, 0x9F, 0x92, 0x96, 98, 0xE2, 0x88, 0x86] is -// Ok val -> val -// Err _ -> "" -// "# -// ), -// roc_std::RocStr::from("💖b∆"), -// roc_std::RocStr -// ); -// } +#[test] +fn str_from_utf8_pass_all() { + assert_evals_to!( + indoc!( + r#" + when Str.fromUtf8 [0xF0, 0x9F, 0x92, 0x96, 98, 0xE2, 0x88, 0x86] is + Ok val -> val + Err _ -> "" + "# + ), + roc_std::RocStr::from("💖b∆"), + roc_std::RocStr + ); +} -// #[test] -// fn str_from_utf8_fail_invalid_start_byte() { -// assert_evals_to!( -// indoc!( -// r#" -// when Str.fromUtf8 [97, 98, 0x80, 99] is -// Err (BadUtf8 InvalidStartByte byteIndex) -> -// if byteIndex == 2 then -// "a" -// else -// "b" -// _ -> "" -// "# -// ), -// roc_std::RocStr::from("a"), -// roc_std::RocStr -// ); -// } +#[test] +fn str_from_utf8_fail_invalid_start_byte() { + assert_evals_to!( + indoc!( + r#" + when Str.fromUtf8 [97, 98, 0x80, 99] is + Err (BadUtf8 InvalidStartByte byteIndex) -> + if byteIndex == 2 then + "a" + else + "b" + _ -> "" + "# + ), + roc_std::RocStr::from("a"), + roc_std::RocStr + ); +} -// #[test] -// fn str_from_utf8_fail_unexpected_end_of_sequence() { -// assert_evals_to!( -// indoc!( -// r#" -// when Str.fromUtf8 [97, 98, 99, 0xC2] is -// Err (BadUtf8 UnexpectedEndOfSequence byteIndex) -> -// if byteIndex == 3 then -// "a" -// else -// "b" -// _ -> "" -// "# -// ), -// roc_std::RocStr::from("a"), -// roc_std::RocStr -// ); -// } +#[test] +fn str_from_utf8_fail_unexpected_end_of_sequence() { + assert_evals_to!( + indoc!( + r#" + when Str.fromUtf8 [97, 98, 99, 0xC2] is + Err (BadUtf8 UnexpectedEndOfSequence byteIndex) -> + if byteIndex == 3 then + "a" + else + "b" + _ -> "" + "# + ), + roc_std::RocStr::from("a"), + roc_std::RocStr + ); +} -// #[test] -// fn str_from_utf8_fail_expected_continuation() { -// assert_evals_to!( -// indoc!( -// r#" -// when Str.fromUtf8 [97, 98, 99, 0xC2, 0x00] is -// Err (BadUtf8 ExpectedContinuation byteIndex) -> -// if byteIndex == 3 then -// "a" -// else -// "b" -// _ -> "" -// "# -// ), -// roc_std::RocStr::from("a"), -// roc_std::RocStr -// ); -// } +#[test] +fn str_from_utf8_fail_expected_continuation() { + assert_evals_to!( + indoc!( + r#" + when Str.fromUtf8 [97, 98, 99, 0xC2, 0x00] is + Err (BadUtf8 ExpectedContinuation byteIndex) -> + if byteIndex == 3 then + "a" + else + "b" + _ -> "" + "# + ), + roc_std::RocStr::from("a"), + roc_std::RocStr + ); +} -// #[test] -// fn str_from_utf8_fail_overlong_encoding() { -// assert_evals_to!( -// indoc!( -// r#" -// when Str.fromUtf8 [97, 0xF0, 0x80, 0x80, 0x80] is -// Err (BadUtf8 OverlongEncoding byteIndex) -> -// if byteIndex == 1 then -// "a" -// else -// "b" -// _ -> "" -// "# -// ), -// roc_std::RocStr::from("a"), -// roc_std::RocStr -// ); -// } +#[test] +fn str_from_utf8_fail_overlong_encoding() { + assert_evals_to!( + indoc!( + r#" + when Str.fromUtf8 [97, 0xF0, 0x80, 0x80, 0x80] is + Err (BadUtf8 OverlongEncoding byteIndex) -> + if byteIndex == 1 then + "a" + else + "b" + _ -> "" + "# + ), + roc_std::RocStr::from("a"), + roc_std::RocStr + ); +} -// #[test] -// fn str_from_utf8_fail_codepoint_too_large() { -// assert_evals_to!( -// indoc!( -// r#" -// when Str.fromUtf8 [97, 0xF4, 0x90, 0x80, 0x80] is -// Err (BadUtf8 CodepointTooLarge byteIndex) -> -// if byteIndex == 1 then -// "a" -// else -// "b" -// _ -> "" -// "# -// ), -// roc_std::RocStr::from("a"), -// roc_std::RocStr -// ); -// } +#[test] +fn str_from_utf8_fail_codepoint_too_large() { + assert_evals_to!( + indoc!( + r#" + when Str.fromUtf8 [97, 0xF4, 0x90, 0x80, 0x80] is + Err (BadUtf8 CodepointTooLarge byteIndex) -> + if byteIndex == 1 then + "a" + else + "b" + _ -> "" + "# + ), + roc_std::RocStr::from("a"), + roc_std::RocStr + ); +} -// #[test] -// fn str_from_utf8_fail_surrogate_half() { -// assert_evals_to!( -// indoc!( -// r#" -// when Str.fromUtf8 [97, 98, 0xED, 0xA0, 0x80] is -// Err (BadUtf8 EncodesSurrogateHalf byteIndex) -> -// if byteIndex == 2 then -// "a" -// else -// "b" -// _ -> "" -// "# -// ), -// roc_std::RocStr::from("a"), -// roc_std::RocStr -// ); -// } +#[test] +fn str_from_utf8_fail_surrogate_half() { + assert_evals_to!( + indoc!( + r#" + when Str.fromUtf8 [97, 98, 0xED, 0xA0, 0x80] is + Err (BadUtf8 EncodesSurrogateHalf byteIndex) -> + if byteIndex == 2 then + "a" + else + "b" + _ -> "" + "# + ), + roc_std::RocStr::from("a"), + roc_std::RocStr + ); +} #[test] fn str_equality() { @@ -717,36 +717,6 @@ fn str_equality() { assert_evals_to!(r#""a" == "b""#, false, bool); } -// #[test] -// fn nested_recursive_literal() { -// assert_evals_to!( -// indoc!( -// r#" -// Expr : [Add Expr Expr, Val I64, Var I64] - -// expr : Expr -// expr = Add (Add (Val 3) (Val 1)) (Add (Val 1) (Var 1)) - -// printExpr : Expr -> Str -// printExpr = \e -> -// when e is -// Add a b -> -// "Add (" -// |> Str.concat (printExpr a) -// |> Str.concat ") (" -// |> Str.concat (printExpr b) -// |> Str.concat ")" -// Val v -> "Val " |> Str.concat (Num.toStr v) -// Var v -> "Var " |> Str.concat (Num.toStr v) - -// printExpr expr -// "# -// ), -// RocStr::from("Add (Add (Val 3) (Val 1)) (Add (Val 1) (Var 1))"), -// RocStr -// ); -// } - #[test] fn str_join_comma_small() { assert_evals_to!( From 60d29c2e25c17e4ea9d48b7caf158264dd834ab9 Mon Sep 17 00:00:00 2001 From: Brian Carroll Date: Mon, 4 Jul 2022 11:02:19 +0100 Subject: [PATCH 63/66] builtins: Move return pointer to first position for fromUtf8C --- crates/compiler/builtins/bitcode/src/str.zig | 2 +- .../compiler/gen_llvm/src/llvm/build_str.rs | 2 +- crates/compiler/gen_wasm/src/low_level.rs | 29 +++++++------------ 3 files changed, 12 insertions(+), 21 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/str.zig b/crates/compiler/builtins/bitcode/src/str.zig index a8732aa2e1..af101421fe 100644 --- a/crates/compiler/builtins/bitcode/src/str.zig +++ b/crates/compiler/builtins/bitcode/src/str.zig @@ -1552,7 +1552,7 @@ const CountAndStart = extern struct { start: usize, }; -pub fn fromUtf8C(arg: RocList, update_mode: UpdateMode, output: *FromUtf8Result) callconv(.C) void { +pub fn fromUtf8C(output: *FromUtf8Result, arg: RocList, update_mode: UpdateMode) callconv(.C) void { output.* = fromUtf8(arg, update_mode); } diff --git a/crates/compiler/gen_llvm/src/llvm/build_str.rs b/crates/compiler/gen_llvm/src/llvm/build_str.rs index 12b24b2dea..ce246320fa 100644 --- a/crates/compiler/gen_llvm/src/llvm/build_str.rs +++ b/crates/compiler/gen_llvm/src/llvm/build_str.rs @@ -194,9 +194,9 @@ pub fn str_from_utf8<'a, 'ctx, 'env>( call_void_bitcode_fn( env, &[ + result_ptr.into(), list_symbol_to_c_abi(env, scope, list).into(), pass_update_mode(env, update_mode), - result_ptr.into(), ], bitcode::STR_FROM_UTF8, ); diff --git a/crates/compiler/gen_wasm/src/low_level.rs b/crates/compiler/gen_wasm/src/low_level.rs index cd2ee66f64..02d7a10346 100644 --- a/crates/compiler/gen_wasm/src/low_level.rs +++ b/crates/compiler/gen_wasm/src/low_level.rs @@ -265,29 +265,20 @@ impl<'a> LowLevelCall<'a> { Low-level op returns a struct with all the data for both Ok and Err. Roc AST wrapper converts this to a tag union, with app-dependent tag IDs. - fromUtf8C(arg: RocList, update_mode: UpdateMode, output: *FromUtf8Result) callconv(.C) void + fromUtf8C(output: *FromUtf8Result, arg: RocList, update_mode: UpdateMode) callconv(.C) void + output: *FromUtf8Result i32 arg: RocList i64, i32 update_mode: UpdateMode i32 - output: *FromUtf8Result i32 */ - - let (ret_ptr, ret_offset) = match &self.ret_storage { - StoredValue::StackMemory { location, .. } => { - location.local_and_offset(backend.storage.stack_frame_pointer) - } - _ => internal_error!("Low-level op StrFromUtf8 should return a struct"), - }; - - // Return pointer is the last arg rather than the first, so we can't use the usual helper. - backend - .storage - .load_symbol_zig(&mut backend.code_builder, self.arguments[0]); + backend.storage.load_symbols_for_call( + backend.env.arena, + &mut backend.code_builder, + self.arguments, + self.ret_symbol, + &WasmLayout::new(&self.ret_layout), + CallConv::Zig, + ); backend.code_builder.i32_const(UPDATE_MODE_IMMUTABLE); - backend.code_builder.get_local(ret_ptr); - if ret_offset != 0 { - backend.code_builder.i32_const(ret_offset as i32); - backend.code_builder.i32_add(); - } backend.call_host_fn_after_loading_args(bitcode::STR_FROM_UTF8, 4, false); } StrTrimLeft => self.load_args_and_call_zig(backend, bitcode::STR_TRIM_LEFT), From e1d8d09472f17fe1710cc0d596511dcc597dbdae Mon Sep 17 00:00:00 2001 From: Brian Carroll Date: Mon, 4 Jul 2022 11:12:46 +0100 Subject: [PATCH 64/66] builtins: move the output argument of Zig fromUtf8RangeC to first position --- crates/compiler/builtins/bitcode/src/str.zig | 2 +- crates/compiler/can/src/builtins.rs | 2 +- crates/compiler/gen_llvm/src/llvm/build_str.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/str.zig b/crates/compiler/builtins/bitcode/src/str.zig index af101421fe..e41e44478f 100644 --- a/crates/compiler/builtins/bitcode/src/str.zig +++ b/crates/compiler/builtins/bitcode/src/str.zig @@ -1607,7 +1607,7 @@ inline fn fromUtf8(arg: RocList, update_mode: UpdateMode) FromUtf8Result { } } -pub fn fromUtf8RangeC(arg: RocList, countAndStart: CountAndStart, output: *FromUtf8Result) callconv(.C) void { +pub fn fromUtf8RangeC(output: *FromUtf8Result, arg: RocList, countAndStart: CountAndStart) callconv(.C) void { output.* = @call(.{ .modifier = always_inline }, fromUtf8Range, .{ arg, countAndStart }); } diff --git a/crates/compiler/can/src/builtins.rs b/crates/compiler/can/src/builtins.rs index 180b144e43..b86eec10c2 100644 --- a/crates/compiler/can/src/builtins.rs +++ b/crates/compiler/can/src/builtins.rs @@ -1905,7 +1905,7 @@ fn str_from_utf8_range(symbol: Symbol, var_store: &mut VarStore) -> Def { // if arg_3.a then // Ok arg_3.str // else - // Err (BadUtf8 { byteIndex: arg_3.byteIndex, problem : arg_3.problem }) + // Err (BadUtf8 arg_3.problem arg_3.byteIndex) let def = crate::def::Def { loc_pattern: no_region(Pattern::Identifier(Symbol::ARG_3)), diff --git a/crates/compiler/gen_llvm/src/llvm/build_str.rs b/crates/compiler/gen_llvm/src/llvm/build_str.rs index ce246320fa..8ed77c2142 100644 --- a/crates/compiler/gen_llvm/src/llvm/build_str.rs +++ b/crates/compiler/gen_llvm/src/llvm/build_str.rs @@ -168,10 +168,10 @@ pub fn str_from_utf8_range<'a, 'ctx, 'env>( call_void_bitcode_fn( env, &[ + result_ptr.into(), list_symbol_to_c_abi(env, scope, list).into(), count, start, - result_ptr.into(), ], bitcode::STR_FROM_UTF8_RANGE, ); From fdc0851883c82bcde9af766f83badfaf74750b96 Mon Sep 17 00:00:00 2001 From: Brian Carroll Date: Mon, 4 Jul 2022 11:13:58 +0100 Subject: [PATCH 65/66] wasm: fix Str.fromUtf8Range --- crates/compiler/gen_wasm/src/low_level.rs | 2 +- crates/compiler/test_gen/src/wasm_str.rs | 216 +++++++++++----------- 2 files changed, 109 insertions(+), 109 deletions(-) diff --git a/crates/compiler/gen_wasm/src/low_level.rs b/crates/compiler/gen_wasm/src/low_level.rs index 02d7a10346..a12665dbfb 100644 --- a/crates/compiler/gen_wasm/src/low_level.rs +++ b/crates/compiler/gen_wasm/src/low_level.rs @@ -281,9 +281,9 @@ impl<'a> LowLevelCall<'a> { backend.code_builder.i32_const(UPDATE_MODE_IMMUTABLE); backend.call_host_fn_after_loading_args(bitcode::STR_FROM_UTF8, 4, false); } + StrFromUtf8Range => self.load_args_and_call_zig(backend, bitcode::STR_FROM_UTF8_RANGE), StrTrimLeft => self.load_args_and_call_zig(backend, bitcode::STR_TRIM_LEFT), StrTrimRight => self.load_args_and_call_zig(backend, bitcode::STR_TRIM_RIGHT), - StrFromUtf8Range => self.load_args_and_call_zig(backend, bitcode::STR_FROM_UTF8_RANGE), StrToUtf8 => self.load_args_and_call_zig(backend, bitcode::STR_TO_UTF8), StrRepeat => self.load_args_and_call_zig(backend, bitcode::STR_REPEAT), StrTrim => self.load_args_and_call_zig(backend, bitcode::STR_TRIM), diff --git a/crates/compiler/test_gen/src/wasm_str.rs b/crates/compiler/test_gen/src/wasm_str.rs index bbc76043ac..e2458572ac 100644 --- a/crates/compiler/test_gen/src/wasm_str.rs +++ b/crates/compiler/test_gen/src/wasm_str.rs @@ -757,120 +757,120 @@ fn str_to_utf8() { ); } -// #[test] -// fn str_from_utf8_range() { -// assert_evals_to!( -// indoc!( -// r#" -// bytes = Str.toUtf8 "hello" -// when Str.fromUtf8Range bytes { count: 5, start: 0 } is -// Ok utf8String -> utf8String -// _ -> "" -// "# -// ), -// RocStr::from("hello"), -// RocStr -// ); -// } +#[test] +fn str_from_utf8_range() { + assert_evals_to!( + indoc!( + r#" + bytes = Str.toUtf8 "hello" + when Str.fromUtf8Range bytes { count: 5, start: 0 } is + Ok utf8String -> utf8String + _ -> "" + "# + ), + RocStr::from("hello"), + RocStr + ); +} -// #[test] -// fn str_from_utf8_range_slice() { -// assert_evals_to!( -// indoc!( -// r#" -// bytes = Str.toUtf8 "hello" -// when Str.fromUtf8Range bytes { count: 4, start: 1 } is -// Ok utf8String -> utf8String -// _ -> "" -// "# -// ), -// RocStr::from("ello"), -// RocStr -// ); -// } +#[test] +fn str_from_utf8_range_slice() { + assert_evals_to!( + indoc!( + r#" + bytes = Str.toUtf8 "hello" + when Str.fromUtf8Range bytes { count: 4, start: 1 } is + Ok utf8String -> utf8String + _ -> "" + "# + ), + RocStr::from("ello"), + RocStr + ); +} -// #[test] -// fn str_from_utf8_range_slice_not_end() { -// assert_evals_to!( -// indoc!( -// r#" -// bytes = Str.toUtf8 "hello" -// when Str.fromUtf8Range bytes { count: 3, start: 1 } is -// Ok utf8String -> utf8String -// _ -> "" -// "# -// ), -// RocStr::from("ell"), -// RocStr -// ); -// } +#[test] +fn str_from_utf8_range_slice_not_end() { + assert_evals_to!( + indoc!( + r#" + bytes = Str.toUtf8 "hello" + when Str.fromUtf8Range bytes { count: 3, start: 1 } is + Ok utf8String -> utf8String + _ -> "" + "# + ), + RocStr::from("ell"), + RocStr + ); +} -// #[test] -// fn str_from_utf8_range_order_does_not_matter() { -// assert_evals_to!( -// indoc!( -// r#" -// bytes = Str.toUtf8 "hello" -// when Str.fromUtf8Range bytes { start: 1, count: 3 } is -// Ok utf8String -> utf8String -// _ -> "" -// "# -// ), -// RocStr::from("ell"), -// RocStr -// ); -// } +#[test] +fn str_from_utf8_range_order_does_not_matter() { + assert_evals_to!( + indoc!( + r#" + bytes = Str.toUtf8 "hello" + when Str.fromUtf8Range bytes { start: 1, count: 3 } is + Ok utf8String -> utf8String + _ -> "" + "# + ), + RocStr::from("ell"), + RocStr + ); +} -// #[test] -// fn str_from_utf8_range_out_of_bounds_start_value() { -// assert_evals_to!( -// indoc!( -// r#" -// bytes = Str.toUtf8 "hello" -// when Str.fromUtf8Range bytes { start: 7, count: 3 } is -// Ok _ -> "" -// Err (BadUtf8 _ _) -> "" -// Err OutOfBounds -> "out of bounds" -// "# -// ), -// RocStr::from("out of bounds"), -// RocStr -// ); -// } +#[test] +fn str_from_utf8_range_out_of_bounds_start_value() { + assert_evals_to!( + indoc!( + r#" + bytes = Str.toUtf8 "hello" + when Str.fromUtf8Range bytes { start: 7, count: 3 } is + Ok _ -> "" + Err (BadUtf8 _ _) -> "" + Err OutOfBounds -> "out of bounds" + "# + ), + RocStr::from("out of bounds"), + RocStr + ); +} -// #[test] -// fn str_from_utf8_range_count_too_high() { -// assert_evals_to!( -// indoc!( -// r#" -// bytes = Str.toUtf8 "hello" -// when Str.fromUtf8Range bytes { start: 0, count: 6 } is -// Ok _ -> "" -// Err (BadUtf8 _ _) -> "" -// Err OutOfBounds -> "out of bounds" -// "# -// ), -// RocStr::from("out of bounds"), -// RocStr -// ); -// } +#[test] +fn str_from_utf8_range_count_too_high() { + assert_evals_to!( + indoc!( + r#" + bytes = Str.toUtf8 "hello" + when Str.fromUtf8Range bytes { start: 0, count: 6 } is + Ok _ -> "" + Err (BadUtf8 _ _) -> "" + Err OutOfBounds -> "out of bounds" + "# + ), + RocStr::from("out of bounds"), + RocStr + ); +} -// #[test] -// fn str_from_utf8_range_count_too_high_for_start() { -// assert_evals_to!( -// indoc!( -// r#" -// bytes = Str.toUtf8 "hello" -// when Str.fromUtf8Range bytes { start: 4, count: 3 } is -// Ok _ -> "" -// Err (BadUtf8 _ _) -> "" -// Err OutOfBounds -> "out of bounds" -// "# -// ), -// RocStr::from("out of bounds"), -// RocStr -// ); -// } +#[test] +fn str_from_utf8_range_count_too_high_for_start() { + assert_evals_to!( + indoc!( + r#" + bytes = Str.toUtf8 "hello" + when Str.fromUtf8Range bytes { start: 4, count: 3 } is + Ok _ -> "" + Err (BadUtf8 _ _) -> "" + Err OutOfBounds -> "out of bounds" + "# + ), + RocStr::from("out of bounds"), + RocStr + ); +} #[test] fn str_repeat_small() { From f4ed7e086e56fa38a022ef0c0e86d3f6b4486bb6 Mon Sep 17 00:00:00 2001 From: Brian Carroll Date: Mon, 4 Jul 2022 12:13:10 +0100 Subject: [PATCH 66/66] wasm: turn off debug features --- crates/compiler/gen_wasm/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/compiler/gen_wasm/src/lib.rs b/crates/compiler/gen_wasm/src/lib.rs index 7b5972f371..1d00e29f64 100644 --- a/crates/compiler/gen_wasm/src/lib.rs +++ b/crates/compiler/gen_wasm/src/lib.rs @@ -270,6 +270,6 @@ pub const DEBUG_SETTINGS: WasmDebugSettings = WasmDebugSettings { let_stmt_ir: false && cfg!(debug_assertions), instructions: false && cfg!(debug_assertions), storage_map: false && cfg!(debug_assertions), - keep_test_binary: true && cfg!(debug_assertions), + keep_test_binary: false && cfg!(debug_assertions), skip_dead_code_elim: false && cfg!(debug_assertions), };