diff --git a/compiler/builtins/bitcode/src/list.zig b/compiler/builtins/bitcode/src/list.zig index 7dda2126c5..d012e92e26 100644 --- a/compiler/builtins/bitcode/src/list.zig +++ b/compiler/builtins/bitcode/src/list.zig @@ -899,42 +899,6 @@ pub fn listSublist( return RocList.empty(); } -pub fn listDrop( - list: RocList, - alignment: u32, - element_width: usize, - drop_count: usize, - dec: Dec, -) callconv(.C) RocList { - if (list.bytes) |source_ptr| { - const size = list.len(); - const keep_count = size - drop_count; - - var i: usize = 0; - const iterations = std.math.min(drop_count, size); - - while (i < iterations) : (i += 1) { - const element = source_ptr + i * element_width; - dec(element); - } - - if (drop_count >= size) { - return RocList.empty(); - } - - const output = RocList.allocate(alignment, keep_count, element_width); - const target_ptr = output.bytes orelse unreachable; - - @memcpy(target_ptr, source_ptr + drop_count * element_width, keep_count * element_width); - - utils.decref(list.bytes, size * element_width, alignment); - - return output; - } else { - return RocList.empty(); - } -} - pub fn listDropAt( list: RocList, alignment: u32, diff --git a/compiler/builtins/bitcode/src/main.zig b/compiler/builtins/bitcode/src/main.zig index c270263b37..345b63e48e 100644 --- a/compiler/builtins/bitcode/src/main.zig +++ b/compiler/builtins/bitcode/src/main.zig @@ -46,7 +46,6 @@ comptime { exportListFn(list.listSortWith, "sort_with"); exportListFn(list.listConcat, "concat"); exportListFn(list.listSublist, "sublist"); - exportListFn(list.listDrop, "drop"); exportListFn(list.listDropAt, "drop_at"); exportListFn(list.listSet, "set"); exportListFn(list.listSetInPlace, "set_in_place"); diff --git a/compiler/builtins/src/bitcode.rs b/compiler/builtins/src/bitcode.rs index 5197afebbf..d00ab60bae 100644 --- a/compiler/builtins/src/bitcode.rs +++ b/compiler/builtins/src/bitcode.rs @@ -185,7 +185,6 @@ pub const LIST_REPEAT: &str = "roc_builtins.list.repeat"; pub const LIST_APPEND: &str = "roc_builtins.list.append"; pub const LIST_PREPEND: &str = "roc_builtins.list.prepend"; pub const LIST_SUBLIST: &str = "roc_builtins.list.sublist"; -pub const LIST_DROP: &str = "roc_builtins.list.drop"; pub const LIST_DROP_AT: &str = "roc_builtins.list.drop_at"; pub const LIST_SWAP: &str = "roc_builtins.list.swap"; pub const LIST_SINGLE: &str = "roc_builtins.list.single"; diff --git a/compiler/can/src/builtins.rs b/compiler/can/src/builtins.rs index 924e0f6567..6e2342ca3e 100644 --- a/compiler/can/src/builtins.rs +++ b/compiler/can/src/builtins.rs @@ -2150,20 +2150,33 @@ fn list_sublist(symbol: Symbol, var_store: &mut VarStore) -> Def { /// List.drop : List elem, Nat -> List elem fn list_drop(symbol: Symbol, var_store: &mut VarStore) -> Def { let list_var = var_store.fresh(); - let index_var = var_store.fresh(); + let len_var = var_store.fresh(); + + let get_list_len = RunLowLevel { + op: LowLevel::ListLen, + args: vec![(list_var, Var(Symbol::ARG_1))], + ret_var: len_var, + }; + + let get_len = RunLowLevel { + op: LowLevel::NumSubWrap, + args: vec![(len_var, get_list_len), (len_var, Var(Symbol::ARG_2))], + ret_var: len_var, + }; let body = RunLowLevel { - op: LowLevel::ListDrop, + op: LowLevel::ListSublist, args: vec![ (list_var, Var(Symbol::ARG_1)), - (index_var, Var(Symbol::ARG_2)), + (len_var, Var(Symbol::ARG_2)), + (len_var, get_len), ], ret_var: list_var, }; defn( symbol, - vec![(list_var, Symbol::ARG_1), (index_var, Symbol::ARG_2)], + vec![(list_var, Symbol::ARG_1), (len_var, Symbol::ARG_2)], var_store, body, list_var, diff --git a/compiler/fmt/src/module.rs b/compiler/fmt/src/module.rs index 6ceca3d28c..17522326c2 100644 --- a/compiler/fmt/src/module.rs +++ b/compiler/fmt/src/module.rs @@ -1,6 +1,6 @@ use crate::spaces::{fmt_spaces, INDENT}; -use bumpalo::collections::{String, Vec}; -use roc_parse::ast::Module; +use bumpalo::collections::String; +use roc_parse::ast::{Collection, Module}; use roc_parse::header::{AppHeader, ExposesEntry, ImportsEntry, InterfaceHeader, PlatformHeader}; use roc_region::all::Located; @@ -64,7 +64,7 @@ pub fn fmt_interface_header<'a>(buf: &mut String<'a>, header: &'a InterfaceHeade fmt_spaces(buf, header.after_imports.iter(), indent); } - fmt_imports(buf, &header.imports, indent); + fmt_imports(buf, header.imports, indent); } pub fn fmt_app_header<'a>(buf: &mut String<'a>, header: &'a AppHeader<'a>) { @@ -76,7 +76,7 @@ pub fn fmt_app_header<'a>(buf: &mut String<'a>, header: &'a AppHeader<'a>) { buf.push_str("imports"); fmt_spaces(buf, header.before_imports.iter(), indent); - fmt_imports(buf, &header.imports, indent); + fmt_imports(buf, header.imports, indent); fmt_spaces(buf, header.after_imports.iter(), indent); } @@ -86,7 +86,7 @@ pub fn fmt_platform_header<'a>(_buf: &mut String<'a>, _header: &'a PlatformHeade fn fmt_imports<'a>( buf: &mut String<'a>, - loc_entries: &'a Vec<'a, Located>>, + loc_entries: Collection<'a, Located>>, indent: u16, ) { buf.push('['); @@ -112,7 +112,7 @@ fn fmt_imports<'a>( fn fmt_exposes<'a>( buf: &mut String<'a>, - loc_entries: &'a Vec<'a, Located>>, + loc_entries: &'a Collection<'a, Located>>, indent: u16, ) { buf.push('['); diff --git a/compiler/gen_dev/src/lib.rs b/compiler/gen_dev/src/lib.rs index e17abf1362..7abe230395 100644 --- a/compiler/gen_dev/src/lib.rs +++ b/compiler/gen_dev/src/lib.rs @@ -227,97 +227,30 @@ where ret_layout, .. } => { - // For most builtins instead of calling a function, we can just inline the low level. - match *func_sym { - Symbol::NUM_ABS => self.build_run_low_level( + // If this function is just a lowlevel wrapper, then inline it + if let Some(lowlevel) = LowLevel::from_inlined_wrapper(*func_sym) { + self.build_run_low_level( sym, - &LowLevel::NumAbs, + &lowlevel, arguments, arg_layouts, ret_layout, - ), - Symbol::NUM_ADD => self.build_run_low_level( - sym, - &LowLevel::NumAdd, - arguments, - arg_layouts, - ret_layout, - ), - Symbol::NUM_ACOS => self.build_run_low_level( - sym, - &LowLevel::NumAcos, - arguments, - arg_layouts, - ret_layout, - ), - Symbol::NUM_ASIN => self.build_run_low_level( - sym, - &LowLevel::NumAsin, - arguments, - arg_layouts, - ret_layout, - ), - Symbol::NUM_ATAN => self.build_run_low_level( - sym, - &LowLevel::NumAtan, - arguments, - arg_layouts, - ret_layout, - ), - Symbol::NUM_MUL => self.build_run_low_level( - sym, - &LowLevel::NumMul, - arguments, - arg_layouts, - ret_layout, - ), - Symbol::NUM_POW_INT => self.build_run_low_level( - sym, - &LowLevel::NumPowInt, - arguments, - arg_layouts, - ret_layout, - ), - Symbol::NUM_SUB => self.build_run_low_level( - sym, - &LowLevel::NumSub, - arguments, - arg_layouts, - ret_layout, - ), - Symbol::NUM_ROUND => self.build_run_low_level( - sym, - &LowLevel::NumRound, - arguments, - arg_layouts, - ret_layout, - ), - Symbol::BOOL_EQ => self.build_run_low_level( - sym, - &LowLevel::Eq, - arguments, - arg_layouts, - ret_layout, - ), - Symbol::STR_CONCAT => self.build_run_low_level( - sym, - &LowLevel::StrConcat, - arguments, - arg_layouts, - ret_layout, - ), - x if x - .module_string(&self.env().interns) - .starts_with(ModuleName::APP) => - { - let fn_name = LayoutIds::default() - .get(*func_sym, layout) - .to_symbol_string(*func_sym, &self.env().interns); - // 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) - } - x => Err(format!("the function, {:?}, is not yet implemented", x)), + ) + } else if func_sym + .module_string(&self.env().interns) + .starts_with(ModuleName::APP) + { + let fn_name = LayoutIds::default() + .get(*func_sym, layout) + .to_symbol_string(*func_sym, &self.env().interns); + // 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 { + Err(format!( + "the function, {:?}, is not yet implemented", + func_sym + )) } } diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index b6eefddee5..32abb31e5f 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -9,10 +9,10 @@ use crate::llvm::build_dict::{ use crate::llvm::build_hash::generic_hash; use crate::llvm::build_list::{ self, allocate_list, empty_list, empty_polymorphic_list, list_any, list_append, list_concat, - list_contains, list_drop, list_drop_at, list_find_trivial_not_found, list_find_unsafe, - list_get_unsafe, list_join, list_keep_errs, list_keep_if, list_keep_oks, list_len, list_map, - list_map2, list_map3, list_map4, list_map_with_index, list_prepend, list_range, list_repeat, - list_reverse, list_set, list_single, list_sort_with, list_sublist, list_swap, + list_contains, list_drop_at, list_find_trivial_not_found, list_find_unsafe, list_get_unsafe, + list_join, list_keep_errs, list_keep_if, list_keep_oks, list_len, list_map, list_map2, + list_map3, list_map4, list_map_with_index, list_prepend, list_range, list_repeat, list_reverse, + list_set, list_single, list_sort_with, list_sublist, list_swap, }; use crate::llvm::build_str::{ empty_str, str_concat, str_count_graphemes, str_ends_with, str_from_float, str_from_int, @@ -5490,27 +5490,6 @@ fn run_low_level<'a, 'ctx, 'env>( _ => unreachable!("Invalid layout {:?} in List.sublist", list_layout), } } - ListDrop => { - // List.drop : List elem, Nat -> List elem - debug_assert_eq!(args.len(), 2); - - let (list, list_layout) = load_symbol_and_layout(scope, &args[0]); - let original_wrapper = list.into_struct_value(); - - let count = load_symbol(scope, &args[1]); - - match list_layout { - Layout::Builtin(Builtin::EmptyList) => empty_list(env), - Layout::Builtin(Builtin::List(element_layout)) => list_drop( - env, - layout_ids, - original_wrapper, - count.into_int_value(), - element_layout, - ), - _ => unreachable!("Invalid layout {:?} in List.drop", list_layout), - } - } ListDropAt => { // List.dropAt : List elem, Nat -> List elem debug_assert_eq!(args.len(), 2); diff --git a/compiler/gen_llvm/src/llvm/build_list.rs b/compiler/gen_llvm/src/llvm/build_list.rs index 6885b8f9cc..3806bc8b72 100644 --- a/compiler/gen_llvm/src/llvm/build_list.rs +++ b/compiler/gen_llvm/src/llvm/build_list.rs @@ -328,28 +328,6 @@ pub fn list_sublist<'a, 'ctx, 'env>( ) } -/// List.drop : List elem, Nat -> List elem -pub fn list_drop<'a, 'ctx, 'env>( - env: &Env<'a, 'ctx, 'env>, - layout_ids: &mut LayoutIds<'a>, - original_wrapper: StructValue<'ctx>, - count: IntValue<'ctx>, - element_layout: &Layout<'a>, -) -> BasicValueEnum<'ctx> { - let dec_element_fn = build_dec_wrapper(env, layout_ids, element_layout); - call_bitcode_fn_returns_list( - env, - &[ - pass_list_cc(env, original_wrapper.into()), - env.alignment_intvalue(element_layout), - layout_width(env, element_layout), - count.into(), - dec_element_fn.as_global_value().as_pointer_value().into(), - ], - bitcode::LIST_DROP, - ) -} - /// List.dropAt : List elem, Nat -> List elem pub fn list_drop_at<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, diff --git a/compiler/gen_wasm/src/backend.rs b/compiler/gen_wasm/src/backend.rs index 9093401626..1d0928611e 100644 --- a/compiler/gen_wasm/src/backend.rs +++ b/compiler/gen_wasm/src/backend.rs @@ -2,12 +2,13 @@ use bumpalo::{self, collections::Vec}; use code_builder::Align; use roc_collections::all::MutMap; +use roc_module::low_level::LowLevel; use roc_module::symbol::Symbol; use roc_mono::ir::{CallType, Expr, JoinPointId, Literal, Proc, Stmt}; use roc_mono::layout::{Builtin, Layout, LayoutIds}; use crate::layout::WasmLayout; -use crate::low_level::{build_call_low_level, LowlevelBuildResult}; +use crate::low_level::{decode_low_level, LowlevelBuildResult}; use crate::storage::{Storage, StoredValue, StoredValueKind}; use crate::wasm_module::linking::{ DataSymbol, LinkingSection, RelocationSection, WasmObjectSymbol, WASM_SYM_BINDING_WEAK, @@ -22,8 +23,8 @@ use crate::wasm_module::{ LocalId, Signature, SymInfo, ValueType, }; use crate::{ - copy_memory, CopyMemoryConfig, Env, BUILTINS_IMPORT_MODULE_NAME, MEMORY_NAME, PTR_TYPE, - STACK_POINTER_GLOBAL_ID, STACK_POINTER_NAME, + copy_memory, CopyMemoryConfig, Env, BUILTINS_IMPORT_MODULE_NAME, MEMORY_NAME, PTR_SIZE, + PTR_TYPE, STACK_POINTER_GLOBAL_ID, STACK_POINTER_NAME, }; /// The memory address where the constants data will be loaded during module instantiation. @@ -469,6 +470,11 @@ impl<'a> WasmBackend<'a> { arguments, }) => match call_type { CallType::ByName { name: func_sym, .. } => { + // If this function is just a lowlevel wrapper, then inline it + if let Some(lowlevel) = LowLevel::from_inlined_wrapper(*func_sym) { + return self.build_low_level(lowlevel, arguments, wasm_layout); + } + let mut wasm_args_tmp: Vec; let (wasm_args, has_return_val) = match wasm_layout { WasmLayout::StackMemory { .. } => { @@ -511,39 +517,71 @@ impl<'a> WasmBackend<'a> { } CallType::LowLevel { op: lowlevel, .. } => { - let return_layout = WasmLayout::new(layout); - self.storage.load_symbols(&mut self.code_builder, arguments); - - let build_result = build_call_low_level( - &mut self.code_builder, - &mut self.storage, - lowlevel, - arguments, - &return_layout, - ); - use LowlevelBuildResult::*; - - match build_result { - Done => Ok(()), - BuiltinCall(name) => { - self.call_imported_builtin(name, arguments, &return_layout); - Ok(()) - } - NotImplemented => Err(format!( - "Low level operation {:?} is not yet implemented", - lowlevel - )), - } + self.build_low_level(*lowlevel, arguments, wasm_layout) } + x => Err(format!("the call type, {:?}, is not yet implemented", x)), }, Expr::Struct(fields) => self.create_struct(sym, layout, fields), + Expr::StructAtIndex { + index, + field_layouts, + structure, + } => { + if let StoredValue::StackMemory { location, .. } = self.storage.get(structure) { + let (local_id, mut offset) = + location.local_and_offset(self.storage.stack_frame_pointer); + for field in field_layouts.iter().take(*index as usize) { + offset += field.stack_size(PTR_SIZE); + } + self.storage.copy_value_from_memory( + &mut self.code_builder, + *sym, + local_id, + offset, + ); + } else { + unreachable!("Unexpected storage for {:?}", structure) + } + Ok(()) + } + x => Err(format!("Expression is not yet implemented {:?}", x)), } } + fn build_low_level( + &mut self, + lowlevel: LowLevel, + arguments: &'a [Symbol], + return_layout: WasmLayout, + ) -> Result<(), String> { + self.storage.load_symbols(&mut self.code_builder, arguments); + + let build_result = decode_low_level( + &mut self.code_builder, + &mut self.storage, + lowlevel, + arguments, + &return_layout, + ); + use LowlevelBuildResult::*; + + match build_result { + Done => Ok(()), + BuiltinCall(name) => { + self.call_imported_builtin(name, arguments, &return_layout); + Ok(()) + } + NotImplemented => Err(format!( + "Low level operation {:?} is not yet implemented", + lowlevel + )), + } + } + fn load_literal( &mut self, lit: &Literal<'a>, @@ -741,14 +779,15 @@ impl<'a> WasmBackend<'a> { }; self.module.import.entries.push(import); - let sym_idx = self.linker_symbols.len() as u32; + let sym_idx = self.linker_symbols.len(); let sym_info = SymInfo::Function(WasmObjectSymbol::Imported { flags: WASM_SYM_UNDEFINED, index: import_index, }); self.linker_symbols.push(sym_info); + self.builtin_sym_index_map.insert(name, sym_idx); - (import_index, sym_idx) + (import_index, sym_idx as u32) } }; self.code_builder.call( diff --git a/compiler/gen_wasm/src/low_level.rs b/compiler/gen_wasm/src/low_level.rs index 7dfbcf3b96..e5b5da9bbb 100644 --- a/compiler/gen_wasm/src/low_level.rs +++ b/compiler/gen_wasm/src/low_level.rs @@ -15,10 +15,10 @@ pub enum LowlevelBuildResult { NotImplemented, } -pub fn build_call_low_level<'a>( +pub fn decode_low_level<'a>( code_builder: &mut CodeBuilder<'a>, storage: &mut Storage<'a>, - lowlevel: &LowLevel, + lowlevel: LowLevel, args: &'a [Symbol], ret_layout: &WasmLayout, ) -> LowlevelBuildResult { @@ -34,9 +34,9 @@ pub fn build_call_low_level<'a>( | ListConcat | ListContains | ListAppend | ListPrepend | ListJoin | ListRange | ListMap | ListMap2 | ListMap3 | ListMap4 | ListMapWithIndex | ListKeepIf | ListWalk | ListWalkUntil | ListWalkBackwards | ListKeepOks | ListKeepErrs | ListSortWith - | ListSublist | ListDrop | ListDropAt | ListSwap | ListAny | ListFindUnsafe | DictSize - | DictEmpty | DictInsert | DictRemove | DictContains | DictGetUnsafe | DictKeys - | DictValues | DictUnion | DictIntersection | DictDifference | DictWalk | SetFromList => { + | ListSublist | ListDropAt | ListSwap | ListAny | ListFindUnsafe | DictSize | DictEmpty + | DictInsert | DictRemove | DictContains | DictGetUnsafe | DictKeys | DictValues + | DictUnion | DictIntersection | DictDifference | DictWalk | SetFromList => { return NotImplemented; } @@ -129,6 +129,9 @@ pub fn build_call_low_level<'a>( NumIsMultipleOf => return NotImplemented, NumAbs => match ret_layout.value_type() { I32 => { + let arg_storage = storage.get(&args[0]).to_owned(); + storage.ensure_value_has_local(code_builder, args[0], arg_storage); + storage.load_symbols(code_builder, args); code_builder.i32_const(0); storage.load_symbols(code_builder, args); code_builder.i32_sub(); @@ -138,6 +141,9 @@ pub fn build_call_low_level<'a>( code_builder.select(); } I64 => { + let arg_storage = storage.get(&args[0]).to_owned(); + storage.ensure_value_has_local(code_builder, args[0], arg_storage); + storage.load_symbols(code_builder, args); code_builder.i64_const(0); storage.load_symbols(code_builder, args); code_builder.i64_sub(); diff --git a/compiler/gen_wasm/src/storage.rs b/compiler/gen_wasm/src/storage.rs index f552d59c59..fbec6ff61a 100644 --- a/compiler/gen_wasm/src/storage.rs +++ b/compiler/gen_wasm/src/storage.rs @@ -319,6 +319,67 @@ impl<'a> Storage<'a> { } } + /// Generate code to copy a StoredValue from an arbitrary memory location + /// (defined by a pointer and offset). + pub fn copy_value_from_memory( + &mut self, + code_builder: &mut CodeBuilder, + to_symbol: Symbol, + from_ptr: LocalId, + from_offset: u32, + ) -> u32 { + let to_storage = self.get(&to_symbol).to_owned(); + match to_storage { + StoredValue::StackMemory { + location, + size, + alignment_bytes, + } => { + let (to_ptr, to_offset) = location.local_and_offset(self.stack_frame_pointer); + copy_memory( + code_builder, + CopyMemoryConfig { + from_ptr, + from_offset, + to_ptr, + to_offset, + size, + alignment_bytes, + }, + ); + size + } + + StoredValue::VirtualMachineStack { + value_type, size, .. + } + | StoredValue::Local { + value_type, size, .. + } => { + use crate::wasm_module::Align::*; + + 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), + (ValueType::I32, 2) => code_builder.i32_load16_s(Bytes2, from_offset), + (ValueType::I32, 1) => code_builder.i32_load8_s(Bytes1, from_offset), + (ValueType::F32, 4) => code_builder.f32_load(Bytes4, from_offset), + (ValueType::F64, 8) => code_builder.f64_load(Bytes8, from_offset), + _ => { + panic!("Cannot store {:?} with alignment of {:?}", value_type, size); + } + }; + + if let StoredValue::Local { local_id, .. } = to_storage { + code_builder.set_local(local_id); + } + + size + } + } + } + /// Generate code to copy from one StoredValue to another /// Copies the _entire_ value. For struct fields etc., see `copy_value_to_memory` pub fn clone_value( diff --git a/compiler/load/src/file.rs b/compiler/load/src/file.rs index 0d20be6415..17a370d49a 100644 --- a/compiler/load/src/file.rs +++ b/compiler/load/src/file.rs @@ -2608,8 +2608,8 @@ fn parse_header<'a>( opt_shorthand, header_src, packages: &[], - exposes: header.exposes.into_bump_slice(), - imports: header.imports.into_bump_slice(), + exposes: header.exposes.items, + imports: header.imports.items, to_platform: None, }; @@ -2642,8 +2642,8 @@ fn parse_header<'a>( opt_shorthand, header_src, packages, - exposes: header.provides.into_bump_slice(), - imports: header.imports.into_bump_slice(), + exposes: header.provides.items, + imports: header.imports.items, to_platform: Some(header.to.value.clone()), }; @@ -3236,7 +3236,7 @@ fn send_header_two<'a>( let extra = HeaderFor::PkgConfig { config_shorthand: shorthand, - platform_main_type: requires[0].value.clone(), + platform_main_type: requires[0].value, main_for_host, }; @@ -3409,8 +3409,7 @@ fn fabricate_pkg_config_module<'a>( header_src: &'a str, module_timing: ModuleTiming, ) -> (ModuleId, Msg<'a>) { - let provides: &'a [Located>] = - header.provides.clone().into_bump_slice(); + let provides: &'a [Located>] = header.provides.items; let info = PlatformHeaderInfo { filename, @@ -3420,8 +3419,8 @@ fn fabricate_pkg_config_module<'a>( app_module_id, packages: &[], provides, - requires: arena.alloc([header.requires.signature.clone()]), - imports: header.imports.clone().into_bump_slice(), + requires: arena.alloc([header.requires.signature]), + imports: header.imports.items, }; send_header_two( @@ -3467,7 +3466,7 @@ fn fabricate_effects_module<'a>( { let mut module_ids = (*module_ids).lock(); - for exposed in header.exposes { + for exposed in header.exposes.iter() { if let ExposesEntry::Exposed(module_name) = exposed.value { module_ids.get_or_insert(&PQModuleName::Qualified( shorthand, @@ -3886,7 +3885,7 @@ fn exposed_from_import<'a>(entry: &ImportsEntry<'a>) -> (QualifiedModuleName<'a> Module(module_name, exposes) => { let mut exposed = Vec::with_capacity(exposes.len()); - for loc_entry in exposes { + for loc_entry in exposes.iter() { exposed.push(ident_from_exposed(&loc_entry.value)); } @@ -3901,7 +3900,7 @@ fn exposed_from_import<'a>(entry: &ImportsEntry<'a>) -> (QualifiedModuleName<'a> Package(package_name, module_name, exposes) => { let mut exposed = Vec::with_capacity(exposes.len()); - for loc_entry in exposes { + for loc_entry in exposes.iter() { exposed.push(ident_from_exposed(&loc_entry.value)); } diff --git a/compiler/module/src/low_level.rs b/compiler/module/src/low_level.rs index bc8d2706d0..77f551b980 100644 --- a/compiler/module/src/low_level.rs +++ b/compiler/module/src/low_level.rs @@ -1,3 +1,5 @@ +use crate::symbol::Symbol; + /// Low-level operations that get translated directly into e.g. LLVM instructions. /// These are always wrapped when exposed to end users, and can only make it /// into an Expr when added directly by can::builtins @@ -45,7 +47,6 @@ pub enum LowLevel { ListKeepErrs, ListSortWith, ListSublist, - ListDrop, ListDropAt, ListSwap, ListAny, @@ -115,106 +116,6 @@ pub enum LowLevel { ExpectTrue, } -macro_rules! first_order { - () => { - StrConcat - | StrJoinWith - | StrIsEmpty - | StrStartsWith - | StrStartsWithCodePt - | StrEndsWith - | StrSplit - | StrCountGraphemes - | StrFromInt - | StrFromUtf8 - | StrFromUtf8Range - | StrToUtf8 - | StrRepeat - | StrTrim - | StrTrimLeft - | StrTrimRight - | StrFromFloat - | ListLen - | ListGetUnsafe - | ListSet - | ListSublist - | ListDrop - | ListDropAt - | ListSingle - | ListRepeat - | ListReverse - | ListConcat - | ListContains - | ListAppend - | ListPrepend - | ListJoin - | ListRange - | ListSwap - | DictSize - | DictEmpty - | DictInsert - | DictRemove - | DictContains - | DictGetUnsafe - | DictKeys - | DictValues - | DictUnion - | DictIntersection - | DictDifference - | SetFromList - | NumAdd - | NumAddWrap - | NumAddChecked - | NumSub - | NumSubWrap - | NumSubChecked - | NumMul - | NumMulWrap - | NumMulChecked - | NumGt - | NumGte - | NumLt - | NumLte - | NumCompare - | NumDivUnchecked - | NumDivCeilUnchecked - | NumRemUnchecked - | NumIsMultipleOf - | NumAbs - | NumNeg - | NumSin - | NumCos - | NumSqrtUnchecked - | NumLogUnchecked - | NumRound - | NumToFloat - | NumPow - | NumCeiling - | NumPowInt - | NumFloor - | NumIsFinite - | NumAtan - | NumAcos - | NumAsin - | NumBitwiseAnd - | NumBitwiseXor - | NumBitwiseOr - | NumShiftLeftBy - | NumShiftRightBy - | NumBytesToU16 - | NumBytesToU32 - | NumShiftRightZfBy - | NumIntCast - | Eq - | NotEq - | And - | Or - | Not - | Hash - | ExpectTrue - }; -} - macro_rules! higher_order { () => { ListMap @@ -241,17 +142,13 @@ impl LowLevel { pub fn is_higher_order(&self) -> bool { use LowLevel::*; - match self { - first_order!() => false, - higher_order!() => true, - } + matches!(self, higher_order!()) } pub fn function_argument_position(&self) -> usize { use LowLevel::*; match self { - first_order!() => unreachable!(), ListMap => 1, ListMap2 => 2, ListMap3 => 3, @@ -267,6 +164,127 @@ impl LowLevel { ListAny => 1, ListFindUnsafe => 1, DictWalk => 2, + _ => unreachable!(), + } + } + + /// Used in dev backends to inline some lowlevel wrapper functions + /// For wrappers that contain logic, we return None to prevent inlining + /// (Mention each explicitly rather than using `_`, to show they have not been forgotten) + pub fn from_inlined_wrapper(symbol: Symbol) -> Option { + use LowLevel::*; + + match symbol { + Symbol::STR_CONCAT => Some(StrConcat), + Symbol::STR_JOIN_WITH => Some(StrJoinWith), + Symbol::STR_IS_EMPTY => Some(StrIsEmpty), + Symbol::STR_STARTS_WITH => Some(StrStartsWith), + Symbol::STR_STARTS_WITH_CODE_PT => Some(StrStartsWithCodePt), + Symbol::STR_ENDS_WITH => Some(StrEndsWith), + Symbol::STR_SPLIT => Some(StrSplit), + Symbol::STR_COUNT_GRAPHEMES => Some(StrCountGraphemes), + Symbol::STR_FROM_INT => Some(StrFromInt), + Symbol::STR_FROM_UTF8 => None, + Symbol::STR_FROM_UTF8_RANGE => None, + Symbol::STR_TO_UTF8 => Some(StrToUtf8), + Symbol::STR_REPEAT => Some(StrRepeat), + Symbol::STR_FROM_FLOAT => Some(StrFromFloat), + Symbol::STR_TRIM => Some(StrTrim), + Symbol::STR_TRIM_LEFT => Some(StrTrimLeft), + Symbol::STR_TRIM_RIGHT => Some(StrTrimRight), + Symbol::LIST_LEN => Some(ListLen), + Symbol::LIST_GET => None, + Symbol::LIST_SET => None, + Symbol::LIST_SINGLE => Some(ListSingle), + Symbol::LIST_REPEAT => Some(ListRepeat), + Symbol::LIST_REVERSE => Some(ListReverse), + Symbol::LIST_CONCAT => Some(ListConcat), + Symbol::LIST_CONTAINS => Some(ListContains), + Symbol::LIST_APPEND => Some(ListAppend), + Symbol::LIST_PREPEND => Some(ListPrepend), + Symbol::LIST_JOIN => Some(ListJoin), + Symbol::LIST_RANGE => Some(ListRange), + Symbol::LIST_MAP => Some(ListMap), + Symbol::LIST_MAP2 => Some(ListMap2), + Symbol::LIST_MAP3 => Some(ListMap3), + Symbol::LIST_MAP4 => Some(ListMap4), + Symbol::LIST_MAP_WITH_INDEX => Some(ListMapWithIndex), + Symbol::LIST_KEEP_IF => Some(ListKeepIf), + Symbol::LIST_WALK => Some(ListWalk), + Symbol::LIST_WALK_UNTIL => Some(ListWalkUntil), + Symbol::LIST_WALK_BACKWARDS => Some(ListWalkBackwards), + Symbol::LIST_KEEP_OKS => Some(ListKeepOks), + Symbol::LIST_KEEP_ERRS => Some(ListKeepErrs), + Symbol::LIST_SORT_WITH => Some(ListSortWith), + Symbol::LIST_SUBLIST => Some(ListSublist), + Symbol::LIST_DROP_AT => Some(ListDropAt), + Symbol::LIST_SWAP => Some(ListSwap), + Symbol::LIST_ANY => Some(ListAny), + Symbol::LIST_FIND => None, + Symbol::DICT_LEN => Some(DictSize), + Symbol::DICT_EMPTY => Some(DictEmpty), + Symbol::DICT_INSERT => Some(DictInsert), + Symbol::DICT_REMOVE => Some(DictRemove), + Symbol::DICT_CONTAINS => Some(DictContains), + Symbol::DICT_GET => None, + Symbol::DICT_KEYS => Some(DictKeys), + Symbol::DICT_VALUES => Some(DictValues), + Symbol::DICT_UNION => Some(DictUnion), + Symbol::DICT_INTERSECTION => Some(DictIntersection), + Symbol::DICT_DIFFERENCE => Some(DictDifference), + Symbol::DICT_WALK => Some(DictWalk), + Symbol::SET_FROM_LIST => Some(SetFromList), + Symbol::NUM_ADD => Some(NumAdd), + Symbol::NUM_ADD_WRAP => Some(NumAddWrap), + Symbol::NUM_ADD_CHECKED => None, + Symbol::NUM_SUB => Some(NumSub), + Symbol::NUM_SUB_WRAP => Some(NumSubWrap), + Symbol::NUM_SUB_CHECKED => None, + Symbol::NUM_MUL => Some(NumMul), + Symbol::NUM_MUL_WRAP => Some(NumMulWrap), + Symbol::NUM_MUL_CHECKED => None, + Symbol::NUM_GT => Some(NumGt), + Symbol::NUM_GTE => Some(NumGte), + Symbol::NUM_LT => Some(NumLt), + Symbol::NUM_LTE => Some(NumLte), + Symbol::NUM_COMPARE => Some(NumCompare), + Symbol::NUM_DIV_FLOAT => None, + Symbol::NUM_DIV_CEIL => None, + Symbol::NUM_REM => None, + Symbol::NUM_IS_MULTIPLE_OF => Some(NumIsMultipleOf), + Symbol::NUM_ABS => Some(NumAbs), + Symbol::NUM_NEG => Some(NumNeg), + Symbol::NUM_SIN => Some(NumSin), + Symbol::NUM_COS => Some(NumCos), + Symbol::NUM_SQRT => None, + Symbol::NUM_LOG => None, + Symbol::NUM_ROUND => Some(NumRound), + Symbol::NUM_TO_FLOAT => Some(NumToFloat), + Symbol::NUM_POW => Some(NumPow), + Symbol::NUM_CEILING => Some(NumCeiling), + Symbol::NUM_POW_INT => Some(NumPowInt), + Symbol::NUM_FLOOR => Some(NumFloor), + // => Some(NumIsFinite), + Symbol::NUM_ATAN => Some(NumAtan), + Symbol::NUM_ACOS => Some(NumAcos), + Symbol::NUM_ASIN => Some(NumAsin), + Symbol::NUM_BYTES_TO_U16 => None, + Symbol::NUM_BYTES_TO_U32 => None, + Symbol::NUM_BITWISE_AND => Some(NumBitwiseAnd), + Symbol::NUM_BITWISE_XOR => Some(NumBitwiseXor), + Symbol::NUM_BITWISE_OR => Some(NumBitwiseOr), + Symbol::NUM_SHIFT_LEFT => Some(NumShiftLeftBy), + Symbol::NUM_SHIFT_RIGHT => Some(NumShiftRightBy), + Symbol::NUM_SHIFT_RIGHT_ZERO_FILL => Some(NumShiftRightZfBy), + Symbol::NUM_INT_CAST => Some(NumIntCast), + Symbol::BOOL_EQ => Some(Eq), + Symbol::BOOL_NEQ => Some(NotEq), + Symbol::BOOL_AND => Some(And), + Symbol::BOOL_OR => Some(Or), + Symbol::BOOL_NOT => Some(Not), + // => Some(Hash), + // => Some(ExpectTrue), + _ => None, } } } diff --git a/compiler/mono/src/borrow.rs b/compiler/mono/src/borrow.rs index 889272bcf8..ab35b9f122 100644 --- a/compiler/mono/src/borrow.rs +++ b/compiler/mono/src/borrow.rs @@ -968,7 +968,6 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] { // List.append should own its first argument ListAppend => arena.alloc_slice_copy(&[owned, owned]), ListSublist => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]), - ListDrop => arena.alloc_slice_copy(&[owned, irrelevant]), ListDropAt => arena.alloc_slice_copy(&[owned, irrelevant]), ListSwap => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]), diff --git a/compiler/mono/src/low_level.rs b/compiler/mono/src/low_level.rs index 2139ea59aa..058a2958eb 100644 --- a/compiler/mono/src/low_level.rs +++ b/compiler/mono/src/low_level.rs @@ -101,7 +101,6 @@ enum FirstOrder { ListGetUnsafe, ListSet, ListSublist, - ListDrop, ListDropAt, ListSingle, ListRepeat, diff --git a/compiler/parse/src/header.rs b/compiler/parse/src/header.rs index 42c35cd0dc..b1fbcc23eb 100644 --- a/compiler/parse/src/header.rs +++ b/compiler/parse/src/header.rs @@ -38,7 +38,7 @@ pub enum PackageOrPath<'a> { Path(StrLiteral<'a>), } -#[derive(Clone, PartialEq, Eq, Debug, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] pub struct ModuleName<'a>(&'a str); impl<'a> From> for &'a str { @@ -60,8 +60,8 @@ impl<'a> ModuleName<'a> { #[derive(Clone, Debug, PartialEq)] pub struct InterfaceHeader<'a> { pub name: Loc>, - pub exposes: Vec<'a, Loc>>, - pub imports: Vec<'a, Loc>>, + pub exposes: Collection<'a, Loc>>, + pub imports: Collection<'a, Loc>>, // Potential comments and newlines - these will typically all be empty. pub before_header: &'a [CommentOrNewline<'a>], @@ -82,8 +82,8 @@ pub enum To<'a> { pub struct AppHeader<'a> { pub name: Loc>, pub packages: Collection<'a, Loc>>, - pub imports: Vec<'a, Loc>>, - pub provides: Vec<'a, Loc>>, + pub imports: Collection<'a, Loc>>, + pub provides: Collection<'a, Loc>>, pub to: Loc>, // Potential comments and newlines - these will typically all be empty. @@ -117,7 +117,7 @@ pub struct PackageHeader<'a> { pub after_imports: &'a [CommentOrNewline<'a>], } -#[derive(Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq)] pub enum PlatformRigid<'a> { Entry { rigid: &'a str, alias: &'a str }, @@ -137,7 +137,7 @@ impl<'a> Spaceable<'a> for PlatformRigid<'a> { #[derive(Clone, Debug, PartialEq)] pub struct PlatformRequires<'a> { - pub rigids: Vec<'a, Loc>>, + pub rigids: Collection<'a, Loc>>, pub signature: Loc>, } @@ -145,10 +145,10 @@ pub struct PlatformRequires<'a> { pub struct PlatformHeader<'a> { pub name: Loc>, pub requires: PlatformRequires<'a>, - pub exposes: Vec<'a, Loc>>>, + pub exposes: Collection<'a, Loc>>>, pub packages: Collection<'a, Loc>>, - pub imports: Vec<'a, Loc>>, - pub provides: Vec<'a, Loc>>, + pub imports: Collection<'a, Loc>>, + pub provides: Collection<'a, Loc>>, pub effects: Effects<'a>, // Potential comments and newlines - these will typically all be empty. @@ -177,7 +177,7 @@ pub struct Effects<'a> { pub entries: &'a [Loc>], } -#[derive(Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq)] pub enum ExposesEntry<'a, T> { /// e.g. `Task` Exposed(T), @@ -196,16 +196,19 @@ impl<'a, T> Spaceable<'a> for ExposesEntry<'a, T> { } } -#[derive(Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq)] pub enum ImportsEntry<'a> { /// e.g. `Task` or `Task.{ Task, after }` - Module(ModuleName<'a>, Vec<'a, Loc>>), + Module( + ModuleName<'a>, + Collection<'a, Loc>>, + ), /// e.g. `base.Task` or `base.Task.{ after }` or `base.{ Task.{ Task, after } }` Package( &'a str, ModuleName<'a>, - Vec<'a, Loc>>, + Collection<'a, Loc>>, ), // Spaces @@ -224,7 +227,7 @@ impl<'a> ExposesEntry<'a, &'a str> { } } -#[derive(Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq)] pub enum TypedIdent<'a> { /// e.g. /// diff --git a/compiler/parse/src/module.rs b/compiler/parse/src/module.rs index eec34ecbcb..f0d2200e49 100644 --- a/compiler/parse/src/module.rs +++ b/compiler/parse/src/module.rs @@ -220,11 +220,11 @@ fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> { #[allow(clippy::type_complexity)] let opt_imports: Option<( (&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), - Vec<'a, Located>>, + Collection<'a, Located>>, )> = opt_imports; let ((before_imports, after_imports), imports) = - opt_imports.unwrap_or_else(|| ((&[] as _, &[] as _), Vec::new_in(arena))); + opt_imports.unwrap_or_else(|| ((&[] as _, &[] as _), Collection::empty())); let provides: ProvidesTo<'a> = provides; // rustc must be told the type here let header = AppHeader { @@ -303,7 +303,7 @@ fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>, EHeader<'a>> { #[derive(Debug)] struct ProvidesTo<'a> { - entries: Vec<'a, Located>>, + entries: Collection<'a, Located>>, to: Located>, before_provides_keyword: &'a [CommentOrNewline<'a>], @@ -362,7 +362,7 @@ fn provides_without_to<'a>() -> impl Parser< 'a, ( (&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), - Vec<'a, Located>>, + Collection<'a, Located>>, ), EProvides<'a>, > { @@ -376,14 +376,16 @@ fn provides_without_to<'a>() -> impl Parser< EProvides::IndentProvides, EProvides::IndentListStart ), - collection_e!( + collection_trailing_sep_e!( word1(b'[', EProvides::ListStart), exposes_entry(EProvides::Identifier), word1(b',', EProvides::ListEnd), word1(b']', EProvides::ListEnd), min_indent, + EProvides::Open, EProvides::Space, - EProvides::IndentListEnd + EProvides::IndentListEnd, + ExposesEntry::SpaceBefore ) ) } @@ -442,15 +444,17 @@ fn platform_requires<'a>() -> impl Parser<'a, PlatformRequires<'a>, ERequires<'a #[inline(always)] fn requires_rigids<'a>( min_indent: u16, -) -> impl Parser<'a, Vec<'a, Located>>, ERequires<'a>> { - collection_e!( +) -> impl Parser<'a, Collection<'a, Located>>, ERequires<'a>> { + collection_trailing_sep_e!( word1(b'{', ERequires::ListStart), specialize(|_, r, c| ERequires::Rigid(r, c), loc!(requires_rigid())), word1(b',', ERequires::ListEnd), word1(b'}', ERequires::ListEnd), min_indent, + ERequires::Open, ERequires::Space, - ERequires::IndentListEnd + ERequires::IndentListEnd, + PlatformRigid::SpaceBefore ) } @@ -487,7 +491,7 @@ fn exposes_values<'a>() -> impl Parser< 'a, ( (&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), - Vec<'a, Located>>, + Collection<'a, Located>>, ), EExposes, > { @@ -502,14 +506,16 @@ fn exposes_values<'a>() -> impl Parser< EExposes::IndentExposes, EExposes::IndentListStart ), - collection_e!( + collection_trailing_sep_e!( word1(b'[', EExposes::ListStart), exposes_entry(EExposes::Identifier), word1(b',', EExposes::ListEnd), word1(b']', EExposes::ListEnd), min_indent, + EExposes::Open, EExposes::Space, - EExposes::IndentListEnd + EExposes::IndentListEnd, + ExposesEntry::SpaceBefore ) ) } @@ -539,7 +545,7 @@ fn exposes_modules<'a>() -> impl Parser< 'a, ( (&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), - Vec<'a, Located>>>, + Collection<'a, Located>>>, ), EExposes, > { @@ -554,14 +560,16 @@ fn exposes_modules<'a>() -> impl Parser< EExposes::IndentExposes, EExposes::IndentListStart ), - collection_e!( + collection_trailing_sep_e!( word1(b'[', EExposes::ListStart), exposes_module(EExposes::Identifier), word1(b',', EExposes::ListEnd), word1(b']', EExposes::ListEnd), min_indent, + EExposes::Open, EExposes::Space, - EExposes::IndentListEnd + EExposes::IndentListEnd, + ExposesEntry::SpaceBefore ) ) } @@ -631,7 +639,7 @@ fn imports<'a>() -> impl Parser< 'a, ( (&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), - Vec<'a, Located>>, + Collection<'a, Located>>, ), EImports, > { @@ -646,14 +654,16 @@ fn imports<'a>() -> impl Parser< EImports::IndentImports, EImports::IndentListStart ), - collection_e!( + collection_trailing_sep_e!( word1(b'[', EImports::ListStart), loc!(imports_entry()), word1(b',', EImports::ListEnd), word1(b']', EImports::ListEnd), min_indent, + EImports::Open, EImports::Space, - EImports::IndentListEnd + EImports::IndentListEnd, + ImportsEntry::SpaceBefore ) ) } @@ -687,14 +697,16 @@ fn effects<'a>() -> impl Parser<'a, Effects<'a>, EEffects<'a>> { space0_e(min_indent, EEffects::Space, EEffects::IndentListStart) ) .parse(arena, state)?; - let (_, entries, state) = collection_e!( + let (_, entries, state) = collection_trailing_sep_e!( word1(b'{', EEffects::ListStart), specialize(EEffects::TypedIdent, loc!(typed_ident())), word1(b',', EEffects::ListEnd), word1(b'}', EEffects::ListEnd), min_indent, + EEffects::Open, EEffects::Space, - EEffects::IndentListEnd + EEffects::IndentListEnd, + TypedIdent::SpaceBefore ) .parse(arena, state)?; @@ -706,7 +718,7 @@ fn effects<'a>() -> impl Parser<'a, Effects<'a>, EEffects<'a>> { spaces_after_type_name, effect_shortname: type_shortname, effect_type_name: type_name, - entries: entries.into_bump_slice(), + entries: entries.items, }, state, )) @@ -768,7 +780,7 @@ fn imports_entry<'a>() -> impl Parser<'a, ImportsEntry<'a>, EImports> { type Temp<'a> = ( (Option<&'a str>, ModuleName<'a>), - Option>>>, + Option>>>, ); map_with_arena!( @@ -785,19 +797,21 @@ fn imports_entry<'a>() -> impl Parser<'a, ImportsEntry<'a>, EImports> { // e.g. `.{ Task, after}` maybe!(skip_first!( word1(b'.', EImports::ExposingDot), - collection_e!( + collection_trailing_sep_e!( word1(b'{', EImports::SetStart), exposes_entry(EImports::Identifier), word1(b',', EImports::SetEnd), word1(b'}', EImports::SetEnd), min_indent, + EImports::Open, EImports::Space, - EImports::IndentSetEnd + EImports::IndentSetEnd, + ExposesEntry::SpaceBefore ) )) ), - |arena, ((opt_shortname, module_name), opt_values): Temp<'a>| { - let exposed_values = opt_values.unwrap_or_else(|| Vec::new_in(arena)); + |_arena, ((opt_shortname, module_name), opt_values): Temp<'a>| { + let exposed_values = opt_values.unwrap_or_else(Collection::empty); match opt_shortname { Some(shortname) => ImportsEntry::Package(shortname, module_name, exposed_values), diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index 07b9800359..76e1480387 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -214,6 +214,7 @@ pub enum EHeader<'a> { #[derive(Debug, Clone, PartialEq, Eq)] pub enum EProvides<'a> { Provides(Row, Col), + Open(Row, Col), To(Row, Col), IndentProvides(Row, Col), IndentTo(Row, Col), @@ -230,6 +231,7 @@ pub enum EProvides<'a> { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum EExposes { Exposes(Row, Col), + Open(Row, Col), IndentExposes(Row, Col), IndentListStart(Row, Col), IndentListEnd(Row, Col), @@ -242,6 +244,7 @@ pub enum EExposes { #[derive(Debug, Clone, PartialEq, Eq)] pub enum ERequires<'a> { Requires(Row, Col), + Open(Row, Col), IndentRequires(Row, Col), IndentListStart(Row, Col), IndentListEnd(Row, Col), @@ -302,6 +305,7 @@ pub enum EPackageEntry<'a> { pub enum EEffects<'a> { Space(BadInputError, Row, Col), Effects(Row, Col), + Open(Row, Col), IndentEffects(Row, Col), ListStart(Row, Col), ListEnd(Row, Col), @@ -315,6 +319,7 @@ pub enum EEffects<'a> { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum EImports { + Open(Row, Col), Imports(Row, Col), IndentImports(Row, Col), IndentListStart(Row, Col), @@ -1183,83 +1188,6 @@ macro_rules! collection { }; } -#[macro_export] -macro_rules! collection_e { - ($opening_brace:expr, $elem:expr, $delimiter:expr, $closing_brace:expr, $min_indent:expr, $space_problem:expr, $indent_problem:expr) => { - skip_first!( - $opening_brace, - skip_first!( - // We specifically allow space characters inside here, so that - // `[ ]` can be successfully parsed as an empty list, and then - // changed by the formatter back into `[]`. - // - // We don't allow newlines or comments in the middle of empty - // roc_collections because those are normally stored in an Expr, - // and there's no Expr in which to store them in an empty collection! - // - // We could change the AST to add extra storage specifically to - // support empty literals containing newlines or comments, but this - // does not seem worth even the tiniest regression in compiler performance. - zero_or_more!($crate::parser::word1(b' ', |row, col| $space_problem( - crate::parser::BadInputError::LineTooLong, - row, - col - ))), - skip_second!( - $crate::parser::sep_by0( - $delimiter, - $crate::blankspace::space0_around_ee( - $elem, - $min_indent, - $space_problem, - $indent_problem, - $indent_problem - ) - ), - $closing_brace - ) - ) - ) - }; -} - -/// Parse zero or more elements between two braces (e.g. square braces). -/// Elements can be optionally surrounded by spaces, and are separated by a -/// delimiter (e.g comma-separated) with optionally a trailing delimiter. -/// Braces and delimiters get discarded. -#[macro_export] -macro_rules! collection_trailing_sep { - ($opening_brace:expr, $elem:expr, $delimiter:expr, $closing_brace:expr, $min_indent:expr) => { - skip_first!( - $opening_brace, - skip_first!( - // We specifically allow space characters inside here, so that - // `[ ]` can be successfully parsed as an empty list, and then - // changed by the formatter back into `[]`. - // - // We don't allow newlines or comments in the middle of empty - // roc_collections because those are normally stored in an Expr, - // and there's no Expr in which to store them in an empty collection! - // - // We could change the AST to add extra storage specifically to - // support empty literals containing newlines or comments, but this - // does not seem worth even the tiniest regression in compiler performance. - zero_or_more!($crate::parser::ascii_char(b' ')), - skip_second!( - and!( - $crate::parser::trailing_sep_by0( - $delimiter, - $crate::blankspace::space0_around($elem, $min_indent) - ), - $crate::blankspace::space0($min_indent) - ), - $closing_brace - ) - ) - ) - }; -} - #[macro_export] macro_rules! collection_trailing_sep_e { ($opening_brace:expr, $elem:expr, $delimiter:expr, $closing_brace:expr, $min_indent:expr, $open_problem:expr, $space_problem:expr, $indent_problem:expr, $space_before:expr) => { diff --git a/compiler/parse/tests/test_parse.rs b/compiler/parse/tests/test_parse.rs index c49820045b..5032dcc010 100644 --- a/compiler/parse/tests/test_parse.rs +++ b/compiler/parse/tests/test_parse.rs @@ -3077,8 +3077,8 @@ mod test_parse { fn empty_app_header() { let arena = Bump::new(); let packages = Collection::empty(); - let imports = Vec::new_in(&arena); - let provides = Vec::new_in(&arena); + let imports = Collection::empty(); + let provides = Collection::empty(); let module_name = StrLiteral::PlainLine("test-app"); let header = AppHeader { name: Located::new(0, 0, 4, 14, module_name), @@ -3117,8 +3117,8 @@ mod test_parse { let arena = Bump::new(); let packages = Collection::empty(); - let imports = Vec::new_in(&arena); - let provides = Vec::new_in(&arena); + let imports = Collection::empty(); + let provides = Collection::empty(); let module_name = StrLiteral::PlainLine("test-app"); let header = AppHeader { before_header: &[], @@ -3166,11 +3166,11 @@ mod test_parse { let loc_pkg_entry = Located::new(1, 1, 15, 33, pkg_entry); let arena = Bump::new(); let packages = Collection::with_items(arena.alloc([loc_pkg_entry])); - let import = ImportsEntry::Package("foo", ModuleName::new("Bar.Baz"), Vec::new_in(&arena)); + let import = ImportsEntry::Package("foo", ModuleName::new("Bar.Baz"), Collection::empty()); let loc_import = Located::new(2, 2, 14, 25, import); - let imports = bumpalo::vec![in &arena; loc_import]; + let imports = Collection::with_items(arena.alloc([loc_import])); let provide_entry = Located::new(3, 3, 15, 24, Exposed("quicksort")); - let provides = bumpalo::vec![in &arena; provide_entry]; + let provides = Collection::with_items(arena.alloc([provide_entry])); let module_name = StrLiteral::PlainLine("quicksort"); let header = AppHeader { @@ -3222,11 +3222,40 @@ mod test_parse { let loc_pkg_entry = Located::new(1, 1, 15, 33, pkg_entry); let arena = Bump::new(); let packages = Collection::with_items(arena.alloc([loc_pkg_entry])); - let import = ImportsEntry::Package("foo", ModuleName::new("Bar.Baz"), Vec::new_in(&arena)); - let loc_import = Located::new(2, 2, 14, 25, import); - let imports = bumpalo::vec![in &arena; loc_import]; - let provide_entry = Located::new(3, 3, 15, 24, Exposed("quicksort")); - let provides = bumpalo::vec![in &arena; provide_entry]; + let import = ImportsEntry::Package( + "foo", + ModuleName::new("Bar"), + Collection::with_items_and_comments( + &arena, + arena.alloc([ + Located::new( + 3, + 3, + 8, + 11, + ExposesEntry::SpaceBefore( + arena.alloc(ExposesEntry::Exposed("Baz")), + arena.alloc([Newline]), + ), + ), + Located::new( + 4, + 4, + 8, + 17, + ExposesEntry::SpaceBefore( + arena.alloc(ExposesEntry::Exposed("FourtyTwo")), + arena.alloc([Newline]), + ), + ), + ]), + arena.alloc([Newline, LineComment(" I'm a happy comment")]), + ), + ); + let loc_import = Located::new(2, 6, 14, 5, import); + let imports = Collection::with_items(arena.alloc([loc_import])); + let provide_entry = Located::new(7, 7, 15, 24, Exposed("quicksort")); + let provides = Collection::with_items(arena.alloc([provide_entry])); let module_name = StrLiteral::PlainLine("quicksort"); let header = AppHeader { @@ -3235,7 +3264,7 @@ mod test_parse { packages, imports, provides, - to: Located::new(3, 3, 30, 34, To::ExistingPackage("base")), + to: Located::new(7, 7, 31, 35, To::ExistingPackage("base")), after_app_keyword: &[], before_packages: newlines, after_packages: &[], @@ -3253,8 +3282,12 @@ mod test_parse { r#" app "quicksort" packages { base: "./platform", } - imports [ foo.Bar.Baz ] - provides [ quicksort ] to base + imports [ foo.Bar.{ + Baz, + FourtyTwo, + # I'm a happy comment + } ] + provides [ quicksort, ] to base "# ); @@ -3285,7 +3318,7 @@ mod test_parse { let region2 = Region::new(0, 0, 45, 47); PlatformRequires { - rigids: Vec::new_in(&arena), + rigids: Collection::empty(), signature: Located::at( region1, TypedIdent::Entry { @@ -3307,10 +3340,10 @@ mod test_parse { before_header: &[], name: Located::new(0, 0, 9, 23, pkg_name), requires, - exposes: Vec::new_in(&arena), + exposes: Collection::empty(), packages: Collection::empty(), - imports: Vec::new_in(&arena), - provides: Vec::new_in(&arena), + imports: Collection::empty(), + provides: Collection::empty(), effects, after_platform_keyword: &[], before_requires: &[], @@ -3352,9 +3385,9 @@ mod test_parse { let loc_pkg_entry = Located::new(3, 3, 15, 27, pkg_entry); let arena = Bump::new(); let packages = Collection::with_items(arena.alloc([loc_pkg_entry])); - let imports = Vec::new_in(&arena); + let imports = Collection::empty(); let provide_entry = Located::new(5, 5, 15, 26, Exposed("mainForHost")); - let provides = bumpalo::vec![in &arena; provide_entry]; + let provides = Collection::with_items(arena.alloc([provide_entry])); let effects = Effects { effect_type_name: "Effect", effect_shortname: "fx", @@ -3370,7 +3403,13 @@ mod test_parse { let region3 = Region::new(1, 1, 14, 26); PlatformRequires { - rigids: bumpalo::vec![ in &arena; Located::at(region3, PlatformRigid::Entry { alias: "Model", rigid: "model" }) ], + rigids: Collection::with_items(arena.alloc([Located::at( + region3, + PlatformRigid::Entry { + alias: "Model", + rigid: "model", + }, + )])), signature: Located::at( region1, TypedIdent::Entry { @@ -3392,7 +3431,7 @@ mod test_parse { before_header: &[], name: Located::new(0, 0, 9, 19, pkg_name), requires, - exposes: Vec::new_in(&arena), + exposes: Collection::empty(), packages, imports, provides, @@ -3432,8 +3471,8 @@ mod test_parse { #[test] fn empty_interface_header() { let arena = Bump::new(); - let exposes = Vec::new_in(&arena); - let imports = Vec::new_in(&arena); + let exposes = Collection::empty(); + let imports = Collection::empty(); let module_name = ModuleName::new("Foo"); let header = InterfaceHeader { before_header: &[], @@ -3464,8 +3503,8 @@ mod test_parse { #[test] fn nested_module() { let arena = Bump::new(); - let exposes = Vec::new_in(&arena); - let imports = Vec::new_in(&arena); + let exposes = Collection::empty(); + let imports = Collection::empty(); let module_name = ModuleName::new("Foo.Bar.Baz"); let header = InterfaceHeader { before_header: &[], diff --git a/compiler/reporting/src/error/parse.rs b/compiler/reporting/src/error/parse.rs index 163a2b118b..505e41a736 100644 --- a/compiler/reporting/src/error/parse.rs +++ b/compiler/reporting/src/error/parse.rs @@ -3093,6 +3093,7 @@ fn to_provides_report<'a>( use roc_parse::parser::EProvides; match *parse_problem { + EProvides::ListEnd(row, col) | // TODO: give this its own error message EProvides::Identifier(row, col) => { let surroundings = Region::from_rows_cols(start_row, start_col, row, col); let region = Region::from_row_col(row, col); @@ -3158,6 +3159,7 @@ fn to_exposes_report<'a>( use roc_parse::parser::EExposes; match *parse_problem { + EExposes::ListEnd(row, col) | // TODO: give this its own error message EExposes::Identifier(row, col) => { let surroundings = Region::from_rows_cols(start_row, start_col, row, col); let region = Region::from_row_col(row, col); diff --git a/compiler/test_gen/src/gen_records.rs b/compiler/test_gen/src/gen_records.rs index 927daf0e18..1c6274e20b 100644 --- a/compiler/test_gen/src/gen_records.rs +++ b/compiler/test_gen/src/gen_records.rs @@ -11,7 +11,7 @@ use crate::helpers::wasm::assert_evals_to; use indoc::indoc; #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))] fn basic_record() { assert_evals_to!( indoc!( @@ -45,7 +45,7 @@ fn basic_record() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))] fn f64_record() { assert_evals_to!( indoc!( @@ -137,7 +137,7 @@ fn fn_record() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))] fn def_record() { assert_evals_to!( indoc!( @@ -192,7 +192,7 @@ fn when_on_record() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))] fn when_record_with_guard_pattern() { assert_evals_to!( indoc!( @@ -207,7 +207,7 @@ fn when_record_with_guard_pattern() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))] fn let_with_record_pattern() { assert_evals_to!( indoc!( @@ -223,7 +223,7 @@ fn let_with_record_pattern() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))] fn record_guard_pattern() { assert_evals_to!( indoc!( @@ -239,7 +239,7 @@ fn record_guard_pattern() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))] fn twice_record_access() { assert_evals_to!( indoc!( @@ -254,7 +254,7 @@ fn twice_record_access() { ); } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-dev"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn empty_record() { assert_evals_to!( indoc!( @@ -873,7 +873,7 @@ fn update_single_element_record() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn booleans_in_record() { assert_evals_to!( indoc!("{ x: 1 == 1, y: 1 == 1 }"), @@ -908,7 +908,7 @@ fn alignment_in_record() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn blue_and_present() { assert_evals_to!( indoc!( @@ -927,7 +927,7 @@ fn blue_and_present() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn blue_and_absent() { assert_evals_to!( indoc!(