mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 13:59:08 +00:00
Bringing in remote trunk
This commit is contained in:
commit
a9c0185225
50 changed files with 1047 additions and 1080 deletions
10
.github/workflows/ci.yml
vendored
10
.github/workflows/ci.yml
vendored
|
@ -55,10 +55,16 @@ jobs:
|
|||
name: cargo test
|
||||
with:
|
||||
command: test
|
||||
args: --no-fail-fast
|
||||
|
||||
- uses: actions-rs/cargo@v1
|
||||
name: cargo test -- --ignored
|
||||
continue-on-error: true
|
||||
with:
|
||||
command: test
|
||||
args: -- --ignored
|
||||
|
||||
- uses: actions-rs/cargo@v1
|
||||
name: cargo test --release
|
||||
with:
|
||||
command: test
|
||||
args: --release --no-fail-fast
|
||||
args: --release
|
||||
|
|
|
@ -1,21 +1,37 @@
|
|||
# Building the Roc compiler from source
|
||||
|
||||
|
||||
## Installing LLVM, valgrind, libunwind, and libc++-dev
|
||||
## Installing LLVM, Zig, valgrind, libunwind, and libc++-dev
|
||||
|
||||
To build the compiler, you need these installed:
|
||||
|
||||
* `libunwind` (macOS should already have this one installed)
|
||||
* `libc++-dev`
|
||||
* a particular version of LLVM
|
||||
* a particular version of Zig (see below)
|
||||
* a particular version of LLVM (see below)
|
||||
|
||||
To run the test suite (via `cargo test`), you additionally need to install:
|
||||
|
||||
* [`valgrind`](https://www.valgrind.org/) (needs special treatment to [install on macOS](https://stackoverflow.com/a/61359781)]
|
||||
* [`valgrind`](https://www.valgrind.org/) (needs special treatment to [install on macOS](https://stackoverflow.com/a/61359781)
|
||||
Alternatively, you can use `cargo test --no-fail-fast` or `cargo test -p specific_tests` to skip over the valgrind failures & tests.
|
||||
|
||||
Some systems may already have `libc++-dev` on them, but if not, you may need to install it. (On Ubuntu, this can be done with `sudo apt-get install libc++-dev`.) macOS systems
|
||||
should already have `libunwind`, but other systems will need to install it
|
||||
(e.g. with `sudo apt-get install libunwind-dev`).
|
||||
### libunwind & libc++-dev
|
||||
|
||||
MacOS systems should already have `libunwind`, but other systems will need to install it (On Ubuntu, this can be donw with `sudo apt-get install libunwind-dev`).
|
||||
Some systems may already have `libc++-dev` on them, but if not, you may need to install it. (On Ubuntu, this can be done with `sudo apt-get install libc++-dev`.)
|
||||
|
||||
### Zig
|
||||
We use a specific version of Zig, a build off the the commit `0088efc4b`. The latest tagged version of Zig, 0.6.0, doesn't include the feature to emit LLVM ir, which is a core feature of how we use Zig. To download this specific version, you can use the following links:
|
||||
* [linux](https://ziglang.org/builds/zig-linux-x86_64-0.6.0+0088efc4b.tar.xz)
|
||||
* [macOS](https://ziglang.org/builds/zig-macos-x86_64-0.6.0+0088efc4b.tar.xz)
|
||||
|
||||
Alternatively, any recent master branch build should work. To install the latest master branch build you can use:
|
||||
* `brew install zig --HEAD` (on macos)
|
||||
* `snap install zig --classic --edge` (on ubunutu)
|
||||
|
||||
Once 0.7.0 is released, we'll switch back to installing the tagged releases and this process will get easier.
|
||||
|
||||
### LLVM
|
||||
|
||||
To see which version of LLVM you need, take a look at `Cargo.toml`, in particular the `branch` section of the `inkwell` dependency. It should have something like `llvmX-Y` where X and Y are the major and minor revisions of LLVM you need.
|
||||
|
||||
|
|
36
Cargo.lock
generated
36
Cargo.lock
generated
|
@ -2217,6 +2217,7 @@ dependencies = [
|
|||
"indoc",
|
||||
"inkwell",
|
||||
"inlinable_string",
|
||||
"libloading",
|
||||
"maplit",
|
||||
"pretty_assertions",
|
||||
"quickcheck",
|
||||
|
@ -2238,7 +2239,7 @@ dependencies = [
|
|||
"roc_unify",
|
||||
"roc_uniq",
|
||||
"target-lexicon",
|
||||
"tokio",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2257,13 +2258,6 @@ dependencies = [
|
|||
"roc_types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roc_builtins_bitcode"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"pretty_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roc_can"
|
||||
version = "0.1.0"
|
||||
|
@ -2298,6 +2292,7 @@ dependencies = [
|
|||
"inkwell",
|
||||
"inlinable_string",
|
||||
"libc",
|
||||
"libloading",
|
||||
"maplit",
|
||||
"pretty_assertions",
|
||||
"quickcheck",
|
||||
|
@ -2323,6 +2318,7 @@ dependencies = [
|
|||
"roc_uniq",
|
||||
"serde",
|
||||
"serde-xml-rs",
|
||||
"serial_test",
|
||||
"strip-ansi-escapes",
|
||||
"target-lexicon",
|
||||
"tempfile",
|
||||
|
@ -2432,10 +2428,12 @@ dependencies = [
|
|||
"inkwell",
|
||||
"inlinable_string",
|
||||
"libc",
|
||||
"libloading",
|
||||
"maplit",
|
||||
"pretty_assertions",
|
||||
"quickcheck",
|
||||
"quickcheck_macros",
|
||||
"roc_build",
|
||||
"roc_builtins",
|
||||
"roc_can",
|
||||
"roc_collections",
|
||||
|
@ -2807,6 +2805,28 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serial_test"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b15f74add9a9d4a3eb2bf739c9a427d266d3895b53d992c3a7c234fec2ff1f1"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"parking_lot 0.10.2",
|
||||
"serial_test_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serial_test_derive"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65f59259be9fc1bf677d06cc1456e97756004a1a5a577480f71430bd7c17ba33"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.21",
|
||||
"quote 1.0.7",
|
||||
"syn 1.0.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha-1"
|
||||
version = "0.8.2"
|
||||
|
|
|
@ -10,7 +10,6 @@ members = [
|
|||
"compiler/types",
|
||||
"compiler/uniq",
|
||||
"compiler/builtins",
|
||||
"compiler/builtins/bitcode",
|
||||
"compiler/constrain",
|
||||
"compiler/unify",
|
||||
"compiler/solve",
|
||||
|
|
|
@ -60,3 +60,11 @@ wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
|
|||
add-apt-repository "${REPO_NAME}"
|
||||
apt-get update
|
||||
apt-get install -y clang-$LLVM_VERSION lldb-$LLVM_VERSION lld-$LLVM_VERSION clangd-$LLVM_VERSION libc++abi-dev libunwind-dev valgrind
|
||||
|
||||
# install zig - can't use apt-get since we require at least a specific commit later then the most recent tag (0.6.0)
|
||||
wget -c https://ziglang.org/builds/zig-linux-x86_64-0.6.0+0088efc4b.tar.xz --no-check-certificate
|
||||
tar -xf zig-linux-x86_64-0.6.0+0088efc4b.tar.xz
|
||||
ln -s "$PWD/zig-linux-x86_64-0.6.0+0088efc4b/zig" /usr/local/bin/zig
|
||||
|
||||
# symlink llvm tools
|
||||
ln -s /usr/bin/llvm-as-10 /usr/local/bin/llvm-as
|
||||
|
|
|
@ -58,6 +58,7 @@ bumpalo = { version = "3.2", features = ["collections"] }
|
|||
inlinable_string = "0.1"
|
||||
tokio = { version = "0.2", features = ["blocking", "fs", "sync", "rt-threaded", "process", "io-driver"] }
|
||||
libc = "0.2"
|
||||
libloading = "0.6"
|
||||
|
||||
# NOTE: rtfeldman/inkwell is a fork of TheDan64/inkwell which does not change anything.
|
||||
#
|
||||
|
@ -88,4 +89,5 @@ quickcheck_macros = "0.8"
|
|||
strip-ansi-escapes = "0.1"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde-xml-rs = "0.4"
|
||||
serial_test = "0.5"
|
||||
tempfile = "3.1.0"
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
use bumpalo::Bump;
|
||||
use roc_build::{link::link, program};
|
||||
use roc_build::{
|
||||
link::{link, rebuild_host, LinkType},
|
||||
program,
|
||||
};
|
||||
use roc_collections::all::MutMap;
|
||||
use roc_gen::llvm::build::OptLevel;
|
||||
use roc_load::file::LoadingProblem;
|
||||
|
@ -19,8 +22,9 @@ fn report_timing(buf: &mut String, label: &str, duration: Duration) {
|
|||
pub fn build_file(
|
||||
target: &Triple,
|
||||
src_dir: PathBuf,
|
||||
filename: PathBuf,
|
||||
roc_file_path: PathBuf,
|
||||
opt_level: OptLevel,
|
||||
link_type: LinkType,
|
||||
) -> Result<PathBuf, LoadingProblem> {
|
||||
let compilation_start = SystemTime::now();
|
||||
let arena = Bump::new();
|
||||
|
@ -35,12 +39,12 @@ pub fn build_file(
|
|||
};
|
||||
let loaded = roc_load::file::load_and_monomorphize(
|
||||
&arena,
|
||||
filename.clone(),
|
||||
roc_file_path.clone(),
|
||||
stdlib,
|
||||
src_dir.as_path(),
|
||||
subs_by_module,
|
||||
)?;
|
||||
let dest_filename = filename.with_file_name("roc_app.o");
|
||||
let app_o_file = roc_file_path.with_file_name("roc_app.o");
|
||||
let buf = &mut String::with_capacity(1024);
|
||||
|
||||
for (module_id, module_timing) in loaded.timings.iter() {
|
||||
|
@ -69,12 +73,14 @@ pub fn build_file(
|
|||
program::gen_from_mono_module(
|
||||
&arena,
|
||||
loaded,
|
||||
filename,
|
||||
roc_file_path,
|
||||
Triple::host(),
|
||||
&dest_filename,
|
||||
&app_o_file,
|
||||
opt_level,
|
||||
);
|
||||
|
||||
println!("\nSuccess! 🎉\n\n\t➡ {}\n", app_o_file.display());
|
||||
|
||||
let compilation_end = compilation_start.elapsed().unwrap();
|
||||
|
||||
println!(
|
||||
|
@ -82,33 +88,37 @@ pub fn build_file(
|
|||
compilation_end.as_millis()
|
||||
);
|
||||
|
||||
let cwd = dest_filename.parent().unwrap();
|
||||
let cwd = app_o_file.parent().unwrap();
|
||||
|
||||
// Step 2: link the precompiled host and compiled app
|
||||
let host_input_path = cwd.join("platform").join("host.o");
|
||||
let binary_path = cwd.join("app"); // TODO should be app.exe on Windows
|
||||
|
||||
// TODO we should no longer need to do this once we have platforms on
|
||||
// a package repository, as we can then get precompiled hosts from there.
|
||||
rebuild_host(host_input_path.as_path());
|
||||
|
||||
// TODO try to move as much of this linking as possible to the precompiled
|
||||
// host, to minimize the amount of host-application linking required.
|
||||
let cmd_result = // TODO use lld
|
||||
let (mut child, binary_path) = // TODO use lld
|
||||
link(
|
||||
target,
|
||||
binary_path.as_path(),
|
||||
host_input_path.as_path(),
|
||||
dest_filename.as_path(),
|
||||
binary_path,
|
||||
&[host_input_path.as_path().to_str().unwrap(), app_o_file.as_path().to_str().unwrap()],
|
||||
link_type
|
||||
)
|
||||
.map_err(|_| {
|
||||
todo!("gracefully handle `rustc` failing to spawn.");
|
||||
})?
|
||||
.wait()
|
||||
.map_err(|_| {
|
||||
})?;
|
||||
|
||||
let cmd_result = child.wait().map_err(|_| {
|
||||
todo!("gracefully handle error after `rustc` spawned");
|
||||
});
|
||||
|
||||
// Clean up the leftover .o file from the Roc, if possible.
|
||||
// (If cleaning it up fails, that's fine. No need to take action.)
|
||||
// TODO compile the dest_filename to a tmpdir, as an extra precaution.
|
||||
let _ = fs::remove_file(dest_filename);
|
||||
// TODO compile the app_o_file to a tmpdir, as an extra precaution.
|
||||
let _ = fs::remove_file(app_o_file);
|
||||
|
||||
// If the cmd errored out, return the Err.
|
||||
cmd_result?;
|
||||
|
|
|
@ -3,6 +3,7 @@ extern crate clap;
|
|||
|
||||
use clap::ArgMatches;
|
||||
use clap::{App, Arg};
|
||||
use roc_build::link::LinkType;
|
||||
use roc_gen::llvm::build::OptLevel;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
@ -91,7 +92,7 @@ pub fn build(target: &Triple, matches: &ArgMatches, run_after_build: bool) -> io
|
|||
}
|
||||
});
|
||||
|
||||
let binary_path = build::build_file(target, src_dir, path, opt_level)
|
||||
let binary_path = build::build_file(target, src_dir, path, opt_level, LinkType::Executable)
|
||||
.expect("TODO gracefully handle build_file failing");
|
||||
|
||||
if run_after_build {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use bumpalo::Bump;
|
||||
use inkwell::context::Context;
|
||||
use inkwell::execution_engine::ExecutionEngine;
|
||||
use inkwell::OptimizationLevel;
|
||||
use roc_build::link::module_to_dylib;
|
||||
use roc_builtins::unique::uniq_stdlib;
|
||||
use roc_can::constraint::Constraint;
|
||||
use roc_can::expected::Expected;
|
||||
|
@ -284,14 +283,6 @@ fn gen(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<ReplOutput, Fa
|
|||
let (module_pass, function_pass) =
|
||||
roc_gen::llvm::build::construct_optimization_passes(module, opt_level);
|
||||
|
||||
let execution_engine = module
|
||||
.create_jit_execution_engine(OptimizationLevel::None)
|
||||
.expect("Error creating JIT execution engine for test");
|
||||
|
||||
// Without calling this, we get a linker error when building this crate
|
||||
// in --release mode and then trying to eval anything in the repl.
|
||||
ExecutionEngine::link_in_mc_jit();
|
||||
|
||||
// Compile and add all the Procs before adding main
|
||||
let env = roc_gen::llvm::build::Env {
|
||||
arena: &arena,
|
||||
|
@ -386,10 +377,12 @@ fn gen(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<ReplOutput, Fa
|
|||
// Uncomment this to see the module's optimized LLVM instruction output:
|
||||
// env.module.print_to_stderr();
|
||||
|
||||
let lib = module_to_dylib(&env.module, &target, opt_level)
|
||||
.expect("Error loading compiled dylib for test");
|
||||
let answer = unsafe {
|
||||
eval::jit_to_ast(
|
||||
&arena,
|
||||
execution_engine,
|
||||
lib,
|
||||
main_fn_name,
|
||||
&main_fn_layout,
|
||||
&content,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use bumpalo::collections::Vec;
|
||||
use bumpalo::Bump;
|
||||
use inkwell::execution_engine::ExecutionEngine;
|
||||
use libloading::Library;
|
||||
use roc_collections::all::MutMap;
|
||||
use roc_gen::{run_jit_function, run_jit_function_dynamic_type};
|
||||
use roc_module::ident::{Lowercase, TagName};
|
||||
|
@ -31,7 +31,7 @@ struct Env<'a, 'env> {
|
|||
#[allow(clippy::too_many_arguments)]
|
||||
pub unsafe fn jit_to_ast<'a>(
|
||||
arena: &'a Bump,
|
||||
execution_engine: ExecutionEngine,
|
||||
lib: Library,
|
||||
main_fn_name: &str,
|
||||
layout: &Layout<'a>,
|
||||
content: &Content,
|
||||
|
@ -48,42 +48,41 @@ pub unsafe fn jit_to_ast<'a>(
|
|||
interns,
|
||||
};
|
||||
|
||||
jit_to_ast_help(&env, execution_engine, main_fn_name, layout, content)
|
||||
jit_to_ast_help(&env, lib, main_fn_name, layout, content)
|
||||
}
|
||||
|
||||
fn jit_to_ast_help<'a>(
|
||||
env: &Env<'a, '_>,
|
||||
execution_engine: ExecutionEngine,
|
||||
lib: Library,
|
||||
main_fn_name: &str,
|
||||
layout: &Layout<'a>,
|
||||
content: &Content,
|
||||
) -> Expr<'a> {
|
||||
match layout {
|
||||
Layout::Builtin(Builtin::Int64) => run_jit_function!(
|
||||
execution_engine,
|
||||
main_fn_name,
|
||||
i64,
|
||||
|num| num_to_ast(env, i64_to_ast(env.arena, num), content)
|
||||
),
|
||||
Layout::Builtin(Builtin::Float64) => run_jit_function!(
|
||||
execution_engine,
|
||||
main_fn_name,
|
||||
f64,
|
||||
|num| num_to_ast(env, f64_to_ast(env.arena, num), content)
|
||||
),
|
||||
Layout::Builtin(Builtin::Str) | Layout::Builtin(Builtin::EmptyStr) => run_jit_function!(
|
||||
execution_engine,
|
||||
main_fn_name,
|
||||
&'static str,
|
||||
|string: &'static str| { str_to_ast(env.arena, env.arena.alloc(string)) }
|
||||
),
|
||||
Layout::Builtin(Builtin::EmptyList) => {
|
||||
run_jit_function!(execution_engine, main_fn_name, &'static str, |_| {
|
||||
Expr::List(Vec::new_in(env.arena))
|
||||
Layout::Builtin(Builtin::Int64) => {
|
||||
run_jit_function!(lib, main_fn_name, i64, |num| num_to_ast(
|
||||
env,
|
||||
i64_to_ast(env.arena, num),
|
||||
content
|
||||
))
|
||||
}
|
||||
Layout::Builtin(Builtin::Float64) => {
|
||||
run_jit_function!(lib, main_fn_name, f64, |num| num_to_ast(
|
||||
env,
|
||||
f64_to_ast(env.arena, num),
|
||||
content
|
||||
))
|
||||
}
|
||||
Layout::Builtin(Builtin::Str) | Layout::Builtin(Builtin::EmptyStr) => {
|
||||
run_jit_function!(lib, main_fn_name, &'static str, |string: &'static str| {
|
||||
str_to_ast(env.arena, env.arena.alloc(string))
|
||||
})
|
||||
}
|
||||
Layout::Builtin(Builtin::EmptyList) => {
|
||||
run_jit_function!(lib, main_fn_name, &'static str, |_| { Expr::List(&[]) })
|
||||
}
|
||||
Layout::Builtin(Builtin::List(_, elem_layout)) => run_jit_function!(
|
||||
execution_engine,
|
||||
lib,
|
||||
main_fn_name,
|
||||
(*const libc::c_void, usize),
|
||||
|(ptr, len): (*const libc::c_void, usize)| {
|
||||
|
@ -111,31 +110,21 @@ fn jit_to_ast_help<'a>(
|
|||
8 => match layout.stack_size(env.ptr_bytes) {
|
||||
8 => {
|
||||
// just one eightbyte, returned as-is
|
||||
run_jit_function!(
|
||||
execution_engine,
|
||||
main_fn_name,
|
||||
[u8; 8],
|
||||
|bytes: [u8; 8]| {
|
||||
run_jit_function!(lib, main_fn_name, [u8; 8], |bytes: [u8; 8]| {
|
||||
ptr_to_ast((&bytes).as_ptr() as *const libc::c_void)
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
16 => {
|
||||
// two eightbytes, returned as-is
|
||||
run_jit_function!(
|
||||
execution_engine,
|
||||
main_fn_name,
|
||||
[u8; 16],
|
||||
|bytes: [u8; 16]| {
|
||||
run_jit_function!(lib, main_fn_name, [u8; 16], |bytes: [u8; 16]| {
|
||||
ptr_to_ast((&bytes).as_ptr() as *const libc::c_void)
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
larger_size => {
|
||||
// anything more than 2 eightbytes
|
||||
// the return "value" is a pointer to the result
|
||||
run_jit_function_dynamic_type!(
|
||||
execution_engine,
|
||||
lib,
|
||||
main_fn_name,
|
||||
larger_size as usize,
|
||||
|bytes: *const u8| { ptr_to_ast(bytes as *const libc::c_void) }
|
||||
|
@ -150,31 +139,21 @@ fn jit_to_ast_help<'a>(
|
|||
match layout.stack_size(env.ptr_bytes) {
|
||||
4 => {
|
||||
// just one fourbyte, returned as-is
|
||||
run_jit_function!(
|
||||
execution_engine,
|
||||
main_fn_name,
|
||||
[u8; 4],
|
||||
|bytes: [u8; 4]| {
|
||||
run_jit_function!(lib, main_fn_name, [u8; 4], |bytes: [u8; 4]| {
|
||||
ptr_to_ast((&bytes).as_ptr() as *const libc::c_void)
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
8 => {
|
||||
// just one fourbyte, returned as-is
|
||||
run_jit_function!(
|
||||
execution_engine,
|
||||
main_fn_name,
|
||||
[u8; 8],
|
||||
|bytes: [u8; 8]| {
|
||||
run_jit_function!(lib, main_fn_name, [u8; 8], |bytes: [u8; 8]| {
|
||||
ptr_to_ast((&bytes).as_ptr() as *const libc::c_void)
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
larger_size => {
|
||||
// anything more than 2 fourbytes
|
||||
// the return "value" is a pointer to the result
|
||||
run_jit_function_dynamic_type!(
|
||||
execution_engine,
|
||||
lib,
|
||||
main_fn_name,
|
||||
larger_size as usize,
|
||||
|bytes: *const u8| { ptr_to_ast(bytes as *const libc::c_void) }
|
||||
|
@ -210,7 +189,7 @@ fn ptr_to_ast<'a>(
|
|||
|
||||
num_to_ast(env, f64_to_ast(env.arena, num), content)
|
||||
}
|
||||
Layout::Builtin(Builtin::EmptyList) => Expr::List(Vec::new_in(env.arena)),
|
||||
Layout::Builtin(Builtin::EmptyList) => Expr::List(&[]),
|
||||
Layout::Builtin(Builtin::List(_, elem_layout)) => {
|
||||
// Turn the (ptr, len) wrapper struct into actual ptr and len values.
|
||||
let len = unsafe { *(ptr.offset(env.ptr_bytes as isize) as *const usize) };
|
||||
|
@ -282,6 +261,8 @@ fn list_to_ast<'a>(
|
|||
output.push(loc_expr);
|
||||
}
|
||||
|
||||
let output = output.into_bump_slice();
|
||||
|
||||
Expr::List(output)
|
||||
}
|
||||
|
||||
|
@ -333,6 +314,8 @@ fn struct_to_ast<'a>(
|
|||
field_ptr = unsafe { field_ptr.offset(field_layout.stack_size(env.ptr_bytes) as isize) };
|
||||
}
|
||||
|
||||
let output = output.into_bump_slice();
|
||||
|
||||
Expr::Record {
|
||||
update: None,
|
||||
fields: output,
|
||||
|
@ -381,7 +364,7 @@ fn num_to_ast<'a>(env: &Env<'a, '_>, num_expr: Expr<'a>, content: &Content) -> E
|
|||
|
||||
Expr::Record {
|
||||
update: None,
|
||||
fields: bumpalo::vec![in arena; loc_assigned_field],
|
||||
fields: arena.alloc([loc_assigned_field]),
|
||||
}
|
||||
}
|
||||
FlatType::TagUnion(tags, _) => {
|
||||
|
@ -423,7 +406,7 @@ fn num_to_ast<'a>(env: &Env<'a, '_>, num_expr: Expr<'a>, content: &Content) -> E
|
|||
region: Region::zero(),
|
||||
});
|
||||
|
||||
bumpalo::vec![in arena; loc_payload]
|
||||
arena.alloc([loc_payload])
|
||||
};
|
||||
|
||||
Expr::Apply(loc_tag_expr, payload, CalledVia::Space)
|
||||
|
|
|
@ -11,120 +11,175 @@ mod helpers;
|
|||
|
||||
#[cfg(test)]
|
||||
mod cli_run {
|
||||
use crate::helpers::{example_file, extract_valgrind_errors, run_roc, run_with_valgrind, Out};
|
||||
use crate::helpers::{
|
||||
example_file, extract_valgrind_errors, run_cmd, run_roc, run_with_valgrind,
|
||||
};
|
||||
use serial_test::serial;
|
||||
|
||||
fn check_output(
|
||||
folder: &str,
|
||||
file: &str,
|
||||
flags: &[&str],
|
||||
expected_ending: &str,
|
||||
use_valgrind: bool,
|
||||
) {
|
||||
let compile_out = run_roc(
|
||||
&[
|
||||
&["build", example_file(folder, file).to_str().unwrap()],
|
||||
flags,
|
||||
]
|
||||
.concat(),
|
||||
);
|
||||
if !compile_out.stderr.is_empty() {
|
||||
panic!(compile_out.stderr);
|
||||
}
|
||||
assert!(compile_out.status.success());
|
||||
|
||||
let out = if use_valgrind {
|
||||
let (valgrind_out, raw_xml) =
|
||||
run_with_valgrind(&[example_file(folder, "app").to_str().unwrap()]);
|
||||
let memory_errors = extract_valgrind_errors(&raw_xml);
|
||||
if !memory_errors.is_empty() {
|
||||
panic!("{:?}", memory_errors);
|
||||
}
|
||||
valgrind_out
|
||||
} else {
|
||||
run_cmd(example_file(folder, "app").to_str().unwrap(), &[])
|
||||
};
|
||||
if !&out.stdout.ends_with(expected_ending) {
|
||||
panic!(
|
||||
"expected output to end with {:?} but instead got {:#?}",
|
||||
expected_ending, out
|
||||
);
|
||||
}
|
||||
assert!(out.status.success());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial(hello_world)]
|
||||
fn run_hello_world() {
|
||||
fn check_hello_world_output(out: Out) {
|
||||
if !out.stderr.is_empty() {
|
||||
panic!(out.stderr);
|
||||
}
|
||||
assert!(out.status.success());
|
||||
|
||||
let (valgrind_out, raw_xml) =
|
||||
run_with_valgrind(&[example_file("hello-world", "app").to_str().unwrap()]);
|
||||
|
||||
let ending = "Hello, World!!!!!!!!!!!!!\n";
|
||||
if !&valgrind_out.stdout.ends_with(ending) {
|
||||
panic!(
|
||||
"expected output to end with {:?} but instead got {:?}",
|
||||
ending, &valgrind_out.stdout
|
||||
check_output(
|
||||
"hello-world",
|
||||
"Hello.roc",
|
||||
&[],
|
||||
"Hello, World!!!!!!!!!!!!!\n",
|
||||
true,
|
||||
);
|
||||
}
|
||||
let memory_errors = extract_valgrind_errors(&raw_xml);
|
||||
if !memory_errors.is_empty() {
|
||||
panic!("{:?}", memory_errors);
|
||||
}
|
||||
assert!(valgrind_out.status.success());
|
||||
}
|
||||
check_hello_world_output(run_roc(&[
|
||||
"build",
|
||||
example_file("hello-world", "Hello.roc").to_str().unwrap(),
|
||||
]));
|
||||
check_hello_world_output(run_roc(&[
|
||||
"build",
|
||||
"--optimize",
|
||||
example_file("hello-world", "Hello.roc").to_str().unwrap(),
|
||||
]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial(hello_world)]
|
||||
fn run_hello_world_optimized() {
|
||||
check_output(
|
||||
"hello-world",
|
||||
"Hello.roc",
|
||||
&[],
|
||||
"Hello, World!!!!!!!!!!!!!\n",
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial(quicksort)]
|
||||
fn run_quicksort() {
|
||||
fn check_quicksort_output(out: Out) {
|
||||
if !out.stderr.is_empty() {
|
||||
panic!(out.stderr);
|
||||
}
|
||||
assert!(out.status.success());
|
||||
|
||||
let (valgrind_out, raw_xml) =
|
||||
run_with_valgrind(&[example_file("quicksort", "app").to_str().unwrap()]);
|
||||
let ending = "[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n";
|
||||
if !&valgrind_out.stdout.ends_with(ending) {
|
||||
panic!(
|
||||
"expected output to end with {:?} but instead got {:?}",
|
||||
ending, &valgrind_out.stdout
|
||||
check_output(
|
||||
"quicksort",
|
||||
"Quicksort.roc",
|
||||
&[],
|
||||
"[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
|
||||
false,
|
||||
);
|
||||
}
|
||||
let memory_errors = extract_valgrind_errors(&raw_xml);
|
||||
if !memory_errors.is_empty() {
|
||||
panic!("{:?}", memory_errors);
|
||||
}
|
||||
assert!(valgrind_out.status.success());
|
||||
}
|
||||
|
||||
// TODO: Uncomment this once we are correctly freeing the RocList even when in dev build.
|
||||
/*
|
||||
check_quicksort_output(run_roc(&[
|
||||
"build",
|
||||
example_file("quicksort", "Quicksort.roc").to_str().unwrap(),
|
||||
]));
|
||||
*/
|
||||
check_quicksort_output(run_roc(&[
|
||||
"build",
|
||||
"--optimize",
|
||||
example_file("quicksort", "Quicksort.roc").to_str().unwrap(),
|
||||
]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_multi_module() {
|
||||
fn check_muti_module_output(out: Out) {
|
||||
if !out.stderr.is_empty() {
|
||||
panic!(out.stderr);
|
||||
}
|
||||
assert!(out.status.success());
|
||||
|
||||
let (valgrind_out, raw_xml) =
|
||||
run_with_valgrind(&[example_file("multi-module", "app").to_str().unwrap()]);
|
||||
let ending = "[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n";
|
||||
if !&valgrind_out.stdout.ends_with(ending) {
|
||||
panic!(
|
||||
"expected output to end with {:?} but instead got {:?}",
|
||||
ending, &valgrind_out.stdout
|
||||
#[serial(quicksort)]
|
||||
fn run_quicksort_optimized() {
|
||||
check_output(
|
||||
"quicksort",
|
||||
"Quicksort.roc",
|
||||
&["--optimize"],
|
||||
"[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
|
||||
false,
|
||||
);
|
||||
}
|
||||
let memory_errors = extract_valgrind_errors(&raw_xml);
|
||||
if !memory_errors.is_empty() {
|
||||
panic!("{:?}", memory_errors);
|
||||
}
|
||||
assert!(valgrind_out.status.success());
|
||||
|
||||
#[test]
|
||||
#[serial(quicksort)]
|
||||
// TODO: Stop ignoring this test once we are correctly freeing the RocList even when in dev build.
|
||||
#[ignore]
|
||||
fn run_quicksort_valgrind() {
|
||||
check_output(
|
||||
"quicksort",
|
||||
"Quicksort.roc",
|
||||
&[],
|
||||
"[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: Uncomment this once we are correctly freeing the RocList even when in dev build.
|
||||
/*
|
||||
check_muti_module_output(run_roc(&[
|
||||
"run",
|
||||
example_file("multi-module", "Quicksort.roc")
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
]));
|
||||
*/
|
||||
check_muti_module_output(run_roc(&[
|
||||
"run",
|
||||
example_file("multi-module", "Quicksort.roc")
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
"--optimize",
|
||||
]));
|
||||
#[test]
|
||||
#[serial(quicksort)]
|
||||
// TODO: Stop ignoring this test once valgrind supports AVX512.
|
||||
#[ignore]
|
||||
fn run_quicksort_optimized_valgrind() {
|
||||
check_output(
|
||||
"quicksort",
|
||||
"Quicksort.roc",
|
||||
&["--optimize"],
|
||||
"[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial(multi_module)]
|
||||
fn run_multi_module() {
|
||||
check_output(
|
||||
"multi-module",
|
||||
"Quicksort.roc",
|
||||
&[],
|
||||
"[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial(multi_module)]
|
||||
fn run_multi_module_optimized() {
|
||||
check_output(
|
||||
"multi-module",
|
||||
"Quicksort.roc",
|
||||
&["--optimize"],
|
||||
"[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial(multi_module)]
|
||||
// TODO: Stop ignoring this test once we are correctly freeing the RocList even when in dev build.
|
||||
#[ignore]
|
||||
fn run_multi_module_valgrind() {
|
||||
check_output(
|
||||
"multi-module",
|
||||
"Quicksort.roc",
|
||||
&[],
|
||||
"[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial(multi_module)]
|
||||
// TODO: Stop ignoring this test once valgrind supports AVX512.
|
||||
#[ignore]
|
||||
fn run_multi_module_optimized_valgrind() {
|
||||
check_output(
|
||||
"multi-module",
|
||||
"Quicksort.roc",
|
||||
&["--optimize"],
|
||||
"[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
|
||||
true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ use std::path::PathBuf;
|
|||
use std::process::{Command, ExitStatus, Stdio};
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Out {
|
||||
pub stdout: String,
|
||||
pub stderr: String,
|
||||
|
@ -131,6 +132,9 @@ enum ValgrindField {
|
|||
Args(ValgrindDummyStruct),
|
||||
Error(ValgrindError),
|
||||
Status(ValgrindDummyStruct),
|
||||
Stack(ValgrindDummyStruct),
|
||||
#[serde(rename = "fatal_signal")]
|
||||
FatalSignal(ValgrindDummyStruct),
|
||||
ErrorCounts(ValgrindDummyStruct),
|
||||
SuppCounts(ValgrindDummyStruct),
|
||||
}
|
||||
|
|
|
@ -26,7 +26,8 @@ im = "14" # im and im-rc should always have the same version!
|
|||
im-rc = "14" # im and im-rc should always have the same version!
|
||||
bumpalo = { version = "3.2", features = ["collections"] }
|
||||
inlinable_string = "0.1.0"
|
||||
tokio = { version = "0.2", features = ["blocking", "fs", "sync", "rt-threaded", "process", "io-driver"] }
|
||||
libloading = "0.6"
|
||||
tempfile = "3.1.0"
|
||||
# NOTE: rtfeldman/inkwell is a fork of TheDan64/inkwell which does not change anything.
|
||||
#
|
||||
# The reason for this fork is that the way Inkwell is designed, you have to use
|
||||
|
|
|
@ -1,35 +1,44 @@
|
|||
use crate::target;
|
||||
use crate::target::arch_str;
|
||||
use inkwell::module::Module;
|
||||
use inkwell::targets::{CodeModel, FileType, RelocMode};
|
||||
use libloading::{Error, Library};
|
||||
use roc_gen::llvm::build::OptLevel;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Child, Command};
|
||||
use target_lexicon::{Architecture, OperatingSystem, Triple};
|
||||
use tempfile::tempdir;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum LinkType {
|
||||
Executable,
|
||||
Dylib,
|
||||
}
|
||||
|
||||
/// input_paths can include the host as well as the app. e.g. &["host.o", "roc_app.o"]
|
||||
pub fn link(
|
||||
target: &Triple,
|
||||
binary_path: &Path,
|
||||
host_input_path: &Path,
|
||||
dest_filename: &Path,
|
||||
) -> io::Result<Child> {
|
||||
// TODO we should no longer need to do this once we have platforms on
|
||||
// a package repository, as we can then get precompiled hosts from there.
|
||||
rebuild_host(host_input_path);
|
||||
|
||||
output_path: PathBuf,
|
||||
input_paths: &[&str],
|
||||
link_type: LinkType,
|
||||
) -> io::Result<(Child, PathBuf)> {
|
||||
match target {
|
||||
Triple {
|
||||
architecture: Architecture::X86_64,
|
||||
operating_system: OperatingSystem::Linux,
|
||||
..
|
||||
} => link_linux(target, binary_path, host_input_path, dest_filename),
|
||||
} => link_linux(target, output_path, input_paths, link_type),
|
||||
Triple {
|
||||
architecture: Architecture::X86_64,
|
||||
operating_system: OperatingSystem::Darwin,
|
||||
..
|
||||
} => link_macos(target, binary_path, host_input_path, dest_filename),
|
||||
} => link_macos(target, output_path, input_paths, link_type),
|
||||
_ => panic!("TODO gracefully handle unsupported target: {:?}", target),
|
||||
}
|
||||
}
|
||||
|
||||
fn rebuild_host(host_input_path: &Path) {
|
||||
pub fn rebuild_host(host_input_path: &Path) {
|
||||
let c_host_src = host_input_path.with_file_name("host.c");
|
||||
let c_host_dest = host_input_path.with_file_name("c_host.o");
|
||||
let rust_host_src = host_input_path.with_file_name("host.rs");
|
||||
|
@ -118,15 +127,16 @@ fn rebuild_host(host_input_path: &Path) {
|
|||
|
||||
fn link_linux(
|
||||
target: &Triple,
|
||||
binary_path: &Path,
|
||||
host_input_path: &Path,
|
||||
dest_filename: &Path,
|
||||
) -> io::Result<Child> {
|
||||
output_path: PathBuf,
|
||||
input_paths: &[&str],
|
||||
link_type: LinkType,
|
||||
) -> io::Result<(Child, PathBuf)> {
|
||||
let libcrt_path = if Path::new("/usr/lib/x86_64-linux-gnu").exists() {
|
||||
Path::new("/usr/lib/x86_64-linux-gnu")
|
||||
} else {
|
||||
Path::new("/usr/lib")
|
||||
};
|
||||
|
||||
let libgcc_path = if Path::new("/lib/x86_64-linux-gnu/libgcc_s.so.1").exists() {
|
||||
Path::new("/lib/x86_64-linux-gnu/libgcc_s.so.1")
|
||||
} else if Path::new("/usr/lib/x86_64-linux-gnu/libgcc_s.so.1").exists() {
|
||||
|
@ -134,8 +144,45 @@ fn link_linux(
|
|||
} else {
|
||||
Path::new("/usr/lib/libgcc_s.so.1")
|
||||
};
|
||||
|
||||
let mut soname;
|
||||
let (base_args, output_path) = match link_type {
|
||||
LinkType::Executable => (
|
||||
// Presumably this S stands for Static, since if we include Scrt1.o
|
||||
// in the linking for dynamic builds, linking fails.
|
||||
vec![libcrt_path.join("Scrt1.o").to_str().unwrap().to_string()],
|
||||
output_path,
|
||||
),
|
||||
LinkType::Dylib => {
|
||||
// TODO: do we acually need the version number on this?
|
||||
// Do we even need the "-soname" argument?
|
||||
//
|
||||
// See https://software.intel.com/content/www/us/en/develop/articles/create-a-unix-including-linux-shared-library.html
|
||||
|
||||
soname = output_path.clone();
|
||||
soname.set_extension("so.1");
|
||||
|
||||
let mut output_path = output_path;
|
||||
|
||||
output_path.set_extension("so.1.0");
|
||||
|
||||
(
|
||||
// TODO: find a way to avoid using a vec! here - should theoretically be
|
||||
// able to do this somehow using &[] but the borrow checker isn't having it.
|
||||
// Also find a way to have these be string slices instead of Strings.
|
||||
vec![
|
||||
"-shared".to_string(),
|
||||
"-soname".to_string(),
|
||||
soname.as_path().to_str().unwrap().to_string(),
|
||||
],
|
||||
output_path,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
// NOTE: order of arguments to `ld` matters here!
|
||||
// The `-l` flags should go after the `.o` arguments
|
||||
Ok((
|
||||
Command::new("ld")
|
||||
// Don't allow LD_ env vars to affect this
|
||||
.env_clear()
|
||||
|
@ -144,12 +191,11 @@ fn link_linux(
|
|||
arch_str(target),
|
||||
libcrt_path.join("crti.o").to_str().unwrap(),
|
||||
libcrt_path.join("crtn.o").to_str().unwrap(),
|
||||
libcrt_path.join("Scrt1.o").to_str().unwrap(),
|
||||
"-dynamic-linker",
|
||||
"/lib64/ld-linux-x86-64.so.2",
|
||||
// Inputs
|
||||
host_input_path.to_str().unwrap(), // host.o
|
||||
dest_filename.to_str().unwrap(), // app.o
|
||||
])
|
||||
.args(&base_args)
|
||||
.args(&["-dynamic-linker", "/lib64/ld-linux-x86-64.so.2"])
|
||||
.args(input_paths)
|
||||
.args(&[
|
||||
// Libraries - see https://github.com/rtfeldman/roc/pull/554#discussion_r496365925
|
||||
// for discussion and further references
|
||||
"-lc",
|
||||
|
@ -164,28 +210,43 @@ fn link_linux(
|
|||
libgcc_path.to_str().unwrap(),
|
||||
// Output
|
||||
"-o",
|
||||
binary_path.to_str().unwrap(), // app
|
||||
output_path.as_path().to_str().unwrap(), // app (or app.so or app.dylib etc.)
|
||||
])
|
||||
.spawn()
|
||||
.spawn()?,
|
||||
output_path,
|
||||
))
|
||||
}
|
||||
|
||||
fn link_macos(
|
||||
target: &Triple,
|
||||
binary_path: &Path,
|
||||
host_input_path: &Path,
|
||||
dest_filename: &Path,
|
||||
) -> io::Result<Child> {
|
||||
output_path: PathBuf,
|
||||
input_paths: &[&str],
|
||||
link_type: LinkType,
|
||||
) -> io::Result<(Child, PathBuf)> {
|
||||
let (link_type_arg, output_path) = match link_type {
|
||||
LinkType::Executable => ("-execute", output_path),
|
||||
LinkType::Dylib => {
|
||||
let mut output_path = output_path;
|
||||
|
||||
output_path.set_extension("dylib");
|
||||
|
||||
("-dylib", output_path)
|
||||
}
|
||||
};
|
||||
|
||||
Ok((
|
||||
// NOTE: order of arguments to `ld` matters here!
|
||||
// The `-l` flags should go after the `.o` arguments
|
||||
Command::new("ld")
|
||||
// Don't allow LD_ env vars to affect this
|
||||
.env_clear()
|
||||
.args(&[
|
||||
link_type_arg,
|
||||
"-arch",
|
||||
target.architecture.to_string().as_str(),
|
||||
// Inputs
|
||||
host_input_path.to_str().unwrap(), // host.o
|
||||
dest_filename.to_str().unwrap(), // roc_app.o
|
||||
])
|
||||
.args(input_paths)
|
||||
.args(&[
|
||||
// Libraries - see https://github.com/rtfeldman/roc/pull/554#discussion_r496392274
|
||||
// for discussion and further references
|
||||
"-lSystem",
|
||||
|
@ -198,7 +259,47 @@ fn link_macos(
|
|||
"-lc++", // TODO shouldn't we need this?
|
||||
// Output
|
||||
"-o",
|
||||
binary_path.to_str().unwrap(), // app
|
||||
output_path.to_str().unwrap(), // app
|
||||
])
|
||||
.spawn()
|
||||
.spawn()?,
|
||||
output_path,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn module_to_dylib(
|
||||
module: &Module,
|
||||
target: &Triple,
|
||||
opt_level: OptLevel,
|
||||
) -> Result<Library, Error> {
|
||||
let dir = tempdir().unwrap();
|
||||
let filename = PathBuf::from("Test.roc");
|
||||
let file_path = dir.path().join(filename);
|
||||
let mut app_o_file = file_path;
|
||||
|
||||
app_o_file.set_file_name("app.o");
|
||||
|
||||
// Emit the .o file using position-indepedent code (PIC) - needed for dylibs
|
||||
let reloc = RelocMode::PIC;
|
||||
let model = CodeModel::Default;
|
||||
let target_machine = target::target_machine(target, opt_level.into(), reloc, model).unwrap();
|
||||
|
||||
target_machine
|
||||
.write_to_file(module, FileType::Object, &app_o_file)
|
||||
.expect("Writing .o file failed");
|
||||
|
||||
// Link app.o into a dylib - e.g. app.so or app.dylib
|
||||
let (mut child, dylib_path) = link(
|
||||
&Triple::host(),
|
||||
app_o_file.clone(),
|
||||
&[app_o_file.to_str().unwrap()],
|
||||
LinkType::Dylib,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
child.wait().unwrap();
|
||||
|
||||
// Load the dylib
|
||||
let path = dylib_path.as_path().to_str().unwrap();
|
||||
|
||||
Library::new(path)
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ use crate::target;
|
|||
use bumpalo::Bump;
|
||||
use inkwell::context::Context;
|
||||
use inkwell::targets::{CodeModel, FileType, RelocMode};
|
||||
use inkwell::OptimizationLevel;
|
||||
use roc_gen::layout_id::LayoutIds;
|
||||
use roc_gen::llvm::build::{build_proc, build_proc_header, module_from_builtins, OptLevel, Scope};
|
||||
use roc_load::file::MonomorphizedModule;
|
||||
|
@ -16,9 +15,9 @@ use target_lexicon::Triple;
|
|||
pub fn gen_from_mono_module(
|
||||
arena: &Bump,
|
||||
loaded: MonomorphizedModule,
|
||||
filename: PathBuf,
|
||||
file_path: PathBuf,
|
||||
target: Triple,
|
||||
dest_filename: &Path,
|
||||
app_o_file: &Path,
|
||||
opt_level: OptLevel,
|
||||
) {
|
||||
use roc_reporting::report::{can_problem, type_problem, RocDocAllocator, DEFAULT_PALETTE};
|
||||
|
@ -32,7 +31,7 @@ pub fn gen_from_mono_module(
|
|||
let alloc = RocDocAllocator::new(&src_lines, home, &loaded.interns);
|
||||
|
||||
for problem in loaded.can_problems.into_iter() {
|
||||
let report = can_problem(&alloc, filename.clone(), problem);
|
||||
let report = can_problem(&alloc, file_path.clone(), problem);
|
||||
let mut buf = String::new();
|
||||
|
||||
report.render_color_terminal(&mut buf, &alloc, &palette);
|
||||
|
@ -41,7 +40,7 @@ pub fn gen_from_mono_module(
|
|||
}
|
||||
|
||||
for problem in loaded.type_problems.into_iter() {
|
||||
let report = type_problem(&alloc, filename.clone(), problem);
|
||||
let report = type_problem(&alloc, file_path.clone(), problem);
|
||||
let mut buf = String::new();
|
||||
|
||||
report.render_color_terminal(&mut buf, &alloc, &palette);
|
||||
|
@ -126,14 +125,11 @@ pub fn gen_from_mono_module(
|
|||
|
||||
// Emit the .o file
|
||||
|
||||
let opt = OptimizationLevel::Aggressive;
|
||||
let reloc = RelocMode::Default;
|
||||
let model = CodeModel::Default;
|
||||
let target_machine = target::target_machine(&target, opt, reloc, model).unwrap();
|
||||
let target_machine = target::target_machine(&target, opt_level.into(), reloc, model).unwrap();
|
||||
|
||||
target_machine
|
||||
.write_to_file(&env.module, FileType::Object, &dest_filename)
|
||||
.write_to_file(&env.module, FileType::Object, &app_o_file)
|
||||
.expect("Writing .o file failed");
|
||||
|
||||
println!("\nSuccess! 🎉\n\n\t➡ {}\n", dest_filename.display());
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ pub fn target_machine(
|
|||
Target::from_name(arch).unwrap().create_target_machine(
|
||||
&TargetTriple::create(target_triple_str(target)),
|
||||
arch,
|
||||
"+avx2", // TODO this string was used uncritically from an example, and should be reexamined
|
||||
"", // TODO: this probably should be TargetMachine::get_host_cpu_features() to enable all features.
|
||||
opt,
|
||||
reloc,
|
||||
model,
|
||||
|
|
2
compiler/builtins/bitcode/.gitignore
vendored
Normal file
2
compiler/builtins/bitcode/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
zig-cache
|
||||
src/zig-cache
|
|
@ -1,13 +0,0 @@
|
|||
[package]
|
||||
name = "roc_builtins_bitcode"
|
||||
version = "0.1.0"
|
||||
authors = ["Richard Feldman <oss@rtfeldman.com>"]
|
||||
repository = "https://github.com/rtfeldman/roc"
|
||||
readme = "README.md"
|
||||
edition = "2018"
|
||||
description = "Generate LLVM bitcode for Roc builtins"
|
||||
license = "Apache-2.0"
|
||||
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "0.5.1"
|
|
@ -5,52 +5,19 @@ When their implementations are simple enough (e.g. addition), they
|
|||
can be implemented directly in Inkwell.
|
||||
|
||||
When their implementations are complex enough, it's nicer to
|
||||
implement them in a higher-level language like Rust, compile the
|
||||
result to LLVM bitcode, and import that bitcode into the compiler.
|
||||
implement them in a higher-level language like Zig, then compile
|
||||
the result to LLVM bitcode, and import that bitcode into the compiler.
|
||||
|
||||
Here is the process for doing that.
|
||||
Compiling the bitcode happens automatically in a Rust build script at `compiler/builtins/build.rs`.
|
||||
Then `builtins/src/bitcode/rs` staticlly imports the compiled bitcode for use in the compiler.
|
||||
|
||||
## Building the bitcode
|
||||
You can find the compiled bitcode in `target/debug/build/roc_builtins-[some random characters]/out/builtins.bc`.
|
||||
There will be two directories like `roc_builtins-[some random characters]`, look for the one that has an
|
||||
`out` directory as a child.
|
||||
|
||||
The source we'll use to generate the bitcode is in `src/lib.rs` in this directory.
|
||||
|
||||
To generate the bitcode, `cd` into `compiler/builtins/bitcode/` and run:
|
||||
|
||||
```bash
|
||||
$ ./regenerate.sh
|
||||
```
|
||||
|
||||
> If you want to take a look at the human-readable LLVM IR rather than the
|
||||
> bitcode, run this instead and look for a `.ll` file instead of a `.bc` file:
|
||||
>
|
||||
> ```bash
|
||||
> $ cargo rustc --release --lib -- --emit=llvm-ir
|
||||
> ```
|
||||
>
|
||||
> Then look in the root `roc` source directory under `target/release/deps/` for a file
|
||||
> with a name like `roc_builtins_bitcode-8da0901c58a73ebf.bc` - except
|
||||
> probably with a different hash before the `.bc`. If there's more than one
|
||||
> `*.bc` file in that directory, delete the whole `deps/` directory and re-run
|
||||
> the `cargo rustc` command above to regenerate it.
|
||||
|
||||
**Note**: In order to be able to address the bitcode functions by name, they need to be defined with the `#[no_mangle]` attribute.
|
||||
|
||||
The bitcode is a bunch of bytes that aren't particularly human-readable.
|
||||
Since Roc is designed to be distributed as a single binary, these bytes
|
||||
need to be included in the raw source somewhere.
|
||||
|
||||
The `llvm/src/build.rs` file statically imports these raw bytes
|
||||
using the [`include_bytes!` macro](https://doc.rust-lang.org/std/macro.include_bytes.html).
|
||||
The current `.bc` file is located at:
|
||||
|
||||
```
|
||||
compiler/gen/src/llvm/builtins.bc
|
||||
```
|
||||
|
||||
The script will automatically replace this `.bc` file with the new one.
|
||||
|
||||
Once that's done, `git status` should show that the `builtins.bc` file
|
||||
has been changed. Commit that change and you're done!
|
||||
> The bitcode is a bunch of bytes that aren't particularly human-readable.
|
||||
> If you want to take a look at the human-readable LLVM IR, look at
|
||||
> `target/debug/build/roc_builtins-[some random characters]/out/builtins.ll`
|
||||
|
||||
## Calling bitcode functions
|
||||
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -euxo pipefail
|
||||
|
||||
# Clear out any existing output files. Sometimes if these are there, rustc
|
||||
# doesn't generate the .bc file - or we can end up with more than one .bc
|
||||
rm -rf ../../../target/release/deps/
|
||||
|
||||
# Regenerate the .bc file
|
||||
cargo rustc --release --lib -- --emit=llvm-bc
|
||||
|
||||
bc_files=$(ls ../../../target/release/deps/*.bc | wc -l)
|
||||
|
||||
if [[ $bc_files != 1 ]]; then
|
||||
echo "More than one .bc file was emitted somehow."
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
cp ../../../target/release/deps/*.bc ../../gen/src/llvm/builtins.bc
|
|
@ -1,134 +0,0 @@
|
|||
// NOTE: Editing this file on its own does nothing! The procedure for
|
||||
// incorporating changes here is in this crate' README.
|
||||
|
||||
#![crate_type = "lib"]
|
||||
#![no_std]
|
||||
|
||||
mod libm;
|
||||
|
||||
/// TODO this is no longer used. Feel free to delete it the next time
|
||||
/// we need to rebuild builtins.bc!
|
||||
#[no_mangle]
|
||||
pub fn i64_to_f64_(num: i64) -> f64 {
|
||||
num as f64
|
||||
}
|
||||
|
||||
/// Adapted from Rust's core::num module, by the Rust core team,
|
||||
/// licensed under the Apache License, version 2.0 - https://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Thank you, Rust core team!
|
||||
#[no_mangle]
|
||||
pub fn pow_int_(mut base: i64, mut exp: i64) -> i64 {
|
||||
let mut acc = 1;
|
||||
|
||||
while exp > 1 {
|
||||
if (exp & 1) == 1 {
|
||||
acc *= base;
|
||||
}
|
||||
exp /= 2;
|
||||
base *= base;
|
||||
}
|
||||
|
||||
// Deal with the final bit of the exponent separately, since
|
||||
// squaring the base afterwards is not necessary and may cause a
|
||||
// needless overflow.
|
||||
if exp == 1 {
|
||||
acc *= base;
|
||||
}
|
||||
|
||||
acc
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn str_split_<'a>(
|
||||
list: &'a mut [&'a [u8]],
|
||||
str_: &'a [u8],
|
||||
delimiter: &[u8],
|
||||
) -> &'a [&'a [u8]] {
|
||||
let mut ret_list_index = 0;
|
||||
|
||||
let str_len = str_.len();
|
||||
let delimiter_len = delimiter.len();
|
||||
|
||||
let mut slice_start_index = 0;
|
||||
|
||||
let mut str_index = 0;
|
||||
|
||||
if str_len > delimiter_len {
|
||||
while str_index <= (str_len - delimiter_len) {
|
||||
let mut delimiter_index = 0;
|
||||
let mut matches_delimiter = true;
|
||||
|
||||
while matches_delimiter && delimiter_index < delimiter_len {
|
||||
let delimiter_char = delimiter[delimiter_index];
|
||||
let str_char = str_[str_index + delimiter_index];
|
||||
|
||||
if delimiter_char != str_char {
|
||||
matches_delimiter = false;
|
||||
}
|
||||
|
||||
delimiter_index += 1;
|
||||
}
|
||||
|
||||
if matches_delimiter {
|
||||
list[ret_list_index] = &str_[slice_start_index..str_index];
|
||||
slice_start_index = str_index + delimiter_len;
|
||||
ret_list_index += 1;
|
||||
str_index += delimiter_len;
|
||||
} else {
|
||||
str_index += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
list[ret_list_index] = &str_[slice_start_index..str_len];
|
||||
|
||||
list
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn count_segments_(str: &[u8], delimiter: &[u8]) -> i64 {
|
||||
let mut count: i64 = 1;
|
||||
|
||||
let str_len = str.len();
|
||||
let delimiter_len = delimiter.len();
|
||||
|
||||
if str_len > delimiter_len {
|
||||
for str_index in 0..(str_len - delimiter_len) {
|
||||
let mut delimiter_index = 0;
|
||||
|
||||
let mut matches_delimiter = true;
|
||||
|
||||
while matches_delimiter && delimiter_index < delimiter_len {
|
||||
let delimiter_char = delimiter[delimiter_index];
|
||||
let str_char = str[str_index + delimiter_index];
|
||||
|
||||
if delimiter_char != str_char {
|
||||
matches_delimiter = false;
|
||||
}
|
||||
|
||||
delimiter_index += 1;
|
||||
}
|
||||
|
||||
if matches_delimiter {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
count
|
||||
}
|
||||
|
||||
/// Adapted from Rust's core::num module, by the Rust core team,
|
||||
/// licensed under the Apache License, version 2.0 - https://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Thank you, Rust core team!
|
||||
#[no_mangle]
|
||||
pub fn is_finite_(num: f64) -> bool {
|
||||
f64::is_finite(num)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn atan_(x: f64) -> f64 {
|
||||
libm::atan(x)
|
||||
}
|
|
@ -1,199 +0,0 @@
|
|||
/// Adapted from Rust's libm module, by the Rust core team,
|
||||
/// licensed under the Apache License, version 2.0 - https://www.apache.org/licenses/LICENSE-2.0
|
||||
/// https://github.com/rust-lang/libm/blob/master/LICENSE-APACHE
|
||||
///
|
||||
/// Thank you, Rust core team!
|
||||
|
||||
// From https://github.com/rust-lang/libm/blob/master/src/math/mod.rs
|
||||
#[cfg(not(debug_assertions))]
|
||||
macro_rules! i {
|
||||
($array:expr, $index:expr) => {
|
||||
unsafe { *$array.get_unchecked($index) }
|
||||
};
|
||||
($array:expr, $index:expr, = , $rhs:expr) => {
|
||||
unsafe {
|
||||
*$array.get_unchecked_mut($index) = $rhs;
|
||||
}
|
||||
};
|
||||
($array:expr, $index:expr, += , $rhs:expr) => {
|
||||
unsafe {
|
||||
*$array.get_unchecked_mut($index) += $rhs;
|
||||
}
|
||||
};
|
||||
($array:expr, $index:expr, -= , $rhs:expr) => {
|
||||
unsafe {
|
||||
*$array.get_unchecked_mut($index) -= $rhs;
|
||||
}
|
||||
};
|
||||
($array:expr, $index:expr, &= , $rhs:expr) => {
|
||||
unsafe {
|
||||
*$array.get_unchecked_mut($index) &= $rhs;
|
||||
}
|
||||
};
|
||||
($array:expr, $index:expr, == , $rhs:expr) => {
|
||||
unsafe { *$array.get_unchecked_mut($index) == $rhs }
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
macro_rules! i {
|
||||
($array:expr, $index:expr) => {
|
||||
*$array.get($index).unwrap()
|
||||
};
|
||||
($array:expr, $index:expr, = , $rhs:expr) => {
|
||||
*$array.get_mut($index).unwrap() = $rhs;
|
||||
};
|
||||
($array:expr, $index:expr, -= , $rhs:expr) => {
|
||||
*$array.get_mut($index).unwrap() -= $rhs;
|
||||
};
|
||||
($array:expr, $index:expr, += , $rhs:expr) => {
|
||||
*$array.get_mut($index).unwrap() += $rhs;
|
||||
};
|
||||
($array:expr, $index:expr, &= , $rhs:expr) => {
|
||||
*$array.get_mut($index).unwrap() &= $rhs;
|
||||
};
|
||||
($array:expr, $index:expr, == , $rhs:expr) => {
|
||||
*$array.get_mut($index).unwrap() == $rhs
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! llvm_intrinsically_optimized {
|
||||
(#[cfg($($clause:tt)*)] $e:expr) => {
|
||||
#[cfg(all(feature = "unstable", $($clause)*))]
|
||||
{
|
||||
if true { // thwart the dead code lint
|
||||
$e
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! force_eval {
|
||||
($e:expr) => {
|
||||
unsafe {
|
||||
::core::ptr::read_volatile(&$e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// From https://github.com/rust-lang/libm/blob/master/src/math/atan.rs
|
||||
|
||||
// Clippy fails CI if we don't include overrides below. Since this is copied
|
||||
// straight from the libm crate, I figure they had a reason to include
|
||||
// the extra percision, so we just silence this warning.
|
||||
|
||||
#[allow(clippy::excessive_precision)]
|
||||
const ATANHI: [f64; 4] = [
|
||||
4.63647609000806093515e-01, /* atan(0.5)hi 0x3FDDAC67, 0x0561BB4F */
|
||||
7.85398163397448278999e-01, /* atan(1.0)hi 0x3FE921FB, 0x54442D18 */
|
||||
9.82793723247329054082e-01, /* atan(1.5)hi 0x3FEF730B, 0xD281F69B */
|
||||
1.57079632679489655800e+00, /* atan(inf)hi 0x3FF921FB, 0x54442D18 */
|
||||
];
|
||||
|
||||
#[allow(clippy::excessive_precision)]
|
||||
const ATANLO: [f64; 4] = [
|
||||
2.26987774529616870924e-17, /* atan(0.5)lo 0x3C7A2B7F, 0x222F65E2 */
|
||||
3.06161699786838301793e-17, /* atan(1.0)lo 0x3C81A626, 0x33145C07 */
|
||||
1.39033110312309984516e-17, /* atan(1.5)lo 0x3C700788, 0x7AF0CBBD */
|
||||
6.12323399573676603587e-17, /* atan(inf)lo 0x3C91A626, 0x33145C07 */
|
||||
];
|
||||
|
||||
#[allow(clippy::excessive_precision)]
|
||||
const AT: [f64; 11] = [
|
||||
3.33333333333329318027e-01, /* 0x3FD55555, 0x5555550D */
|
||||
-1.99999999998764832476e-01, /* 0xBFC99999, 0x9998EBC4 */
|
||||
1.42857142725034663711e-01, /* 0x3FC24924, 0x920083FF */
|
||||
-1.11111104054623557880e-01, /* 0xBFBC71C6, 0xFE231671 */
|
||||
9.09088713343650656196e-02, /* 0x3FB745CD, 0xC54C206E */
|
||||
-7.69187620504482999495e-02, /* 0xBFB3B0F2, 0xAF749A6D */
|
||||
6.66107313738753120669e-02, /* 0x3FB10D66, 0xA0D03D51 */
|
||||
-5.83357013379057348645e-02, /* 0xBFADDE2D, 0x52DEFD9A */
|
||||
4.97687799461593236017e-02, /* 0x3FA97B4B, 0x24760DEB */
|
||||
-3.65315727442169155270e-02, /* 0xBFA2B444, 0x2C6A6C2F */
|
||||
1.62858201153657823623e-02, /* 0x3F90AD3A, 0xE322DA11 */
|
||||
];
|
||||
|
||||
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
|
||||
pub fn fabs(x: f64) -> f64 {
|
||||
// On wasm32 we know that LLVM's intrinsic will compile to an optimized
|
||||
// `f64.abs` native instruction, so we can leverage this for both code size
|
||||
// and speed.
|
||||
llvm_intrinsically_optimized! {
|
||||
#[cfg(target_arch = "wasm32")] {
|
||||
return unsafe { ::core::intrinsics::fabsf64(x) }
|
||||
}
|
||||
}
|
||||
f64::from_bits(x.to_bits() & (u64::MAX / 2))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
|
||||
pub fn atan(x: f64) -> f64 {
|
||||
let mut x = x;
|
||||
let mut ix = (x.to_bits() >> 32) as u32;
|
||||
let sign = ix >> 31;
|
||||
ix &= 0x7fff_ffff;
|
||||
if ix >= 0x4410_0000 {
|
||||
if x.is_nan() {
|
||||
return x;
|
||||
}
|
||||
|
||||
let z = ATANHI[3] + f64::from_bits(0x0380_0000); // 0x1p-120f
|
||||
return if sign != 0 { -z } else { z };
|
||||
}
|
||||
|
||||
let id = if ix < 0x3fdc_0000 {
|
||||
/* |x| < 0.4375 */
|
||||
if ix < 0x3e40_0000 {
|
||||
/* |x| < 2^-27 */
|
||||
if ix < 0x0010_0000 {
|
||||
/* raise underflow for subnormal x */
|
||||
force_eval!(x as f32);
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
-1
|
||||
} else {
|
||||
x = fabs(x);
|
||||
if ix < 0x3ff30000 {
|
||||
/* |x| < 1.1875 */
|
||||
if ix < 0x3fe60000 {
|
||||
/* 7/16 <= |x| < 11/16 */
|
||||
x = (2. * x - 1.) / (2. + x);
|
||||
0
|
||||
} else {
|
||||
/* 11/16 <= |x| < 19/16 */
|
||||
x = (x - 1.) / (x + 1.);
|
||||
1
|
||||
}
|
||||
} else if ix < 0x40038000 {
|
||||
/* |x| < 2.4375 */
|
||||
x = (x - 1.5) / (1. + 1.5 * x);
|
||||
2
|
||||
} else {
|
||||
/* 2.4375 <= |x| < 2^66 */
|
||||
x = -1. / x;
|
||||
3
|
||||
}
|
||||
};
|
||||
|
||||
let z = x * x;
|
||||
let w = z * z;
|
||||
/* break sum from i=0 to 10 AT[i]z**(i+1) into odd and even poly */
|
||||
let s1 = z * (AT[0] + w * (AT[2] + w * (AT[4] + w * (AT[6] + w * (AT[8] + w * AT[10])))));
|
||||
let s2 = w * (AT[1] + w * (AT[3] + w * (AT[5] + w * (AT[7] + w * AT[9]))));
|
||||
|
||||
if id < 0 {
|
||||
return x - x * (s1 + s2);
|
||||
}
|
||||
|
||||
let z = i!(ATANHI, id as usize) - (x * (s1 + s2) - i!(ATANLO, id as usize) - x);
|
||||
|
||||
if sign != 0 {
|
||||
-z
|
||||
} else {
|
||||
z
|
||||
}
|
||||
}
|
14
compiler/builtins/bitcode/src/main.zig
Normal file
14
compiler/builtins/bitcode/src/main.zig
Normal file
|
@ -0,0 +1,14 @@
|
|||
const std = @import("std");
|
||||
const math = std.math;
|
||||
|
||||
export fn atan_(num: f64) f64 {
|
||||
return math.atan(num);
|
||||
}
|
||||
|
||||
export fn is_finite_(num: f64) bool {
|
||||
return math.isFinite(num);
|
||||
}
|
||||
|
||||
export fn pow_int_(base: i64, exp: i64) i64 {
|
||||
return math.pow(i64, base, exp);
|
||||
}
|
70
compiler/builtins/build.rs
Normal file
70
compiler/builtins/build.rs
Normal file
|
@ -0,0 +1,70 @@
|
|||
use std::convert::AsRef;
|
||||
use std::env;
|
||||
use std::ffi::OsStr;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
use std::str;
|
||||
|
||||
fn run_command<S, I>(command: &str, args: I)
|
||||
where
|
||||
I: IntoIterator<Item = S>,
|
||||
S: AsRef<OsStr>,
|
||||
{
|
||||
let output_result = Command::new(OsStr::new(&command)).args(args).output();
|
||||
match output_result {
|
||||
Ok(output) => match output.status.success() {
|
||||
true => (),
|
||||
false => {
|
||||
let error_str = match str::from_utf8(&output.stderr) {
|
||||
Ok(stderr) => stderr.to_string(),
|
||||
Err(_) => format!("Failed to run \"{}\"", command),
|
||||
};
|
||||
panic!("{} failed: {}", command, error_str);
|
||||
}
|
||||
},
|
||||
Err(reason) => panic!("{} failed: {}", command, reason),
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
|
||||
// "." is relative to where "build.rs" is
|
||||
let src_path = Path::new(".").join("bitcode").join("src").join("main.zig");
|
||||
let src_path_str = src_path.to_str().expect("Invalid src path");
|
||||
println!("Building main.zig from: {}", src_path_str);
|
||||
|
||||
let zig_cache_dir = Path::new(&out_dir).join("zig-cache");
|
||||
let zig_cache_dir_str = zig_cache_dir.to_str().expect("Invalid zig cache dir");
|
||||
println!("Setting zig cache to: {}", zig_cache_dir_str);
|
||||
|
||||
let dest_ll_path = Path::new(&out_dir).join("builtins.ll");
|
||||
let dest_ll = dest_ll_path.to_str().expect("Invalid dest ir path");
|
||||
let emit_ir_arg = "-femit-llvm-ir=".to_owned() + dest_ll;
|
||||
println!("Compiling zig llvm-ir to: {}", dest_ll);
|
||||
|
||||
run_command(
|
||||
"zig",
|
||||
&[
|
||||
"build-obj",
|
||||
src_path_str,
|
||||
&emit_ir_arg,
|
||||
"-fno-emit-bin",
|
||||
"--strip",
|
||||
"-O",
|
||||
"ReleaseFast",
|
||||
"--cache-dir",
|
||||
zig_cache_dir_str,
|
||||
],
|
||||
);
|
||||
|
||||
let dest_bc_path = Path::new(&out_dir).join("builtins.bc");
|
||||
let dest_bc = dest_bc_path.to_str().expect("Invalid dest bc path");
|
||||
println!("Compiling bitcode to: {}", dest_bc);
|
||||
|
||||
run_command("llvm-as", &[dest_ll, "-o", dest_bc]);
|
||||
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
println!("cargo:rerun-if-changed={}", src_path_str);
|
||||
println!("cargo:rustc-env=BUILTINS_BC={}", dest_bc);
|
||||
}
|
18
compiler/builtins/src/bitcode.rs
Normal file
18
compiler/builtins/src/bitcode.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
use std::fs::File;
|
||||
use std::io::prelude::Read;
|
||||
use std::vec::Vec;
|
||||
|
||||
pub fn get_bytes() -> Vec<u8> {
|
||||
// In the build script for the builtins module, we compile the builtins bitcode and set
|
||||
// BUILTINS_BC to the path to the compiled output.
|
||||
let path: &'static str = env!(
|
||||
"BUILTINS_BC",
|
||||
"Env var BUILTINS_BC not found. Is there a problem with the build script?"
|
||||
);
|
||||
let mut builtins_bitcode = File::open(path).expect("Unable to find builtins bitcode source");
|
||||
let mut buffer = Vec::new();
|
||||
builtins_bitcode
|
||||
.read_to_end(&mut buffer)
|
||||
.expect("Unable to read builtins bitcode");
|
||||
buffer
|
||||
}
|
|
@ -10,5 +10,6 @@
|
|||
// and encouraging shortcuts here creates bad incentives. I would rather temporarily
|
||||
// re-enable this when working on performance optimizations than have it block PRs.
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
pub mod bitcode;
|
||||
pub mod std;
|
||||
pub mod unique;
|
||||
|
|
|
@ -110,7 +110,7 @@ pub fn canonicalize_defs<'a>(
|
|||
mut output: Output,
|
||||
var_store: &mut VarStore,
|
||||
original_scope: &Scope,
|
||||
loc_defs: &'a bumpalo::collections::Vec<'a, &'a Located<ast::Def<'a>>>,
|
||||
loc_defs: &'a [&'a Located<ast::Def<'a>>],
|
||||
pattern_type: PatternType,
|
||||
) -> (CanDefs, Scope, Output, MutMap<Symbol, Region>) {
|
||||
// Canonicalizing defs while detecting shadowing involves a multi-step process:
|
||||
|
@ -1243,7 +1243,7 @@ pub fn can_defs_with_return<'a>(
|
|||
env: &mut Env<'a>,
|
||||
var_store: &mut VarStore,
|
||||
scope: Scope,
|
||||
loc_defs: &'a bumpalo::collections::Vec<'a, &'a Located<ast::Def<'a>>>,
|
||||
loc_defs: &'a [&'a Located<ast::Def<'a>>],
|
||||
loc_ret: &'a Located<ast::Expr<'a>>,
|
||||
) -> (Expr, Output) {
|
||||
let (unsorted, mut scope, defs_output, symbols_introduced) = canonicalize_defs(
|
||||
|
|
|
@ -309,7 +309,7 @@ pub fn canonicalize_expr<'a>(
|
|||
let mut args = Vec::new();
|
||||
let mut outputs = Vec::new();
|
||||
|
||||
for loc_arg in loc_args {
|
||||
for loc_arg in loc_args.iter() {
|
||||
let (arg_expr, arg_out) =
|
||||
canonicalize_expr(env, var_store, scope, loc_arg.region, &loc_arg.value);
|
||||
|
||||
|
@ -450,7 +450,7 @@ pub fn canonicalize_expr<'a>(
|
|||
|
||||
let mut bound_by_argument_patterns = MutSet::default();
|
||||
|
||||
for loc_pattern in loc_arg_patterns.into_iter() {
|
||||
for loc_pattern in loc_arg_patterns.iter() {
|
||||
let (new_output, can_arg) = canonicalize_pattern(
|
||||
env,
|
||||
var_store,
|
||||
|
@ -562,7 +562,7 @@ pub fn canonicalize_expr<'a>(
|
|||
|
||||
let mut can_branches = Vec::with_capacity(branches.len());
|
||||
|
||||
for branch in branches {
|
||||
for branch in branches.iter() {
|
||||
let (can_when_branch, branch_references) =
|
||||
canonicalize_when_branch(env, var_store, scope, region, *branch, &mut output);
|
||||
|
||||
|
@ -788,7 +788,7 @@ fn canonicalize_when_branch<'a>(
|
|||
let mut scope = original_scope.clone();
|
||||
|
||||
// TODO report symbols not bound in all patterns
|
||||
for loc_pattern in &branch.patterns {
|
||||
for loc_pattern in branch.patterns.iter() {
|
||||
let (new_output, can_pattern) = canonicalize_pattern(
|
||||
env,
|
||||
var_store,
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use crate::builtins;
|
||||
use crate::def::{canonicalize_defs, sort_can_defs, Declaration};
|
||||
use crate::env::Env;
|
||||
use crate::expr::Output;
|
||||
|
@ -42,7 +43,7 @@ pub struct ModuleOutput {
|
|||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn canonicalize_module_defs<'a>(
|
||||
arena: &Bump,
|
||||
loc_defs: bumpalo::collections::Vec<'a, Located<ast::Def<'a>>>,
|
||||
loc_defs: &'a [Located<ast::Def<'a>>],
|
||||
home: ModuleId,
|
||||
module_ids: &ModuleIds,
|
||||
exposed_ident_ids: IdentIds,
|
||||
|
@ -65,9 +66,9 @@ pub fn canonicalize_module_defs<'a>(
|
|||
let mut desugared =
|
||||
bumpalo::collections::Vec::with_capacity_in(loc_defs.len() + num_deps, arena);
|
||||
|
||||
for loc_def in loc_defs {
|
||||
for loc_def in loc_defs.iter() {
|
||||
desugared.push(&*arena.alloc(Located {
|
||||
value: desugar_def(arena, arena.alloc(loc_def.value)),
|
||||
value: desugar_def(arena, &loc_def.value),
|
||||
region: loc_def.region,
|
||||
}));
|
||||
}
|
||||
|
@ -259,6 +260,15 @@ pub fn canonicalize_module_defs<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
// Add builtin defs (e.g. List.get) to the module's defs
|
||||
let builtin_defs = builtins::builtin_defs(var_store);
|
||||
|
||||
for (symbol, def) in builtin_defs {
|
||||
if references.contains(&symbol) {
|
||||
declarations.push(Declaration::Builtin(def));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ModuleOutput {
|
||||
aliases,
|
||||
rigid_variables,
|
||||
|
|
|
@ -90,9 +90,10 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a
|
|||
List(elems) | Nested(List(elems)) => {
|
||||
let mut new_elems = Vec::with_capacity_in(elems.len(), arena);
|
||||
|
||||
for elem in elems {
|
||||
for elem in elems.iter() {
|
||||
new_elems.push(desugar_expr(arena, elem));
|
||||
}
|
||||
let new_elems = new_elems.into_bump_slice();
|
||||
let value: Expr<'a> = List(new_elems);
|
||||
|
||||
arena.alloc(Located {
|
||||
|
@ -103,7 +104,7 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a
|
|||
Record { fields, update } | Nested(Record { fields, update }) => {
|
||||
let mut new_fields = Vec::with_capacity_in(fields.len(), arena);
|
||||
|
||||
for field in fields {
|
||||
for field in fields.iter() {
|
||||
let value = desugar_field(arena, &field.value);
|
||||
|
||||
new_fields.push(Located {
|
||||
|
@ -112,6 +113,8 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a
|
|||
});
|
||||
}
|
||||
|
||||
let new_fields = new_fields.into_bump_slice();
|
||||
|
||||
arena.alloc(Located {
|
||||
region: loc_expr.region,
|
||||
value: Record {
|
||||
|
@ -130,7 +133,7 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a
|
|||
Defs(defs, loc_ret) | Nested(Defs(defs, loc_ret)) => {
|
||||
let mut desugared_defs = Vec::with_capacity_in(defs.len(), arena);
|
||||
|
||||
for loc_def in defs.into_iter() {
|
||||
for loc_def in defs.iter() {
|
||||
let loc_def = Located {
|
||||
value: desugar_def(arena, &loc_def.value),
|
||||
region: loc_def.region,
|
||||
|
@ -139,6 +142,8 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a
|
|||
desugared_defs.push(&*arena.alloc(loc_def));
|
||||
}
|
||||
|
||||
let desugared_defs = desugared_defs.into_bump_slice();
|
||||
|
||||
arena.alloc(Located {
|
||||
value: Defs(desugared_defs, desugar_expr(arena, loc_ret)),
|
||||
region: loc_expr.region,
|
||||
|
@ -147,10 +152,12 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a
|
|||
Apply(loc_fn, loc_args, called_via) | Nested(Apply(loc_fn, loc_args, called_via)) => {
|
||||
let mut desugared_args = Vec::with_capacity_in(loc_args.len(), arena);
|
||||
|
||||
for loc_arg in loc_args {
|
||||
for loc_arg in loc_args.iter() {
|
||||
desugared_args.push(desugar_expr(arena, loc_arg));
|
||||
}
|
||||
|
||||
let desugared_args = desugared_args.into_bump_slice();
|
||||
|
||||
arena.alloc(Located {
|
||||
value: Apply(desugar_expr(arena, loc_fn), desugared_args, *called_via),
|
||||
region: loc_expr.region,
|
||||
|
@ -160,11 +167,11 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a
|
|||
let loc_desugared_cond = &*arena.alloc(desugar_expr(arena, &loc_cond_expr));
|
||||
let mut desugared_branches = Vec::with_capacity_in(branches.len(), arena);
|
||||
|
||||
for branch in branches.into_iter() {
|
||||
for branch in branches.iter() {
|
||||
let desugared = desugar_expr(arena, &branch.value);
|
||||
|
||||
let mut alternatives = Vec::with_capacity_in(branch.patterns.len(), arena);
|
||||
for loc_pattern in &branch.patterns {
|
||||
for loc_pattern in branch.patterns.iter() {
|
||||
alternatives.push(Located {
|
||||
region: loc_pattern.region,
|
||||
value: Pattern::Nested(&loc_pattern.value),
|
||||
|
@ -177,6 +184,8 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a
|
|||
None
|
||||
};
|
||||
|
||||
let alternatives = alternatives.into_bump_slice();
|
||||
|
||||
desugared_branches.push(&*arena.alloc(WhenBranch {
|
||||
patterns: alternatives,
|
||||
value: Located {
|
||||
|
@ -187,6 +196,8 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a
|
|||
}));
|
||||
}
|
||||
|
||||
let desugared_branches = desugared_branches.into_bump_slice();
|
||||
|
||||
arena.alloc(Located {
|
||||
value: When(loc_desugared_cond, desugared_branches),
|
||||
region: loc_expr.region,
|
||||
|
@ -213,6 +224,8 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a
|
|||
let loc_fn_var = arena.alloc(Located { region, value });
|
||||
let desugared_args = bumpalo::vec![in arena; desugar_expr(arena, loc_arg)];
|
||||
|
||||
let desugared_args = desugared_args.into_bump_slice();
|
||||
|
||||
arena.alloc(Located {
|
||||
value: Apply(loc_fn_var, desugared_args, CalledVia::UnaryOp(op)),
|
||||
region: loc_expr.region,
|
||||
|
@ -463,10 +476,12 @@ fn desugar_bin_op<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'_>>) -> &'a L
|
|||
|
||||
args.push(left);
|
||||
|
||||
for arg in arguments {
|
||||
for arg in arguments.iter() {
|
||||
args.push(arg);
|
||||
}
|
||||
|
||||
let args = args.into_bump_slice();
|
||||
|
||||
Apply(function, args, CalledVia::BinOp(Pizza))
|
||||
}
|
||||
expr => {
|
||||
|
@ -480,6 +495,8 @@ fn desugar_bin_op<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'_>>) -> &'a L
|
|||
region: right.region,
|
||||
});
|
||||
|
||||
let args = args.into_bump_slice();
|
||||
|
||||
Apply(function, args, CalledVia::BinOp(Pizza))
|
||||
}
|
||||
}
|
||||
|
@ -498,6 +515,8 @@ fn desugar_bin_op<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'_>>) -> &'a L
|
|||
region: loc_op.region,
|
||||
});
|
||||
|
||||
let args = args.into_bump_slice();
|
||||
|
||||
Apply(loc_expr, args, CalledVia::BinOp(binop))
|
||||
}
|
||||
};
|
||||
|
|
|
@ -4,7 +4,7 @@ use crate::pattern::fmt_pattern;
|
|||
use crate::spaces::{
|
||||
add_spaces, fmt_comments_only, fmt_condition_spaces, fmt_spaces, newline, INDENT,
|
||||
};
|
||||
use bumpalo::collections::{String, Vec};
|
||||
use bumpalo::collections::String;
|
||||
use roc_module::operator::{self, BinOp};
|
||||
use roc_parse::ast::StrSegment;
|
||||
use roc_parse::ast::{AssignedField, Base, CommentOrNewline, Expr, Pattern, WhenBranch};
|
||||
|
@ -194,12 +194,12 @@ impl<'a> Formattable<'a> for Expr<'a> {
|
|||
if multiline_args {
|
||||
let arg_indent = indent + INDENT;
|
||||
|
||||
for loc_arg in loc_args {
|
||||
for loc_arg in loc_args.iter() {
|
||||
newline(buf, arg_indent);
|
||||
loc_arg.format_with_options(buf, Parens::InApply, Newlines::No, arg_indent);
|
||||
}
|
||||
} else {
|
||||
for loc_arg in loc_args {
|
||||
for loc_arg in loc_args.iter() {
|
||||
buf.push(' ');
|
||||
loc_arg.format_with_options(buf, Parens::InApply, Newlines::Yes, indent);
|
||||
}
|
||||
|
@ -707,7 +707,7 @@ fn fmt_if<'a>(
|
|||
|
||||
pub fn fmt_closure<'a>(
|
||||
buf: &mut String<'a>,
|
||||
loc_patterns: &'a Vec<'a, Located<Pattern<'a>>>,
|
||||
loc_patterns: &'a [Located<Pattern<'a>>],
|
||||
loc_ret: &'a Located<Expr<'a>>,
|
||||
indent: u16,
|
||||
) {
|
||||
|
|
|
@ -40,12 +40,15 @@ inlinable_string = "0.1"
|
|||
# This way, GitHub Actions works and nobody's builds get broken.
|
||||
inkwell = { git = "https://github.com/rtfeldman/inkwell", tag = "llvm10-0.release2" }
|
||||
target-lexicon = "0.10"
|
||||
libloading = "0.6"
|
||||
|
||||
[dev-dependencies]
|
||||
roc_can = { path = "../can" }
|
||||
roc_parse = { path = "../parse" }
|
||||
roc_load = { path = "../load" }
|
||||
roc_reporting = { path = "../reporting" }
|
||||
roc_build = { path = "../build" }
|
||||
roc_std = { path = "../../roc_std" }
|
||||
pretty_assertions = "0.5.1"
|
||||
maplit = "1.0.1"
|
||||
indoc = "0.3.3"
|
||||
|
@ -54,4 +57,3 @@ quickcheck_macros = "0.8"
|
|||
tokio = { version = "0.2", features = ["blocking", "fs", "sync", "rt-threaded"] }
|
||||
bumpalo = { version = "3.2", features = ["collections"] }
|
||||
libc = "0.2"
|
||||
roc_std = { path = "../../roc_std" }
|
||||
|
|
|
@ -29,6 +29,7 @@ use inkwell::values::{
|
|||
};
|
||||
use inkwell::OptimizationLevel;
|
||||
use inkwell::{AddressSpace, IntPredicate};
|
||||
use roc_builtins::bitcode;
|
||||
use roc_collections::all::{ImMap, MutSet};
|
||||
use roc_module::low_level::LowLevel;
|
||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||
|
@ -50,6 +51,15 @@ pub enum OptLevel {
|
|||
Optimize,
|
||||
}
|
||||
|
||||
impl Into<OptimizationLevel> for OptLevel {
|
||||
fn into(self) -> OptimizationLevel {
|
||||
match self {
|
||||
OptLevel::Normal => OptimizationLevel::None,
|
||||
OptLevel::Optimize => OptimizationLevel::Aggressive,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq)]
|
||||
pub struct Scope<'a, 'ctx> {
|
||||
symbols: ImMap<Symbol, (Layout<'a>, PointerValue<'ctx>)>,
|
||||
|
@ -172,8 +182,9 @@ impl<'a, 'ctx, 'env> Env<'a, 'ctx, 'env> {
|
|||
}
|
||||
|
||||
pub fn module_from_builtins<'ctx>(ctx: &'ctx Context, module_name: &str) -> Module<'ctx> {
|
||||
let memory_buffer =
|
||||
MemoryBuffer::create_from_memory_range(include_bytes!("builtins.bc"), module_name);
|
||||
let bitcode_bytes = bitcode::get_bytes();
|
||||
|
||||
let memory_buffer = MemoryBuffer::create_from_memory_range(&bitcode_bytes, module_name);
|
||||
|
||||
let module = Module::parse_bitcode_from_buffer(&memory_buffer, ctx)
|
||||
.unwrap_or_else(|err| panic!("Unable to import builtins bitcode. LLVM error: {:?}", err));
|
||||
|
|
Binary file not shown.
|
@ -28,24 +28,23 @@ impl<T: Sized> Into<Result<T, String>> for RocCallResult<T> {
|
|||
|
||||
#[macro_export]
|
||||
macro_rules! run_jit_function {
|
||||
($execution_engine: expr, $main_fn_name: expr, $ty:ty, $transform:expr) => {{
|
||||
($lib: expr, $main_fn_name: expr, $ty:ty, $transform:expr) => {{
|
||||
let v: std::vec::Vec<roc_problem::can::Problem> = std::vec::Vec::new();
|
||||
run_jit_function!($execution_engine, $main_fn_name, $ty, $transform, v)
|
||||
run_jit_function!($lib, $main_fn_name, $ty, $transform, v)
|
||||
}};
|
||||
|
||||
($execution_engine: expr, $main_fn_name: expr, $ty:ty, $transform:expr, $errors:expr) => {{
|
||||
($lib: expr, $main_fn_name: expr, $ty:ty, $transform:expr, $errors:expr) => {{
|
||||
use inkwell::context::Context;
|
||||
use inkwell::execution_engine::JitFunction;
|
||||
use roc_gen::run_roc::RocCallResult;
|
||||
|
||||
unsafe {
|
||||
let main: JitFunction<unsafe extern "C" fn() -> RocCallResult<$ty>> = $execution_engine
|
||||
.get_function($main_fn_name)
|
||||
let main: libloading::Symbol<unsafe extern "C" fn() -> RocCallResult<$ty>> = $lib
|
||||
.get($main_fn_name.as_bytes())
|
||||
.ok()
|
||||
.ok_or(format!("Unable to JIT compile `{}`", $main_fn_name))
|
||||
.expect("errored");
|
||||
|
||||
match main.call().into() {
|
||||
match main().into() {
|
||||
Ok(success) => {
|
||||
// only if there are no exceptions thrown, check for errors
|
||||
assert_eq!(
|
||||
|
@ -68,26 +67,25 @@ macro_rules! run_jit_function {
|
|||
/// It explicitly allocates a buffer that the roc main function can write its result into.
|
||||
#[macro_export]
|
||||
macro_rules! run_jit_function_dynamic_type {
|
||||
($execution_engine: expr, $main_fn_name: expr, $bytes:expr, $transform:expr) => {{
|
||||
($lib: expr, $main_fn_name: expr, $bytes:expr, $transform:expr) => {{
|
||||
let v: std::vec::Vec<roc_problem::can::Problem> = std::vec::Vec::new();
|
||||
run_jit_function_dynamic_type!($execution_engine, $main_fn_name, $bytes, $transform, v)
|
||||
run_jit_function_dynamic_type!($lib, $main_fn_name, $bytes, $transform, v)
|
||||
}};
|
||||
|
||||
($execution_engine: expr, $main_fn_name: expr, $bytes:expr, $transform:expr, $errors:expr) => {{
|
||||
($lib: expr, $main_fn_name: expr, $bytes:expr, $transform:expr, $errors:expr) => {{
|
||||
use inkwell::context::Context;
|
||||
use inkwell::execution_engine::JitFunction;
|
||||
use roc_gen::run_roc::RocCallResult;
|
||||
|
||||
unsafe {
|
||||
let main: JitFunction<unsafe extern "C" fn(*const u8)> = $execution_engine
|
||||
.get_function($main_fn_name)
|
||||
let main: libloading::Symbol<unsafe extern "C" fn(*const u8)> = $lib
|
||||
.get($main_fn_name.as_bytes())
|
||||
.ok()
|
||||
.ok_or(format!("Unable to JIT compile `{}`", $main_fn_name))
|
||||
.expect("errored");
|
||||
|
||||
let layout = std::alloc::Layout::array::<u8>($bytes).unwrap();
|
||||
let result = std::alloc::alloc(layout);
|
||||
main.call(result);
|
||||
main(result);
|
||||
|
||||
let flag = *result;
|
||||
|
||||
|
|
|
@ -691,19 +691,19 @@ mod gen_num {
|
|||
assert_evals_to!("Num.atan 10", 1.4711276743037347, f64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = r#"Roc failed with message: "integer addition overflowed!"#)]
|
||||
fn int_overflow() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
9_223_372_036_854_775_807 + 1
|
||||
"#
|
||||
),
|
||||
0,
|
||||
i64
|
||||
);
|
||||
}
|
||||
// #[test]
|
||||
// #[should_panic(expected = r#"Roc failed with message: "integer addition overflowed!"#)]
|
||||
// fn int_overflow() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// 9_223_372_036_854_775_807 + 1
|
||||
// "#
|
||||
// ),
|
||||
// 0,
|
||||
// i64
|
||||
// );
|
||||
// }
|
||||
|
||||
#[test]
|
||||
fn int_add_checked() {
|
||||
|
@ -775,17 +775,17 @@ mod gen_num {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = r#"Roc failed with message: "float addition overflowed!"#)]
|
||||
fn float_overflow() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
1.7976931348623157e308 + 1.7976931348623157e308
|
||||
"#
|
||||
),
|
||||
0.0,
|
||||
f64
|
||||
);
|
||||
}
|
||||
// #[test]
|
||||
// #[should_panic(expected = r#"Roc failed with message: "float addition overflowed!"#)]
|
||||
// fn float_overflow() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// 1.7976931348623157e308 + 1.7976931348623157e308
|
||||
// "#
|
||||
// ),
|
||||
// 0.0,
|
||||
// f64
|
||||
// );
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -884,22 +884,22 @@ mod gen_primitives {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Roc failed with message: ")]
|
||||
fn exception() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
if True then
|
||||
x + z
|
||||
else
|
||||
y + z
|
||||
"#
|
||||
),
|
||||
3,
|
||||
i64
|
||||
);
|
||||
}
|
||||
// #[test]
|
||||
// #[should_panic(expected = "Roc failed with message: ")]
|
||||
// fn exception() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// if True then
|
||||
// x + z
|
||||
// else
|
||||
// y + z
|
||||
// "#
|
||||
// ),
|
||||
// 3,
|
||||
// i64
|
||||
// );
|
||||
// }
|
||||
|
||||
#[test]
|
||||
fn closure() {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use libloading::Library;
|
||||
use roc_build::link::module_to_dylib;
|
||||
use roc_collections::all::{MutMap, MutSet};
|
||||
|
||||
fn promote_expr_to_module(src: &str) -> String {
|
||||
|
@ -19,12 +21,7 @@ pub fn helper<'a>(
|
|||
stdlib: roc_builtins::std::StdLib,
|
||||
leak: bool,
|
||||
context: &'a inkwell::context::Context,
|
||||
) -> (
|
||||
&'static str,
|
||||
Vec<roc_problem::can::Problem>,
|
||||
inkwell::execution_engine::ExecutionEngine<'a>,
|
||||
) {
|
||||
use inkwell::OptimizationLevel;
|
||||
) -> (&'static str, Vec<roc_problem::can::Problem>, Library) {
|
||||
use roc_gen::llvm::build::{build_proc, build_proc_header, Scope};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
|
@ -166,10 +163,6 @@ pub fn helper<'a>(
|
|||
let (module_pass, function_pass) =
|
||||
roc_gen::llvm::build::construct_optimization_passes(module, opt_level);
|
||||
|
||||
let execution_engine = module
|
||||
.create_jit_execution_engine(OptimizationLevel::None)
|
||||
.expect("Error creating JIT execution engine for test");
|
||||
|
||||
// Compile and add all the Procs before adding main
|
||||
let env = roc_gen::llvm::build::Env {
|
||||
arena: &arena,
|
||||
|
@ -265,7 +258,10 @@ pub fn helper<'a>(
|
|||
// Uncomment this to see the module's optimized LLVM instruction output:
|
||||
// env.module.print_to_stderr();
|
||||
|
||||
(main_fn_name, errors, execution_engine.clone())
|
||||
let lib = module_to_dylib(&env.module, &target, opt_level)
|
||||
.expect("Error loading compiled dylib for test");
|
||||
|
||||
(main_fn_name, errors, lib)
|
||||
}
|
||||
|
||||
// TODO this is almost all code duplication with assert_llvm_evals_to
|
||||
|
@ -284,7 +280,7 @@ macro_rules! assert_opt_evals_to {
|
|||
|
||||
let stdlib = roc_builtins::unique::uniq_stdlib();
|
||||
|
||||
let (main_fn_name, errors, execution_engine) =
|
||||
let (main_fn_name, errors, lib) =
|
||||
$crate::helpers::eval::helper(&arena, $src, stdlib, $leak, &context);
|
||||
|
||||
let transform = |success| {
|
||||
|
@ -292,7 +288,7 @@ macro_rules! assert_opt_evals_to {
|
|||
let given = $transform(success);
|
||||
assert_eq!(&given, &expected);
|
||||
};
|
||||
run_jit_function!(execution_engine, main_fn_name, $ty, transform, errors)
|
||||
run_jit_function!(lib, main_fn_name, $ty, transform, errors)
|
||||
};
|
||||
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||
|
@ -312,7 +308,7 @@ macro_rules! assert_llvm_evals_to {
|
|||
let context = Context::create();
|
||||
let stdlib = roc_builtins::std::standard_stdlib();
|
||||
|
||||
let (main_fn_name, errors, execution_engine) =
|
||||
let (main_fn_name, errors, lib) =
|
||||
$crate::helpers::eval::helper(&arena, $src, stdlib, $leak, &context);
|
||||
|
||||
let transform = |success| {
|
||||
|
@ -320,7 +316,7 @@ macro_rules! assert_llvm_evals_to {
|
|||
let given = $transform(success);
|
||||
assert_eq!(&given, &expected);
|
||||
};
|
||||
run_jit_function!(execution_engine, main_fn_name, $ty, transform, errors)
|
||||
run_jit_function!(lib, main_fn_name, $ty, transform, errors)
|
||||
};
|
||||
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||
|
|
|
@ -32,7 +32,7 @@ pub struct DocEntry {
|
|||
pub fn generate_module_docs<'a>(
|
||||
module_name: ModuleName,
|
||||
exposed_ident_ids: &'a IdentIds,
|
||||
parsed_defs: &'a bumpalo::collections::Vec<'a, Located<Def<'a>>>,
|
||||
parsed_defs: &'a [Located<Def<'a>>],
|
||||
) -> ModuleDocumentation {
|
||||
let (entries, _) =
|
||||
parsed_defs
|
||||
|
|
|
@ -2020,7 +2020,8 @@ fn parse_and_constrain<'a>(
|
|||
let module_id = header.module_id;
|
||||
|
||||
// Generate documentation information
|
||||
// TODO: store timing information?
|
||||
// TODO: store timing information
|
||||
// TODO: only run this if we're doing a doc gen pass!
|
||||
let module_docs = crate::docs::generate_module_docs(
|
||||
header.module_name,
|
||||
&header.exposed_ident_ids,
|
||||
|
@ -2030,7 +2031,7 @@ fn parse_and_constrain<'a>(
|
|||
let mut var_store = VarStore::default();
|
||||
let canonicalized = canonicalize_module_defs(
|
||||
&arena,
|
||||
parsed_defs,
|
||||
&parsed_defs,
|
||||
module_id,
|
||||
module_ids,
|
||||
header.exposed_ident_ids,
|
||||
|
@ -2041,17 +2042,7 @@ fn parse_and_constrain<'a>(
|
|||
);
|
||||
let canonicalize_end = SystemTime::now();
|
||||
let (module, declarations, ident_ids, constraint, problems) = match canonicalized {
|
||||
Ok(mut module_output) => {
|
||||
// Add builtin defs (e.g. List.get) to the module's defs
|
||||
let builtin_defs = roc_can::builtins::builtin_defs(&mut var_store);
|
||||
let references = &module_output.references;
|
||||
|
||||
for (symbol, def) in builtin_defs {
|
||||
if references.contains(&symbol) {
|
||||
module_output.declarations.push(Declaration::Builtin(def));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(module_output) => {
|
||||
let constraint = constrain_module(&module_output, module_id, mode, &mut var_store);
|
||||
|
||||
// Now that we're done with parsing, canonicalization, and constraint gen,
|
||||
|
|
|
@ -13,6 +13,9 @@ mod helpers;
|
|||
#[cfg(test)]
|
||||
mod test_mono {
|
||||
use roc_collections::all::MutMap;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_mono::ir::Proc;
|
||||
use roc_mono::layout::Layout;
|
||||
|
||||
fn promote_expr_to_module(src: &str) -> String {
|
||||
let mut buffer = String::from("app Test provides [ main ] imports []\n\nmain =\n");
|
||||
|
@ -27,15 +30,6 @@ mod test_mono {
|
|||
buffer
|
||||
}
|
||||
|
||||
// NOTE because the Show instance of module names is different in --release mode,
|
||||
// these tests would all fail. In the future, when we do interesting optimizations,
|
||||
// we'll likely want some tests for --release too.
|
||||
#[cfg(not(debug_assertions))]
|
||||
fn compiles_to_ir(_src: &str, _expected: &str) {
|
||||
// just do nothing
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
fn compiles_to_ir(src: &str, expected: &str) {
|
||||
use bumpalo::Bump;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
@ -84,12 +78,22 @@ mod test_mono {
|
|||
println!("Ignoring {} canonicalization problems", can_problems.len());
|
||||
}
|
||||
|
||||
assert!(type_problems.is_empty());
|
||||
assert!(mono_problems.is_empty());
|
||||
assert_eq!(type_problems, Vec::new());
|
||||
assert_eq!(mono_problems, Vec::new());
|
||||
|
||||
debug_assert_eq!(exposed_to_host.len(), 1);
|
||||
|
||||
let main_fn_symbol = exposed_to_host.keys().copied().nth(0).unwrap();
|
||||
|
||||
verify_procedures(expected, procedures, main_fn_symbol);
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
fn verify_procedures(
|
||||
expected: &str,
|
||||
procedures: MutMap<(Symbol, Layout<'_>), Proc<'_>>,
|
||||
main_fn_symbol: Symbol,
|
||||
) {
|
||||
let index = procedures
|
||||
.keys()
|
||||
.position(|(s, _)| *s == main_fn_symbol)
|
||||
|
@ -120,6 +124,18 @@ mod test_mono {
|
|||
}
|
||||
}
|
||||
|
||||
// NOTE because the Show instance of module names is different in --release mode,
|
||||
// these tests would all fail. In the future, when we do interesting optimizations,
|
||||
// we'll likely want some tests for --release too.
|
||||
#[cfg(not(debug_assertions))]
|
||||
fn verify_procedures(
|
||||
_expected: &str,
|
||||
_procedures: MutMap<(Symbol, Layout<'_>), Proc<'_>>,
|
||||
_main_fn_symbol: Symbol,
|
||||
) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ir_int_literal() {
|
||||
compiles_to_ir(
|
||||
|
@ -154,7 +170,6 @@ mod test_mono {
|
|||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[test]
|
||||
fn ir_when_maybe() {
|
||||
compiles_to_ir(
|
||||
|
|
|
@ -29,7 +29,7 @@ pub struct InterfaceHeader<'a> {
|
|||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct WhenBranch<'a> {
|
||||
pub patterns: Vec<'a, Loc<Pattern<'a>>>,
|
||||
pub patterns: &'a [Loc<Pattern<'a>>],
|
||||
pub value: Loc<Expr<'a>>,
|
||||
pub guard: Option<Loc<Expr<'a>>>,
|
||||
}
|
||||
|
@ -188,10 +188,10 @@ pub enum Expr<'a> {
|
|||
AccessorFunction(&'a str),
|
||||
|
||||
// Collection Literals
|
||||
List(Vec<'a, &'a Loc<Expr<'a>>>),
|
||||
List(&'a [&'a Loc<Expr<'a>>]),
|
||||
Record {
|
||||
update: Option<&'a Loc<Expr<'a>>>,
|
||||
fields: Vec<'a, Loc<AssignedField<'a, Expr<'a>>>>,
|
||||
fields: &'a [Loc<AssignedField<'a, Expr<'a>>>],
|
||||
},
|
||||
|
||||
// Lookups
|
||||
|
@ -205,14 +205,14 @@ pub enum Expr<'a> {
|
|||
PrivateTag(&'a str),
|
||||
|
||||
// Pattern Matching
|
||||
Closure(&'a Vec<'a, Loc<Pattern<'a>>>, &'a Loc<Expr<'a>>),
|
||||
Closure(&'a [Loc<Pattern<'a>>], &'a Loc<Expr<'a>>),
|
||||
/// Multiple defs in a row
|
||||
Defs(Vec<'a, &'a Loc<Def<'a>>>, &'a Loc<Expr<'a>>),
|
||||
Defs(&'a [&'a Loc<Def<'a>>], &'a Loc<Expr<'a>>),
|
||||
|
||||
// Application
|
||||
/// To apply by name, do Apply(Var(...), ...)
|
||||
/// To apply a tag by name, do Apply(Tag(...), ...)
|
||||
Apply(&'a Loc<Expr<'a>>, Vec<'a, &'a Loc<Expr<'a>>>, CalledVia),
|
||||
Apply(&'a Loc<Expr<'a>>, &'a [&'a Loc<Expr<'a>>], CalledVia),
|
||||
BinOp(&'a (Loc<Expr<'a>>, Loc<BinOp>, Loc<Expr<'a>>)),
|
||||
UnaryOp(&'a Loc<Expr<'a>>, Loc<UnaryOp>),
|
||||
|
||||
|
@ -226,7 +226,7 @@ pub enum Expr<'a> {
|
|||
/// Vec, because there may be many patterns, and the guard
|
||||
/// is Option<Expr> because each branch may be preceded by
|
||||
/// a guard (".. if ..").
|
||||
Vec<'a, &'a WhenBranch<'a>>,
|
||||
&'a [&'a WhenBranch<'a>],
|
||||
),
|
||||
|
||||
// Blank Space (e.g. comments, spaces, newlines) before or after an expression.
|
||||
|
|
|
@ -82,7 +82,7 @@ macro_rules! loc_parenthetical_expr {
|
|||
region: loc_expr_with_extras.region,
|
||||
value: Expr::Apply(
|
||||
arena.alloc(loc_expr),
|
||||
allocated_args,
|
||||
allocated_args.into_bump_slice(),
|
||||
CalledVia::Space,
|
||||
),
|
||||
},
|
||||
|
@ -250,7 +250,7 @@ fn expr_to_pattern<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<'a>,
|
|||
|
||||
let mut arg_patterns = Vec::with_capacity_in(loc_args.len(), arena);
|
||||
|
||||
for loc_arg in loc_args {
|
||||
for loc_arg in loc_args.iter() {
|
||||
let region = loc_arg.region;
|
||||
let value = expr_to_pattern(arena, &loc_arg.value)?;
|
||||
|
||||
|
@ -279,7 +279,7 @@ fn expr_to_pattern<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<'a>,
|
|||
} => {
|
||||
let mut loc_patterns = Vec::with_capacity_in(fields.len(), arena);
|
||||
|
||||
for loc_assigned_field in fields {
|
||||
for loc_assigned_field in fields.iter() {
|
||||
let region = loc_assigned_field.region;
|
||||
let value = assigned_expr_field_to_pattern(arena, &loc_assigned_field.value)?;
|
||||
|
||||
|
@ -691,7 +691,10 @@ fn parse_def_expr<'a>(
|
|||
// for formatting reasons, we must insert the first def first!
|
||||
defs.insert(0, arena.alloc(loc_first_def));
|
||||
|
||||
Ok((Expr::Defs(defs, arena.alloc(loc_ret)), state))
|
||||
Ok((
|
||||
Expr::Defs(defs.into_bump_slice(), arena.alloc(loc_ret)),
|
||||
state,
|
||||
))
|
||||
},
|
||||
)
|
||||
.parse(arena, state)
|
||||
|
@ -766,6 +769,8 @@ fn parse_def_signature<'a>(
|
|||
// corresponding definition (the one with the body).
|
||||
defs.insert(0, arena.alloc(loc_first_def));
|
||||
|
||||
let defs = defs.into_bump_slice();
|
||||
|
||||
Ok((Expr::Defs(defs, arena.alloc(loc_ret)), state))
|
||||
},
|
||||
)
|
||||
|
@ -848,7 +853,12 @@ fn closure<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
|||
),
|
||||
|arena: &'a Bump, opt_contents| match opt_contents {
|
||||
None => Expr::MalformedClosure,
|
||||
Some((params, loc_body)) => Expr::Closure(arena.alloc(params), arena.alloc(loc_body)),
|
||||
Some((params, loc_body)) => {
|
||||
let params: Vec<'a, Located<Pattern<'a>>> = params;
|
||||
let params: &'a [Located<Pattern<'a>>] = params.into_bump_slice();
|
||||
|
||||
Expr::Closure(params, arena.alloc(loc_body))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -1119,7 +1129,10 @@ mod when {
|
|||
let (branches, state) =
|
||||
attempt!(Attempting::WhenBranch, branches(min_indent)).parse(arena, state)?;
|
||||
|
||||
Ok((Expr::When(arena.alloc(loc_condition), branches), state))
|
||||
Ok((
|
||||
Expr::When(arena.alloc(loc_condition), branches.into_bump_slice()),
|
||||
state,
|
||||
))
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -1152,7 +1165,7 @@ mod when {
|
|||
|
||||
// Record this as the first branch, then optionally parse additional branches.
|
||||
branches.push(arena.alloc(WhenBranch {
|
||||
patterns: loc_first_patterns,
|
||||
patterns: loc_first_patterns.into_bump_slice(),
|
||||
value: loc_first_expr,
|
||||
guard: loc_first_guard,
|
||||
}));
|
||||
|
@ -1173,10 +1186,13 @@ mod when {
|
|||
),
|
||||
branch_result(indented_more)
|
||||
),
|
||||
|((patterns, guard), expr)| WhenBranch {
|
||||
patterns,
|
||||
|((patterns, guard), expr)| {
|
||||
let patterns: Vec<'a, _> = patterns;
|
||||
WhenBranch {
|
||||
patterns: patterns.into_bump_slice(),
|
||||
value: expr,
|
||||
guard
|
||||
guard,
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -1444,7 +1460,11 @@ fn ident_etc<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
|||
}
|
||||
|
||||
Ok((
|
||||
Expr::Apply(arena.alloc(loc_expr), allocated_args, CalledVia::Space),
|
||||
Expr::Apply(
|
||||
arena.alloc(loc_expr),
|
||||
allocated_args.into_bump_slice(),
|
||||
CalledVia::Space,
|
||||
),
|
||||
state,
|
||||
))
|
||||
}
|
||||
|
@ -1638,7 +1658,7 @@ pub fn list_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
|||
allocated.push(&*arena.alloc(parsed_elem));
|
||||
}
|
||||
|
||||
Expr::List(allocated)
|
||||
Expr::List(allocated.into_bump_slice())
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
@ -1663,7 +1683,7 @@ pub fn record_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
|||
// This is a record literal, not a destructure.
|
||||
let mut value = Expr::Record {
|
||||
update: opt_update.map(|loc_expr| &*arena.alloc(loc_expr)),
|
||||
fields: loc_assigned_fields.value,
|
||||
fields: loc_assigned_fields.value.into_bump_slice(),
|
||||
};
|
||||
|
||||
// there can be field access, e.g. `{ x : 4 }.x`
|
||||
|
|
|
@ -54,8 +54,8 @@ mod test_parse {
|
|||
fn assert_segments<E: Fn(&Bump) -> Vec<'_, ast::StrSegment<'_>>>(input: &str, to_expected: E) {
|
||||
let arena = Bump::new();
|
||||
let actual = parse_with(&arena, arena.alloc(input));
|
||||
let expected_slice = to_expected(&arena).into_bump_slice();
|
||||
let expected_expr = Expr::Str(Line(expected_slice));
|
||||
let expected_slice = to_expected(&arena);
|
||||
let expected_expr = Expr::Str(Line(&expected_slice));
|
||||
|
||||
assert_eq!(Ok(expected_expr), actual);
|
||||
}
|
||||
|
@ -78,8 +78,8 @@ mod test_parse {
|
|||
("\\\"", EscapedChar::Quote),
|
||||
] {
|
||||
let actual = parse_with(&arena, arena.alloc(to_input(string)));
|
||||
let expected_slice = to_expected(*escaped, &arena).into_bump_slice();
|
||||
let expected_expr = Expr::Str(Line(expected_slice));
|
||||
let expected_slice = to_expected(*escaped, &arena);
|
||||
let expected_expr = Expr::Str(Line(&expected_slice));
|
||||
|
||||
assert_eq!(Ok(expected_expr), actual);
|
||||
}
|
||||
|
@ -420,7 +420,7 @@ mod test_parse {
|
|||
fn empty_record() {
|
||||
let arena = Bump::new();
|
||||
let expected = Record {
|
||||
fields: Vec::new_in(&arena),
|
||||
fields: &[],
|
||||
update: None,
|
||||
};
|
||||
let actual = parse_with(&arena, "{}");
|
||||
|
@ -441,9 +441,9 @@ mod test_parse {
|
|||
&[],
|
||||
arena.alloc(Located::new(0, 0, 25, 26, Num("0"))),
|
||||
);
|
||||
let fields = bumpalo::vec![in &arena;
|
||||
let fields = &[
|
||||
Located::new(0, 0, 16, 20, label1),
|
||||
Located::new(0, 0, 22, 26, label2)
|
||||
Located::new(0, 0, 22, 26, label2),
|
||||
];
|
||||
let var = Var {
|
||||
module_name: "Foo.Bar",
|
||||
|
@ -565,10 +565,7 @@ mod test_parse {
|
|||
#[test]
|
||||
fn newline_before_add() {
|
||||
let arena = Bump::new();
|
||||
let spaced_int = Expr::SpaceAfter(
|
||||
arena.alloc(Num("3")),
|
||||
bumpalo::vec![in &arena; Newline].into_bump_slice(),
|
||||
);
|
||||
let spaced_int = Expr::SpaceAfter(arena.alloc(Num("3")), &[Newline]);
|
||||
let tuple = arena.alloc((
|
||||
Located::new(0, 0, 0, 1, spaced_int),
|
||||
Located::new(1, 1, 0, 1, Plus),
|
||||
|
@ -583,10 +580,7 @@ mod test_parse {
|
|||
#[test]
|
||||
fn newline_before_sub() {
|
||||
let arena = Bump::new();
|
||||
let spaced_int = Expr::SpaceAfter(
|
||||
arena.alloc(Num("3")),
|
||||
bumpalo::vec![in &arena; Newline].into_bump_slice(),
|
||||
);
|
||||
let spaced_int = Expr::SpaceAfter(arena.alloc(Num("3")), &[Newline]);
|
||||
let tuple = arena.alloc((
|
||||
Located::new(0, 0, 0, 1, spaced_int),
|
||||
Located::new(1, 1, 0, 1, Minus),
|
||||
|
@ -601,9 +595,7 @@ mod test_parse {
|
|||
#[test]
|
||||
fn newline_after_mul() {
|
||||
let arena = Bump::new();
|
||||
let spaced_int = arena
|
||||
.alloc(Num("4"))
|
||||
.before(bumpalo::vec![in &arena; Newline].into_bump_slice());
|
||||
let spaced_int = arena.alloc(Num("4")).before(&[Newline]);
|
||||
let tuple = arena.alloc((
|
||||
Located::new(0, 0, 0, 1, Num("3")),
|
||||
Located::new(0, 0, 3, 4, Star),
|
||||
|
@ -618,9 +610,7 @@ mod test_parse {
|
|||
#[test]
|
||||
fn newline_after_sub() {
|
||||
let arena = Bump::new();
|
||||
let spaced_int = arena
|
||||
.alloc(Num("4"))
|
||||
.before(bumpalo::vec![in &arena; Newline].into_bump_slice());
|
||||
let spaced_int = arena.alloc(Num("4")).before(&[Newline]);
|
||||
let tuple = arena.alloc((
|
||||
Located::new(0, 0, 0, 1, Num("3")),
|
||||
Located::new(0, 0, 3, 4, Minus),
|
||||
|
@ -635,9 +625,7 @@ mod test_parse {
|
|||
#[test]
|
||||
fn comment_with_non_ascii() {
|
||||
let arena = Bump::new();
|
||||
let spaced_int = arena
|
||||
.alloc(Num("3"))
|
||||
.after(bumpalo::vec![in &arena; LineComment(" 2 × 2")].into_bump_slice());
|
||||
let spaced_int = arena.alloc(Num("3")).after(&[LineComment(" 2 × 2")]);
|
||||
let tuple = arena.alloc((
|
||||
Located::new(0, 0, 0, 1, spaced_int),
|
||||
Located::new(1, 1, 0, 1, Plus),
|
||||
|
@ -652,9 +640,7 @@ mod test_parse {
|
|||
#[test]
|
||||
fn comment_before_op() {
|
||||
let arena = Bump::new();
|
||||
let spaced_int = arena
|
||||
.alloc(Num("3"))
|
||||
.after(bumpalo::vec![in &arena; LineComment(" test!")].into_bump_slice());
|
||||
let spaced_int = arena.alloc(Num("3")).after(&[LineComment(" test!")]);
|
||||
let tuple = arena.alloc((
|
||||
Located::new(0, 0, 0, 1, spaced_int),
|
||||
Located::new(1, 1, 0, 1, Plus),
|
||||
|
@ -669,9 +655,7 @@ mod test_parse {
|
|||
#[test]
|
||||
fn comment_after_op() {
|
||||
let arena = Bump::new();
|
||||
let spaced_int = arena
|
||||
.alloc(Num("92"))
|
||||
.before(bumpalo::vec![in &arena; LineComment(" test!")].into_bump_slice());
|
||||
let spaced_int = arena.alloc(Num("92")).before(&[LineComment(" test!")]);
|
||||
let tuple = arena.alloc((
|
||||
Located::new(0, 0, 0, 2, Num("12")),
|
||||
Located::new(0, 0, 4, 5, Star),
|
||||
|
@ -686,12 +670,8 @@ mod test_parse {
|
|||
#[test]
|
||||
fn ops_with_newlines() {
|
||||
let arena = Bump::new();
|
||||
let spaced_int1 = arena
|
||||
.alloc(Num("3"))
|
||||
.after(bumpalo::vec![in &arena; Newline].into_bump_slice());
|
||||
let spaced_int2 = arena
|
||||
.alloc(Num("4"))
|
||||
.before(bumpalo::vec![in &arena; Newline, Newline].into_bump_slice());
|
||||
let spaced_int1 = arena.alloc(Num("3")).after(&[Newline]);
|
||||
let spaced_int2 = arena.alloc(Num("4")).before(&[Newline, Newline]);
|
||||
let tuple = arena.alloc((
|
||||
Located::new(0, 0, 0, 1, spaced_int1),
|
||||
Located::new(1, 1, 0, 1, Plus),
|
||||
|
@ -881,7 +861,7 @@ mod test_parse {
|
|||
let arena = Bump::new();
|
||||
let arg1 = arena.alloc(Located::new(0, 0, 6, 8, Num("12")));
|
||||
let arg2 = arena.alloc(Located::new(0, 0, 9, 11, Num("34")));
|
||||
let args = bumpalo::vec![in &arena; &*arg1, &*arg2];
|
||||
let args = &[&*arg1, &*arg2];
|
||||
let expected = Expr::Apply(
|
||||
arena.alloc(Located::new(0, 0, 0, 5, Expr::PrivateTag("@Whee"))),
|
||||
args,
|
||||
|
@ -897,7 +877,7 @@ mod test_parse {
|
|||
let arena = Bump::new();
|
||||
let arg1 = arena.alloc(Located::new(0, 0, 5, 7, Num("12")));
|
||||
let arg2 = arena.alloc(Located::new(0, 0, 8, 10, Num("34")));
|
||||
let args = bumpalo::vec![in &arena; &*arg1, &*arg2];
|
||||
let args = &[&*arg1, &*arg2];
|
||||
let expected = Expr::Apply(
|
||||
arena.alloc(Located::new(0, 0, 0, 4, Expr::GlobalTag("Whee"))),
|
||||
args,
|
||||
|
@ -915,7 +895,7 @@ mod test_parse {
|
|||
let int2 = ParensAround(arena.alloc(Num("34")));
|
||||
let arg1 = arena.alloc(Located::new(0, 0, 6, 8, int1));
|
||||
let arg2 = arena.alloc(Located::new(0, 0, 11, 13, int2));
|
||||
let args = bumpalo::vec![in &arena; &*arg1, &*arg2];
|
||||
let args = &[&*arg1, &*arg2];
|
||||
let expected = Expr::Apply(
|
||||
arena.alloc(Located::new(0, 0, 0, 4, Expr::GlobalTag("Whee"))),
|
||||
args,
|
||||
|
@ -949,11 +929,8 @@ mod test_parse {
|
|||
fn tag_pattern() {
|
||||
let arena = Bump::new();
|
||||
let pattern = Located::new(0, 0, 1, 6, Pattern::GlobalTag("Thing"));
|
||||
let patterns = bumpalo::vec![in &arena; pattern];
|
||||
let expected = Closure(
|
||||
arena.alloc(patterns),
|
||||
arena.alloc(Located::new(0, 0, 10, 12, Num("42"))),
|
||||
);
|
||||
let patterns = &[pattern];
|
||||
let expected = Closure(patterns, arena.alloc(Located::new(0, 0, 10, 12, Num("42"))));
|
||||
let actual = parse_with(&arena, "\\Thing -> 42");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
|
@ -973,7 +950,7 @@ mod test_parse {
|
|||
#[test]
|
||||
fn empty_list() {
|
||||
let arena = Bump::new();
|
||||
let elems = Vec::new_in(&arena);
|
||||
let elems = &[];
|
||||
let expected = List(elems);
|
||||
let actual = parse_with(&arena, "[]");
|
||||
|
||||
|
@ -984,7 +961,7 @@ mod test_parse {
|
|||
fn spaces_inside_empty_list() {
|
||||
// This is a regression test!
|
||||
let arena = Bump::new();
|
||||
let elems = Vec::new_in(&arena);
|
||||
let elems = &[];
|
||||
let expected = List(elems);
|
||||
let actual = parse_with(&arena, "[ ]");
|
||||
|
||||
|
@ -994,7 +971,7 @@ mod test_parse {
|
|||
#[test]
|
||||
fn packed_singleton_list() {
|
||||
let arena = Bump::new();
|
||||
let elems = bumpalo::vec![in &arena; &*arena.alloc(Located::new(0, 0, 1, 2, Num("1")))];
|
||||
let elems = &[&*arena.alloc(Located::new(0, 0, 1, 2, Num("1")))];
|
||||
let expected = List(elems);
|
||||
let actual = parse_with(&arena, "[1]");
|
||||
|
||||
|
@ -1004,7 +981,7 @@ mod test_parse {
|
|||
#[test]
|
||||
fn spaced_singleton_list() {
|
||||
let arena = Bump::new();
|
||||
let elems = bumpalo::vec![in &arena; &*arena.alloc(Located::new(0, 0, 2, 3, Num("1")))];
|
||||
let elems = &[&*arena.alloc(Located::new(0, 0, 2, 3, Num("1")))];
|
||||
let expected = List(elems);
|
||||
let actual = parse_with(&arena, "[ 1 ]");
|
||||
|
||||
|
@ -1090,7 +1067,7 @@ mod test_parse {
|
|||
fn basic_apply() {
|
||||
let arena = Bump::new();
|
||||
let arg = arena.alloc(Located::new(0, 0, 5, 6, Num("1")));
|
||||
let args = bumpalo::vec![in &arena; &*arg];
|
||||
let args = &[&*arg];
|
||||
let expr = Var {
|
||||
module_name: "",
|
||||
ident: "whee",
|
||||
|
@ -1110,7 +1087,7 @@ mod test_parse {
|
|||
let arena = Bump::new();
|
||||
let arg1 = arena.alloc(Located::new(0, 0, 6, 8, Num("12")));
|
||||
let arg2 = arena.alloc(Located::new(0, 0, 10, 12, Num("34")));
|
||||
let args = bumpalo::vec![in &arena; &*arg1, &*arg2];
|
||||
let args = &[&*arg1, &*arg2];
|
||||
let expected = Expr::Apply(
|
||||
arena.alloc(Located::new(
|
||||
0,
|
||||
|
@ -1163,7 +1140,7 @@ mod test_parse {
|
|||
ident: "d",
|
||||
},
|
||||
));
|
||||
let args = bumpalo::vec![in &arena; &*arg1, &*arg2, &*arg3];
|
||||
let args = &[&*arg1, &*arg2, &*arg3];
|
||||
let expected = Expr::Apply(
|
||||
arena.alloc(Located::new(
|
||||
0,
|
||||
|
@ -1187,7 +1164,7 @@ mod test_parse {
|
|||
fn parenthetical_apply() {
|
||||
let arena = Bump::new();
|
||||
let arg = arena.alloc(Located::new(0, 0, 7, 8, Num("1")));
|
||||
let args = bumpalo::vec![in &arena; &*arg];
|
||||
let args = &[&*arg];
|
||||
let parens_var = Expr::ParensAround(arena.alloc(Var {
|
||||
module_name: "",
|
||||
ident: "whee",
|
||||
|
@ -1249,7 +1226,7 @@ mod test_parse {
|
|||
ident: "foo",
|
||||
},
|
||||
));
|
||||
let args = bumpalo::vec![in &arena; &*arg1, &*arg2];
|
||||
let args = &[&*arg1, &*arg2];
|
||||
let apply_expr = Expr::Apply(
|
||||
arena.alloc(Located::new(
|
||||
0,
|
||||
|
@ -1285,7 +1262,7 @@ mod test_parse {
|
|||
ident: "foo",
|
||||
},
|
||||
));
|
||||
let args = bumpalo::vec![in &arena; &*arg1, &*arg2];
|
||||
let args = &[&*arg1, &*arg2];
|
||||
let apply_expr = Expr::Apply(
|
||||
arena.alloc(Located::new(
|
||||
0,
|
||||
|
@ -1321,7 +1298,7 @@ mod test_parse {
|
|||
ident: "foo",
|
||||
},
|
||||
));
|
||||
let args = bumpalo::vec![in &arena; &*arg1, &*arg2];
|
||||
let args = &[&*arg1, &*arg2];
|
||||
let apply_expr = Expr::ParensAround(arena.alloc(Expr::Apply(
|
||||
arena.alloc(Located::new(
|
||||
0,
|
||||
|
@ -1357,7 +1334,7 @@ mod test_parse {
|
|||
ident: "foo",
|
||||
},
|
||||
));
|
||||
let args = bumpalo::vec![in &arena; &*arg1, &*arg2];
|
||||
let args = &[&*arg1, &*arg2];
|
||||
let apply_expr = Expr::ParensAround(arena.alloc(Expr::Apply(
|
||||
arena.alloc(Located::new(
|
||||
0,
|
||||
|
@ -1390,7 +1367,7 @@ mod test_parse {
|
|||
let loc_arg1_expr = Located::new(0, 0, 10, 13, var1);
|
||||
let arg_op = UnaryOp(arena.alloc(loc_arg1_expr), loc_op);
|
||||
let arg2 = arena.alloc(Located::new(0, 0, 9, 13, arg_op));
|
||||
let args = bumpalo::vec![in &arena; &*arg1, &*arg2];
|
||||
let args = &[&*arg1, &*arg2];
|
||||
let var2 = Var {
|
||||
module_name: "",
|
||||
ident: "whee",
|
||||
|
@ -1428,11 +1405,8 @@ mod test_parse {
|
|||
fn single_arg_closure() {
|
||||
let arena = Bump::new();
|
||||
let pattern = Located::new(0, 0, 1, 2, Identifier("a"));
|
||||
let patterns = bumpalo::vec![in &arena; pattern];
|
||||
let expected = Closure(
|
||||
arena.alloc(patterns),
|
||||
arena.alloc(Located::new(0, 0, 6, 8, Num("42"))),
|
||||
);
|
||||
let patterns = &[pattern];
|
||||
let expected = Closure(patterns, arena.alloc(Located::new(0, 0, 6, 8, Num("42"))));
|
||||
let actual = parse_with(&arena, "\\a -> 42");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
|
@ -1442,11 +1416,8 @@ mod test_parse {
|
|||
fn single_underscore_closure() {
|
||||
let arena = Bump::new();
|
||||
let pattern = Located::new(0, 0, 1, 2, Underscore);
|
||||
let patterns = bumpalo::vec![in &arena; pattern];
|
||||
let expected = Closure(
|
||||
arena.alloc(patterns),
|
||||
arena.alloc(Located::new(0, 0, 6, 8, Num("42"))),
|
||||
);
|
||||
let patterns = &[pattern];
|
||||
let expected = Closure(patterns, arena.alloc(Located::new(0, 0, 6, 8, Num("42"))));
|
||||
let actual = parse_with(&arena, "\\_ -> 42");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
|
@ -1468,11 +1439,8 @@ mod test_parse {
|
|||
let arena = Bump::new();
|
||||
let arg1 = Located::new(0, 0, 1, 2, Identifier("a"));
|
||||
let arg2 = Located::new(0, 0, 4, 5, Identifier("b"));
|
||||
let patterns = bumpalo::vec![in &arena; arg1, arg2];
|
||||
let expected = Closure(
|
||||
arena.alloc(patterns),
|
||||
arena.alloc(Located::new(0, 0, 9, 11, Num("42"))),
|
||||
);
|
||||
let patterns = &[arg1, arg2];
|
||||
let expected = Closure(patterns, arena.alloc(Located::new(0, 0, 9, 11, Num("42"))));
|
||||
let actual = parse_with(&arena, "\\a, b -> 42");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
|
@ -1520,7 +1488,7 @@ mod test_parse {
|
|||
arena.alloc(Located::new(1, 1, 2, 3, Num("5"))),
|
||||
);
|
||||
let loc_def = &*arena.alloc(Located::new(1, 1, 0, 1, def));
|
||||
let defs = bumpalo::vec![in &arena; loc_def];
|
||||
let defs = &[loc_def];
|
||||
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
|
||||
let loc_ret = Located::new(3, 3, 0, 2, ret);
|
||||
let reset_indentation = bumpalo::vec![in &arena; LineComment(" leading comment")];
|
||||
|
@ -1550,7 +1518,7 @@ mod test_parse {
|
|||
arena.alloc(Located::new(1, 1, 4, 5, Num("5"))),
|
||||
);
|
||||
let loc_def = &*arena.alloc(Located::new(1, 1, 0, 1, def));
|
||||
let defs = bumpalo::vec![in &arena; loc_def];
|
||||
let defs = &[loc_def];
|
||||
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
|
||||
let loc_ret = Located::new(3, 3, 0, 2, ret);
|
||||
let reset_indentation = bumpalo::vec![in &arena; LineComment(" leading comment")];
|
||||
|
@ -1589,7 +1557,7 @@ mod test_parse {
|
|||
newline.into_bump_slice(),
|
||||
);
|
||||
let loc_def2 = &*arena.alloc(Located::new(2, 2, 0, 5, def2));
|
||||
let defs = bumpalo::vec![in &arena; loc_def1, loc_def2];
|
||||
let defs = &[loc_def1, loc_def2];
|
||||
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
|
||||
let loc_ret = Located::new(4, 4, 0, 2, ret);
|
||||
let reset_indentation = bumpalo::vec![in &arena; LineComment(" leading comment")];
|
||||
|
@ -1621,13 +1589,7 @@ mod test_parse {
|
|||
Located::new(1, 1, 5, 7, Identifier("y"))
|
||||
];
|
||||
let def1 = Def::Body(
|
||||
arena.alloc(Located::new(
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
8,
|
||||
RecordDestructure(fields.into_bump_slice()),
|
||||
)),
|
||||
arena.alloc(Located::new(1, 1, 1, 8, RecordDestructure(&fields))),
|
||||
arena.alloc(Located::new(1, 1, 11, 12, Num("5"))),
|
||||
);
|
||||
let loc_def1 = &*arena.alloc(Located::new(1, 1, 1, 8, def1));
|
||||
|
@ -1636,16 +1598,16 @@ mod test_parse {
|
|||
arena.alloc(Located::new(2, 2, 0, 1, Identifier("y"))),
|
||||
arena.alloc(Located::new(2, 2, 4, 5, Num("6"))),
|
||||
)),
|
||||
newline.into_bump_slice(),
|
||||
&newline,
|
||||
);
|
||||
let loc_def2 = &*arena.alloc(Located::new(2, 2, 0, 5, def2));
|
||||
let defs = bumpalo::vec![in &arena; loc_def1, loc_def2 ];
|
||||
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
|
||||
let defs = &[loc_def1, loc_def2];
|
||||
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), &newlines);
|
||||
let loc_ret = Located::new(4, 4, 0, 2, ret);
|
||||
let reset_indentation = bumpalo::vec![in &arena; LineComment(" leading comment")];
|
||||
let expected = Expr::SpaceBefore(
|
||||
arena.alloc(Defs(defs, arena.alloc(loc_ret))),
|
||||
reset_indentation.into_bump_slice(),
|
||||
&reset_indentation,
|
||||
);
|
||||
|
||||
assert_parses_to(
|
||||
|
@ -1675,12 +1637,12 @@ mod test_parse {
|
|||
arena.alloc(Located::new(1, 1, 0, 3, Identifier("foo"))),
|
||||
arena.alloc(Located::new(1, 1, 6, 7, Num("4"))),
|
||||
);
|
||||
let spaced_def = Def::SpaceBefore(arena.alloc(def), newline.into_bump_slice());
|
||||
let spaced_def = Def::SpaceBefore(arena.alloc(def), &newline);
|
||||
let loc_def = &*arena.alloc(Located::new(1, 1, 0, 7, spaced_def));
|
||||
|
||||
let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 3, signature));
|
||||
let defs = bumpalo::vec![in &arena; loc_ann, loc_def];
|
||||
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
|
||||
let defs = &[loc_ann, loc_def];
|
||||
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), &newlines);
|
||||
let loc_ret = Located::new(3, 3, 0, 2, ret);
|
||||
let expected = Defs(defs, arena.alloc(loc_ret));
|
||||
|
||||
|
@ -1719,7 +1681,7 @@ mod test_parse {
|
|||
);
|
||||
|
||||
let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 3, signature));
|
||||
let defs = bumpalo::vec![in &arena; loc_ann];
|
||||
let defs = &[loc_ann];
|
||||
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
|
||||
let loc_ret = Located::new(2, 2, 0, 2, ret);
|
||||
let expected = Defs(defs, arena.alloc(loc_ret));
|
||||
|
@ -1755,7 +1717,7 @@ mod test_parse {
|
|||
};
|
||||
|
||||
let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 4, signature));
|
||||
let defs = bumpalo::vec![in &arena; loc_ann];
|
||||
let defs = &[loc_ann];
|
||||
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
|
||||
let loc_ret = Located::new(2, 2, 0, 2, ret);
|
||||
let expected = Defs(defs, arena.alloc(loc_ret));
|
||||
|
@ -1810,7 +1772,7 @@ mod test_parse {
|
|||
let loc_def = &*arena.alloc(Located::new(1, 1, 0, 17, spaced));
|
||||
|
||||
let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 3, signature));
|
||||
let defs = bumpalo::vec![in &arena; loc_ann, loc_def];
|
||||
let defs = &[loc_ann, loc_def];
|
||||
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
|
||||
let loc_ret = Located::new(3, 3, 0, 2, ret);
|
||||
let expected = Defs(defs, arena.alloc(loc_ret));
|
||||
|
@ -1865,7 +1827,7 @@ mod test_parse {
|
|||
let loc_def = &*arena.alloc(Located::new(1, 1, 0, 10, spaced_def));
|
||||
|
||||
let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 3, signature));
|
||||
let defs = bumpalo::vec![in &arena; loc_ann, loc_def];
|
||||
let defs = &[loc_ann, loc_def];
|
||||
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
|
||||
let loc_ret = Located::new(3, 3, 0, 2, ret);
|
||||
let expected = Defs(defs, arena.alloc(loc_ret));
|
||||
|
@ -1918,7 +1880,7 @@ mod test_parse {
|
|||
let loc_def = &*arena.alloc(Located::new(1, 1, 0, 10, spaced_def));
|
||||
|
||||
let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 3, signature));
|
||||
let defs = bumpalo::vec![in &arena; loc_ann, loc_def];
|
||||
let defs = &[loc_ann, loc_def];
|
||||
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
|
||||
let loc_ret = Located::new(3, 3, 0, 2, ret);
|
||||
let expected = Defs(defs, arena.alloc(loc_ret));
|
||||
|
@ -1972,7 +1934,7 @@ mod test_parse {
|
|||
let loc_def = &*arena.alloc(Located::new(1, 1, 0, 10, spaced_def));
|
||||
|
||||
let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 3, signature));
|
||||
let defs = bumpalo::vec![in &arena; loc_ann, loc_def];
|
||||
let defs = &[loc_ann, loc_def];
|
||||
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
|
||||
let loc_ret = Located::new(3, 3, 0, 2, ret);
|
||||
let expected = Defs(defs, arena.alloc(loc_ret));
|
||||
|
@ -2025,7 +1987,7 @@ mod test_parse {
|
|||
let loc_def = &*arena.alloc(Located::new(1, 1, 0, 10, spaced_def));
|
||||
|
||||
let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 3, signature));
|
||||
let defs = bumpalo::vec![in &arena; loc_ann, loc_def];
|
||||
let defs = &[loc_ann, loc_def];
|
||||
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
|
||||
let loc_ret = Located::new(3, 3, 0, 2, ret);
|
||||
let expected = Defs(defs, arena.alloc(loc_ret));
|
||||
|
@ -2057,24 +2019,21 @@ mod test_parse {
|
|||
let expr1 = Num("1");
|
||||
let loc_expr1 = Located::new(1, 1, 7, 8, expr1);
|
||||
let branch1 = &*arena.alloc(WhenBranch {
|
||||
patterns: bumpalo::vec![in &arena;loc_pattern1],
|
||||
patterns: arena.alloc([loc_pattern1]),
|
||||
value: loc_expr1,
|
||||
guard: None,
|
||||
});
|
||||
let newlines = bumpalo::vec![in &arena; Newline];
|
||||
let pattern2 = Pattern::SpaceBefore(
|
||||
arena.alloc(StrLiteral(PlainLine("mise"))),
|
||||
newlines.into_bump_slice(),
|
||||
);
|
||||
let newlines = &[Newline];
|
||||
let pattern2 = Pattern::SpaceBefore(arena.alloc(StrLiteral(PlainLine("mise"))), newlines);
|
||||
let loc_pattern2 = Located::new(2, 2, 1, 7, pattern2);
|
||||
let expr2 = Num("2");
|
||||
let loc_expr2 = Located::new(2, 2, 11, 12, expr2);
|
||||
let branch2 = &*arena.alloc(WhenBranch {
|
||||
patterns: bumpalo::vec![in &arena;loc_pattern2 ],
|
||||
patterns: arena.alloc([loc_pattern2]),
|
||||
value: loc_expr2,
|
||||
guard: None,
|
||||
});
|
||||
let branches = bumpalo::vec![in &arena; branch1, branch2];
|
||||
let branches = &[branch1, branch2];
|
||||
let var = Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
|
@ -2098,29 +2057,27 @@ mod test_parse {
|
|||
#[test]
|
||||
fn when_with_numbers() {
|
||||
let arena = Bump::new();
|
||||
let newlines = bumpalo::vec![in &arena; Newline];
|
||||
let pattern1 =
|
||||
Pattern::SpaceBefore(arena.alloc(NumLiteral("1")), newlines.into_bump_slice());
|
||||
let newlines = &[Newline];
|
||||
let pattern1 = Pattern::SpaceBefore(arena.alloc(NumLiteral("1")), newlines);
|
||||
let loc_pattern1 = Located::new(1, 1, 1, 2, pattern1);
|
||||
let expr1 = Num("2");
|
||||
let loc_expr1 = Located::new(1, 1, 6, 7, expr1);
|
||||
let branch1 = &*arena.alloc(WhenBranch {
|
||||
patterns: bumpalo::vec![in &arena;loc_pattern1],
|
||||
patterns: arena.alloc([loc_pattern1]),
|
||||
value: loc_expr1,
|
||||
guard: None,
|
||||
});
|
||||
let newlines = bumpalo::vec![in &arena; Newline];
|
||||
let pattern2 =
|
||||
Pattern::SpaceBefore(arena.alloc(NumLiteral("3")), newlines.into_bump_slice());
|
||||
let newlines = &[Newline];
|
||||
let pattern2 = Pattern::SpaceBefore(arena.alloc(NumLiteral("3")), newlines);
|
||||
let loc_pattern2 = Located::new(2, 2, 1, 2, pattern2);
|
||||
let expr2 = Num("4");
|
||||
let loc_expr2 = Located::new(2, 2, 6, 7, expr2);
|
||||
let branch2 = &*arena.alloc(WhenBranch {
|
||||
patterns: bumpalo::vec![in &arena;loc_pattern2],
|
||||
patterns: arena.alloc([loc_pattern2]),
|
||||
value: loc_expr2,
|
||||
guard: None,
|
||||
});
|
||||
let branches = bumpalo::vec![in &arena; branch1, branch2];
|
||||
let branches = &[branch1, branch2];
|
||||
let var = Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
|
@ -2144,35 +2101,32 @@ mod test_parse {
|
|||
#[test]
|
||||
fn when_with_records() {
|
||||
let arena = Bump::new();
|
||||
let newlines = bumpalo::vec![in &arena; Newline];
|
||||
let identifiers1 = bumpalo::vec![in &arena; Located::new(1, 1, 3, 4, Identifier("y")) ];
|
||||
let pattern1 = Pattern::SpaceBefore(
|
||||
arena.alloc(RecordDestructure(identifiers1.into_bump_slice())),
|
||||
newlines.into_bump_slice(),
|
||||
);
|
||||
let newlines = &[Newline];
|
||||
let identifiers1 = &[Located::new(1, 1, 3, 4, Identifier("y"))];
|
||||
let pattern1 = Pattern::SpaceBefore(arena.alloc(RecordDestructure(identifiers1)), newlines);
|
||||
let loc_pattern1 = Located::new(1, 1, 1, 6, pattern1);
|
||||
let expr1 = Num("2");
|
||||
let loc_expr1 = Located::new(1, 1, 10, 11, expr1);
|
||||
let branch1 = &*arena.alloc(WhenBranch {
|
||||
patterns: bumpalo::vec![in &arena;loc_pattern1 ],
|
||||
patterns: arena.alloc([loc_pattern1]),
|
||||
value: loc_expr1,
|
||||
guard: None,
|
||||
});
|
||||
let newlines = bumpalo::vec![in &arena; Newline];
|
||||
let identifiers2 = bumpalo::vec![in &arena; Located::new(2, 2, 3, 4, Identifier("z")), Located::new(2, 2, 6, 7, Identifier("w")) ];
|
||||
let pattern2 = Pattern::SpaceBefore(
|
||||
arena.alloc(RecordDestructure(identifiers2.into_bump_slice())),
|
||||
newlines.into_bump_slice(),
|
||||
);
|
||||
let newlines = &[Newline];
|
||||
let identifiers2 = &[
|
||||
Located::new(2, 2, 3, 4, Identifier("z")),
|
||||
Located::new(2, 2, 6, 7, Identifier("w")),
|
||||
];
|
||||
let pattern2 = Pattern::SpaceBefore(arena.alloc(RecordDestructure(identifiers2)), newlines);
|
||||
let loc_pattern2 = Located::new(2, 2, 1, 9, pattern2);
|
||||
let expr2 = Num("4");
|
||||
let loc_expr2 = Located::new(2, 2, 13, 14, expr2);
|
||||
let branch2 = &*arena.alloc(WhenBranch {
|
||||
patterns: bumpalo::vec![in &arena;loc_pattern2 ],
|
||||
patterns: arena.alloc([loc_pattern2]),
|
||||
value: loc_expr2,
|
||||
guard: None,
|
||||
});
|
||||
let branches = bumpalo::vec![in &arena; branch1, branch2];
|
||||
let branches = &[branch1, branch2];
|
||||
let var = Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
|
@ -2196,41 +2150,33 @@ mod test_parse {
|
|||
#[test]
|
||||
fn when_with_alternative_patterns() {
|
||||
let arena = Bump::new();
|
||||
let newlines = bumpalo::vec![in &arena; Newline];
|
||||
let pattern1 = Pattern::SpaceBefore(
|
||||
arena.alloc(StrLiteral(PlainLine("blah"))),
|
||||
newlines.into_bump_slice(),
|
||||
);
|
||||
let newlines = &[Newline];
|
||||
let pattern1 = Pattern::SpaceBefore(arena.alloc(StrLiteral(PlainLine("blah"))), newlines);
|
||||
let pattern1_alt = StrLiteral(PlainLine("blop"));
|
||||
let loc_pattern1 = Located::new(1, 1, 1, 7, pattern1);
|
||||
let loc_pattern1_alt = Located::new(1, 1, 10, 16, pattern1_alt);
|
||||
let expr1 = Num("1");
|
||||
let loc_expr1 = Located::new(1, 1, 20, 21, expr1);
|
||||
let branch1 = &*arena.alloc(WhenBranch {
|
||||
patterns: bumpalo::vec![in &arena;loc_pattern1, loc_pattern1_alt],
|
||||
patterns: arena.alloc([loc_pattern1, loc_pattern1_alt]),
|
||||
value: loc_expr1,
|
||||
guard: None,
|
||||
});
|
||||
let newlines = bumpalo::vec![in &arena; Newline];
|
||||
let pattern2 = Pattern::SpaceBefore(
|
||||
arena.alloc(StrLiteral(PlainLine("foo"))),
|
||||
newlines.into_bump_slice(),
|
||||
);
|
||||
let newlines = bumpalo::vec![in &arena; Newline];
|
||||
let pattern2_alt = Pattern::SpaceBefore(
|
||||
arena.alloc(StrLiteral(PlainLine("bar"))),
|
||||
newlines.into_bump_slice(),
|
||||
);
|
||||
let newlines = &[Newline];
|
||||
let pattern2 = Pattern::SpaceBefore(arena.alloc(StrLiteral(PlainLine("foo"))), newlines);
|
||||
let newlines = &[Newline];
|
||||
let pattern2_alt =
|
||||
Pattern::SpaceBefore(arena.alloc(StrLiteral(PlainLine("bar"))), newlines);
|
||||
let loc_pattern2 = Located::new(2, 2, 1, 6, pattern2);
|
||||
let loc_pattern2_alt = Located::new(3, 3, 1, 6, pattern2_alt);
|
||||
let expr2 = Num("2");
|
||||
let loc_expr2 = Located::new(3, 3, 10, 11, expr2);
|
||||
let branch2 = &*arena.alloc(WhenBranch {
|
||||
patterns: bumpalo::vec![in &arena;loc_pattern2, loc_pattern2_alt],
|
||||
patterns: arena.alloc([loc_pattern2, loc_pattern2_alt]),
|
||||
value: loc_expr2,
|
||||
guard: None,
|
||||
});
|
||||
let branches = bumpalo::vec![in &arena; branch1, branch2];
|
||||
let branches = &[branch1, branch2];
|
||||
let var = Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
|
@ -2317,9 +2263,9 @@ mod test_parse {
|
|||
use roc_parse::ast::Def::*;
|
||||
|
||||
let arena = Bump::new();
|
||||
let newlines1 = bumpalo::vec![in &arena; Newline, Newline];
|
||||
let newlines2 = bumpalo::vec![in &arena; Newline];
|
||||
let newlines3 = bumpalo::vec![in &arena; Newline];
|
||||
let newlines1 = &[Newline, Newline];
|
||||
let newlines2 = &[Newline];
|
||||
let newlines3 = &[Newline];
|
||||
let pattern1 = Identifier("foo");
|
||||
let pattern2 = Identifier("bar");
|
||||
let pattern3 = Identifier("baz");
|
||||
|
@ -2328,27 +2274,27 @@ mod test_parse {
|
|||
arena.alloc(Located::new(0, 0, 0, 3, pattern1)),
|
||||
arena.alloc(Located::new(0, 0, 6, 7, Num("1"))),
|
||||
)),
|
||||
newlines1.into_bump_slice(),
|
||||
newlines1,
|
||||
);
|
||||
let def2 = SpaceAfter(
|
||||
arena.alloc(Body(
|
||||
arena.alloc(Located::new(2, 2, 0, 3, pattern2)),
|
||||
arena.alloc(Located::new(2, 2, 6, 10, Str(PlainLine("hi")))),
|
||||
)),
|
||||
newlines2.into_bump_slice(),
|
||||
newlines2,
|
||||
);
|
||||
let def3 = SpaceAfter(
|
||||
arena.alloc(Body(
|
||||
arena.alloc(Located::new(3, 3, 0, 3, pattern3)),
|
||||
arena.alloc(Located::new(3, 3, 6, 13, Str(PlainLine("stuff")))),
|
||||
)),
|
||||
newlines3.into_bump_slice(),
|
||||
newlines3,
|
||||
);
|
||||
|
||||
let expected = bumpalo::vec![in &arena;
|
||||
Located::new(0, 0, 0, 7, def1),
|
||||
Located::new(2, 2, 0, 10, def2),
|
||||
Located::new(3, 3, 0, 13, def3)
|
||||
Located::new(3, 3, 0, 13, def3),
|
||||
];
|
||||
let src = indoc!(
|
||||
r#"
|
||||
|
@ -2369,15 +2315,15 @@ mod test_parse {
|
|||
fn newline_after_equals() {
|
||||
// Regression test for https://github.com/rtfeldman/roc/issues/51
|
||||
let arena = Bump::new();
|
||||
let newlines = bumpalo::vec![in &arena; Newline, Newline];
|
||||
let newlines = &[Newline, Newline];
|
||||
let num = arena.alloc(Num("5"));
|
||||
let def = Def::Body(
|
||||
arena.alloc(Located::new(0, 0, 0, 1, Identifier("x"))),
|
||||
arena.alloc(Located::new(1, 1, 4, 5, Expr::SpaceBefore(num, &[Newline]))),
|
||||
);
|
||||
let loc_def = &*arena.alloc(Located::new(0, 0, 0, 1, def));
|
||||
let defs = bumpalo::vec![in &arena; loc_def];
|
||||
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
|
||||
let defs = &[loc_def];
|
||||
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines);
|
||||
let loc_ret = Located::new(3, 3, 0, 2, ret);
|
||||
let expected = Defs(defs, arena.alloc(loc_ret));
|
||||
|
||||
|
@ -2399,24 +2345,24 @@ mod test_parse {
|
|||
#[test]
|
||||
fn basic_docs() {
|
||||
let arena = Bump::new();
|
||||
let newlines = bumpalo::vec![in &arena; Newline, Newline];
|
||||
let newlines = &[Newline, Newline];
|
||||
let def = Def::Body(
|
||||
arena.alloc(Located::new(4, 4, 0, 1, Identifier("x"))),
|
||||
arena.alloc(Located::new(4, 4, 4, 5, Num("5"))),
|
||||
);
|
||||
let loc_def = &*arena.alloc(Located::new(4, 4, 0, 1, def));
|
||||
let defs = bumpalo::vec![in &arena; loc_def];
|
||||
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
|
||||
let defs = &[loc_def];
|
||||
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines);
|
||||
let loc_ret = Located::new(6, 6, 0, 2, ret);
|
||||
let reset_indentation = bumpalo::vec![in &arena;
|
||||
let reset_indentation = &[
|
||||
DocComment("first line of docs"),
|
||||
DocComment(" second line"),
|
||||
DocComment(" third line"),
|
||||
DocComment("fourth line")
|
||||
DocComment("fourth line"),
|
||||
];
|
||||
let expected = Expr::SpaceBefore(
|
||||
arena.alloc(Defs(defs, arena.alloc(loc_ret))),
|
||||
reset_indentation.into_bump_slice(),
|
||||
reset_indentation,
|
||||
);
|
||||
|
||||
assert_parses_to(
|
||||
|
@ -2438,16 +2384,16 @@ mod test_parse {
|
|||
#[test]
|
||||
fn not_docs() {
|
||||
let arena = Bump::new();
|
||||
let newlines = bumpalo::vec![in &arena; Newline, Newline];
|
||||
let newlines = &[Newline, Newline];
|
||||
let def = Def::Body(
|
||||
arena.alloc(Located::new(4, 4, 0, 1, Identifier("x"))),
|
||||
arena.alloc(Located::new(4, 4, 4, 5, Num("5"))),
|
||||
);
|
||||
let loc_def = &*arena.alloc(Located::new(4, 4, 0, 1, def));
|
||||
let defs = bumpalo::vec![in &arena; loc_def];
|
||||
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
|
||||
let defs = &[loc_def];
|
||||
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines);
|
||||
let loc_ret = Located::new(6, 6, 0, 2, ret);
|
||||
let reset_indentation = bumpalo::vec![in &arena;
|
||||
let reset_indentation = &[
|
||||
LineComment("######"),
|
||||
LineComment("## not docs!"),
|
||||
LineComment("#still not docs"),
|
||||
|
@ -2455,7 +2401,7 @@ mod test_parse {
|
|||
];
|
||||
let expected = Expr::SpaceBefore(
|
||||
arena.alloc(Defs(defs, arena.alloc(loc_ret))),
|
||||
reset_indentation.into_bump_slice(),
|
||||
reset_indentation,
|
||||
);
|
||||
|
||||
assert_parses_to(
|
||||
|
@ -2477,16 +2423,16 @@ mod test_parse {
|
|||
#[test]
|
||||
fn mixed_docs() {
|
||||
let arena = Bump::new();
|
||||
let newlines = bumpalo::vec![in &arena; Newline, Newline];
|
||||
let newlines = &[Newline, Newline];
|
||||
let def = Def::Body(
|
||||
arena.alloc(Located::new(4, 4, 0, 1, Identifier("x"))),
|
||||
arena.alloc(Located::new(4, 4, 4, 5, Num("5"))),
|
||||
);
|
||||
let loc_def = &*arena.alloc(Located::new(4, 4, 0, 1, def));
|
||||
let defs = bumpalo::vec![in &arena; loc_def];
|
||||
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
|
||||
let defs = &[loc_def];
|
||||
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines);
|
||||
let loc_ret = Located::new(6, 6, 0, 2, ret);
|
||||
let reset_indentation = bumpalo::vec![in &arena;
|
||||
let reset_indentation = &[
|
||||
LineComment("## not docs!"),
|
||||
DocComment("docs, but with a problem"),
|
||||
DocComment("(namely that this is a mix of docs and regular comments)"),
|
||||
|
@ -2494,7 +2440,7 @@ mod test_parse {
|
|||
];
|
||||
let expected = Expr::SpaceBefore(
|
||||
arena.alloc(Defs(defs, arena.alloc(loc_ret))),
|
||||
reset_indentation.into_bump_slice(),
|
||||
reset_indentation,
|
||||
);
|
||||
|
||||
assert_parses_to(
|
||||
|
@ -2517,30 +2463,27 @@ mod test_parse {
|
|||
fn malformed_pattern_field_access() {
|
||||
// See https://github.com/rtfeldman/roc/issues/399
|
||||
let arena = Bump::new();
|
||||
let newlines = bumpalo::vec![in &arena; Newline];
|
||||
let pattern1 = Pattern::SpaceBefore(
|
||||
arena.alloc(Pattern::Malformed("bar.and")),
|
||||
newlines.into_bump_slice(),
|
||||
);
|
||||
let newlines = &[Newline];
|
||||
let pattern1 = Pattern::SpaceBefore(arena.alloc(Pattern::Malformed("bar.and")), newlines);
|
||||
let loc_pattern1 = Located::new(1, 1, 4, 11, pattern1);
|
||||
let expr1 = Num("1");
|
||||
let loc_expr1 = Located::new(1, 1, 15, 16, expr1);
|
||||
let branch1 = &*arena.alloc(WhenBranch {
|
||||
patterns: bumpalo::vec![in &arena;loc_pattern1],
|
||||
patterns: arena.alloc([loc_pattern1]),
|
||||
value: loc_expr1,
|
||||
guard: None,
|
||||
});
|
||||
let newlines = bumpalo::vec![in &arena; Newline];
|
||||
let pattern2 = Pattern::SpaceBefore(arena.alloc(Underscore), newlines.into_bump_slice());
|
||||
let newlines = &[Newline];
|
||||
let pattern2 = Pattern::SpaceBefore(arena.alloc(Underscore), newlines);
|
||||
let loc_pattern2 = Located::new(2, 2, 4, 5, pattern2);
|
||||
let expr2 = Num("4");
|
||||
let loc_expr2 = Located::new(2, 2, 9, 10, expr2);
|
||||
let branch2 = &*arena.alloc(WhenBranch {
|
||||
patterns: bumpalo::vec![in &arena;loc_pattern2 ],
|
||||
patterns: arena.alloc([loc_pattern2]),
|
||||
value: loc_expr2,
|
||||
guard: None,
|
||||
});
|
||||
let branches = bumpalo::vec![in &arena; branch1, branch2];
|
||||
let branches = &[branch1, branch2];
|
||||
let var = Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
|
@ -2565,30 +2508,27 @@ mod test_parse {
|
|||
fn malformed_pattern_module_name() {
|
||||
// See https://github.com/rtfeldman/roc/issues/399
|
||||
let arena = Bump::new();
|
||||
let newlines = bumpalo::vec![in &arena; Newline];
|
||||
let pattern1 = Pattern::SpaceBefore(
|
||||
arena.alloc(Pattern::Malformed("Foo.and")),
|
||||
newlines.into_bump_slice(),
|
||||
);
|
||||
let newlines = &[Newline];
|
||||
let pattern1 = Pattern::SpaceBefore(arena.alloc(Pattern::Malformed("Foo.and")), newlines);
|
||||
let loc_pattern1 = Located::new(1, 1, 4, 11, pattern1);
|
||||
let expr1 = Num("1");
|
||||
let loc_expr1 = Located::new(1, 1, 15, 16, expr1);
|
||||
let branch1 = &*arena.alloc(WhenBranch {
|
||||
patterns: bumpalo::vec![in &arena;loc_pattern1],
|
||||
patterns: arena.alloc([loc_pattern1]),
|
||||
value: loc_expr1,
|
||||
guard: None,
|
||||
});
|
||||
let newlines = bumpalo::vec![in &arena; Newline];
|
||||
let pattern2 = Pattern::SpaceBefore(arena.alloc(Underscore), newlines.into_bump_slice());
|
||||
let newlines = &[Newline];
|
||||
let pattern2 = Pattern::SpaceBefore(arena.alloc(Underscore), newlines);
|
||||
let loc_pattern2 = Located::new(2, 2, 4, 5, pattern2);
|
||||
let expr2 = Num("4");
|
||||
let loc_expr2 = Located::new(2, 2, 9, 10, expr2);
|
||||
let branch2 = &*arena.alloc(WhenBranch {
|
||||
patterns: bumpalo::vec![in &arena;loc_pattern2 ],
|
||||
patterns: arena.alloc([loc_pattern2]),
|
||||
value: loc_expr2,
|
||||
guard: None,
|
||||
});
|
||||
let branches = bumpalo::vec![in &arena; branch1, branch2];
|
||||
let branches = &[branch1, branch2];
|
||||
let var = Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
|
|
|
@ -25,7 +25,7 @@ roc_solve = { path = "../solve" }
|
|||
pretty_assertions = "0.5.1"
|
||||
maplit = "1.0.1"
|
||||
indoc = "0.3.3"
|
||||
tempfile = "3.0.1"
|
||||
tempfile = "3.1.0"
|
||||
quickcheck = "0.8"
|
||||
quickcheck_macros = "0.8"
|
||||
bumpalo = { version = "3.2", features = ["collections"] }
|
||||
|
|
|
@ -1369,7 +1369,7 @@ mod solve_expr {
|
|||
indoc!(
|
||||
r#"
|
||||
int : Num Integer
|
||||
int = 5.5
|
||||
int = 5
|
||||
|
||||
int
|
||||
"#
|
||||
|
@ -1384,7 +1384,7 @@ mod solve_expr {
|
|||
indoc!(
|
||||
r#"
|
||||
int : Num.Num Num.Integer
|
||||
int = 5.5
|
||||
int = 5
|
||||
|
||||
int
|
||||
"#
|
||||
|
@ -2039,16 +2039,17 @@ mod solve_expr {
|
|||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
|
||||
Peano : [ S Peano, Z ]
|
||||
|
||||
map : Peano -> Peano
|
||||
map = \peano ->
|
||||
when peano is
|
||||
Z -> Z
|
||||
S rest ->
|
||||
map rest |> S
|
||||
|
||||
S rest -> S (map rest)
|
||||
|
||||
main =
|
||||
map
|
||||
"#
|
||||
),
|
||||
|
|
|
@ -328,11 +328,7 @@ impl Subs {
|
|||
let l_key = self.utable.get_root_key(key);
|
||||
|
||||
self.utable.update_value(l_key, |node| {
|
||||
let mut new_desc = node.value.clone();
|
||||
|
||||
new_desc.rank = rank;
|
||||
|
||||
node.value = new_desc;
|
||||
node.value.rank = rank;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -340,11 +336,7 @@ impl Subs {
|
|||
let l_key = self.utable.get_root_key(key);
|
||||
|
||||
self.utable.update_value(l_key, |node| {
|
||||
let mut new_desc = node.value.clone();
|
||||
|
||||
new_desc.mark = mark;
|
||||
|
||||
node.value = new_desc;
|
||||
node.value.mark = mark;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -352,11 +344,7 @@ impl Subs {
|
|||
let l_key = self.utable.get_root_key(key);
|
||||
|
||||
self.utable.update_value(l_key, |node| {
|
||||
let mut new_desc = node.value.clone();
|
||||
|
||||
new_desc.content = content;
|
||||
|
||||
node.value = new_desc;
|
||||
node.value.content = content;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
36
nix/zig.nix
Normal file
36
nix/zig.nix
Normal file
|
@ -0,0 +1,36 @@
|
|||
{ pkgs, isMacOS }:
|
||||
|
||||
# We require at least specific commit of Zig after the latest tagged
|
||||
# release (0.6.0), so we just download the binaries for that commit
|
||||
|
||||
let
|
||||
version = "0.6.0+0088efc4b";
|
||||
osName =
|
||||
if isMacOS
|
||||
then "macos"
|
||||
else "linux";
|
||||
archiveName = "zig-${osName}-x86_64-${version}";
|
||||
sha256 =
|
||||
if isMacOS
|
||||
then "665c1a7f472cfc5e0715f0ddf6ff8409fb749ac91cbbae68c443b4a37ebd058e"
|
||||
else "bab70ae3bd0af538022bc3ef50d8f34fa8dceac39ba7d9e5d528eee7e6d5a1cf";
|
||||
in
|
||||
pkgs.stdenv.mkDerivation {
|
||||
pname = "zig";
|
||||
version = version;
|
||||
buildInputs = [ pkgs.gzip ];
|
||||
src = pkgs.fetchurl {
|
||||
inherit sha256;
|
||||
name = "${archiveName}.tar.xz";
|
||||
url = "https://ziglang.org/builds/${archiveName}.tar.xz";
|
||||
};
|
||||
phases = [ "installPhase" ];
|
||||
installPhase = ''
|
||||
mkdir -p $out/bin
|
||||
tar -xf $src
|
||||
cp ${archiveName}/zig $out/zig
|
||||
cp -r ${archiveName}/lib $out/lib
|
||||
ln -s "$out/zig" "$out/bin/zig"
|
||||
chmod +x $out/bin/zig
|
||||
'';
|
||||
}
|
28
nix/zls.nix
Normal file
28
nix/zls.nix
Normal file
|
@ -0,0 +1,28 @@
|
|||
{ pkgs, zig }:
|
||||
|
||||
# As of 2020-10-25, building zls is not available on Nix. For some reason,
|
||||
# this hangs on `zig build`. I'll try to figure it our later.
|
||||
|
||||
let
|
||||
rev = "e8c20351d85da8eb4bf22480045b994007284d69";
|
||||
in
|
||||
pkgs.stdenv.mkDerivation {
|
||||
pname = "zig-language-server";
|
||||
version = rev;
|
||||
src = pkgs.fetchgit {
|
||||
inherit rev;
|
||||
fetchSubmodules = true;
|
||||
url = "https://github.com/zigtools/zls.git";
|
||||
sha256 = "06g8gml1g0fmvcfysy93bd1hb64vjd2v12x3kgxz58kmk5z0168y";
|
||||
};
|
||||
phases = [ "buildPhase" "installPhase" ];
|
||||
buildInputs = [ zig ];
|
||||
buildPhase = ''
|
||||
zig build
|
||||
'';
|
||||
installPhase = ''
|
||||
mkdir -p $out/bin
|
||||
cp ./zig-cache/bin/zls $out/bin/zls
|
||||
chmod +x $out/bin/zls
|
||||
'';
|
||||
}
|
28
shell.nix
28
shell.nix
|
@ -1,17 +1,19 @@
|
|||
let
|
||||
{ }:
|
||||
|
||||
with {
|
||||
# Look here for information about how pin version of nixpkgs
|
||||
# → https://nixos.wiki/wiki/FAQ/Pinning_Nixpkgs
|
||||
pinnedPkgs = import (builtins.fetchGit {
|
||||
name = "nixpkgs-20.03";
|
||||
url = "https://github.com/nixos/nixpkgs/";
|
||||
ref = "refs/heads/release-20.03";
|
||||
pkgs = import (builtins.fetchGit {
|
||||
name = "nixpkgs-2020-10-24";
|
||||
url = "https://github.com/nixos/nixpkgs-channels/";
|
||||
ref = "refs/heads/nixpkgs-unstable";
|
||||
rev = "502845c3e31ef3de0e424f3fcb09217df2ce6df6";
|
||||
}) { };
|
||||
|
||||
# This allows overriding pkgs by passing `--arg pkgs ...`
|
||||
in { pkgs ? pinnedPkgs }:
|
||||
isMacOS = builtins.currentSystem == "x86_64-darwin";
|
||||
};
|
||||
|
||||
let
|
||||
isMacOS = builtins.currentSystem == "x86_64-darwin";
|
||||
darwin-frameworks =
|
||||
if isMacOS then
|
||||
with pkgs.darwin.apple_sdk.frameworks; [
|
||||
|
@ -27,17 +29,25 @@ let
|
|||
[ ];
|
||||
llvm = pkgs.llvm_10;
|
||||
lld = pkgs.lld_10; # this should match llvm's version
|
||||
zig = import ./nix/zig.nix { inherit pkgs isMacOS; };
|
||||
inputs =
|
||||
[
|
||||
# build libraries
|
||||
pkgs.rustup
|
||||
pkgs.cargo
|
||||
llvm
|
||||
# libraries for llvm
|
||||
pkgs.valgrind
|
||||
zig
|
||||
# llb deps
|
||||
pkgs.libffi
|
||||
pkgs.libxml2
|
||||
pkgs.zlib
|
||||
# faster builds - see https://github.com/rtfeldman/roc/blob/trunk/BUILDING_FROM_SOURCE.md#use-lld-for-the-linker
|
||||
lld
|
||||
# dev tools
|
||||
pkgs.rust-analyzer
|
||||
# (import ./nix/zls.nix { inherit pkgs zig; })
|
||||
pkgs.ccls
|
||||
];
|
||||
in pkgs.mkShell {
|
||||
buildInputs = inputs ++ darwin-frameworks;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue