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();
}
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,

View file

@ -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");

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_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";

View file

@ -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,

View file

@ -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<ImportsEntry<'a>>>,
loc_entries: Collection<'a, Located<ImportsEntry<'a>>>,
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<ExposesEntry<'a, &'a str>>>,
loc_entries: &'a Collection<'a, Located<ExposesEntry<'a, &'a str>>>,
indent: u16,
) {
buf.push('[');

View file

@ -227,88 +227,18 @@ 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
)
} else if func_sym
.module_string(&self.env().interns)
.starts_with(ModuleName::APP) =>
.starts_with(ModuleName::APP)
{
let fn_name = LayoutIds::default()
.get(*func_sym, layout)
@ -316,8 +246,11 @@ where
// 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 {
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_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);

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
pub fn list_drop_at<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,

View file

@ -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<Symbol>;
let (wasm_args, has_return_val) = match wasm_layout {
WasmLayout::StackMemory { .. } => {
@ -511,10 +517,50 @@ impl<'a> WasmBackend<'a> {
}
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);
let build_result = build_call_low_level(
let build_result = decode_low_level(
&mut self.code_builder,
&mut self.storage,
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(
&mut self,
@ -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(

View file

@ -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();

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
/// Copies the _entire_ value. For struct fields etc., see `copy_value_to_memory`
pub fn clone_value(

View file

@ -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<ExposesEntry<'a, &'a str>>] =
header.provides.clone().into_bump_slice();
let provides: &'a [Located<ExposesEntry<'a, &'a str>>] = 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));
}

View file

@ -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<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
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]),

View file

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

View file

@ -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<ModuleName<'a>> for &'a str {
@ -60,8 +60,8 @@ impl<'a> ModuleName<'a> {
#[derive(Clone, Debug, PartialEq)]
pub struct InterfaceHeader<'a> {
pub name: Loc<ModuleName<'a>>,
pub exposes: Vec<'a, Loc<ExposesEntry<'a, &'a str>>>,
pub imports: Vec<'a, Loc<ImportsEntry<'a>>>,
pub exposes: Collection<'a, Loc<ExposesEntry<'a, &'a str>>>,
pub imports: Collection<'a, Loc<ImportsEntry<'a>>>,
// 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<StrLiteral<'a>>,
pub packages: Collection<'a, Loc<PackageEntry<'a>>>,
pub imports: Vec<'a, Loc<ImportsEntry<'a>>>,
pub provides: Vec<'a, Loc<ExposesEntry<'a, &'a str>>>,
pub imports: Collection<'a, Loc<ImportsEntry<'a>>>,
pub provides: Collection<'a, Loc<ExposesEntry<'a, &'a str>>>,
pub to: Loc<To<'a>>,
// 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<PlatformRigid<'a>>>,
pub rigids: Collection<'a, Loc<PlatformRigid<'a>>>,
pub signature: Loc<TypedIdent<'a>>,
}
@ -145,10 +145,10 @@ pub struct PlatformRequires<'a> {
pub struct PlatformHeader<'a> {
pub name: Loc<PackageName<'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 imports: Vec<'a, Loc<ImportsEntry<'a>>>,
pub provides: Vec<'a, Loc<ExposesEntry<'a, &'a str>>>,
pub imports: Collection<'a, Loc<ImportsEntry<'a>>>,
pub provides: Collection<'a, Loc<ExposesEntry<'a, &'a str>>>,
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<TypedIdent<'a>>],
}
#[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<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 } }`
Package(
&'a str,
ModuleName<'a>,
Vec<'a, Loc<ExposesEntry<'a, &'a str>>>,
Collection<'a, Loc<ExposesEntry<'a, &'a str>>>,
),
// 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.
///

View file

@ -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<ImportsEntry<'a>>>,
Collection<'a, Located<ImportsEntry<'a>>>,
)> = 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<ExposesEntry<'a, &'a str>>>,
entries: Collection<'a, Located<ExposesEntry<'a, &'a str>>>,
to: Located<To<'a>>,
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<ExposesEntry<'a, &'a str>>>,
Collection<'a, Located<ExposesEntry<'a, &'a str>>>,
),
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<PlatformRigid<'a>>>, ERequires<'a>> {
collection_e!(
) -> impl Parser<'a, Collection<'a, Located<PlatformRigid<'a>>>, 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<ExposesEntry<'a, &'a str>>>,
Collection<'a, Located<ExposesEntry<'a, &'a str>>>,
),
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<ExposesEntry<'a, ModuleName<'a>>>>,
Collection<'a, Located<ExposesEntry<'a, ModuleName<'a>>>>,
),
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<ImportsEntry<'a>>>,
Collection<'a, Located<ImportsEntry<'a>>>,
),
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<Vec<'a, Located<ExposesEntry<'a, &'a str>>>>,
Option<Collection<'a, Located<ExposesEntry<'a, &'a str>>>>,
);
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),

View file

@ -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) => {

View file

@ -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: &[],

View file

@ -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);

View file

@ -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!(