Merge remote-tracking branch 'origin/trunk' into tag-union-imitate-rust

This commit is contained in:
Folkert 2021-11-11 12:53:03 +01:00
commit c827256e47
41 changed files with 957 additions and 532 deletions

29
Cargo.lock generated
View file

@ -3122,8 +3122,6 @@ name = "roc_build"
version = "0.1.0"
dependencies = [
"bumpalo",
"im",
"im-rc",
"inkwell 0.1.0",
"libloading 0.7.1",
"roc_builtins",
@ -3164,8 +3162,6 @@ name = "roc_can"
version = "0.1.0"
dependencies = [
"bumpalo",
"im",
"im-rc",
"indoc",
"pretty_assertions",
"roc_builtins",
@ -3187,8 +3183,6 @@ dependencies = [
"cli_utils",
"const_format",
"criterion",
"im",
"im-rc",
"indoc",
"inkwell 0.1.0",
"libloading 0.7.1",
@ -3298,8 +3292,6 @@ dependencies = [
"fs_extra",
"futures",
"glyph_brush",
"im",
"im-rc",
"libc",
"log",
"nonempty",
@ -3339,8 +3331,6 @@ name = "roc_fmt"
version = "0.1.0"
dependencies = [
"bumpalo",
"im",
"im-rc",
"indoc",
"pretty_assertions",
"roc_collections",
@ -3354,8 +3344,6 @@ name = "roc_gen_dev"
version = "0.1.0"
dependencies = [
"bumpalo",
"im",
"im-rc",
"object 0.26.2",
"roc_builtins",
"roc_can",
@ -3377,22 +3365,13 @@ name = "roc_gen_llvm"
version = "0.1.0"
dependencies = [
"bumpalo",
"im",
"im-rc",
"inkwell 0.1.0",
"morphic_lib",
"roc_builtins",
"roc_can",
"roc_collections",
"roc_module",
"roc_mono",
"roc_parse",
"roc_problem",
"roc_region",
"roc_solve",
"roc_std",
"roc_types",
"roc_unify",
"target-lexicon",
]
@ -3478,20 +3457,16 @@ version = "0.1.0"
dependencies = [
"bumpalo",
"hashbrown 0.11.2",
"linked-hash-map",
"morphic_lib",
"roc_builtins",
"roc_can",
"roc_collections",
"roc_module",
"roc_parse",
"roc_problem",
"roc_region",
"roc_solve",
"roc_std",
"roc_types",
"roc_unify",
"ven_ena",
"ven_graph",
"ven_pretty",
]
@ -3531,8 +3506,6 @@ version = "0.1.0"
dependencies = [
"bumpalo",
"distance",
"im",
"im-rc",
"indoc",
"pretty_assertions",
"roc_builtins",
@ -4074,8 +4047,6 @@ version = "0.1.0"
dependencies = [
"bumpalo",
"either",
"im",
"im-rc",
"indoc",
"inkwell 0.1.0",
"libc",

View file

@ -111,12 +111,14 @@ test-all:
build-nightly-release:
FROM +test-rust
COPY --dir .git ./
COPY --dir .git LICENSE LEGAL_DETAILS ./
# version.txt is used by the CLI: roc --version
RUN printf "nightly pre-release, built from commit " > version.txt
RUN git log --pretty=format:'%h' -n 1 >> version.txt
RUN printf " on: " >> version.txt
RUN date >> version.txt
RUN cargo build --features with_sound --release
RUN cd ./target/release && tar -czvf roc_linux_x86_64.tar.gz ./roc
RUN cd ./target/release && tar -czvf roc_linux_x86_64.tar.gz ./roc ../../LICENSE ../../LEGAL_DETAILS ../../examples/hello-world ../../examples/hello-rust ../../examples/hello-zig ../../compiler/builtins/bitcode/src/ ../../roc_std
SAVE ARTIFACT ./target/release/roc_linux_x86_64.tar.gz AS LOCAL roc_linux_x86_64.tar.gz
# compile everything needed for benchmarks and output a self-contained dir from which benchmarks can be run.

View file

@ -186,6 +186,7 @@ pub fn update_str_expr(
let insert_either = match str_expr {
Expr2::SmallStr(arr_string) => {
// TODO make sure this works for unicode "characters"
let insert_res = arr_string.try_insert(insert_index as u8, new_char);
match insert_res {

View file

@ -68,8 +68,6 @@ clap = { version = "= 3.0.0-beta.5", default-features = false, features = ["std"
const_format = "0.2.22"
rustyline = { git = "https://github.com/rtfeldman/rustyline", tag = "prompt-fix" }
rustyline-derive = { git = "https://github.com/rtfeldman/rustyline", tag = "prompt-fix" }
im = "15.0.0"
im-rc = "15.0.0"
bumpalo = { version = "3.8.0", features = ["collections"] }
libloading = "0.7.1"
mimalloc = { version = "0.1.26", default-features = false }

View file

@ -23,6 +23,7 @@ pub const CMD_REPL: &str = "repl";
pub const CMD_EDIT: &str = "edit";
pub const CMD_DOCS: &str = "docs";
pub const CMD_CHECK: &str = "check";
pub const CMD_VERSION: &str = "version";
pub const FLAG_DEBUG: &str = "debug";
pub const FLAG_DEV: &str = "dev";
@ -103,8 +104,11 @@ pub fn build_app<'a>() -> App<'a> {
.subcommand(App::new(CMD_REPL)
.about("Launch the interactive Read Eval Print Loop (REPL)")
)
.subcommand(App::new(CMD_VERSION)
.about("Print version information")
)
.subcommand(App::new(CMD_CHECK)
.about("Build a binary from the given .roc file, but don't run it")
.about("When developing, it's recommended to run `check` before `build`. It may provide a useful error message in cases where `build` panics")
.arg(
Arg::new(FLAG_TIME)
.long(FLAG_TIME)
@ -190,7 +194,9 @@ pub fn build_app<'a>() -> App<'a> {
if cfg!(feature = "editor") {
app.subcommand(
App::new(CMD_EDIT).about("Launch the Roc editor").arg(
App::new(CMD_EDIT)
.about("Launch the Roc editor (Work In Progress)")
.arg(
Arg::new(DIRECTORY_OR_FILES)
.index(1)
.multiple_values(true)

View file

@ -1,13 +1,16 @@
use roc_cli::build::check_file;
use roc_cli::{
build_app, docs, repl, BuildConfig, CMD_BUILD, CMD_CHECK, CMD_DOCS, CMD_EDIT, CMD_REPL,
DIRECTORY_OR_FILES, FLAG_TIME, ROC_FILE,
CMD_VERSION, DIRECTORY_OR_FILES, FLAG_TIME, ROC_FILE,
};
use roc_load::file::LoadingProblem;
use std::fs::{self, FileType};
use std::io;
use std::path::{Path, PathBuf};
#[macro_use]
extern crate const_format;
#[global_allocator]
static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
@ -122,6 +125,11 @@ fn main() -> io::Result<()> {
Ok(0)
}
Some(CMD_VERSION) => {
println!("roc {}", concatcp!(include_str!("../../version.txt"), "\n"));
Ok(0)
}
_ => unreachable!(),
}?;

View file

@ -7,19 +7,6 @@ extern crate indoc;
#[cfg(test)]
mod repl_eval {
use cli_utils::helpers;
use roc_gen_llvm::run_roc::RocCallResult;
#[test]
fn check_discriminant_size() {
// tells us if the size of the discriminant has changed. Lots of other code
// relies on this size
let value: i64 = 1234;
assert_eq!(
std::mem::size_of_val(&RocCallResult::Success(value)),
roc_gen_llvm::run_roc::ROC_CALL_RESULT_DISCRIMINANT_SIZE
+ std::mem::size_of_val(&value)
)
}
const ERROR_MESSAGE_START: char = '─';

View file

@ -24,8 +24,6 @@ roc_gen_wasm = { path = "../gen_wasm", optional = true }
roc_gen_dev = { path = "../gen_dev", default-features = false }
roc_reporting = { path = "../reporting" }
roc_std = { path = "../../roc_std" }
im = "15.0.0"
im-rc = "15.0.0"
bumpalo = { version = "3.8.0", features = ["collections"] }
libloading = "0.7.1"
tempfile = "3.2.0"

View file

@ -992,6 +992,22 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
Box::new(list_type(flex(TVAR1))),
);
// sublist : List elem, { start : Nat, len : Nat } -> List elem
add_top_level_function_type!(
Symbol::LIST_SUBLIST,
vec![
list_type(flex(TVAR1)),
SolvedType::Record {
fields: vec![
("start".into(), RecordField::Required(nat_type())),
("len".into(), RecordField::Required(nat_type())),
],
ext: Box::new(SolvedType::EmptyRecord),
},
],
Box::new(list_type(flex(TVAR1))),
);
// drop : List elem, Nat -> List elem
add_top_level_function_type!(
Symbol::LIST_DROP,

View file

@ -14,8 +14,6 @@ roc_problem = { path = "../problem" }
roc_types = { path = "../types" }
roc_builtins = { path = "../builtins" }
ven_graph = { path = "../../vendor/pathfinding" }
im = "15.0.0"
im-rc = "15.0.0"
bumpalo = { version = "3.8.0", features = ["collections"] }
[dev-dependencies]

View file

@ -94,6 +94,7 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
LIST_MAP4 => list_map4,
LIST_TAKE_FIRST => list_take_first,
LIST_TAKE_LAST => list_take_last,
LIST_SUBLIST => list_sublist,
LIST_DROP => list_drop,
LIST_DROP_AT => list_drop_at,
LIST_DROP_FIRST => list_drop_first,
@ -2060,6 +2061,54 @@ fn list_take_last(symbol: Symbol, var_store: &mut VarStore) -> Def {
)
}
/// List.sublist : List elem, { start : Nat, len : Nat } -> List elem
fn list_sublist(symbol: Symbol, var_store: &mut VarStore) -> Def {
let list_var = var_store.fresh();
let rec_var = var_store.fresh();
let sym_list = Symbol::ARG_1;
let sym_rec = Symbol::ARG_2;
let start_var = var_store.fresh();
let len_var = var_store.fresh();
let get_start = Access {
record_var: rec_var,
ext_var: var_store.fresh(),
field_var: var_store.fresh(),
loc_expr: Box::new(no_region(Var(sym_rec))),
field: "start".into(),
};
let get_len = Access {
record_var: rec_var,
ext_var: var_store.fresh(),
field_var: var_store.fresh(),
loc_expr: Box::new(no_region(Var(sym_rec))),
field: "len".into(),
};
let body_drop = RunLowLevel {
op: LowLevel::ListDrop,
args: vec![(list_var, Var(sym_list)), (start_var, get_start)],
ret_var: list_var,
};
let body_take = RunLowLevel {
op: LowLevel::ListTakeFirst,
args: vec![(list_var, body_drop), (len_var, get_len)],
ret_var: list_var,
};
defn(
symbol,
vec![(list_var, sym_list), (rec_var, sym_rec)],
var_store,
body_take,
list_var,
)
}
/// List.drop : List elem, Nat -> List elem
fn list_drop(symbol: Symbol, var_store: &mut VarStore) -> Def {
let list_var = var_store.fresh();

View file

@ -872,7 +872,7 @@ fn canonicalize_pending_def<'a>(
// TODO try to remove this .clone()!
value: loc_can_expr.value.clone(),
},
pattern_vars: im::HashMap::clone(&vars_by_symbol),
pattern_vars: vars_by_symbol.clone(),
annotation: Some(Annotation {
signature: typ.clone(),
introduced_variables: output.introduced_variables.clone(),
@ -1093,7 +1093,7 @@ fn canonicalize_pending_def<'a>(
// TODO try to remove this .clone()!
value: loc_can_expr.value.clone(),
},
pattern_vars: im::HashMap::clone(&vars_by_symbol),
pattern_vars: vars_by_symbol.clone(),
annotation: Some(Annotation {
signature: typ.clone(),
introduced_variables: output.introduced_variables.clone(),
@ -1231,7 +1231,7 @@ fn canonicalize_pending_def<'a>(
region: loc_can_expr.region,
value: loc_can_expr.value.clone(),
},
pattern_vars: im::HashMap::clone(&vars_by_symbol),
pattern_vars: vars_by_symbol.clone(),
annotation: None,
},
);

View file

@ -952,7 +952,7 @@ pub fn local_successors<'a>(
references: &'a References,
closures: &'a MutMap<Symbol, References>,
) -> ImSet<Symbol> {
let mut answer = im_rc::hashset::HashSet::clone(&references.lookups);
let mut answer = references.lookups.clone();
for call_symbol in references.calls.iter() {
answer = answer.union(call_successors(*call_symbol, closures));
@ -962,7 +962,7 @@ pub fn local_successors<'a>(
}
fn call_successors(call_symbol: Symbol, closures: &MutMap<Symbol, References>) -> ImSet<Symbol> {
let mut answer = im_rc::hashset::HashSet::default();
let mut answer = ImSet::default();
let mut seen = MutSet::default();
let mut queue = vec![call_symbol];

View file

@ -10,8 +10,6 @@ roc_collections = { path = "../collections" }
roc_region = { path = "../region" }
roc_module = { path = "../module" }
roc_parse = { path = "../parse" }
im = "15.0.0"
im-rc = "15.0.0"
bumpalo = { version = "3.8.0", features = ["collections"] }
[dev-dependencies]

View file

@ -16,8 +16,6 @@ roc_builtins = { path = "../builtins" }
roc_unify = { path = "../unify" }
roc_solve = { path = "../solve" }
roc_mono = { path = "../mono" }
im = "15.0.0"
im-rc = "15.0.0"
bumpalo = { version = "3.8.0", features = ["collections"] }
target-lexicon = "0.12.2"
# TODO: Deal with the update of object to 0.27.

View file

@ -8,23 +8,11 @@ edition = "2018"
[dependencies]
roc_collections = { path = "../collections" }
roc_region = { path = "../region" }
roc_module = { path = "../module" }
roc_problem = { path = "../problem" }
roc_types = { path = "../types" }
roc_builtins = { path = "../builtins" }
roc_unify = { path = "../unify" }
roc_solve = { path = "../solve" }
roc_mono = { path = "../mono" }
roc_std = { path = "../../roc_std" }
morphic_lib = { path = "../../vendor/morphic_lib" }
im = "15.0.0"
im-rc = "15.0.0"
bumpalo = { version = "3.8.0", features = ["collections"] }
inkwell = { path = "../../vendor/inkwell" }
target-lexicon = "0.12.2"
[dev-dependencies]
roc_can = { path = "../can" }
roc_parse = { path = "../parse" }
bumpalo = { version = "3.8.0", features = ["collections"] }

View file

@ -3351,8 +3351,7 @@ fn expose_function_to_host_help_c_abi_generic<'a, 'ctx, 'env>(
builder.position_at_end(entry);
let elements = [Layout::Builtin(Builtin::Int64), return_layout];
let wrapped_layout = Layout::Struct(&elements);
let wrapped_layout = roc_result_layout(env.arena, return_layout);
call_roc_function(env, roc_function, &wrapped_layout, arguments_for_call)
} else {
call_roc_function(env, roc_function, &return_layout, arguments_for_call)
@ -3380,18 +3379,11 @@ fn expose_function_to_host_help_c_abi_gen_test<'a, 'ctx, 'env>(
return_layout: Layout<'a>,
c_function_name: &str,
) -> FunctionValue<'ctx> {
let context = env.context;
// a tagged union to indicate to the test loader that a panic occurred.
// especially when running 32-bit binaries on a 64-bit machine, there
// does not seem to be a smarter solution
let wrapper_return_type = context.struct_type(
&[
context.i64_type().into(),
basic_type_from_layout(env, &return_layout),
],
false,
);
let wrapper_return_type =
roc_result_type(env, roc_function.get_type().get_return_type().unwrap());
let mut cc_argument_types = Vec::with_capacity_in(arguments.len(), env.arena);
for layout in arguments {
@ -3510,8 +3502,6 @@ fn expose_function_to_host_help_c_abi<'a, 'ctx, 'env>(
return_layout: Layout<'a>,
c_function_name: &str,
) -> FunctionValue<'ctx> {
let context = env.context;
if env.is_gen_test {
return expose_function_to_host_help_c_abi_gen_test(
env,
@ -3533,15 +3523,7 @@ fn expose_function_to_host_help_c_abi<'a, 'ctx, 'env>(
);
let wrapper_return_type = if env.is_gen_test {
context
.struct_type(
&[
context.i64_type().into(),
roc_function.get_type().get_return_type().unwrap(),
],
false,
)
.into()
roc_result_type(env, roc_function.get_type().get_return_type().unwrap()).into()
} else {
// roc_function.get_type().get_return_type().unwrap()
basic_type_from_layout(env, &return_layout)
@ -3708,12 +3690,7 @@ fn set_jump_and_catch_long_jump<'a, 'ctx, 'env>(
let builder = env.builder;
let return_type = basic_type_from_layout(env, &return_layout);
let call_result_type = context.struct_type(
&[context.i64_type().into(), return_type.as_basic_type_enum()],
false,
);
let call_result_type = roc_result_type(env, return_type.as_basic_type_enum());
let result_alloca = builder.build_alloca(call_result_type, "result");
let then_block = context.append_basic_block(parent, "then_block");
@ -3811,12 +3788,8 @@ fn set_jump_and_catch_long_jump<'a, 'ctx, 'env>(
ptr_int
};
let u8_ptr = env.context.i8_type().ptr_type(AddressSpace::Generic);
let return_type = context.struct_type(&[context.i64_type().into(), u8_ptr.into()], false);
// let return_type = call_result_type;
let return_value = {
let v1 = return_type.const_zero();
let v1 = call_result_type.const_zero();
// flag is non-zero, indicating failure
let flag = context.i64_type().const_int(1, false);
@ -3831,17 +3804,7 @@ fn set_jump_and_catch_long_jump<'a, 'ctx, 'env>(
v3
};
// bitcast result alloca so we can store our concrete type { flag, error_msg } in there
let result_alloca_bitcast = builder
.build_bitcast(
result_alloca,
return_type.ptr_type(AddressSpace::Generic),
"result_alloca_bitcast",
)
.into_pointer_value();
// store our return value
builder.build_store(result_alloca_bitcast, return_value);
builder.build_store(result_alloca, return_value);
env.builder.build_unconditional_branch(cont_block);
}
@ -3866,6 +3829,30 @@ fn make_exception_catcher<'a, 'ctx, 'env>(
function_value
}
fn roc_result_layout<'a>(arena: &'a Bump, return_layout: Layout<'a>) -> Layout<'a> {
let elements = [
Layout::Builtin(Builtin::Int64),
Layout::Builtin(Builtin::Usize),
return_layout,
];
Layout::Struct(arena.alloc(elements))
}
fn roc_result_type<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
return_type: BasicTypeEnum<'ctx>,
) -> StructType<'ctx> {
env.context.struct_type(
&[
env.context.i64_type().into(),
env.context.i8_type().ptr_type(AddressSpace::Generic).into(),
return_type,
],
false,
)
}
fn make_good_roc_result<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
return_layout: Layout<'a>,
@ -3874,11 +3861,7 @@ fn make_good_roc_result<'a, 'ctx, 'env>(
let context = env.context;
let builder = env.builder;
let content_type = basic_type_from_layout(env, &return_layout);
let wrapper_return_type =
context.struct_type(&[context.i64_type().into(), content_type], false);
let v1 = wrapper_return_type.const_zero();
let v1 = roc_result_type(env, return_value.get_type()).const_zero();
let v2 = builder
.build_insert_value(v1, context.i64_type().const_zero(), 0, "set_no_error")
@ -3890,11 +3873,11 @@ fn make_good_roc_result<'a, 'ctx, 'env>(
"load_call_result_passed_by_ptr",
);
builder
.build_insert_value(v2, loaded, 1, "set_call_result")
.build_insert_value(v2, loaded, 2, "set_call_result")
.unwrap()
} else {
builder
.build_insert_value(v2, return_value, 1, "set_call_result")
.build_insert_value(v2, return_value, 2, "set_call_result")
.unwrap()
};
@ -3923,13 +3906,8 @@ fn make_exception_catching_wrapper<'a, 'ctx, 'env>(
}
};
let wrapper_return_type = context.struct_type(
&[
context.i64_type().into(),
basic_type_from_layout(env, &return_layout),
],
false,
);
let wrapper_return_type =
roc_result_type(env, roc_function.get_type().get_return_type().unwrap());
// argument_types.push(wrapper_return_type.ptr_type(AddressSpace::Generic).into());

View file

@ -1,22 +1,23 @@
use std::ffi::CString;
use std::mem::MaybeUninit;
use std::os::raw::c_char;
use RocCallResult::*;
/// This must have the same size as the repr() of RocCallResult!
pub const ROC_CALL_RESULT_DISCRIMINANT_SIZE: usize = std::mem::size_of::<u64>();
#[repr(u64)]
pub enum RocCallResult<T> {
Success(T),
Failure(*mut c_char),
#[repr(C)]
pub struct RocCallResult<T> {
tag: u64,
error_msg: *mut c_char,
value: MaybeUninit<T>,
}
impl<T: Sized> From<RocCallResult<T>> for Result<T, String> {
fn from(call_result: RocCallResult<T>) -> Self {
match call_result {
Success(value) => Ok(value),
Failure(failure) => Err({
let raw = unsafe { CString::from_raw(failure) };
match call_result.tag {
0 => Ok(unsafe { call_result.value.assume_init() }),
_ => Err({
let raw = unsafe { CString::from_raw(call_result.error_msg) };
let result = format!("{:?}", raw);
@ -86,7 +87,7 @@ macro_rules! run_jit_function_dynamic_type {
.ok_or(format!("Unable to JIT compile `{}`", $main_fn_name))
.expect("errored");
let size = roc_gen_llvm::run_roc::ROC_CALL_RESULT_DISCRIMINANT_SIZE + $bytes;
let size = std::mem::size_of::<RocCallResult<()>>() + $bytes;
let layout = std::alloc::Layout::array::<u8>(size).unwrap();
let result = std::alloc::alloc(layout);
main(result);
@ -94,7 +95,7 @@ macro_rules! run_jit_function_dynamic_type {
let flag = *result;
if flag == 0 {
$transform(result.offset(8) as *const u8)
$transform(result.add(std::mem::size_of::<RocCallResult<()>>()) as *const u8)
} else {
use std::ffi::CString;
use std::os::raw::c_char;

View file

@ -1,14 +1,13 @@
use bumpalo::{self, collections::Vec};
use code_builder::Align;
use roc_builtins::bitcode::{self, FloatWidth};
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::{Layout, LayoutIds};
use crate::layout::WasmLayout;
use crate::low_level::{build_call_low_level, LowlevelBuildResult};
use crate::storage::{Storage, StoredValue, StoredValueKind};
use crate::wasm_module::linking::{
DataSymbol, LinkingSection, RelocationSection, WasmObjectSymbol, WASM_SYM_BINDING_WEAK,
@ -24,7 +23,7 @@ use crate::wasm_module::{
};
use crate::{
copy_memory, CopyMemoryConfig, Env, BUILTINS_IMPORT_MODULE_NAME, MEMORY_NAME, PTR_TYPE,
STACK_POINTER_NAME,
STACK_POINTER_GLOBAL_ID, STACK_POINTER_NAME,
};
/// The memory address where the constants data will be loaded during module instantiation.
@ -84,12 +83,12 @@ impl<'a> WasmBackend<'a> {
exports.push(Export {
name: STACK_POINTER_NAME.to_string(),
ty: ExportType::Global,
index: 0,
index: STACK_POINTER_GLOBAL_ID,
});
linker_symbols.push(SymInfo::Global(WasmObjectSymbol::Defined {
flags: WASM_SYM_BINDING_WEAK,
index: 0,
flags: WASM_SYM_BINDING_WEAK, // TODO: this works but means external .o files decide how much stack we have!
index: STACK_POINTER_GLOBAL_ID,
name: STACK_POINTER_NAME.to_string(),
}));
@ -487,7 +486,29 @@ impl<'a> WasmBackend<'a> {
}
CallType::LowLevel { op: lowlevel, .. } => {
self.build_call_low_level(lowlevel, arguments, layout)
let return_layout = WasmLayout::new(layout);
self.storage.load_symbols(&mut self.code_builder, arguments);
let build_result = build_call_low_level(
&mut self.code_builder,
&mut self.storage,
lowlevel,
arguments,
&return_layout,
);
use LowlevelBuildResult::*;
match build_result {
Done => Ok(()),
BuiltinCall(name) => {
self.call_imported_builtin(name, arguments, &return_layout);
Ok(())
}
NotImplemented => Err(format!(
"Low level operation {:?} is not yet implemented",
lowlevel
)),
}
}
x => Err(format!("the call type, {:?}, is not yet implemented", x)),
},
@ -665,90 +686,11 @@ impl<'a> WasmBackend<'a> {
Ok(())
}
fn build_call_low_level(
&mut self,
lowlevel: &LowLevel,
args: &'a [Symbol],
return_layout: &Layout<'a>,
) -> Result<(), String> {
self.storage.load_symbols(&mut self.code_builder, args);
let wasm_layout = WasmLayout::new(return_layout);
let ret_type = wasm_layout.value_type();
let panic_ret_type = || panic!("Invalid return type for {:?}: {:?}", lowlevel, ret_type);
match lowlevel {
LowLevel::NumAdd => match ret_type {
ValueType::I32 => self.code_builder.i32_add(),
ValueType::I64 => self.code_builder.i64_add(),
ValueType::F32 => self.code_builder.f32_add(),
ValueType::F64 => self.code_builder.f64_add(),
},
LowLevel::NumSub => match ret_type {
ValueType::I32 => self.code_builder.i32_sub(),
ValueType::I64 => self.code_builder.i64_sub(),
ValueType::F32 => self.code_builder.f32_sub(),
ValueType::F64 => self.code_builder.f64_sub(),
},
LowLevel::NumMul => match ret_type {
ValueType::I32 => self.code_builder.i32_mul(),
ValueType::I64 => self.code_builder.i64_mul(),
ValueType::F32 => self.code_builder.f32_mul(),
ValueType::F64 => self.code_builder.f64_mul(),
},
LowLevel::NumGt => match self.get_uniform_arg_type(args) {
ValueType::I32 => self.code_builder.i32_gt_s(),
ValueType::I64 => self.code_builder.i64_gt_s(),
ValueType::F32 => self.code_builder.f32_gt(),
ValueType::F64 => self.code_builder.f64_gt(),
},
LowLevel::Eq => match self.get_uniform_arg_type(args) {
ValueType::I32 => self.code_builder.i32_eq(),
ValueType::I64 => self.code_builder.i64_eq(),
ValueType::F32 => self.code_builder.f32_eq(),
ValueType::F64 => self.code_builder.f64_eq(),
},
LowLevel::NumNeg => match ret_type {
ValueType::I32 => {
self.code_builder.i32_const(-1);
self.code_builder.i32_mul();
}
ValueType::I64 => {
self.code_builder.i64_const(-1);
self.code_builder.i64_mul();
}
ValueType::F32 => self.code_builder.f32_neg(),
ValueType::F64 => self.code_builder.f64_neg(),
},
LowLevel::NumAtan => {
let name = match ret_type {
ValueType::F32 => &bitcode::NUM_ATAN[FloatWidth::F32],
ValueType::F64 => &bitcode::NUM_ATAN[FloatWidth::F64],
_ => panic_ret_type(),
};
self.call_imported_builtin(name, &[ret_type], Some(ret_type));
}
_ => {
return Err(format!("unsupported low-level op {:?}", lowlevel));
}
};
Ok(())
}
/// Get the ValueType for a set of arguments that are required to have the same type
fn get_uniform_arg_type(&self, args: &'a [Symbol]) -> ValueType {
let value_type = self.storage.get(&args[0]).value_type();
for arg in args.iter().skip(1) {
debug_assert!(self.storage.get(arg).value_type() == value_type);
}
value_type
}
fn call_imported_builtin(
&mut self,
name: &'a str,
arg_types: &[ValueType],
ret_type: Option<ValueType>,
arguments: &[Symbol],
ret_layout: &WasmLayout,
) {
let (fn_index, linker_symbol_index) = match self.builtin_sym_index_map.get(name) {
Some(sym_idx) => match &self.linker_symbols[*sym_idx] {
@ -759,11 +701,11 @@ impl<'a> WasmBackend<'a> {
},
None => {
let mut param_types = Vec::with_capacity_in(arg_types.len(), self.env.arena);
param_types.extend_from_slice(arg_types);
let mut param_types = Vec::with_capacity_in(arguments.len(), self.env.arena);
param_types.extend(arguments.iter().map(|a| self.storage.get(a).value_type()));
let signature_index = self.module.types.insert(Signature {
param_types,
ret_type,
ret_type: Some(ret_layout.value_type()), // TODO: handle builtins with no return value
});
let import_index = self.module.import.entries.len() as u32;
@ -787,8 +729,8 @@ impl<'a> WasmBackend<'a> {
self.code_builder.call(
fn_index,
linker_symbol_index,
arg_types.len(),
ret_type.is_some(),
arguments.len(),
true, // TODO: handle builtins with no return value
);
}
}

View file

@ -71,6 +71,14 @@ impl WasmLayout {
}
}
pub fn size(&self) -> u32 {
match self {
Self::Primitive(_, size) => *size,
Self::StackMemory { size, .. } => *size,
Self::HeapMemory => PTR_SIZE,
}
}
pub fn is_stack_memory(&self) -> bool {
matches!(self, Self::StackMemory { .. })
}

View file

@ -1,5 +1,6 @@
mod backend;
mod layout;
mod low_level;
mod storage;
pub mod wasm_module;
@ -126,7 +127,15 @@ pub fn copy_memory(code_builder: &mut CodeBuilder, config: CopyMemoryConfig) {
/// Round up to alignment_bytes (which must be a power of 2)
pub fn round_up_to_alignment(unaligned: i32, alignment_bytes: i32) -> i32 {
debug_assert!(alignment_bytes.count_ones() == 1);
if alignment_bytes <= 1 {
return unaligned;
}
if alignment_bytes.count_ones() != 1 {
panic!(
"Cannot align to {} bytes. Not a power of 2.",
alignment_bytes
);
}
let mut aligned = unaligned;
aligned += alignment_bytes - 1; // if lower bits are non-zero, push it over the next boundary
aligned &= -alignment_bytes; // mask with a flag that has upper bits 1, lower bits 0

View file

@ -0,0 +1,360 @@
use roc_builtins::bitcode::{self, FloatWidth};
use roc_module::low_level::{LowLevel, LowLevel::*};
use roc_module::symbol::Symbol;
use crate::layout::WasmLayout;
use crate::storage::Storage;
use crate::wasm_module::{
CodeBuilder,
ValueType::{self, *},
};
pub enum LowlevelBuildResult {
Done,
BuiltinCall(&'static str),
NotImplemented,
}
pub fn build_call_low_level<'a>(
code_builder: &mut CodeBuilder<'a>,
storage: &mut Storage<'a>,
lowlevel: &LowLevel,
args: &'a [Symbol],
ret_layout: &WasmLayout,
) -> LowlevelBuildResult {
use LowlevelBuildResult::*;
let panic_ret_type = || panic!("Invalid return layout for {:?}: {:?}", lowlevel, ret_layout);
match lowlevel {
StrConcat | StrJoinWith | StrIsEmpty | StrStartsWith | StrStartsWithCodePt
| StrEndsWith | StrSplit | StrCountGraphemes | StrFromInt | StrFromUtf8 | StrTrimLeft
| StrFromUtf8Range | StrToUtf8 | StrRepeat | StrFromFloat | StrTrim | ListLen
| ListGetUnsafe | ListSet | ListSingle | ListRepeat | ListReverse | ListConcat
| ListContains | ListAppend | ListPrepend | ListJoin | ListRange | ListMap | ListMap2
| ListMap3 | ListMap4 | ListMapWithIndex | ListKeepIf | ListWalk | ListWalkUntil
| ListWalkBackwards | ListKeepOks | ListKeepErrs | ListSortWith | ListTakeFirst
| ListTakeLast | ListDrop | ListDropAt | ListSwap | ListAny | ListFindUnsafe | DictSize
| DictEmpty | DictInsert | DictRemove | DictContains | DictGetUnsafe | DictKeys
| DictValues | DictUnion | DictIntersection | DictDifference | DictWalk | SetFromList => {
return NotImplemented;
}
NumAdd => match ret_layout.value_type() {
I32 => code_builder.i32_add(),
I64 => code_builder.i64_add(),
F32 => code_builder.f32_add(),
F64 => code_builder.f64_add(),
},
NumAddWrap => match ret_layout.value_type() {
I32 => {
code_builder.i32_add();
wrap_i32(code_builder, ret_layout.size());
}
I64 => code_builder.i64_add(),
F32 => code_builder.f32_add(),
F64 => code_builder.f64_add(),
},
NumAddChecked => return NotImplemented,
NumSub => match ret_layout.value_type() {
I32 => code_builder.i32_sub(),
I64 => code_builder.i64_sub(),
F32 => code_builder.f32_sub(),
F64 => code_builder.f64_sub(),
},
NumSubWrap => match ret_layout.value_type() {
I32 => {
code_builder.i32_sub();
wrap_i32(code_builder, ret_layout.size());
}
I64 => code_builder.i64_sub(),
F32 => code_builder.f32_sub(),
F64 => code_builder.f64_sub(),
},
NumSubChecked => return NotImplemented,
NumMul => match ret_layout.value_type() {
I32 => code_builder.i32_mul(),
I64 => code_builder.i64_mul(),
F32 => code_builder.f32_mul(),
F64 => code_builder.f64_mul(),
},
NumMulWrap => match ret_layout.value_type() {
I32 => {
code_builder.i32_mul();
wrap_i32(code_builder, ret_layout.size());
}
I64 => code_builder.i64_mul(),
F32 => code_builder.f32_mul(),
F64 => code_builder.f64_mul(),
},
NumMulChecked => return NotImplemented,
NumGt => match storage.get(&args[0]).value_type() {
I32 => code_builder.i32_gt_s(),
I64 => code_builder.i64_gt_s(),
F32 => code_builder.f32_gt(),
F64 => code_builder.f64_gt(),
},
NumGte => match storage.get(&args[0]).value_type() {
I32 => code_builder.i32_ge_s(),
I64 => code_builder.i64_ge_s(),
F32 => code_builder.f32_ge(),
F64 => code_builder.f64_ge(),
},
NumLt => match storage.get(&args[0]).value_type() {
I32 => code_builder.i32_lt_s(),
I64 => code_builder.i64_lt_s(),
F32 => code_builder.f32_lt(),
F64 => code_builder.f64_lt(),
},
NumLte => match storage.get(&args[0]).value_type() {
I32 => code_builder.i32_le_s(),
I64 => code_builder.i64_le_s(),
F32 => code_builder.f32_le(),
F64 => code_builder.f64_le(),
},
NumCompare => return NotImplemented,
NumDivUnchecked => match ret_layout.value_type() {
I32 => code_builder.i32_div_s(),
I64 => code_builder.i64_div_s(),
F32 => code_builder.f32_div(),
F64 => code_builder.f64_div(),
},
NumDivCeilUnchecked => return NotImplemented,
NumRemUnchecked => match ret_layout.value_type() {
I32 => code_builder.i32_rem_s(),
I64 => code_builder.i64_rem_s(),
F32 => return NotImplemented,
F64 => return NotImplemented,
},
NumIsMultipleOf => return NotImplemented,
NumAbs => match ret_layout.value_type() {
I32 => {
code_builder.i32_const(0);
storage.load_symbols(code_builder, args);
code_builder.i32_sub();
storage.load_symbols(code_builder, args);
code_builder.i32_const(0);
code_builder.i32_ge_s();
code_builder.select();
}
I64 => {
code_builder.i64_const(0);
storage.load_symbols(code_builder, args);
code_builder.i64_sub();
storage.load_symbols(code_builder, args);
code_builder.i64_const(0);
code_builder.i64_ge_s();
code_builder.select();
}
F32 => code_builder.f32_abs(),
F64 => code_builder.f64_abs(),
},
NumNeg => {
match ret_layout.value_type() {
I32 => {
// Unfortunate local.set/local.get
code_builder.i32_const(0);
storage.load_symbols(code_builder, args);
code_builder.i32_sub();
}
I64 => {
// Unfortunate local.set/local.get
code_builder.i64_const(0);
storage.load_symbols(code_builder, args);
code_builder.i64_sub();
}
F32 => code_builder.f32_neg(),
F64 => code_builder.f64_neg(),
}
}
NumSin => return NotImplemented,
NumCos => return NotImplemented,
NumSqrtUnchecked => return NotImplemented,
NumLogUnchecked => return NotImplemented,
NumRound => {
// FIXME
// thread 'gen_num::f64_round' panicked at 'called `Result::unwrap()` on an `Err` value:
// Io(Os { code: 2, kind: NotFound, message: "No such file or directory" })',
// compiler/test_gen/src/helpers/wasm.rs:185:53
// Note: Wasm has a `nearest` op, but it does round-to-even when fraction is exactly 0.5
// which fails tests. Will this do? Or is specific behaviour important?
let width = float_width_from_layout(ret_layout);
return BuiltinCall(&bitcode::NUM_ROUND[width]);
}
NumToFloat => match (ret_layout.value_type(), storage.get(&args[0]).value_type()) {
(F32, I32) => code_builder.f32_convert_s_i32(),
(F32, I64) => code_builder.f32_convert_s_i64(),
(F32, F32) => {}
(F32, F64) => code_builder.f32_demote_f64(),
(F64, I32) => code_builder.f64_convert_s_i32(),
(F64, I64) => code_builder.f64_convert_s_i64(),
(F64, F32) => code_builder.f64_promote_f32(),
(F64, F64) => {}
_ => panic_ret_type(),
},
NumPow => return NotImplemented,
NumCeiling => match ret_layout.value_type() {
I32 => {
code_builder.f32_ceil();
code_builder.i32_trunc_s_f32()
}
I64 => {
code_builder.f64_ceil();
code_builder.i64_trunc_s_f64()
}
_ => panic_ret_type(),
},
NumPowInt => return NotImplemented,
NumFloor => match ret_layout.value_type() {
I32 => {
code_builder.f32_floor();
code_builder.i32_trunc_s_f32()
}
I64 => {
code_builder.f64_floor();
code_builder.i64_trunc_s_f64()
}
_ => panic_ret_type(),
},
NumIsFinite => match ret_layout.value_type() {
I32 => code_builder.i32_const(1),
I64 => code_builder.i32_const(1),
F32 => {
code_builder.i32_reinterpret_f32();
code_builder.i32_const(0x7f800000);
code_builder.i32_and();
code_builder.i32_const(0x7f800000);
code_builder.i32_ne();
}
F64 => {
code_builder.i64_reinterpret_f64();
code_builder.i64_const(0x7ff0000000000000);
code_builder.i64_and();
code_builder.i64_const(0x7ff0000000000000);
code_builder.i64_ne();
}
},
NumAtan => {
let width = float_width_from_layout(ret_layout);
return BuiltinCall(&bitcode::NUM_ATAN[width]);
}
NumAcos => {
let width = float_width_from_layout(ret_layout);
return BuiltinCall(&bitcode::NUM_ACOS[width]);
}
NumAsin => {
let width = float_width_from_layout(ret_layout);
return BuiltinCall(&bitcode::NUM_ASIN[width]);
}
NumBytesToU16 => return NotImplemented,
NumBytesToU32 => return NotImplemented,
NumBitwiseAnd => match ret_layout.value_type() {
I32 => code_builder.i32_and(),
I64 => code_builder.i64_and(),
_ => panic_ret_type(),
},
NumBitwiseXor => match ret_layout.value_type() {
I32 => code_builder.i32_xor(),
I64 => code_builder.i64_xor(),
_ => panic_ret_type(),
},
NumBitwiseOr => match ret_layout.value_type() {
I32 => code_builder.i32_or(),
I64 => code_builder.i64_or(),
_ => panic_ret_type(),
},
NumShiftLeftBy => {
// Unfortunate local.set/local.get
storage.load_symbols(code_builder, &[args[1], args[0]]);
match ret_layout.value_type() {
I32 => code_builder.i32_shl(),
I64 => code_builder.i64_shl(),
_ => panic_ret_type(),
}
}
NumShiftRightBy => match ret_layout.value_type() {
I32 => code_builder.i32_shr_s(),
I64 => code_builder.i64_shr_s(),
_ => panic_ret_type(),
},
NumShiftRightZfBy => match ret_layout.value_type() {
I32 => code_builder.i32_shr_u(),
I64 => code_builder.i64_shr_u(),
_ => panic_ret_type(),
},
NumIntCast => match (ret_layout.value_type(), storage.get(&args[0]).value_type()) {
(I32, I32) => {}
(I32, I64) => code_builder.i32_wrap_i64(),
(I32, F32) => code_builder.i32_trunc_s_f32(),
(I32, F64) => code_builder.i32_trunc_s_f64(),
(I64, I32) => code_builder.i64_extend_s_i32(),
(I64, I64) => {}
(I64, F32) => code_builder.i64_trunc_s_f32(),
(I64, F64) => code_builder.i64_trunc_s_f64(),
(F32, I32) => code_builder.f32_convert_s_i32(),
(F32, I64) => code_builder.f32_convert_s_i64(),
(F32, F32) => {}
(F32, F64) => code_builder.f32_demote_f64(),
(F64, I32) => code_builder.f64_convert_s_i32(),
(F64, I64) => code_builder.f64_convert_s_i64(),
(F64, F32) => code_builder.f64_promote_f32(),
(F64, F64) => {}
},
Eq => {
// TODO: For non-number types, this will implement pointer equality, which is wrong
match storage.get(&args[0]).value_type() {
I32 => code_builder.i32_eq(),
I64 => code_builder.i64_eq(),
F32 => code_builder.f32_eq(),
F64 => code_builder.f64_eq(),
}
}
NotEq => {
// TODO: For non-number types, this will implement pointer inequality, which is wrong
match storage.get(&args[0]).value_type() {
I32 => code_builder.i32_ne(),
I64 => code_builder.i64_ne(),
F32 => code_builder.f32_ne(),
F64 => code_builder.f64_ne(),
}
}
And => code_builder.i32_and(),
Or => code_builder.i32_or(),
Not => code_builder.i32_eqz(),
Hash => return NotImplemented,
ExpectTrue => return NotImplemented,
}
Done
}
/// Wrap an integer whose Wasm representation is i32
fn wrap_i32(code_builder: &mut CodeBuilder, size: u32) {
match size {
1 => {
// Underlying Roc value is i8
code_builder.i32_const(24);
code_builder.i32_shl();
code_builder.i32_const(24);
code_builder.i32_shr_s();
}
2 => {
// Underlying Roc value is i16
code_builder.i32_const(16);
code_builder.i32_shl();
code_builder.i32_const(16);
code_builder.i32_shr_s();
}
_ => {} // the only other possible value is 4, and i32 wraps natively
}
}
fn float_width_from_layout(wasm_layout: &WasmLayout) -> FloatWidth {
if wasm_layout.value_type() == ValueType::F32 {
FloatWidth::F32
} else {
FloatWidth::F64
}
}

View file

@ -6,7 +6,7 @@ use std::fmt::Debug;
use roc_module::symbol::Symbol;
use super::linking::{IndexRelocType, OffsetRelocType, RelocationEntry};
use super::opcodes::*;
use super::opcodes::{OpCode, OpCode::*};
use super::serialize::{SerialBuffer, Serialize};
use crate::{round_up_to_alignment, FRAME_ALIGNMENT_BYTES, STACK_POINTER_GLOBAL_ID};
@ -244,7 +244,7 @@ impl<'a> CodeBuilder<'a> {
// Symbol is not on top of the stack. Find it.
if let Some(found_index) = self.vm_stack.iter().rposition(|&s| s == symbol) {
// Insert a local.set where the value was created
self.add_insertion(pushed_at, SETLOCAL, next_local_id.0);
self.add_insertion(pushed_at, SETLOCAL as u8, next_local_id.0);
// Take the value out of the stack where local.set was inserted
self.vm_stack.remove(found_index);
@ -267,7 +267,7 @@ impl<'a> CodeBuilder<'a> {
Popped { pushed_at } => {
// This Symbol is being used for a second time
// Insert a local.tee where it was pushed, so we don't interfere with the first usage
self.add_insertion(pushed_at, TEELOCAL, next_local_id.0);
self.add_insertion(pushed_at, TEELOCAL as u8, next_local_id.0);
// Insert a local.get at the current position
self.get_local(next_local_id);
@ -331,14 +331,14 @@ impl<'a> CodeBuilder<'a> {
fn build_stack_frame_push(&mut self, frame_size: i32, frame_pointer: LocalId) {
// Can't use the usual instruction methods because they push to self.code.
// This is the only case where we push instructions somewhere different.
self.preamble.push(GETGLOBAL);
self.preamble.push(GETGLOBAL as u8);
self.preamble.encode_u32(STACK_POINTER_GLOBAL_ID);
self.preamble.push(I32CONST);
self.preamble.push(I32CONST as u8);
self.preamble.encode_i32(frame_size);
self.preamble.push(I32SUB);
self.preamble.push(TEELOCAL);
self.preamble.push(I32SUB as u8);
self.preamble.push(TEELOCAL as u8);
self.preamble.encode_u32(frame_pointer.0);
self.preamble.push(SETGLOBAL);
self.preamble.push(SETGLOBAL as u8);
self.preamble.encode_u32(STACK_POINTER_GLOBAL_ID);
}
@ -366,7 +366,7 @@ impl<'a> CodeBuilder<'a> {
self.build_stack_frame_pop(aligned_size, frame_ptr_id);
}
self.code.push(END);
self.code.push(END as u8);
let inner_len = self.preamble.len() + self.code.len() + self.insert_bytes.len();
self.inner_length.encode_u32(inner_len as u32);
@ -433,27 +433,28 @@ impl<'a> CodeBuilder<'a> {
/// Base method for generating instructions
/// Emits the opcode and simulates VM stack push/pop
fn inst(&mut self, opcode: u8, pops: usize, push: bool) {
fn inst(&mut self, opcode: OpCode, pops: usize, push: bool) {
let new_len = self.vm_stack.len() - pops as usize;
self.vm_stack.truncate(new_len);
if push {
self.vm_stack.push(Symbol::WASM_ANONYMOUS_STACK_VALUE);
}
self.code.push(opcode);
self.code.push(opcode as u8);
}
fn inst_imm8(&mut self, opcode: u8, pops: usize, push: bool, immediate: u8) {
fn inst_imm8(&mut self, opcode: OpCode, pops: usize, push: bool, immediate: u8) {
self.inst(opcode, pops, push);
self.code.push(immediate);
}
// public for use in test code
pub fn inst_imm32(&mut self, opcode: u8, pops: usize, push: bool, immediate: u32) {
pub fn inst_imm32(&mut self, opcode: OpCode, pops: usize, push: bool, immediate: u32) {
self.inst(opcode, pops, push);
self.code.encode_u32(immediate);
}
fn inst_mem(&mut self, opcode: u8, pops: usize, push: bool, align: Align, offset: u32) {
fn inst_mem(&mut self, opcode: OpCode, pops: usize, push: bool, align: Align, offset: u32) {
self.inst(opcode, pops, push);
self.code.push(align as u8);
self.code.encode_u32(offset);
@ -526,7 +527,7 @@ impl<'a> CodeBuilder<'a> {
if has_return_val {
self.vm_stack.push(Symbol::WASM_ANONYMOUS_STACK_VALUE);
}
self.code.push(CALL);
self.code.push(CALL as u8);
// Write the index of the function to be called.
// Also make a RelocationEntry so the linker can see that this byte offset relates to a function by name.

View file

@ -1,178 +1,182 @@
pub const UNREACHABLE: u8 = 0x00;
pub const NOP: u8 = 0x01;
pub const BLOCK: u8 = 0x02;
pub const LOOP: u8 = 0x03;
pub const IF: u8 = 0x04;
pub const ELSE: u8 = 0x05;
pub const END: u8 = 0x0b;
pub const BR: u8 = 0x0c;
pub const BRIF: u8 = 0x0d;
pub const BRTABLE: u8 = 0x0e;
pub const RETURN: u8 = 0x0f;
pub const CALL: u8 = 0x10;
pub const CALLINDIRECT: u8 = 0x11;
pub const DROP: u8 = 0x1a;
pub const SELECT: u8 = 0x1b;
pub const GETLOCAL: u8 = 0x20;
pub const SETLOCAL: u8 = 0x21;
pub const TEELOCAL: u8 = 0x22;
pub const GETGLOBAL: u8 = 0x23;
pub const SETGLOBAL: u8 = 0x24;
pub const I32LOAD: u8 = 0x28;
pub const I64LOAD: u8 = 0x29;
pub const F32LOAD: u8 = 0x2a;
pub const F64LOAD: u8 = 0x2b;
pub const I32LOAD8S: u8 = 0x2c;
pub const I32LOAD8U: u8 = 0x2d;
pub const I32LOAD16S: u8 = 0x2e;
pub const I32LOAD16U: u8 = 0x2f;
pub const I64LOAD8S: u8 = 0x30;
pub const I64LOAD8U: u8 = 0x31;
pub const I64LOAD16S: u8 = 0x32;
pub const I64LOAD16U: u8 = 0x33;
pub const I64LOAD32S: u8 = 0x34;
pub const I64LOAD32U: u8 = 0x35;
pub const I32STORE: u8 = 0x36;
pub const I64STORE: u8 = 0x37;
pub const F32STORE: u8 = 0x38;
pub const F64STORE: u8 = 0x39;
pub const I32STORE8: u8 = 0x3a;
pub const I32STORE16: u8 = 0x3b;
pub const I64STORE8: u8 = 0x3c;
pub const I64STORE16: u8 = 0x3d;
pub const I64STORE32: u8 = 0x3e;
pub const CURRENTMEMORY: u8 = 0x3f;
pub const GROWMEMORY: u8 = 0x40;
pub const I32CONST: u8 = 0x41;
pub const I64CONST: u8 = 0x42;
pub const F32CONST: u8 = 0x43;
pub const F64CONST: u8 = 0x44;
pub const I32EQZ: u8 = 0x45;
pub const I32EQ: u8 = 0x46;
pub const I32NE: u8 = 0x47;
pub const I32LTS: u8 = 0x48;
pub const I32LTU: u8 = 0x49;
pub const I32GTS: u8 = 0x4a;
pub const I32GTU: u8 = 0x4b;
pub const I32LES: u8 = 0x4c;
pub const I32LEU: u8 = 0x4d;
pub const I32GES: u8 = 0x4e;
pub const I32GEU: u8 = 0x4f;
pub const I64EQZ: u8 = 0x50;
pub const I64EQ: u8 = 0x51;
pub const I64NE: u8 = 0x52;
pub const I64LTS: u8 = 0x53;
pub const I64LTU: u8 = 0x54;
pub const I64GTS: u8 = 0x55;
pub const I64GTU: u8 = 0x56;
pub const I64LES: u8 = 0x57;
pub const I64LEU: u8 = 0x58;
pub const I64GES: u8 = 0x59;
pub const I64GEU: u8 = 0x5a;
#[repr(u8)]
#[derive(Debug)]
pub enum OpCode {
UNREACHABLE = 0x00,
NOP = 0x01,
BLOCK = 0x02,
LOOP = 0x03,
IF = 0x04,
ELSE = 0x05,
END = 0x0b,
BR = 0x0c,
BRIF = 0x0d,
BRTABLE = 0x0e,
RETURN = 0x0f,
CALL = 0x10,
CALLINDIRECT = 0x11,
DROP = 0x1a,
SELECT = 0x1b,
GETLOCAL = 0x20,
SETLOCAL = 0x21,
TEELOCAL = 0x22,
GETGLOBAL = 0x23,
SETGLOBAL = 0x24,
I32LOAD = 0x28,
I64LOAD = 0x29,
F32LOAD = 0x2a,
F64LOAD = 0x2b,
I32LOAD8S = 0x2c,
I32LOAD8U = 0x2d,
I32LOAD16S = 0x2e,
I32LOAD16U = 0x2f,
I64LOAD8S = 0x30,
I64LOAD8U = 0x31,
I64LOAD16S = 0x32,
I64LOAD16U = 0x33,
I64LOAD32S = 0x34,
I64LOAD32U = 0x35,
I32STORE = 0x36,
I64STORE = 0x37,
F32STORE = 0x38,
F64STORE = 0x39,
I32STORE8 = 0x3a,
I32STORE16 = 0x3b,
I64STORE8 = 0x3c,
I64STORE16 = 0x3d,
I64STORE32 = 0x3e,
CURRENTMEMORY = 0x3f,
GROWMEMORY = 0x40,
I32CONST = 0x41,
I64CONST = 0x42,
F32CONST = 0x43,
F64CONST = 0x44,
I32EQZ = 0x45,
I32EQ = 0x46,
I32NE = 0x47,
I32LTS = 0x48,
I32LTU = 0x49,
I32GTS = 0x4a,
I32GTU = 0x4b,
I32LES = 0x4c,
I32LEU = 0x4d,
I32GES = 0x4e,
I32GEU = 0x4f,
I64EQZ = 0x50,
I64EQ = 0x51,
I64NE = 0x52,
I64LTS = 0x53,
I64LTU = 0x54,
I64GTS = 0x55,
I64GTU = 0x56,
I64LES = 0x57,
I64LEU = 0x58,
I64GES = 0x59,
I64GEU = 0x5a,
pub const F32EQ: u8 = 0x5b;
pub const F32NE: u8 = 0x5c;
pub const F32LT: u8 = 0x5d;
pub const F32GT: u8 = 0x5e;
pub const F32LE: u8 = 0x5f;
pub const F32GE: u8 = 0x60;
F32EQ = 0x5b,
F32NE = 0x5c,
F32LT = 0x5d,
F32GT = 0x5e,
F32LE = 0x5f,
F32GE = 0x60,
pub const F64EQ: u8 = 0x61;
pub const F64NE: u8 = 0x62;
pub const F64LT: u8 = 0x63;
pub const F64GT: u8 = 0x64;
pub const F64LE: u8 = 0x65;
pub const F64GE: u8 = 0x66;
F64EQ = 0x61,
F64NE = 0x62,
F64LT = 0x63,
F64GT = 0x64,
F64LE = 0x65,
F64GE = 0x66,
pub const I32CLZ: u8 = 0x67;
pub const I32CTZ: u8 = 0x68;
pub const I32POPCNT: u8 = 0x69;
pub const I32ADD: u8 = 0x6a;
pub const I32SUB: u8 = 0x6b;
pub const I32MUL: u8 = 0x6c;
pub const I32DIVS: u8 = 0x6d;
pub const I32DIVU: u8 = 0x6e;
pub const I32REMS: u8 = 0x6f;
pub const I32REMU: u8 = 0x70;
pub const I32AND: u8 = 0x71;
pub const I32OR: u8 = 0x72;
pub const I32XOR: u8 = 0x73;
pub const I32SHL: u8 = 0x74;
pub const I32SHRS: u8 = 0x75;
pub const I32SHRU: u8 = 0x76;
pub const I32ROTL: u8 = 0x77;
pub const I32ROTR: u8 = 0x78;
I32CLZ = 0x67,
I32CTZ = 0x68,
I32POPCNT = 0x69,
I32ADD = 0x6a,
I32SUB = 0x6b,
I32MUL = 0x6c,
I32DIVS = 0x6d,
I32DIVU = 0x6e,
I32REMS = 0x6f,
I32REMU = 0x70,
I32AND = 0x71,
I32OR = 0x72,
I32XOR = 0x73,
I32SHL = 0x74,
I32SHRS = 0x75,
I32SHRU = 0x76,
I32ROTL = 0x77,
I32ROTR = 0x78,
pub const I64CLZ: u8 = 0x79;
pub const I64CTZ: u8 = 0x7a;
pub const I64POPCNT: u8 = 0x7b;
pub const I64ADD: u8 = 0x7c;
pub const I64SUB: u8 = 0x7d;
pub const I64MUL: u8 = 0x7e;
pub const I64DIVS: u8 = 0x7f;
pub const I64DIVU: u8 = 0x80;
pub const I64REMS: u8 = 0x81;
pub const I64REMU: u8 = 0x82;
pub const I64AND: u8 = 0x83;
pub const I64OR: u8 = 0x84;
pub const I64XOR: u8 = 0x85;
pub const I64SHL: u8 = 0x86;
pub const I64SHRS: u8 = 0x87;
pub const I64SHRU: u8 = 0x88;
pub const I64ROTL: u8 = 0x89;
pub const I64ROTR: u8 = 0x8a;
pub const F32ABS: u8 = 0x8b;
pub const F32NEG: u8 = 0x8c;
pub const F32CEIL: u8 = 0x8d;
pub const F32FLOOR: u8 = 0x8e;
pub const F32TRUNC: u8 = 0x8f;
pub const F32NEAREST: u8 = 0x90;
pub const F32SQRT: u8 = 0x91;
pub const F32ADD: u8 = 0x92;
pub const F32SUB: u8 = 0x93;
pub const F32MUL: u8 = 0x94;
pub const F32DIV: u8 = 0x95;
pub const F32MIN: u8 = 0x96;
pub const F32MAX: u8 = 0x97;
pub const F32COPYSIGN: u8 = 0x98;
pub const F64ABS: u8 = 0x99;
pub const F64NEG: u8 = 0x9a;
pub const F64CEIL: u8 = 0x9b;
pub const F64FLOOR: u8 = 0x9c;
pub const F64TRUNC: u8 = 0x9d;
pub const F64NEAREST: u8 = 0x9e;
pub const F64SQRT: u8 = 0x9f;
pub const F64ADD: u8 = 0xa0;
pub const F64SUB: u8 = 0xa1;
pub const F64MUL: u8 = 0xa2;
pub const F64DIV: u8 = 0xa3;
pub const F64MIN: u8 = 0xa4;
pub const F64MAX: u8 = 0xa5;
pub const F64COPYSIGN: u8 = 0xa6;
I64CLZ = 0x79,
I64CTZ = 0x7a,
I64POPCNT = 0x7b,
I64ADD = 0x7c,
I64SUB = 0x7d,
I64MUL = 0x7e,
I64DIVS = 0x7f,
I64DIVU = 0x80,
I64REMS = 0x81,
I64REMU = 0x82,
I64AND = 0x83,
I64OR = 0x84,
I64XOR = 0x85,
I64SHL = 0x86,
I64SHRS = 0x87,
I64SHRU = 0x88,
I64ROTL = 0x89,
I64ROTR = 0x8a,
F32ABS = 0x8b,
F32NEG = 0x8c,
F32CEIL = 0x8d,
F32FLOOR = 0x8e,
F32TRUNC = 0x8f,
F32NEAREST = 0x90,
F32SQRT = 0x91,
F32ADD = 0x92,
F32SUB = 0x93,
F32MUL = 0x94,
F32DIV = 0x95,
F32MIN = 0x96,
F32MAX = 0x97,
F32COPYSIGN = 0x98,
F64ABS = 0x99,
F64NEG = 0x9a,
F64CEIL = 0x9b,
F64FLOOR = 0x9c,
F64TRUNC = 0x9d,
F64NEAREST = 0x9e,
F64SQRT = 0x9f,
F64ADD = 0xa0,
F64SUB = 0xa1,
F64MUL = 0xa2,
F64DIV = 0xa3,
F64MIN = 0xa4,
F64MAX = 0xa5,
F64COPYSIGN = 0xa6,
pub const I32WRAPI64: u8 = 0xa7;
pub const I32TRUNCSF32: u8 = 0xa8;
pub const I32TRUNCUF32: u8 = 0xa9;
pub const I32TRUNCSF64: u8 = 0xaa;
pub const I32TRUNCUF64: u8 = 0xab;
pub const I64EXTENDSI32: u8 = 0xac;
pub const I64EXTENDUI32: u8 = 0xad;
pub const I64TRUNCSF32: u8 = 0xae;
pub const I64TRUNCUF32: u8 = 0xaf;
pub const I64TRUNCSF64: u8 = 0xb0;
pub const I64TRUNCUF64: u8 = 0xb1;
pub const F32CONVERTSI32: u8 = 0xb2;
pub const F32CONVERTUI32: u8 = 0xb3;
pub const F32CONVERTSI64: u8 = 0xb4;
pub const F32CONVERTUI64: u8 = 0xb5;
pub const F32DEMOTEF64: u8 = 0xb6;
pub const F64CONVERTSI32: u8 = 0xb7;
pub const F64CONVERTUI32: u8 = 0xb8;
pub const F64CONVERTSI64: u8 = 0xb9;
pub const F64CONVERTUI64: u8 = 0xba;
pub const F64PROMOTEF32: u8 = 0xbb;
I32WRAPI64 = 0xa7,
I32TRUNCSF32 = 0xa8,
I32TRUNCUF32 = 0xa9,
I32TRUNCSF64 = 0xaa,
I32TRUNCUF64 = 0xab,
I64EXTENDSI32 = 0xac,
I64EXTENDUI32 = 0xad,
I64TRUNCSF32 = 0xae,
I64TRUNCUF32 = 0xaf,
I64TRUNCSF64 = 0xb0,
I64TRUNCUF64 = 0xb1,
F32CONVERTSI32 = 0xb2,
F32CONVERTUI32 = 0xb3,
F32CONVERTSI64 = 0xb4,
F32CONVERTUI64 = 0xb5,
F32DEMOTEF64 = 0xb6,
F64CONVERTSI32 = 0xb7,
F64CONVERTUI32 = 0xb8,
F64CONVERTSI64 = 0xb9,
F64CONVERTUI64 = 0xba,
F64PROMOTEF32 = 0xbb,
pub const I32REINTERPRETF32: u8 = 0xbc;
pub const I64REINTERPRETF64: u8 = 0xbd;
pub const F32REINTERPRETI32: u8 = 0xbe;
pub const F64REINTERPRETI64: u8 = 0xbf;
I32REINTERPRETF32 = 0xbc,
I64REINTERPRETF64 = 0xbd,
F32REINTERPRETI32 = 0xbe,
F64REINTERPRETI64 = 0xbf,
}

View file

@ -4,7 +4,7 @@ use bumpalo::Bump;
use super::linking::{
IndexRelocType, LinkingSection, RelocationEntry, RelocationSection, SymInfo, WasmObjectSymbol,
};
use super::opcodes;
use super::opcodes::OpCode;
use super::serialize::{SerialBuffer, Serialize};
use super::{CodeBuilder, ValueType};
@ -353,23 +353,23 @@ impl Serialize for ConstExpr {
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
match self {
ConstExpr::I32(x) => {
buffer.append_u8(opcodes::I32CONST);
buffer.append_u8(OpCode::I32CONST as u8);
buffer.encode_i32(*x);
}
ConstExpr::I64(x) => {
buffer.append_u8(opcodes::I64CONST);
buffer.append_u8(OpCode::I64CONST as u8);
buffer.encode_i64(*x);
}
ConstExpr::F32(x) => {
buffer.append_u8(opcodes::F32CONST);
buffer.append_u8(OpCode::F32CONST as u8);
buffer.encode_f32(*x);
}
ConstExpr::F64(x) => {
buffer.append_u8(opcodes::F64CONST);
buffer.append_u8(OpCode::F64CONST as u8);
buffer.encode_f64(*x);
}
}
buffer.append_u8(opcodes::END);
buffer.append_u8(OpCode::END as u8);
}
}

View file

@ -1734,7 +1734,7 @@ fn update<'a>(
let mut shorthands = (*state.arc_shorthands).lock();
for (shorthand, package_or_path) in header.packages.iter() {
shorthands.insert(shorthand, package_or_path.clone());
shorthands.insert(shorthand, *package_or_path);
}
if let PkgConfig {
@ -2273,7 +2273,7 @@ fn finish_specialization(
let package_or_path = match platform_path {
Valid(To::ExistingPackage(shorthand)) => {
match (*state.arc_shorthands).lock().get(shorthand) {
Some(p_or_p) => p_or_p.clone(),
Some(p_or_p) => *p_or_p,
None => unreachable!(),
}
}
@ -2978,7 +2978,7 @@ fn send_header<'a>(
package_or_path,
..
} => {
package_entries.insert(*shorthand, package_or_path.value.clone());
package_entries.insert(*shorthand, package_or_path.value);
}
SpaceBefore(inner, _) | SpaceAfter(inner, _) => {
parse_entries.push(inner);
@ -3211,7 +3211,7 @@ fn send_header_two<'a>(
package_or_path,
..
} => {
package_entries.insert(*shorthand, package_or_path.value.clone());
package_entries.insert(*shorthand, package_or_path.value);
}
SpaceBefore(inner, _) | SpaceAfter(inner, _) => {
parse_entries.push(inner);

View file

@ -1070,6 +1070,7 @@ define_builtins! {
46 LIST_TAKE_LAST: "takeLast"
47 LIST_FIND: "find"
48 LIST_FIND_RESULT: "#find_result" // symbol used in the definition of List.find
49 LIST_SUBLIST: "sublist"
}
5 RESULT: "Result" => {
0 RESULT_RESULT: "Result" imported // the Result.Result type alias

View file

@ -19,11 +19,4 @@ ven_pretty = { path = "../../vendor/pretty" }
morphic_lib = { path = "../../vendor/morphic_lib" }
bumpalo = { version = "3.8.0", features = ["collections"] }
hashbrown = { version = "0.11.2", features = [ "bumpalo" ] }
ven_ena = { path = "../../vendor/ena" }
ven_graph = { path = "../../vendor/pathfinding" }
linked-hash-map = "0.5.4"
[dev-dependencies]
roc_builtins = { path = "../builtins" }
roc_parse = { path = "../parse" }
roc_solve = { path = "../solve" }

View file

@ -1,8 +1,46 @@
/// UNUSED
///
/// but kept for future reference
///
///
// pub fn optimize_refcount_operations<'i, T>(
// arena: &'a Bump,
// home: ModuleId,
// ident_ids: &'i mut IdentIds,
// procs: &mut MutMap<T, Proc<'a>>,
// ) {
// use crate::expand_rc;
//
// let deferred = expand_rc::Deferred {
// inc_dec_map: Default::default(),
// assignments: Vec::new_in(arena),
// decrefs: Vec::new_in(arena),
// };
//
// let mut env = expand_rc::Env {
// home,
// arena,
// ident_ids,
// layout_map: Default::default(),
// alias_map: Default::default(),
// constructor_map: Default::default(),
// deferred,
// };
//
// for (_, proc) in procs.iter_mut() {
// let b = expand_rc::expand_and_cancel_proc(
// &mut env,
// arena.alloc(proc.body.clone()),
// proc.args,
// );
// proc.body = b.clone();
// }
// }
use crate::ir::{BranchInfo, Expr, ModifyRc, Stmt};
use crate::layout::{Layout, UnionLayout};
use bumpalo::collections::Vec;
use bumpalo::Bump;
use linked_hash_map::LinkedHashMap;
// use linked_hash_map::LinkedHashMap;
use roc_collections::all::MutMap;
use roc_module::symbol::{IdentIds, ModuleId, Symbol};

View file

@ -370,40 +370,6 @@ impl<'a> Proc<'a> {
}
}
pub fn optimize_refcount_operations<'i, T>(
arena: &'a Bump,
home: ModuleId,
ident_ids: &'i mut IdentIds,
procs: &mut MutMap<T, Proc<'a>>,
) {
use crate::expand_rc;
let deferred = expand_rc::Deferred {
inc_dec_map: Default::default(),
assignments: Vec::new_in(arena),
decrefs: Vec::new_in(arena),
};
let mut env = expand_rc::Env {
home,
arena,
ident_ids,
layout_map: Default::default(),
alias_map: Default::default(),
constructor_map: Default::default(),
deferred,
};
for (_, proc) in procs.iter_mut() {
let b = expand_rc::expand_and_cancel_proc(
&mut env,
arena.alloc(proc.body.clone()),
proc.args,
);
proc.body = b.clone();
}
}
fn make_tail_recursive(&mut self, env: &mut Env<'a, '_>) {
let mut args = Vec::with_capacity_in(self.args.len(), env.arena);
let mut proc_args = Vec::with_capacity_in(self.args.len(), env.arena);

View file

@ -4,7 +4,6 @@
pub mod alias_analysis;
pub mod borrow;
pub mod expand_rc;
pub mod inc_dec;
pub mod ir;
pub mod layout;

View file

@ -9,13 +9,13 @@ use crate::string_literal;
use bumpalo::collections::Vec;
use roc_region::all::Loc;
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub struct PackageName<'a> {
pub account: &'a str,
pub pkg: &'a str,
}
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub enum Version<'a> {
Exact(&'a str),
Range {
@ -32,7 +32,7 @@ pub enum VersionComparison {
DisallowsEqual,
}
#[derive(Clone, PartialEq, Debug)]
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum PackageOrPath<'a> {
Package(PackageName<'a>, Version<'a>),
Path(StrLiteral<'a>),
@ -240,7 +240,7 @@ pub enum TypedIdent<'a> {
SpaceAfter(&'a TypedIdent<'a>, &'a [CommentOrNewline<'a>]),
}
#[derive(Clone, Debug, PartialEq)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum PackageEntry<'a> {
Entry {
shorthand: &'a str,

View file

@ -586,6 +586,7 @@ struct Packages<'a> {
before_packages_keyword: &'a [CommentOrNewline<'a>],
after_packages_keyword: &'a [CommentOrNewline<'a>],
final_comments: &'a [CommentOrNewline<'a>],
}
#[inline(always)]
@ -602,21 +603,24 @@ fn packages<'a>() -> impl Parser<'a, Packages<'a>, EPackages<'a>> {
EPackages::IndentPackages,
EPackages::IndentListStart
),
collection_e!(
collection_trailing_sep_e!(
word1(b'{', EPackages::ListStart),
specialize(EPackages::PackageEntry, loc!(package_entry())),
word1(b',', EPackages::ListEnd),
word1(b'}', EPackages::ListEnd),
min_indent,
EPackages::Open,
EPackages::Space,
EPackages::IndentListEnd
EPackages::IndentListEnd,
PackageEntry::SpaceBefore
)
),
|((before_packages_keyword, after_packages_keyword), entries)| {
|((before_packages_keyword, after_packages_keyword), (entries, final_comments))| {
Packages {
entries,
before_packages_keyword,
after_packages_keyword,
final_comments,
}
}
)

View file

@ -265,6 +265,7 @@ pub enum ETypedIdent<'a> {
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EPackages<'a> {
Open(Row, Col),
Space(BadInputError, Row, Col),
Packages(Row, Col),
IndentPackages(Row, Col),

View file

@ -3223,6 +3223,62 @@ mod test_parse {
assert_eq!(Ok(expected), actual);
}
#[test]
fn full_app_header_trailing_commas() {
use ExposesEntry::Exposed;
use PackageOrPath::Path;
let newlines = &[Newline];
let pkg_entry = PackageEntry::Entry {
shorthand: "base",
spaces_after_shorthand: &[],
package_or_path: Located::new(1, 1, 21, 33, Path(PlainLine("./platform"))),
};
let loc_pkg_entry = Located::new(1, 1, 15, 33, pkg_entry);
let arena = Bump::new();
let packages = bumpalo::vec![in &arena; 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 module_name = StrLiteral::PlainLine("quicksort");
let header = AppHeader {
before_header: &[],
name: Located::new(0, 0, 4, 15, module_name),
packages,
imports,
provides,
to: Located::new(3, 3, 30, 34, To::ExistingPackage("base")),
after_app_keyword: &[],
before_packages: newlines,
after_packages: &[],
before_imports: newlines,
after_imports: &[],
before_provides: newlines,
after_provides: &[],
before_to: &[],
after_to: &[],
};
let expected = roc_parse::ast::Module::App { header };
let src = indoc!(
r#"
app "quicksort"
packages { base: "./platform", }
imports [ foo.Bar.Baz ]
provides [ quicksort ] to base
"#
);
let actual = roc_parse::module::parse_header(&arena, State::new(src.as_bytes()))
.map(|tuple| tuple.0);
assert_eq!(Ok(expected), actual);
}
#[test]
fn empty_platform_header() {
let pkg_name = PackageName {

View file

@ -16,8 +16,6 @@ roc_can = { path = "../can" }
roc_solve = { path = "../solve" }
roc_mono = { path = "../mono" }
ven_pretty = { path = "../../vendor/pretty" }
im = "15.0.0"
im-rc = "15.0.0"
distance = "0.4.0"
bumpalo = { version = "3.8.0", features = ["collections"] }

View file

@ -3781,6 +3781,18 @@ mod solve_expr {
);
}
#[test]
fn list_sublist() {
infer_eq_without_problem(
indoc!(
r#"
List.sublist
"#
),
"List a, { len : Nat, start : Nat } -> List a",
);
}
#[test]
fn list_drop_last() {
infer_eq_without_problem(

View file

@ -29,8 +29,6 @@ roc_can = { path = "../can" }
roc_parse = { path = "../parse" }
roc_build = { path = "../build" }
roc_std = { path = "../../roc_std" }
im = "15.0.0"
im-rc = "15.0.0"
bumpalo = { version = "3.8.0", features = ["collections"] }
either = "1.6.1"
libc = "0.2.106"

View file

@ -208,6 +208,46 @@ fn list_take_last() {
);
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
fn list_sublist() {
assert_evals_to!(
"List.sublist [1, 2, 3] { start: 0 , len: 2 } ",
RocList::from_slice(&[1, 2]),
RocList<i64>
);
assert_evals_to!(
"List.sublist [1, 2, 3] { start: 1 , len: 2 } ",
RocList::from_slice(&[2, 3]),
RocList<i64>
);
assert_evals_to!(
"List.sublist [1, 2, 3] { start: 2 , len: 2 } ",
RocList::from_slice(&[3]),
RocList<i64>
);
assert_evals_to!(
"List.sublist [1, 2, 3] { start: 3 , len: 2 } ",
RocList::from_slice(&[]),
RocList<i64>
);
assert_evals_to!(
"List.sublist [] { start: 1 , len: 1 } ",
RocList::from_slice(&[]),
RocList<i64>
);
assert_evals_to!(
"List.sublist [1, 2, 3] { start: 1 , len: 0 } ",
RocList::from_slice(&[]),
RocList<i64>
);
assert_evals_to!(
"List.sublist [1, 2, 3] { start: 0 , len: 5 } ",
RocList::from_slice(&[1, 2, 3]),
RocList<i64>
);
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
fn list_drop() {
@ -2301,7 +2341,7 @@ fn list_any() {
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[ignore]
#[should_panic(expected = r#"Roc failed with message: "UnresolvedTypeVar"#)]
fn list_any_empty_with_unknown_element_type() {
// Segfaults with invalid memory reference. Running this as a stand-alone
// Roc program, generates the following error message:

View file

@ -530,7 +530,7 @@ fn f64_round() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn f64_abs() {
assert_evals_to!("Num.abs -4.7", 4.7, f64);
assert_evals_to!("Num.abs 5.8", 5.8, f64);
@ -539,7 +539,7 @@ fn f64_abs() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn i64_abs() {
assert_evals_to!("Num.abs -6", 6, i64);
assert_evals_to!("Num.abs 7", 7, i64);
@ -713,7 +713,7 @@ fn gen_int_eq() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn gen_int_neq() {
assert_evals_to!(
indoc!(
@ -767,7 +767,7 @@ fn gen_dec_neq() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn gen_wrap_int_neq() {
assert_evals_to!(
indoc!(
@ -950,14 +950,14 @@ fn gen_rem_div_by_zero_i64() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn gen_is_zero_i64() {
assert_evals_to!("Num.isZero 0", true, bool);
assert_evals_to!("Num.isZero 1", false, bool);
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn gen_is_positive_i64() {
assert_evals_to!("Num.isPositive 0", false, bool);
assert_evals_to!("Num.isPositive 1", true, bool);
@ -965,7 +965,7 @@ fn gen_is_positive_i64() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn gen_is_negative_i64() {
assert_evals_to!("Num.isNegative 0", false, bool);
assert_evals_to!("Num.isNegative 3", false, bool);
@ -973,7 +973,7 @@ fn gen_is_negative_i64() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn gen_is_positive_f64() {
assert_evals_to!("Num.isPositive 0.0", false, bool);
assert_evals_to!("Num.isPositive 4.7", true, bool);
@ -981,7 +981,7 @@ fn gen_is_positive_f64() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn gen_is_negative_f64() {
assert_evals_to!("Num.isNegative 0.0", false, bool);
assert_evals_to!("Num.isNegative 9.9", false, bool);
@ -989,7 +989,7 @@ fn gen_is_negative_f64() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn gen_is_zero_f64() {
assert_evals_to!("Num.isZero 0", true, bool);
assert_evals_to!("Num.isZero 0_0", true, bool);
@ -998,14 +998,14 @@ fn gen_is_zero_f64() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn gen_is_odd() {
assert_evals_to!("Num.isOdd 4", false, bool);
assert_evals_to!("Num.isOdd 5", true, bool);
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn gen_is_even() {
assert_evals_to!("Num.isEven 6", true, bool);
assert_evals_to!("Num.isEven 7", false, bool);
@ -1033,7 +1033,7 @@ fn tan() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn bitwise_and() {
assert_evals_to!("Num.bitwiseAnd 20 20", 20, i64);
assert_evals_to!("Num.bitwiseAnd 25 10", 8, i64);
@ -1041,7 +1041,7 @@ fn bitwise_and() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn bitwise_xor() {
assert_evals_to!("Num.bitwiseXor 20 20", 0, i64);
assert_evals_to!("Num.bitwiseXor 15 14", 1, i64);
@ -1050,14 +1050,14 @@ fn bitwise_xor() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn bitwise_or() {
assert_evals_to!("Num.bitwiseOr 1 1", 1, i64);
assert_evals_to!("Num.bitwiseOr 1 2", 3, i64);
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn lt_i64() {
assert_evals_to!("1 < 2", true, bool);
assert_evals_to!("1 < 1", false, bool);
@ -1066,7 +1066,7 @@ fn lt_i64() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn lte_i64() {
assert_evals_to!("1 <= 1", true, bool);
assert_evals_to!("2 <= 1", false, bool);
@ -1084,7 +1084,7 @@ fn gt_i64() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn gte_i64() {
assert_evals_to!("1 >= 1", true, bool);
assert_evals_to!("1 >= 2", false, bool);
@ -1093,7 +1093,7 @@ fn gte_i64() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn lt_f64() {
assert_evals_to!("1.1 < 1.2", true, bool);
assert_evals_to!("1.1 < 1.1", false, bool);
@ -1102,7 +1102,7 @@ fn lt_f64() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn lte_f64() {
assert_evals_to!("1.1 <= 1.1", true, bool);
assert_evals_to!("1.2 <= 1.1", false, bool);
@ -1120,7 +1120,7 @@ fn gt_f64() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn gte_f64() {
assert_evals_to!("1.1 >= 1.1", true, bool);
assert_evals_to!("1.1 >= 1.2", false, bool);
@ -1214,7 +1214,7 @@ fn tail_call_elimination() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-wasm"))]
fn int_negate() {
assert_evals_to!("Num.neg 123", -123, i64);
assert_evals_to!("Num.neg Num.maxInt", -i64::MAX, i64);
@ -1272,19 +1272,19 @@ fn gen_basic_fn() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn int_to_float() {
assert_evals_to!("Num.toFloat 0x9", 9.0, f64);
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn num_to_float() {
assert_evals_to!("Num.toFloat 9", 9.0, f64);
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn float_to_float() {
assert_evals_to!("Num.toFloat 0.5", 0.5, f64);
}
@ -1312,13 +1312,13 @@ fn pow() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn ceiling() {
assert_evals_to!("Num.ceiling 1.1", 2, i64);
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn floor() {
assert_evals_to!("Num.floor 1.9", 1, i64);
}
@ -1379,7 +1379,7 @@ fn int_add_checked() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn int_add_wrap() {
assert_evals_to!(
indoc!(
@ -1483,7 +1483,7 @@ fn int_sub_overflow() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn int_sub_wrap() {
assert_evals_to!(
indoc!(
@ -1628,7 +1628,7 @@ fn float_negative_mul_overflow() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn int_mul_wrap() {
assert_evals_to!(
indoc!(
@ -1698,7 +1698,7 @@ fn float_mul_checked() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn shift_left_by() {
assert_evals_to!("Num.shiftLeftBy 0 0b0000_0001", 0b0000_0001, i64);
assert_evals_to!("Num.shiftLeftBy 1 0b0000_0001", 0b0000_0010, i64);
@ -1706,7 +1706,7 @@ fn shift_left_by() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[ignore]
fn shift_right_by() {
// Sign Extended Right Shift
@ -1716,7 +1716,7 @@ fn shift_right_by() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[ignore]
fn shift_right_zf_by() {
// Logical Right Shift
@ -1921,7 +1921,7 @@ fn bytes_to_u32_random_u8s() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn when_on_i32() {
assert_evals_to!(
indoc!(
@ -1944,7 +1944,7 @@ fn when_on_i32() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn when_on_i16() {
assert_evals_to!(
indoc!(

View file

@ -32,8 +32,6 @@ roc_unify = { path = "../compiler/unify" }
roc_reporting = { path = "../compiler/reporting" }
roc_solve = { path = "../compiler/solve" }
ven_graph = { path = "../vendor/pathfinding" }
im = "15.0.0"
im-rc = "15.0.0"
bumpalo = { version = "3.8.0", features = ["collections"] }
arraystring = "0.3.0"
libc = "0.2.106"