Merge branch 'trunk' into decision-tree-remove-clone

This commit is contained in:
Folkert de Vries 2021-11-15 22:34:56 +01:00 committed by GitHub
commit f49bd04e68
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 463 additions and 491 deletions

View file

@ -899,42 +899,6 @@ pub fn listSublist(
return RocList.empty(); 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( pub fn listDropAt(
list: RocList, list: RocList,
alignment: u32, alignment: u32,

View file

@ -46,7 +46,6 @@ comptime {
exportListFn(list.listSortWith, "sort_with"); exportListFn(list.listSortWith, "sort_with");
exportListFn(list.listConcat, "concat"); exportListFn(list.listConcat, "concat");
exportListFn(list.listSublist, "sublist"); exportListFn(list.listSublist, "sublist");
exportListFn(list.listDrop, "drop");
exportListFn(list.listDropAt, "drop_at"); exportListFn(list.listDropAt, "drop_at");
exportListFn(list.listSet, "set"); exportListFn(list.listSet, "set");
exportListFn(list.listSetInPlace, "set_in_place"); exportListFn(list.listSetInPlace, "set_in_place");

View file

@ -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_APPEND: &str = "roc_builtins.list.append";
pub const LIST_PREPEND: &str = "roc_builtins.list.prepend"; pub const LIST_PREPEND: &str = "roc_builtins.list.prepend";
pub const LIST_SUBLIST: &str = "roc_builtins.list.sublist"; 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_DROP_AT: &str = "roc_builtins.list.drop_at";
pub const LIST_SWAP: &str = "roc_builtins.list.swap"; pub const LIST_SWAP: &str = "roc_builtins.list.swap";
pub const LIST_SINGLE: &str = "roc_builtins.list.single"; pub const LIST_SINGLE: &str = "roc_builtins.list.single";

View file

@ -2150,20 +2150,33 @@ fn list_sublist(symbol: Symbol, var_store: &mut VarStore) -> Def {
/// List.drop : List elem, Nat -> List elem /// List.drop : List elem, Nat -> List elem
fn list_drop(symbol: Symbol, var_store: &mut VarStore) -> Def { fn list_drop(symbol: Symbol, var_store: &mut VarStore) -> Def {
let list_var = var_store.fresh(); 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 { let body = RunLowLevel {
op: LowLevel::ListDrop, op: LowLevel::ListSublist,
args: vec![ args: vec![
(list_var, Var(Symbol::ARG_1)), (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, ret_var: list_var,
}; };
defn( defn(
symbol, 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, var_store,
body, body,
list_var, list_var,

View file

@ -1,6 +1,6 @@
use crate::spaces::{fmt_spaces, INDENT}; use crate::spaces::{fmt_spaces, INDENT};
use bumpalo::collections::{String, Vec}; use bumpalo::collections::String;
use roc_parse::ast::Module; use roc_parse::ast::{Collection, Module};
use roc_parse::header::{AppHeader, ExposesEntry, ImportsEntry, InterfaceHeader, PlatformHeader}; use roc_parse::header::{AppHeader, ExposesEntry, ImportsEntry, InterfaceHeader, PlatformHeader};
use roc_region::all::Located; 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_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>) { 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"); buf.push_str("imports");
fmt_spaces(buf, header.before_imports.iter(), indent); 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); 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>( fn fmt_imports<'a>(
buf: &mut String<'a>, buf: &mut String<'a>,
loc_entries: &'a Vec<'a, Located<ImportsEntry<'a>>>, loc_entries: Collection<'a, Located<ImportsEntry<'a>>>,
indent: u16, indent: u16,
) { ) {
buf.push('['); buf.push('[');
@ -112,7 +112,7 @@ fn fmt_imports<'a>(
fn fmt_exposes<'a>( fn fmt_exposes<'a>(
buf: &mut String<'a>, buf: &mut String<'a>,
loc_entries: &'a Vec<'a, Located<ExposesEntry<'a, &'a str>>>, loc_entries: &'a Collection<'a, Located<ExposesEntry<'a, &'a str>>>,
indent: u16, indent: u16,
) { ) {
buf.push('['); buf.push('[');

View file

@ -227,88 +227,18 @@ where
ret_layout, ret_layout,
.. ..
} => { } => {
// For most builtins instead of calling a function, we can just inline the low level. // If this function is just a lowlevel wrapper, then inline it
match *func_sym { if let Some(lowlevel) = LowLevel::from_inlined_wrapper(*func_sym) {
Symbol::NUM_ABS => self.build_run_low_level( self.build_run_low_level(
sym, sym,
&LowLevel::NumAbs, &lowlevel,
arguments, arguments,
arg_layouts, arg_layouts,
ret_layout, ret_layout,
), )
Symbol::NUM_ADD => self.build_run_low_level( } else if func_sym
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) .module_string(&self.env().interns)
.starts_with(ModuleName::APP) => .starts_with(ModuleName::APP)
{ {
let fn_name = LayoutIds::default() let fn_name = LayoutIds::default()
.get(*func_sym, layout) .get(*func_sym, layout)
@ -316,8 +246,11 @@ where
// Now that the arguments are needed, load them if they are literals. // Now that the arguments are needed, load them if they are literals.
self.load_literal_symbols(arguments)?; self.load_literal_symbols(arguments)?;
self.build_fn_call(sym, fn_name, arguments, arg_layouts, ret_layout) self.build_fn_call(sym, fn_name, arguments, arg_layouts, ret_layout)
} } else {
x => Err(format!("the function, {:?}, is not yet implemented", x)), Err(format!(
"the function, {:?}, is not yet implemented",
func_sym
))
} }
} }

View file

@ -9,10 +9,10 @@ use crate::llvm::build_dict::{
use crate::llvm::build_hash::generic_hash; use crate::llvm::build_hash::generic_hash;
use crate::llvm::build_list::{ use crate::llvm::build_list::{
self, allocate_list, empty_list, empty_polymorphic_list, list_any, list_append, list_concat, 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_contains, list_drop_at, list_find_trivial_not_found, list_find_unsafe, list_get_unsafe,
list_get_unsafe, list_join, list_keep_errs, list_keep_if, list_keep_oks, list_len, list_map, list_join, list_keep_errs, list_keep_if, list_keep_oks, list_len, list_map, list_map2,
list_map2, list_map3, list_map4, list_map_with_index, list_prepend, list_range, list_repeat, list_map3, list_map4, list_map_with_index, list_prepend, list_range, list_repeat, list_reverse,
list_reverse, list_set, list_single, list_sort_with, list_sublist, list_swap, list_set, list_single, list_sort_with, list_sublist, list_swap,
}; };
use crate::llvm::build_str::{ use crate::llvm::build_str::{
empty_str, str_concat, str_count_graphemes, str_ends_with, str_from_float, str_from_int, 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), _ => 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 => { ListDropAt => {
// List.dropAt : List elem, Nat -> List elem // List.dropAt : List elem, Nat -> List elem
debug_assert_eq!(args.len(), 2); debug_assert_eq!(args.len(), 2);

View file

@ -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 /// List.dropAt : List elem, Nat -> List elem
pub fn list_drop_at<'a, 'ctx, 'env>( pub fn list_drop_at<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,

View file

@ -2,12 +2,13 @@ use bumpalo::{self, collections::Vec};
use code_builder::Align; use code_builder::Align;
use roc_collections::all::MutMap; use roc_collections::all::MutMap;
use roc_module::low_level::LowLevel;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_mono::ir::{CallType, Expr, JoinPointId, Literal, Proc, Stmt}; use roc_mono::ir::{CallType, Expr, JoinPointId, Literal, Proc, Stmt};
use roc_mono::layout::{Builtin, Layout, LayoutIds}; use roc_mono::layout::{Builtin, Layout, LayoutIds};
use crate::layout::WasmLayout; 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::storage::{Storage, StoredValue, StoredValueKind};
use crate::wasm_module::linking::{ use crate::wasm_module::linking::{
DataSymbol, LinkingSection, RelocationSection, WasmObjectSymbol, WASM_SYM_BINDING_WEAK, DataSymbol, LinkingSection, RelocationSection, WasmObjectSymbol, WASM_SYM_BINDING_WEAK,
@ -22,8 +23,8 @@ use crate::wasm_module::{
LocalId, Signature, SymInfo, ValueType, LocalId, Signature, SymInfo, ValueType,
}; };
use crate::{ use crate::{
copy_memory, CopyMemoryConfig, Env, BUILTINS_IMPORT_MODULE_NAME, MEMORY_NAME, PTR_TYPE, copy_memory, CopyMemoryConfig, Env, BUILTINS_IMPORT_MODULE_NAME, MEMORY_NAME, PTR_SIZE,
STACK_POINTER_GLOBAL_ID, STACK_POINTER_NAME, PTR_TYPE, STACK_POINTER_GLOBAL_ID, STACK_POINTER_NAME,
}; };
/// The memory address where the constants data will be loaded during module instantiation. /// The memory address where the constants data will be loaded during module instantiation.
@ -469,6 +470,11 @@ impl<'a> WasmBackend<'a> {
arguments, arguments,
}) => match call_type { }) => match call_type {
CallType::ByName { name: func_sym, .. } => { 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<Symbol>; let mut wasm_args_tmp: Vec<Symbol>;
let (wasm_args, has_return_val) = match wasm_layout { let (wasm_args, has_return_val) = match wasm_layout {
WasmLayout::StackMemory { .. } => { WasmLayout::StackMemory { .. } => {
@ -511,10 +517,50 @@ impl<'a> WasmBackend<'a> {
} }
CallType::LowLevel { op: lowlevel, .. } => { CallType::LowLevel { op: lowlevel, .. } => {
let return_layout = WasmLayout::new(layout); 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); self.storage.load_symbols(&mut self.code_builder, arguments);
let build_result = build_call_low_level( let build_result = decode_low_level(
&mut self.code_builder, &mut self.code_builder,
&mut self.storage, &mut self.storage,
lowlevel, lowlevel,
@ -535,14 +581,6 @@ impl<'a> WasmBackend<'a> {
)), )),
} }
} }
x => Err(format!("the call type, {:?}, is not yet implemented", x)),
},
Expr::Struct(fields) => self.create_struct(sym, layout, fields),
x => Err(format!("Expression is not yet implemented {:?}", x)),
}
}
fn load_literal( fn load_literal(
&mut self, &mut self,
@ -741,14 +779,15 @@ impl<'a> WasmBackend<'a> {
}; };
self.module.import.entries.push(import); 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 { let sym_info = SymInfo::Function(WasmObjectSymbol::Imported {
flags: WASM_SYM_UNDEFINED, flags: WASM_SYM_UNDEFINED,
index: import_index, index: import_index,
}); });
self.linker_symbols.push(sym_info); 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( self.code_builder.call(

View file

@ -15,10 +15,10 @@ pub enum LowlevelBuildResult {
NotImplemented, NotImplemented,
} }
pub fn build_call_low_level<'a>( pub fn decode_low_level<'a>(
code_builder: &mut CodeBuilder<'a>, code_builder: &mut CodeBuilder<'a>,
storage: &mut Storage<'a>, storage: &mut Storage<'a>,
lowlevel: &LowLevel, lowlevel: LowLevel,
args: &'a [Symbol], args: &'a [Symbol],
ret_layout: &WasmLayout, ret_layout: &WasmLayout,
) -> LowlevelBuildResult { ) -> LowlevelBuildResult {
@ -34,9 +34,9 @@ pub fn build_call_low_level<'a>(
| ListConcat | ListContains | ListAppend | ListPrepend | ListJoin | ListRange | ListMap | ListConcat | ListContains | ListAppend | ListPrepend | ListJoin | ListRange | ListMap
| ListMap2 | ListMap3 | ListMap4 | ListMapWithIndex | ListKeepIf | ListWalk | ListMap2 | ListMap3 | ListMap4 | ListMapWithIndex | ListKeepIf | ListWalk
| ListWalkUntil | ListWalkBackwards | ListKeepOks | ListKeepErrs | ListSortWith | ListWalkUntil | ListWalkBackwards | ListKeepOks | ListKeepErrs | ListSortWith
| ListSublist | ListDrop | ListDropAt | ListSwap | ListAny | ListFindUnsafe | DictSize | ListSublist | ListDropAt | ListSwap | ListAny | ListFindUnsafe | DictSize | DictEmpty
| DictEmpty | DictInsert | DictRemove | DictContains | DictGetUnsafe | DictKeys | DictInsert | DictRemove | DictContains | DictGetUnsafe | DictKeys | DictValues
| DictValues | DictUnion | DictIntersection | DictDifference | DictWalk | SetFromList => { | DictUnion | DictIntersection | DictDifference | DictWalk | SetFromList => {
return NotImplemented; return NotImplemented;
} }
@ -129,6 +129,9 @@ pub fn build_call_low_level<'a>(
NumIsMultipleOf => return NotImplemented, NumIsMultipleOf => return NotImplemented,
NumAbs => match ret_layout.value_type() { NumAbs => match ret_layout.value_type() {
I32 => { 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); code_builder.i32_const(0);
storage.load_symbols(code_builder, args); storage.load_symbols(code_builder, args);
code_builder.i32_sub(); code_builder.i32_sub();
@ -138,6 +141,9 @@ pub fn build_call_low_level<'a>(
code_builder.select(); code_builder.select();
} }
I64 => { 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); code_builder.i64_const(0);
storage.load_symbols(code_builder, args); storage.load_symbols(code_builder, args);
code_builder.i64_sub(); code_builder.i64_sub();

View file

@ -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 /// Generate code to copy from one StoredValue to another
/// Copies the _entire_ value. For struct fields etc., see `copy_value_to_memory` /// Copies the _entire_ value. For struct fields etc., see `copy_value_to_memory`
pub fn clone_value( pub fn clone_value(

View file

@ -2608,8 +2608,8 @@ fn parse_header<'a>(
opt_shorthand, opt_shorthand,
header_src, header_src,
packages: &[], packages: &[],
exposes: header.exposes.into_bump_slice(), exposes: header.exposes.items,
imports: header.imports.into_bump_slice(), imports: header.imports.items,
to_platform: None, to_platform: None,
}; };
@ -2642,8 +2642,8 @@ fn parse_header<'a>(
opt_shorthand, opt_shorthand,
header_src, header_src,
packages, packages,
exposes: header.provides.into_bump_slice(), exposes: header.provides.items,
imports: header.imports.into_bump_slice(), imports: header.imports.items,
to_platform: Some(header.to.value.clone()), to_platform: Some(header.to.value.clone()),
}; };
@ -3236,7 +3236,7 @@ fn send_header_two<'a>(
let extra = HeaderFor::PkgConfig { let extra = HeaderFor::PkgConfig {
config_shorthand: shorthand, config_shorthand: shorthand,
platform_main_type: requires[0].value.clone(), platform_main_type: requires[0].value,
main_for_host, main_for_host,
}; };
@ -3409,8 +3409,7 @@ fn fabricate_pkg_config_module<'a>(
header_src: &'a str, header_src: &'a str,
module_timing: ModuleTiming, module_timing: ModuleTiming,
) -> (ModuleId, Msg<'a>) { ) -> (ModuleId, Msg<'a>) {
let provides: &'a [Located<ExposesEntry<'a, &'a str>>] = let provides: &'a [Located<ExposesEntry<'a, &'a str>>] = header.provides.items;
header.provides.clone().into_bump_slice();
let info = PlatformHeaderInfo { let info = PlatformHeaderInfo {
filename, filename,
@ -3420,8 +3419,8 @@ fn fabricate_pkg_config_module<'a>(
app_module_id, app_module_id,
packages: &[], packages: &[],
provides, provides,
requires: arena.alloc([header.requires.signature.clone()]), requires: arena.alloc([header.requires.signature]),
imports: header.imports.clone().into_bump_slice(), imports: header.imports.items,
}; };
send_header_two( send_header_two(
@ -3467,7 +3466,7 @@ fn fabricate_effects_module<'a>(
{ {
let mut module_ids = (*module_ids).lock(); 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 { if let ExposesEntry::Exposed(module_name) = exposed.value {
module_ids.get_or_insert(&PQModuleName::Qualified( module_ids.get_or_insert(&PQModuleName::Qualified(
shorthand, shorthand,
@ -3886,7 +3885,7 @@ fn exposed_from_import<'a>(entry: &ImportsEntry<'a>) -> (QualifiedModuleName<'a>
Module(module_name, exposes) => { Module(module_name, exposes) => {
let mut exposed = Vec::with_capacity(exposes.len()); 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)); 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) => { Package(package_name, module_name, exposes) => {
let mut exposed = Vec::with_capacity(exposes.len()); 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)); exposed.push(ident_from_exposed(&loc_entry.value));
} }

View file

@ -1,3 +1,5 @@
use crate::symbol::Symbol;
/// Low-level operations that get translated directly into e.g. LLVM instructions. /// 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 /// These are always wrapped when exposed to end users, and can only make it
/// into an Expr when added directly by can::builtins /// into an Expr when added directly by can::builtins
@ -45,7 +47,6 @@ pub enum LowLevel {
ListKeepErrs, ListKeepErrs,
ListSortWith, ListSortWith,
ListSublist, ListSublist,
ListDrop,
ListDropAt, ListDropAt,
ListSwap, ListSwap,
ListAny, ListAny,
@ -115,106 +116,6 @@ pub enum LowLevel {
ExpectTrue, 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 { macro_rules! higher_order {
() => { () => {
ListMap ListMap
@ -241,17 +142,13 @@ impl LowLevel {
pub fn is_higher_order(&self) -> bool { pub fn is_higher_order(&self) -> bool {
use LowLevel::*; use LowLevel::*;
match self { matches!(self, higher_order!())
first_order!() => false,
higher_order!() => true,
}
} }
pub fn function_argument_position(&self) -> usize { pub fn function_argument_position(&self) -> usize {
use LowLevel::*; use LowLevel::*;
match self { match self {
first_order!() => unreachable!(),
ListMap => 1, ListMap => 1,
ListMap2 => 2, ListMap2 => 2,
ListMap3 => 3, ListMap3 => 3,
@ -267,6 +164,127 @@ impl LowLevel {
ListAny => 1, ListAny => 1,
ListFindUnsafe => 1, ListFindUnsafe => 1,
DictWalk => 2, 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<LowLevel> {
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,
} }
} }
} }

View file

@ -968,7 +968,6 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
// List.append should own its first argument // List.append should own its first argument
ListAppend => arena.alloc_slice_copy(&[owned, owned]), ListAppend => arena.alloc_slice_copy(&[owned, owned]),
ListSublist => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]), ListSublist => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]),
ListDrop => arena.alloc_slice_copy(&[owned, irrelevant]),
ListDropAt => arena.alloc_slice_copy(&[owned, irrelevant]), ListDropAt => arena.alloc_slice_copy(&[owned, irrelevant]),
ListSwap => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]), ListSwap => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]),

View file

@ -101,7 +101,6 @@ enum FirstOrder {
ListGetUnsafe, ListGetUnsafe,
ListSet, ListSet,
ListSublist, ListSublist,
ListDrop,
ListDropAt, ListDropAt,
ListSingle, ListSingle,
ListRepeat, ListRepeat,

View file

@ -38,7 +38,7 @@ pub enum PackageOrPath<'a> {
Path(StrLiteral<'a>), Path(StrLiteral<'a>),
} }
#[derive(Clone, PartialEq, Eq, Debug, Hash)] #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub struct ModuleName<'a>(&'a str); pub struct ModuleName<'a>(&'a str);
impl<'a> From<ModuleName<'a>> for &'a str { impl<'a> From<ModuleName<'a>> for &'a str {
@ -60,8 +60,8 @@ impl<'a> ModuleName<'a> {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct InterfaceHeader<'a> { pub struct InterfaceHeader<'a> {
pub name: Loc<ModuleName<'a>>, pub name: Loc<ModuleName<'a>>,
pub exposes: Vec<'a, Loc<ExposesEntry<'a, &'a str>>>, pub exposes: Collection<'a, Loc<ExposesEntry<'a, &'a str>>>,
pub imports: Vec<'a, Loc<ImportsEntry<'a>>>, pub imports: Collection<'a, Loc<ImportsEntry<'a>>>,
// Potential comments and newlines - these will typically all be empty. // Potential comments and newlines - these will typically all be empty.
pub before_header: &'a [CommentOrNewline<'a>], pub before_header: &'a [CommentOrNewline<'a>],
@ -82,8 +82,8 @@ pub enum To<'a> {
pub struct AppHeader<'a> { pub struct AppHeader<'a> {
pub name: Loc<StrLiteral<'a>>, pub name: Loc<StrLiteral<'a>>,
pub packages: Collection<'a, Loc<PackageEntry<'a>>>, pub packages: Collection<'a, Loc<PackageEntry<'a>>>,
pub imports: Vec<'a, Loc<ImportsEntry<'a>>>, pub imports: Collection<'a, Loc<ImportsEntry<'a>>>,
pub provides: Vec<'a, Loc<ExposesEntry<'a, &'a str>>>, pub provides: Collection<'a, Loc<ExposesEntry<'a, &'a str>>>,
pub to: Loc<To<'a>>, pub to: Loc<To<'a>>,
// Potential comments and newlines - these will typically all be empty. // Potential comments and newlines - these will typically all be empty.
@ -117,7 +117,7 @@ pub struct PackageHeader<'a> {
pub after_imports: &'a [CommentOrNewline<'a>], pub after_imports: &'a [CommentOrNewline<'a>],
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Copy, Clone, Debug, PartialEq)]
pub enum PlatformRigid<'a> { pub enum PlatformRigid<'a> {
Entry { rigid: &'a str, alias: &'a str }, Entry { rigid: &'a str, alias: &'a str },
@ -137,7 +137,7 @@ impl<'a> Spaceable<'a> for PlatformRigid<'a> {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct PlatformRequires<'a> { pub struct PlatformRequires<'a> {
pub rigids: Vec<'a, Loc<PlatformRigid<'a>>>, pub rigids: Collection<'a, Loc<PlatformRigid<'a>>>,
pub signature: Loc<TypedIdent<'a>>, pub signature: Loc<TypedIdent<'a>>,
} }
@ -145,10 +145,10 @@ pub struct PlatformRequires<'a> {
pub struct PlatformHeader<'a> { pub struct PlatformHeader<'a> {
pub name: Loc<PackageName<'a>>, pub name: Loc<PackageName<'a>>,
pub requires: PlatformRequires<'a>, pub requires: PlatformRequires<'a>,
pub exposes: Vec<'a, Loc<ExposesEntry<'a, ModuleName<'a>>>>, pub exposes: Collection<'a, Loc<ExposesEntry<'a, ModuleName<'a>>>>,
pub packages: Collection<'a, Loc<PackageEntry<'a>>>, pub packages: Collection<'a, Loc<PackageEntry<'a>>>,
pub imports: Vec<'a, Loc<ImportsEntry<'a>>>, pub imports: Collection<'a, Loc<ImportsEntry<'a>>>,
pub provides: Vec<'a, Loc<ExposesEntry<'a, &'a str>>>, pub provides: Collection<'a, Loc<ExposesEntry<'a, &'a str>>>,
pub effects: Effects<'a>, pub effects: Effects<'a>,
// Potential comments and newlines - these will typically all be empty. // Potential comments and newlines - these will typically all be empty.
@ -177,7 +177,7 @@ pub struct Effects<'a> {
pub entries: &'a [Loc<TypedIdent<'a>>], pub entries: &'a [Loc<TypedIdent<'a>>],
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Copy, Clone, Debug, PartialEq)]
pub enum ExposesEntry<'a, T> { pub enum ExposesEntry<'a, T> {
/// e.g. `Task` /// e.g. `Task`
Exposed(T), 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> { pub enum ImportsEntry<'a> {
/// e.g. `Task` or `Task.{ Task, after }` /// e.g. `Task` or `Task.{ Task, after }`
Module(ModuleName<'a>, Vec<'a, Loc<ExposesEntry<'a, &'a str>>>), Module(
ModuleName<'a>,
Collection<'a, Loc<ExposesEntry<'a, &'a str>>>,
),
/// e.g. `base.Task` or `base.Task.{ after }` or `base.{ Task.{ Task, after } }` /// e.g. `base.Task` or `base.Task.{ after }` or `base.{ Task.{ Task, after } }`
Package( Package(
&'a str, &'a str,
ModuleName<'a>, ModuleName<'a>,
Vec<'a, Loc<ExposesEntry<'a, &'a str>>>, Collection<'a, Loc<ExposesEntry<'a, &'a str>>>,
), ),
// Spaces // Spaces
@ -224,7 +227,7 @@ impl<'a> ExposesEntry<'a, &'a str> {
} }
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Copy, Clone, Debug, PartialEq)]
pub enum TypedIdent<'a> { pub enum TypedIdent<'a> {
/// e.g. /// e.g.
/// ///

View file

@ -220,11 +220,11 @@ fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> {
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
let opt_imports: Option<( let opt_imports: Option<(
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), (&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
Vec<'a, Located<ImportsEntry<'a>>>, Collection<'a, Located<ImportsEntry<'a>>>,
)> = opt_imports; )> = opt_imports;
let ((before_imports, after_imports), 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 provides: ProvidesTo<'a> = provides; // rustc must be told the type here
let header = AppHeader { let header = AppHeader {
@ -303,7 +303,7 @@ fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>, EHeader<'a>> {
#[derive(Debug)] #[derive(Debug)]
struct ProvidesTo<'a> { struct ProvidesTo<'a> {
entries: Vec<'a, Located<ExposesEntry<'a, &'a str>>>, entries: Collection<'a, Located<ExposesEntry<'a, &'a str>>>,
to: Located<To<'a>>, to: Located<To<'a>>,
before_provides_keyword: &'a [CommentOrNewline<'a>], before_provides_keyword: &'a [CommentOrNewline<'a>],
@ -362,7 +362,7 @@ fn provides_without_to<'a>() -> impl Parser<
'a, 'a,
( (
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), (&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
Vec<'a, Located<ExposesEntry<'a, &'a str>>>, Collection<'a, Located<ExposesEntry<'a, &'a str>>>,
), ),
EProvides<'a>, EProvides<'a>,
> { > {
@ -376,14 +376,16 @@ fn provides_without_to<'a>() -> impl Parser<
EProvides::IndentProvides, EProvides::IndentProvides,
EProvides::IndentListStart EProvides::IndentListStart
), ),
collection_e!( collection_trailing_sep_e!(
word1(b'[', EProvides::ListStart), word1(b'[', EProvides::ListStart),
exposes_entry(EProvides::Identifier), exposes_entry(EProvides::Identifier),
word1(b',', EProvides::ListEnd), word1(b',', EProvides::ListEnd),
word1(b']', EProvides::ListEnd), word1(b']', EProvides::ListEnd),
min_indent, min_indent,
EProvides::Open,
EProvides::Space, 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)] #[inline(always)]
fn requires_rigids<'a>( fn requires_rigids<'a>(
min_indent: u16, min_indent: u16,
) -> impl Parser<'a, Vec<'a, Located<PlatformRigid<'a>>>, ERequires<'a>> { ) -> impl Parser<'a, Collection<'a, Located<PlatformRigid<'a>>>, ERequires<'a>> {
collection_e!( collection_trailing_sep_e!(
word1(b'{', ERequires::ListStart), word1(b'{', ERequires::ListStart),
specialize(|_, r, c| ERequires::Rigid(r, c), loc!(requires_rigid())), specialize(|_, r, c| ERequires::Rigid(r, c), loc!(requires_rigid())),
word1(b',', ERequires::ListEnd), word1(b',', ERequires::ListEnd),
word1(b'}', ERequires::ListEnd), word1(b'}', ERequires::ListEnd),
min_indent, min_indent,
ERequires::Open,
ERequires::Space, ERequires::Space,
ERequires::IndentListEnd ERequires::IndentListEnd,
PlatformRigid::SpaceBefore
) )
} }
@ -487,7 +491,7 @@ fn exposes_values<'a>() -> impl Parser<
'a, 'a,
( (
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), (&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
Vec<'a, Located<ExposesEntry<'a, &'a str>>>, Collection<'a, Located<ExposesEntry<'a, &'a str>>>,
), ),
EExposes, EExposes,
> { > {
@ -502,14 +506,16 @@ fn exposes_values<'a>() -> impl Parser<
EExposes::IndentExposes, EExposes::IndentExposes,
EExposes::IndentListStart EExposes::IndentListStart
), ),
collection_e!( collection_trailing_sep_e!(
word1(b'[', EExposes::ListStart), word1(b'[', EExposes::ListStart),
exposes_entry(EExposes::Identifier), exposes_entry(EExposes::Identifier),
word1(b',', EExposes::ListEnd), word1(b',', EExposes::ListEnd),
word1(b']', EExposes::ListEnd), word1(b']', EExposes::ListEnd),
min_indent, min_indent,
EExposes::Open,
EExposes::Space, EExposes::Space,
EExposes::IndentListEnd EExposes::IndentListEnd,
ExposesEntry::SpaceBefore
) )
) )
} }
@ -539,7 +545,7 @@ fn exposes_modules<'a>() -> impl Parser<
'a, 'a,
( (
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), (&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
Vec<'a, Located<ExposesEntry<'a, ModuleName<'a>>>>, Collection<'a, Located<ExposesEntry<'a, ModuleName<'a>>>>,
), ),
EExposes, EExposes,
> { > {
@ -554,14 +560,16 @@ fn exposes_modules<'a>() -> impl Parser<
EExposes::IndentExposes, EExposes::IndentExposes,
EExposes::IndentListStart EExposes::IndentListStart
), ),
collection_e!( collection_trailing_sep_e!(
word1(b'[', EExposes::ListStart), word1(b'[', EExposes::ListStart),
exposes_module(EExposes::Identifier), exposes_module(EExposes::Identifier),
word1(b',', EExposes::ListEnd), word1(b',', EExposes::ListEnd),
word1(b']', EExposes::ListEnd), word1(b']', EExposes::ListEnd),
min_indent, min_indent,
EExposes::Open,
EExposes::Space, EExposes::Space,
EExposes::IndentListEnd EExposes::IndentListEnd,
ExposesEntry::SpaceBefore
) )
) )
} }
@ -631,7 +639,7 @@ fn imports<'a>() -> impl Parser<
'a, 'a,
( (
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), (&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
Vec<'a, Located<ImportsEntry<'a>>>, Collection<'a, Located<ImportsEntry<'a>>>,
), ),
EImports, EImports,
> { > {
@ -646,14 +654,16 @@ fn imports<'a>() -> impl Parser<
EImports::IndentImports, EImports::IndentImports,
EImports::IndentListStart EImports::IndentListStart
), ),
collection_e!( collection_trailing_sep_e!(
word1(b'[', EImports::ListStart), word1(b'[', EImports::ListStart),
loc!(imports_entry()), loc!(imports_entry()),
word1(b',', EImports::ListEnd), word1(b',', EImports::ListEnd),
word1(b']', EImports::ListEnd), word1(b']', EImports::ListEnd),
min_indent, min_indent,
EImports::Open,
EImports::Space, 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) space0_e(min_indent, EEffects::Space, EEffects::IndentListStart)
) )
.parse(arena, state)?; .parse(arena, state)?;
let (_, entries, state) = collection_e!( let (_, entries, state) = collection_trailing_sep_e!(
word1(b'{', EEffects::ListStart), word1(b'{', EEffects::ListStart),
specialize(EEffects::TypedIdent, loc!(typed_ident())), specialize(EEffects::TypedIdent, loc!(typed_ident())),
word1(b',', EEffects::ListEnd), word1(b',', EEffects::ListEnd),
word1(b'}', EEffects::ListEnd), word1(b'}', EEffects::ListEnd),
min_indent, min_indent,
EEffects::Open,
EEffects::Space, EEffects::Space,
EEffects::IndentListEnd EEffects::IndentListEnd,
TypedIdent::SpaceBefore
) )
.parse(arena, state)?; .parse(arena, state)?;
@ -706,7 +718,7 @@ fn effects<'a>() -> impl Parser<'a, Effects<'a>, EEffects<'a>> {
spaces_after_type_name, spaces_after_type_name,
effect_shortname: type_shortname, effect_shortname: type_shortname,
effect_type_name: type_name, effect_type_name: type_name,
entries: entries.into_bump_slice(), entries: entries.items,
}, },
state, state,
)) ))
@ -768,7 +780,7 @@ fn imports_entry<'a>() -> impl Parser<'a, ImportsEntry<'a>, EImports> {
type Temp<'a> = ( type Temp<'a> = (
(Option<&'a str>, ModuleName<'a>), (Option<&'a str>, ModuleName<'a>),
Option<Vec<'a, Located<ExposesEntry<'a, &'a str>>>>, Option<Collection<'a, Located<ExposesEntry<'a, &'a str>>>>,
); );
map_with_arena!( map_with_arena!(
@ -785,19 +797,21 @@ fn imports_entry<'a>() -> impl Parser<'a, ImportsEntry<'a>, EImports> {
// e.g. `.{ Task, after}` // e.g. `.{ Task, after}`
maybe!(skip_first!( maybe!(skip_first!(
word1(b'.', EImports::ExposingDot), word1(b'.', EImports::ExposingDot),
collection_e!( collection_trailing_sep_e!(
word1(b'{', EImports::SetStart), word1(b'{', EImports::SetStart),
exposes_entry(EImports::Identifier), exposes_entry(EImports::Identifier),
word1(b',', EImports::SetEnd), word1(b',', EImports::SetEnd),
word1(b'}', EImports::SetEnd), word1(b'}', EImports::SetEnd),
min_indent, min_indent,
EImports::Open,
EImports::Space, EImports::Space,
EImports::IndentSetEnd EImports::IndentSetEnd,
ExposesEntry::SpaceBefore
) )
)) ))
), ),
|arena, ((opt_shortname, module_name), opt_values): Temp<'a>| { |_arena, ((opt_shortname, module_name), opt_values): Temp<'a>| {
let exposed_values = opt_values.unwrap_or_else(|| Vec::new_in(arena)); let exposed_values = opt_values.unwrap_or_else(Collection::empty);
match opt_shortname { match opt_shortname {
Some(shortname) => ImportsEntry::Package(shortname, module_name, exposed_values), Some(shortname) => ImportsEntry::Package(shortname, module_name, exposed_values),

View file

@ -214,6 +214,7 @@ pub enum EHeader<'a> {
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum EProvides<'a> { pub enum EProvides<'a> {
Provides(Row, Col), Provides(Row, Col),
Open(Row, Col),
To(Row, Col), To(Row, Col),
IndentProvides(Row, Col), IndentProvides(Row, Col),
IndentTo(Row, Col), IndentTo(Row, Col),
@ -230,6 +231,7 @@ pub enum EProvides<'a> {
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EExposes { pub enum EExposes {
Exposes(Row, Col), Exposes(Row, Col),
Open(Row, Col),
IndentExposes(Row, Col), IndentExposes(Row, Col),
IndentListStart(Row, Col), IndentListStart(Row, Col),
IndentListEnd(Row, Col), IndentListEnd(Row, Col),
@ -242,6 +244,7 @@ pub enum EExposes {
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum ERequires<'a> { pub enum ERequires<'a> {
Requires(Row, Col), Requires(Row, Col),
Open(Row, Col),
IndentRequires(Row, Col), IndentRequires(Row, Col),
IndentListStart(Row, Col), IndentListStart(Row, Col),
IndentListEnd(Row, Col), IndentListEnd(Row, Col),
@ -302,6 +305,7 @@ pub enum EPackageEntry<'a> {
pub enum EEffects<'a> { pub enum EEffects<'a> {
Space(BadInputError, Row, Col), Space(BadInputError, Row, Col),
Effects(Row, Col), Effects(Row, Col),
Open(Row, Col),
IndentEffects(Row, Col), IndentEffects(Row, Col),
ListStart(Row, Col), ListStart(Row, Col),
ListEnd(Row, Col), ListEnd(Row, Col),
@ -315,6 +319,7 @@ pub enum EEffects<'a> {
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EImports { pub enum EImports {
Open(Row, Col),
Imports(Row, Col), Imports(Row, Col),
IndentImports(Row, Col), IndentImports(Row, Col),
IndentListStart(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_export]
macro_rules! collection_trailing_sep_e { 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) => { ($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) => {

View file

@ -3077,8 +3077,8 @@ mod test_parse {
fn empty_app_header() { fn empty_app_header() {
let arena = Bump::new(); let arena = Bump::new();
let packages = Collection::empty(); let packages = Collection::empty();
let imports = Vec::new_in(&arena); let imports = Collection::empty();
let provides = Vec::new_in(&arena); let provides = Collection::empty();
let module_name = StrLiteral::PlainLine("test-app"); let module_name = StrLiteral::PlainLine("test-app");
let header = AppHeader { let header = AppHeader {
name: Located::new(0, 0, 4, 14, module_name), name: Located::new(0, 0, 4, 14, module_name),
@ -3117,8 +3117,8 @@ mod test_parse {
let arena = Bump::new(); let arena = Bump::new();
let packages = Collection::empty(); let packages = Collection::empty();
let imports = Vec::new_in(&arena); let imports = Collection::empty();
let provides = Vec::new_in(&arena); let provides = Collection::empty();
let module_name = StrLiteral::PlainLine("test-app"); let module_name = StrLiteral::PlainLine("test-app");
let header = AppHeader { let header = AppHeader {
before_header: &[], before_header: &[],
@ -3166,11 +3166,11 @@ mod test_parse {
let loc_pkg_entry = Located::new(1, 1, 15, 33, pkg_entry); let loc_pkg_entry = Located::new(1, 1, 15, 33, pkg_entry);
let arena = Bump::new(); let arena = Bump::new();
let packages = Collection::with_items(arena.alloc([loc_pkg_entry])); 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 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 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 module_name = StrLiteral::PlainLine("quicksort");
let header = AppHeader { let header = AppHeader {
@ -3222,11 +3222,40 @@ mod test_parse {
let loc_pkg_entry = Located::new(1, 1, 15, 33, pkg_entry); let loc_pkg_entry = Located::new(1, 1, 15, 33, pkg_entry);
let arena = Bump::new(); let arena = Bump::new();
let packages = Collection::with_items(arena.alloc([loc_pkg_entry])); 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(
let loc_import = Located::new(2, 2, 14, 25, import); "foo",
let imports = bumpalo::vec![in &arena; loc_import]; ModuleName::new("Bar"),
let provide_entry = Located::new(3, 3, 15, 24, Exposed("quicksort")); Collection::with_items_and_comments(
let provides = bumpalo::vec![in &arena; provide_entry]; &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 module_name = StrLiteral::PlainLine("quicksort");
let header = AppHeader { let header = AppHeader {
@ -3235,7 +3264,7 @@ mod test_parse {
packages, packages,
imports, imports,
provides, provides,
to: Located::new(3, 3, 30, 34, To::ExistingPackage("base")), to: Located::new(7, 7, 31, 35, To::ExistingPackage("base")),
after_app_keyword: &[], after_app_keyword: &[],
before_packages: newlines, before_packages: newlines,
after_packages: &[], after_packages: &[],
@ -3253,8 +3282,12 @@ mod test_parse {
r#" r#"
app "quicksort" app "quicksort"
packages { base: "./platform", } packages { base: "./platform", }
imports [ foo.Bar.Baz ] imports [ foo.Bar.{
provides [ quicksort ] to base 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); let region2 = Region::new(0, 0, 45, 47);
PlatformRequires { PlatformRequires {
rigids: Vec::new_in(&arena), rigids: Collection::empty(),
signature: Located::at( signature: Located::at(
region1, region1,
TypedIdent::Entry { TypedIdent::Entry {
@ -3307,10 +3340,10 @@ mod test_parse {
before_header: &[], before_header: &[],
name: Located::new(0, 0, 9, 23, pkg_name), name: Located::new(0, 0, 9, 23, pkg_name),
requires, requires,
exposes: Vec::new_in(&arena), exposes: Collection::empty(),
packages: Collection::empty(), packages: Collection::empty(),
imports: Vec::new_in(&arena), imports: Collection::empty(),
provides: Vec::new_in(&arena), provides: Collection::empty(),
effects, effects,
after_platform_keyword: &[], after_platform_keyword: &[],
before_requires: &[], before_requires: &[],
@ -3352,9 +3385,9 @@ mod test_parse {
let loc_pkg_entry = Located::new(3, 3, 15, 27, pkg_entry); let loc_pkg_entry = Located::new(3, 3, 15, 27, pkg_entry);
let arena = Bump::new(); let arena = Bump::new();
let packages = Collection::with_items(arena.alloc([loc_pkg_entry])); 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 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 { let effects = Effects {
effect_type_name: "Effect", effect_type_name: "Effect",
effect_shortname: "fx", effect_shortname: "fx",
@ -3370,7 +3403,13 @@ mod test_parse {
let region3 = Region::new(1, 1, 14, 26); let region3 = Region::new(1, 1, 14, 26);
PlatformRequires { 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( signature: Located::at(
region1, region1,
TypedIdent::Entry { TypedIdent::Entry {
@ -3392,7 +3431,7 @@ mod test_parse {
before_header: &[], before_header: &[],
name: Located::new(0, 0, 9, 19, pkg_name), name: Located::new(0, 0, 9, 19, pkg_name),
requires, requires,
exposes: Vec::new_in(&arena), exposes: Collection::empty(),
packages, packages,
imports, imports,
provides, provides,
@ -3432,8 +3471,8 @@ mod test_parse {
#[test] #[test]
fn empty_interface_header() { fn empty_interface_header() {
let arena = Bump::new(); let arena = Bump::new();
let exposes = Vec::new_in(&arena); let exposes = Collection::empty();
let imports = Vec::new_in(&arena); let imports = Collection::empty();
let module_name = ModuleName::new("Foo"); let module_name = ModuleName::new("Foo");
let header = InterfaceHeader { let header = InterfaceHeader {
before_header: &[], before_header: &[],
@ -3464,8 +3503,8 @@ mod test_parse {
#[test] #[test]
fn nested_module() { fn nested_module() {
let arena = Bump::new(); let arena = Bump::new();
let exposes = Vec::new_in(&arena); let exposes = Collection::empty();
let imports = Vec::new_in(&arena); let imports = Collection::empty();
let module_name = ModuleName::new("Foo.Bar.Baz"); let module_name = ModuleName::new("Foo.Bar.Baz");
let header = InterfaceHeader { let header = InterfaceHeader {
before_header: &[], before_header: &[],

View file

@ -3093,6 +3093,7 @@ fn to_provides_report<'a>(
use roc_parse::parser::EProvides; use roc_parse::parser::EProvides;
match *parse_problem { match *parse_problem {
EProvides::ListEnd(row, col) | // TODO: give this its own error message
EProvides::Identifier(row, col) => { EProvides::Identifier(row, col) => {
let surroundings = Region::from_rows_cols(start_row, start_col, row, col); let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
let region = Region::from_row_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; use roc_parse::parser::EExposes;
match *parse_problem { match *parse_problem {
EExposes::ListEnd(row, col) | // TODO: give this its own error message
EExposes::Identifier(row, col) => { EExposes::Identifier(row, col) => {
let surroundings = Region::from_rows_cols(start_row, start_col, row, col); let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
let region = Region::from_row_col(row, col); let region = Region::from_row_col(row, col);

View file

@ -11,7 +11,7 @@ use crate::helpers::wasm::assert_evals_to;
use indoc::indoc; use indoc::indoc;
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn basic_record() { fn basic_record() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -45,7 +45,7 @@ fn basic_record() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn f64_record() { fn f64_record() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -137,7 +137,7 @@ fn fn_record() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn def_record() { fn def_record() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -192,7 +192,7 @@ fn when_on_record() {
} }
#[test] #[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() { fn when_record_with_guard_pattern() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -207,7 +207,7 @@ fn when_record_with_guard_pattern() {
} }
#[test] #[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() { fn let_with_record_pattern() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -223,7 +223,7 @@ fn let_with_record_pattern() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn record_guard_pattern() { fn record_guard_pattern() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -239,7 +239,7 @@ fn record_guard_pattern() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn twice_record_access() { fn twice_record_access() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -254,7 +254,7 @@ fn twice_record_access() {
); );
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-dev"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn empty_record() { fn empty_record() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -873,7 +873,7 @@ fn update_single_element_record() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn booleans_in_record() { fn booleans_in_record() {
assert_evals_to!( assert_evals_to!(
indoc!("{ x: 1 == 1, y: 1 == 1 }"), indoc!("{ x: 1 == 1, y: 1 == 1 }"),
@ -908,7 +908,7 @@ fn alignment_in_record() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn blue_and_present() { fn blue_and_present() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -927,7 +927,7 @@ fn blue_and_present() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn blue_and_absent() { fn blue_and_absent() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(