Merge branch 'trunk' into fix/docs-tests

This commit is contained in:
Pablo Hirafuji 2020-10-30 06:54:19 -03:00
commit 29f9364696
39 changed files with 1072 additions and 477 deletions

View file

@ -56,6 +56,13 @@ jobs:
with: with:
command: test command: test
- uses: actions-rs/cargo@v1
name: cargo test -- --ignored
continue-on-error: true
with:
command: test
args: -- --ignored
- uses: actions-rs/cargo@v1 - uses: actions-rs/cargo@v1
name: cargo test --release name: cargo test --release
with: with:

View file

@ -1,21 +1,37 @@
# Building the Roc compiler from source # 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: To build the compiler, you need these installed:
* `libunwind` (macOS should already have this one installed) * `libunwind` (macOS should already have this one installed)
* `libc++-dev` * `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: 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 ### libunwind & libc++-dev
should already have `libunwind`, but other systems will need to install it
(e.g. with `sudo apt-get install libunwind-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. 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.

23
Cargo.lock generated
View file

@ -2320,6 +2320,7 @@ dependencies = [
"roc_uniq", "roc_uniq",
"serde", "serde",
"serde-xml-rs", "serde-xml-rs",
"serial_test",
"strip-ansi-escapes", "strip-ansi-escapes",
"target-lexicon", "target-lexicon",
"tempfile", "tempfile",
@ -2806,6 +2807,28 @@ dependencies = [
"serde", "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]] [[package]]
name = "sha-1" name = "sha-1"
version = "0.8.2" version = "0.8.2"

View file

@ -60,3 +60,8 @@ wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
add-apt-repository "${REPO_NAME}" add-apt-repository "${REPO_NAME}"
apt-get update 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 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

View file

@ -89,4 +89,5 @@ quickcheck_macros = "0.8"
strip-ansi-escapes = "0.1" strip-ansi-escapes = "0.1"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde-xml-rs = "0.4" serde-xml-rs = "0.4"
serial_test = "0.5"
tempfile = "3.1.0" tempfile = "3.1.0"

View file

@ -79,9 +79,7 @@ fn jit_to_ast_help<'a>(
}) })
} }
Layout::Builtin(Builtin::EmptyList) => { Layout::Builtin(Builtin::EmptyList) => {
run_jit_function!(lib, main_fn_name, &'static str, |_| { run_jit_function!(lib, main_fn_name, &'static str, |_| { Expr::List(&[]) })
Expr::List(Vec::new_in(env.arena))
})
} }
Layout::Builtin(Builtin::List(_, elem_layout)) => run_jit_function!( Layout::Builtin(Builtin::List(_, elem_layout)) => run_jit_function!(
lib, lib,
@ -191,7 +189,7 @@ fn ptr_to_ast<'a>(
num_to_ast(env, f64_to_ast(env.arena, num), content) 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)) => { Layout::Builtin(Builtin::List(_, elem_layout)) => {
// Turn the (ptr, len) wrapper struct into actual ptr and len values. // 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) }; let len = unsafe { *(ptr.offset(env.ptr_bytes as isize) as *const usize) };
@ -263,6 +261,8 @@ fn list_to_ast<'a>(
output.push(loc_expr); output.push(loc_expr);
} }
let output = output.into_bump_slice();
Expr::List(output) Expr::List(output)
} }
@ -314,6 +314,8 @@ fn struct_to_ast<'a>(
field_ptr = unsafe { field_ptr.offset(field_layout.stack_size(env.ptr_bytes) as isize) }; field_ptr = unsafe { field_ptr.offset(field_layout.stack_size(env.ptr_bytes) as isize) };
} }
let output = output.into_bump_slice();
Expr::Record { Expr::Record {
update: None, update: None,
fields: output, fields: output,
@ -362,7 +364,7 @@ fn num_to_ast<'a>(env: &Env<'a, '_>, num_expr: Expr<'a>, content: &Content) -> E
Expr::Record { Expr::Record {
update: None, update: None,
fields: bumpalo::vec![in arena; loc_assigned_field], fields: arena.alloc([loc_assigned_field]),
} }
} }
FlatType::TagUnion(tags, _) => { FlatType::TagUnion(tags, _) => {
@ -404,7 +406,7 @@ fn num_to_ast<'a>(env: &Env<'a, '_>, num_expr: Expr<'a>, content: &Content) -> E
region: Region::zero(), region: Region::zero(),
}); });
bumpalo::vec![in arena; loc_payload] arena.alloc([loc_payload])
}; };
Expr::Apply(loc_tag_expr, payload, CalledVia::Space) Expr::Apply(loc_tag_expr, payload, CalledVia::Space)

View file

@ -12,123 +12,174 @@ mod helpers;
#[cfg(test)] #[cfg(test)]
mod cli_run { mod cli_run {
use crate::helpers::{ use crate::helpers::{
example_file, extract_valgrind_errors, run_cmd, run_roc, run_with_valgrind, Out, example_file, extract_valgrind_errors, run_cmd, run_roc, run_with_valgrind,
}; };
use serial_test::serial;
#[test] fn check_output(
fn run_hello_world() { folder: &str,
fn check_hello_world_output(out: Out) { file: &str,
if !out.stderr.is_empty() { flags: &[&str],
panic!(out.stderr); expected_ending: &str,
} use_valgrind: bool,
assert!(out.status.success()); ) {
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) = let (valgrind_out, raw_xml) =
run_with_valgrind(&[example_file("hello-world", "app").to_str().unwrap()]); run_with_valgrind(&[example_file(folder, "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
);
}
let memory_errors = extract_valgrind_errors(&raw_xml); let memory_errors = extract_valgrind_errors(&raw_xml);
if !memory_errors.is_empty() { if !memory_errors.is_empty() {
panic!("{:?}", memory_errors); panic!("{:?}", memory_errors);
} }
assert!(valgrind_out.status.success()); 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
);
} }
check_hello_world_output(run_roc(&[ assert!(out.status.success());
"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] #[test]
#[serial(hello_world)]
fn run_hello_world() {
check_output(
"hello-world",
"Hello.roc",
&[],
"Hello, World!!!!!!!!!!!!!\n",
true,
);
}
#[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 run_quicksort() {
fn check_quicksort_output(out: Out) { check_output(
if !out.stderr.is_empty() { "quicksort",
panic!(out.stderr); "Quicksort.roc",
} &[],
assert!(out.status.success()); "[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
false,
// let (valgrind_out, raw_xml) = );
// run_with_valgrind(&[example_file("quicksort", "app").to_str().unwrap()]);
let valgrind_out = run_cmd(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
);
}
// 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] #[test]
#[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,
);
}
#[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,
);
}
#[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() { fn run_multi_module() {
fn check_muti_module_output(out: Out) { check_output(
if !out.stderr.is_empty() { "multi-module",
panic!(out.stderr); "Quicksort.roc",
} &[],
assert!(out.status.success()); "[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
false,
);
}
// let (valgrind_out, raw_xml) = #[test]
// run_with_valgrind(&[example_file("multi-module", "app").to_str().unwrap()]); #[serial(multi_module)]
let valgrind_out = run_cmd(example_file("multi-module", "app").to_str().unwrap(), &[]); fn run_multi_module_optimized() {
let ending = "[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n"; check_output(
if !&valgrind_out.stdout.ends_with(ending) { "multi-module",
panic!( "Quicksort.roc",
"expected output to end with {:?} but instead got {:?}", &["--optimize"],
ending, &valgrind_out.stdout "[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. #[test]
/* #[serial(multi_module)]
check_muti_module_output(run_roc(&[ // TODO: Stop ignoring this test once we are correctly freeing the RocList even when in dev build.
"run", #[ignore]
example_file("multi-module", "Quicksort.roc") fn run_multi_module_valgrind() {
.to_str() check_output(
.unwrap(), "multi-module",
])); "Quicksort.roc",
*/ &[],
check_muti_module_output(run_roc(&[ "[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
"run", true,
example_file("multi-module", "Quicksort.roc") );
.to_str() }
.unwrap(),
"--optimize", #[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,
);
} }
} }

View file

@ -15,6 +15,7 @@ use std::path::PathBuf;
use std::process::{Command, ExitStatus, Stdio}; use std::process::{Command, ExitStatus, Stdio};
use tempfile::NamedTempFile; use tempfile::NamedTempFile;
#[derive(Debug)]
pub struct Out { pub struct Out {
pub stdout: String, pub stdout: String,
pub stderr: String, pub stderr: String,
@ -131,6 +132,9 @@ enum ValgrindField {
Args(ValgrindDummyStruct), Args(ValgrindDummyStruct),
Error(ValgrindError), Error(ValgrindError),
Status(ValgrindDummyStruct), Status(ValgrindDummyStruct),
Stack(ValgrindDummyStruct),
#[serde(rename = "fatal_signal")]
FatalSignal(ValgrindDummyStruct),
ErrorCounts(ValgrindDummyStruct), ErrorCounts(ValgrindDummyStruct),
SuppCounts(ValgrindDummyStruct), SuppCounts(ValgrindDummyStruct),
} }

View file

@ -68,7 +68,7 @@ pub fn target_machine(
Target::from_name(arch).unwrap().create_target_machine( Target::from_name(arch).unwrap().create_target_machine(
&TargetTriple::create(target_triple_str(target)), &TargetTriple::create(target_triple_str(target)),
arch, 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, opt,
reloc, reloc,
model, model,

View file

@ -1 +1,2 @@
lib.ll zig-cache
src/zig-cache

View file

@ -5,24 +5,19 @@ When their implementations are simple enough (e.g. addition), they
can be implemented directly in Inkwell. can be implemented directly in Inkwell.
When their implementations are complex enough, it's nicer to When their implementations are complex enough, it's nicer to
implement them in a higher-level language like C (or eventually Zig), implement them in a higher-level language like Zig, then compile
compile the result to LLVM bitcode, and import that bitcode into the compiler. the result to LLVM bitcode, and import that bitcode into the compiler.
Compiling the bitcode happens automatically in a Rust build script at `compiler/gen/build.rs`. Compiling the bitcode happens automatically in a Rust build script at `compiler/builtins/build.rs`.
You can find the compiled bitcode in `target/debug/build/roc_gen-[some random characters]/out/builtins.bc`. Then `builtins/src/bitcode/rs` staticlly imports the compiled bitcode for use in the compiler.
> If you want to take a look at the human-readable LLVM IR, cd into `compiler/builtins/bitcode` and You can find the compiled bitcode in `target/debug/build/roc_builtins-[some random characters]/out/builtins.bc`.
> run the following command. It should create `compiler/builtins/bitcode/lib.ll` There will be two directories like `roc_builtins-[some random characters]`, look for the one that has an
> `out` directory as a child.
> ```bash
> clang -S -emit-llvm src/lib.c
> ```
The bitcode is a bunch of bytes that aren't particularly human-readable. > 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 > If you want to take a look at the human-readable LLVM IR, look at
need to be included in the raw source somewhere. > `target/debug/build/roc_builtins-[some random characters]/out/builtins.ll`
The `llvm/src/build.rs` file statically imports these raw bytes.
## Calling bitcode functions ## Calling bitcode functions

View file

@ -1,33 +0,0 @@
#include <math.h>
#include <stdbool.h>
// float -> f32
// double -> f64
// int -> i16
// long int -> i32
// long long -> i64
bool is_finite_(double num) { return isfinite(num); }
double atan_(double num) { return atan(num); }
long long pow_int_(long long base, long long exp) {
int 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;
}
return acc;
}

View file

@ -0,0 +1,374 @@
const std = @import("std");
const math = std.math;
const expect = std.testing.expect;
const roc_builtins_namespace = "roc_builtins";
const math_namespace = roc_builtins_namespace ++ ".math";
const str_namespace = roc_builtins_namespace ++ ".str";
comptime { @export(atan, .{ .name = math_namespace ++ ".atan", .linkage = .Strong }); }
fn atan(num: f64) callconv(.C) f64 {
return math.atan(num);
}
comptime { @export(isFinite, .{ .name = math_namespace ++ ".is_finite", .linkage = .Strong }); }
fn isFinite(num: f64) callconv(.C) bool {
return math.isFinite(num);
}
comptime { @export(powInt, .{ .name = math_namespace ++ ".pow_int", .linkage = .Strong }); }
fn powInt(base: i64, exp: i64) callconv(.C) i64 {
return math.pow(i64, base, exp);
}
// Str.split
const RocStr = struct {
str_bytes_ptrs: [*]u8,
str_len: usize,
pub fn init(bytes: [*]u8, len: usize) RocStr {
return RocStr {
.str_bytes_ptrs = bytes,
.str_len = len
};
}
pub fn eq(self: RocStr, other: RocStr) bool {
if (self.str_len != other.str_len) {
return false;
}
var areEq: bool = true;
var index: usize = 0;
while (index < self.str_len and areEq) {
areEq = areEq and self.str_bytes_ptrs[index] == other.str_bytes_ptrs[index];
index = index + 1;
}
return areEq;
}
test "RocStr.eq: equal" {
const str1_len = 3;
var str1: [str1_len]u8 = "abc".*;
const str1_ptr: [*]u8 = &str1;
var roc_str1 = RocStr.init(str1_ptr, str1_len);
const str2_len = 3;
var str2: [str2_len]u8 = "abc".*;
const str2_ptr: [*]u8 = &str2;
var roc_str2 = RocStr.init(str2_ptr, str2_len);
expect(RocStr.eq(roc_str1, roc_str2));
}
test "RocStr.eq: not equal different length" {
const str1_len = 4;
var str1: [str1_len]u8 = "abcd".*;
const str1_ptr: [*]u8 = &str1;
var roc_str1 = RocStr.init(str1_ptr, str1_len);
const str2_len = 3;
var str2: [str2_len]u8 = "abc".*;
const str2_ptr: [*]u8 = &str2;
var roc_str2 = RocStr.init(str2_ptr, str2_len);
expect(!RocStr.eq(roc_str1, roc_str2));
}
test "RocStr.eq: not equal same length" {
const str1_len = 3;
var str1: [str1_len]u8 = "acb".*;
const str1_ptr: [*]u8 = &str1;
var roc_str1 = RocStr.init(str1_ptr, str1_len);
const str2_len = 3;
var str2: [str2_len]u8 = "abc".*;
const str2_ptr: [*]u8 = &str2;
var roc_str2 = RocStr.init(str2_ptr, str2_len);
expect(!RocStr.eq(roc_str1, roc_str2));
}
};
comptime { @export(strSplitInPlace, .{ .name = str_namespace ++ ".str_split_in_place", .linkage = .Strong }); }
fn strSplitInPlace(
array: [*]RocStr,
array_len: usize,
str_bytes_ptrs: [*]u8,
str_len: usize,
delimiter_bytes: [*]u8,
delimiter_len: usize
) callconv(.C) void {
var ret_array_index : usize = 0;
var sliceStart_index : usize = 0;
var str_index : usize = 0;
if (str_len > delimiter_len) {
const end_index : usize = str_len - delimiter_len;
while (str_index <= end_index) {
var delimiter_index : usize = 0;
var matches_delimiter = true;
while (delimiter_index < delimiter_len) {
var delimiterChar = delimiter_bytes[delimiter_index];
var strChar = str_bytes_ptrs[str_index + delimiter_index];
if (delimiterChar != strChar) {
matches_delimiter = false;
break;
}
delimiter_index += 1;
}
if (matches_delimiter) {
array[ret_array_index] = RocStr.init(str_bytes_ptrs + sliceStart_index, str_index - sliceStart_index);
sliceStart_index = str_index + delimiter_len;
ret_array_index += 1;
str_index += delimiter_len;
} else {
str_index += 1;
}
}
}
array[ret_array_index] = RocStr.init(str_bytes_ptrs + sliceStart_index, str_len - sliceStart_index);
}
test "strSplitInPlace: no delimiter" {
// Str.split "abc" "!" == [ "abc" ]
var str: [3]u8 = "abc".*;
const str_ptr: [*]u8 = &str;
var delimiter: [1]u8 = "!".*;
const delimiter_ptr: [*]u8 = &delimiter;
var array: [1]RocStr = undefined;
const array_ptr: [*]RocStr = &array;
strSplitInPlace(
array_ptr,
1,
str_ptr,
3,
delimiter_ptr,
1
);
var expected = [1]RocStr{
RocStr.init(str_ptr, 3),
};
expect(array.len == expected.len);
expect(RocStr.eq(array[0], expected[0]));
}
test "strSplitInPlace: delimiter on sides" {
// Str.split "tttghittt" "ttt" == [ "", "ghi", "" ]
const str_len: usize = 9;
var str: [str_len]u8 = "tttghittt".*;
const str_ptr: [*]u8 = &str;
const delimiter_len = 3;
var delimiter: [delimiter_len]u8 = "ttt".*;
const delimiter_ptr: [*]u8 = &delimiter;
const array_len : usize = 3;
var array: [array_len]RocStr = [_]RocStr{
undefined ,
undefined,
undefined,
};
const array_ptr: [*]RocStr = &array;
strSplitInPlace(
array_ptr,
array_len,
str_ptr,
str_len,
delimiter_ptr,
delimiter_len
);
const expected_str_len: usize = 3;
var expected_str: [expected_str_len]u8 = "ghi".*;
const expected_str_ptr: [*]u8 = &expected_str;
var expectedRocStr = RocStr.init(expected_str_ptr, expected_str_len);
expect(array.len == 3);
expect(array[0].str_len == 0);
expect(RocStr.eq(array[1], expectedRocStr));
expect(array[2].str_len == 0);
}
test "strSplitInPlace: three pieces" {
// Str.split "a!b!c" "!" == [ "a", "b", "c" ]
const str_len: usize = 5;
var str: [str_len]u8 = "a!b!c".*;
const str_ptr: [*]u8 = &str;
const delimiter_len = 1;
var delimiter: [delimiter_len]u8 = "!".*;
const delimiter_ptr: [*]u8 = &delimiter;
const array_len : usize = 3;
var array: [array_len]RocStr = undefined;
const array_ptr: [*]RocStr = &array;
strSplitInPlace(
array_ptr,
array_len,
str_ptr,
str_len,
delimiter_ptr,
delimiter_len
);
var a: [1]u8 = "a".*;
const a_ptr: [*]u8 = &a;
var b: [1]u8 = "b".*;
const b_ptr: [*]u8 = &b;
var c: [1]u8 = "c".*;
const c_ptr: [*]u8 = &c;
var expected_array = [array_len]RocStr{
RocStr{
.str_bytes_ptrs = a_ptr,
.str_len = 1,
},
RocStr{
.str_bytes_ptrs = b_ptr,
.str_len = 1,
},
RocStr{
.str_bytes_ptrs = c_ptr,
.str_len = 1,
}
};
expect(expected_array.len == array.len);
expect(RocStr.eq(array[0], expected_array[0]));
expect(RocStr.eq(array[1], expected_array[1]));
expect(RocStr.eq(array[2], expected_array[2]));
}
// This is used for `Str.split : Str, Str -> Array Str
// It is used to count how many segments the input `_str`
// needs to be broken into, so that we can allocate a array
// of that size. It always returns at least 1.
comptime { @export(countSegments, .{ .name = str_namespace ++ ".count_segements", .linkage = .Strong }); }
fn countSegments(
str_bytes_ptrs: [*]u8,
str_len: usize,
delimiter_bytes: [*]u8,
delimiter_len: usize
) callconv(.C) i64 {
var count: i64 = 1;
if (str_len > delimiter_len) {
var str_index: usize = 0;
const end_cond: usize = str_len - delimiter_len;
while (str_index < end_cond) {
var delimiter_index: usize = 0;
var matches_delimiter = true;
while (delimiter_index < delimiter_len) {
const delimiterChar = delimiter_bytes[delimiter_index];
const strChar = str_bytes_ptrs[str_index + delimiter_index];
if (delimiterChar != strChar) {
matches_delimiter = false;
break;
}
delimiter_index += 1;
}
if (matches_delimiter) {
count += 1;
}
str_index += 1;
}
}
return count;
}
test "countSegments: long delimiter" {
// Str.split "str" "delimiter" == [ "str" ]
// 1 segment
const str_len: usize = 3;
var str: [str_len]u8 = "str".*;
const str_ptr: [*]u8 = &str;
const delimiter_len = 9;
var delimiter: [delimiter_len]u8 = "delimiter".*;
const delimiter_ptr: [*]u8 = &delimiter;
const segments_count = countSegments(
str_ptr,
str_len,
delimiter_ptr,
delimiter_len
);
expect(segments_count == 1);
}
test "countSegments: delimiter at start" {
// Str.split "hello there" "hello" == [ "", " there" ]
// 2 segments
const str_len: usize = 11;
var str: [str_len]u8 = "hello there".*;
const str_ptr: [*]u8 = &str;
const delimiter_len = 5;
var delimiter: [delimiter_len]u8 = "hello".*;
const delimiter_ptr: [*]u8 = &delimiter;
const segments_count = countSegments(
str_ptr,
str_len,
delimiter_ptr,
delimiter_len
);
expect(segments_count == 2);
}
test "countSegments: delimiter interspered" {
// Str.split "a!b!c" "!" == [ "a", "b", "c" ]
// 3 segments
const str_len: usize = 5;
var str: [str_len]u8 = "a!b!c".*;
const str_ptr: [*]u8 = &str;
const delimiter_len = 1;
var delimiter: [delimiter_len]u8 = "!".*;
const delimiter_ptr: [*]u8 = &delimiter;
const segments_count = countSegments(
str_ptr,
str_len,
delimiter_ptr,
delimiter_len
);
expect(segments_count == 3);
}

View 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-10", &[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);
}

View file

@ -0,0 +1,25 @@
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
}
pub const MATH_ATAN: &str = "roc_builtins.math.atan";
pub const MATH_IS_FINITE: &str = "roc_builtins.math.is_finite";
pub const MATH_POW_INT: &str = "roc_builtins.math.pow_int";
pub const STR_COUNT_SEGEMENTS: &str = "roc_builtins.str.count_segements";
pub const STR_STR_SPLIT_IN_PLACE: &str = "roc_builtins.str.str_split_in_place";

View file

@ -10,5 +10,6 @@
// and encouraging shortcuts here creates bad incentives. I would rather temporarily // and encouraging shortcuts here creates bad incentives. I would rather temporarily
// re-enable this when working on performance optimizations than have it block PRs. // re-enable this when working on performance optimizations than have it block PRs.
#![allow(clippy::large_enum_variant)] #![allow(clippy::large_enum_variant)]
pub mod bitcode;
pub mod std; pub mod std;
pub mod unique; pub mod unique;

View file

@ -110,7 +110,7 @@ pub fn canonicalize_defs<'a>(
mut output: Output, mut output: Output,
var_store: &mut VarStore, var_store: &mut VarStore,
original_scope: &Scope, 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, pattern_type: PatternType,
) -> (CanDefs, Scope, Output, MutMap<Symbol, Region>) { ) -> (CanDefs, Scope, Output, MutMap<Symbol, Region>) {
// Canonicalizing defs while detecting shadowing involves a multi-step process: // 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>, env: &mut Env<'a>,
var_store: &mut VarStore, var_store: &mut VarStore,
scope: Scope, 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>>, loc_ret: &'a Located<ast::Expr<'a>>,
) -> (Expr, Output) { ) -> (Expr, Output) {
let (unsorted, mut scope, defs_output, symbols_introduced) = canonicalize_defs( let (unsorted, mut scope, defs_output, symbols_introduced) = canonicalize_defs(

View file

@ -309,7 +309,7 @@ pub fn canonicalize_expr<'a>(
let mut args = Vec::new(); let mut args = Vec::new();
let mut outputs = 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) = let (arg_expr, arg_out) =
canonicalize_expr(env, var_store, scope, loc_arg.region, &loc_arg.value); 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(); 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( let (new_output, can_arg) = canonicalize_pattern(
env, env,
var_store, var_store,
@ -562,7 +562,7 @@ pub fn canonicalize_expr<'a>(
let mut can_branches = Vec::with_capacity(branches.len()); let mut can_branches = Vec::with_capacity(branches.len());
for branch in branches { for branch in branches.iter() {
let (can_when_branch, branch_references) = let (can_when_branch, branch_references) =
canonicalize_when_branch(env, var_store, scope, region, *branch, &mut output); 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(); let mut scope = original_scope.clone();
// TODO report symbols not bound in all patterns // 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( let (new_output, can_pattern) = canonicalize_pattern(
env, env,
var_store, var_store,

View file

@ -43,7 +43,7 @@ pub struct ModuleOutput {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn canonicalize_module_defs<'a>( pub fn canonicalize_module_defs<'a>(
arena: &Bump, arena: &Bump,
loc_defs: bumpalo::collections::Vec<'a, Located<ast::Def<'a>>>, loc_defs: &'a [Located<ast::Def<'a>>],
home: ModuleId, home: ModuleId,
module_ids: &ModuleIds, module_ids: &ModuleIds,
exposed_ident_ids: IdentIds, exposed_ident_ids: IdentIds,
@ -66,9 +66,9 @@ pub fn canonicalize_module_defs<'a>(
let mut desugared = let mut desugared =
bumpalo::collections::Vec::with_capacity_in(loc_defs.len() + num_deps, arena); 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 { 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, region: loc_def.region,
})); }));
} }

View file

@ -90,9 +90,10 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a
List(elems) | Nested(List(elems)) => { List(elems) | Nested(List(elems)) => {
let mut new_elems = Vec::with_capacity_in(elems.len(), arena); 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)); new_elems.push(desugar_expr(arena, elem));
} }
let new_elems = new_elems.into_bump_slice();
let value: Expr<'a> = List(new_elems); let value: Expr<'a> = List(new_elems);
arena.alloc(Located { 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 }) => { Record { fields, update } | Nested(Record { fields, update }) => {
let mut new_fields = Vec::with_capacity_in(fields.len(), arena); 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); let value = desugar_field(arena, &field.value);
new_fields.push(Located { 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 { arena.alloc(Located {
region: loc_expr.region, region: loc_expr.region,
value: Record { 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)) => { Defs(defs, loc_ret) | Nested(Defs(defs, loc_ret)) => {
let mut desugared_defs = Vec::with_capacity_in(defs.len(), arena); 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 { let loc_def = Located {
value: desugar_def(arena, &loc_def.value), value: desugar_def(arena, &loc_def.value),
region: loc_def.region, 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)); desugared_defs.push(&*arena.alloc(loc_def));
} }
let desugared_defs = desugared_defs.into_bump_slice();
arena.alloc(Located { arena.alloc(Located {
value: Defs(desugared_defs, desugar_expr(arena, loc_ret)), value: Defs(desugared_defs, desugar_expr(arena, loc_ret)),
region: loc_expr.region, 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)) => { 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); 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)); desugared_args.push(desugar_expr(arena, loc_arg));
} }
let desugared_args = desugared_args.into_bump_slice();
arena.alloc(Located { arena.alloc(Located {
value: Apply(desugar_expr(arena, loc_fn), desugared_args, *called_via), value: Apply(desugar_expr(arena, loc_fn), desugared_args, *called_via),
region: loc_expr.region, 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 loc_desugared_cond = &*arena.alloc(desugar_expr(arena, &loc_cond_expr));
let mut desugared_branches = Vec::with_capacity_in(branches.len(), arena); 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 desugared = desugar_expr(arena, &branch.value);
let mut alternatives = Vec::with_capacity_in(branch.patterns.len(), arena); 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 { alternatives.push(Located {
region: loc_pattern.region, region: loc_pattern.region,
value: Pattern::Nested(&loc_pattern.value), 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 None
}; };
let alternatives = alternatives.into_bump_slice();
desugared_branches.push(&*arena.alloc(WhenBranch { desugared_branches.push(&*arena.alloc(WhenBranch {
patterns: alternatives, patterns: alternatives,
value: Located { 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 { arena.alloc(Located {
value: When(loc_desugared_cond, desugared_branches), value: When(loc_desugared_cond, desugared_branches),
region: loc_expr.region, 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 loc_fn_var = arena.alloc(Located { region, value });
let desugared_args = bumpalo::vec![in arena; desugar_expr(arena, loc_arg)]; let desugared_args = bumpalo::vec![in arena; desugar_expr(arena, loc_arg)];
let desugared_args = desugared_args.into_bump_slice();
arena.alloc(Located { arena.alloc(Located {
value: Apply(loc_fn_var, desugared_args, CalledVia::UnaryOp(op)), value: Apply(loc_fn_var, desugared_args, CalledVia::UnaryOp(op)),
region: loc_expr.region, 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); args.push(left);
for arg in arguments { for arg in arguments.iter() {
args.push(arg); args.push(arg);
} }
let args = args.into_bump_slice();
Apply(function, args, CalledVia::BinOp(Pizza)) Apply(function, args, CalledVia::BinOp(Pizza))
} }
expr => { expr => {
@ -480,6 +495,8 @@ fn desugar_bin_op<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'_>>) -> &'a L
region: right.region, region: right.region,
}); });
let args = args.into_bump_slice();
Apply(function, args, CalledVia::BinOp(Pizza)) 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, region: loc_op.region,
}); });
let args = args.into_bump_slice();
Apply(loc_expr, args, CalledVia::BinOp(binop)) Apply(loc_expr, args, CalledVia::BinOp(binop))
} }
}; };

View file

@ -4,7 +4,7 @@ use crate::pattern::fmt_pattern;
use crate::spaces::{ use crate::spaces::{
add_spaces, fmt_comments_only, fmt_condition_spaces, fmt_spaces, newline, INDENT, 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_module::operator::{self, BinOp};
use roc_parse::ast::StrSegment; use roc_parse::ast::StrSegment;
use roc_parse::ast::{AssignedField, Base, CommentOrNewline, Expr, Pattern, WhenBranch}; use roc_parse::ast::{AssignedField, Base, CommentOrNewline, Expr, Pattern, WhenBranch};
@ -194,12 +194,12 @@ impl<'a> Formattable<'a> for Expr<'a> {
if multiline_args { if multiline_args {
let arg_indent = indent + INDENT; let arg_indent = indent + INDENT;
for loc_arg in loc_args { for loc_arg in loc_args.iter() {
newline(buf, arg_indent); newline(buf, arg_indent);
loc_arg.format_with_options(buf, Parens::InApply, Newlines::No, arg_indent); loc_arg.format_with_options(buf, Parens::InApply, Newlines::No, arg_indent);
} }
} else { } else {
for loc_arg in loc_args { for loc_arg in loc_args.iter() {
buf.push(' '); buf.push(' ');
loc_arg.format_with_options(buf, Parens::InApply, Newlines::Yes, indent); loc_arg.format_with_options(buf, Parens::InApply, Newlines::Yes, indent);
} }
@ -707,7 +707,7 @@ fn fmt_if<'a>(
pub fn fmt_closure<'a>( pub fn fmt_closure<'a>(
buf: &mut String<'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>>, loc_ret: &'a Located<Expr<'a>>,
indent: u16, indent: u16,
) { ) {

View file

@ -1,23 +0,0 @@
use std::env;
use std::fs;
use std::path::Path;
use std::process::Command;
fn main() {
let src_path = fs::canonicalize("./../builtins/bitcode/src/lib.c")
.expect("Failed to resolve bitcode source");
let src = src_path.to_str().expect("Invalid src path");
let out_dir = env::var_os("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("builtins.bc");
let dest = dest_path.to_str().expect("Invalid dest path");
Command::new("clang")
.args(&["-emit-llvm", "-o", dest, "-c", src])
.status()
.unwrap();
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed={}", src);
println!("cargo:rustc-env=BUILTINS_BC={}", dest);
}

View file

@ -29,13 +29,12 @@ use inkwell::values::{
}; };
use inkwell::OptimizationLevel; use inkwell::OptimizationLevel;
use inkwell::{AddressSpace, IntPredicate}; use inkwell::{AddressSpace, IntPredicate};
use roc_builtins::bitcode;
use roc_collections::all::{ImMap, MutSet}; use roc_collections::all::{ImMap, MutSet};
use roc_module::low_level::LowLevel; use roc_module::low_level::LowLevel;
use roc_module::symbol::{Interns, ModuleId, Symbol}; use roc_module::symbol::{Interns, ModuleId, Symbol};
use roc_mono::ir::{JoinPointId, Wrapped}; use roc_mono::ir::{JoinPointId, Wrapped};
use roc_mono::layout::{Builtin, Layout, MemoryMode}; use roc_mono::layout::{Builtin, Layout, MemoryMode};
use std::fs::File;
use std::io::prelude::Read;
use target_lexicon::CallingConvention; use target_lexicon::CallingConvention;
/// This is for Inkwell's FunctionValue::verify - we want to know the verification /// This is for Inkwell's FunctionValue::verify - we want to know the verification
@ -183,19 +182,9 @@ impl<'a, 'ctx, 'env> Env<'a, 'ctx, 'env> {
} }
pub fn module_from_builtins<'ctx>(ctx: &'ctx Context, module_name: &str) -> Module<'ctx> { pub fn module_from_builtins<'ctx>(ctx: &'ctx Context, module_name: &str) -> Module<'ctx> {
// In the build script for the gen module, we compile the builtins bitcode and set let bitcode_bytes = bitcode::get_bytes();
// 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 = std::vec::Vec::new();
builtins_bitcode
.read_to_end(&mut buffer)
.expect("Unable to read builtins bitcode");
let memory_buffer = MemoryBuffer::create_from_memory_range(&buffer, module_name); let memory_buffer = MemoryBuffer::create_from_memory_range(&bitcode_bytes, module_name);
let module = Module::parse_bitcode_from_buffer(&memory_buffer, ctx) let module = Module::parse_bitcode_from_buffer(&memory_buffer, ctx)
.unwrap_or_else(|err| panic!("Unable to import builtins bitcode. LLVM error: {:?}", err)); .unwrap_or_else(|err| panic!("Unable to import builtins bitcode. LLVM error: {:?}", err));
@ -2541,7 +2530,12 @@ fn build_int_binop<'a, 'ctx, 'env>(
NumLte => bd.build_int_compare(SLE, lhs, rhs, "int_lte").into(), NumLte => bd.build_int_compare(SLE, lhs, rhs, "int_lte").into(),
NumRemUnchecked => bd.build_int_signed_rem(lhs, rhs, "rem_int").into(), NumRemUnchecked => bd.build_int_signed_rem(lhs, rhs, "rem_int").into(),
NumDivUnchecked => bd.build_int_signed_div(lhs, rhs, "div_int").into(), NumDivUnchecked => bd.build_int_signed_div(lhs, rhs, "div_int").into(),
NumPowInt => call_bitcode_fn(NumPowInt, env, &[lhs.into(), rhs.into()], "pow_int_"), NumPowInt => call_bitcode_fn(
NumPowInt,
env,
&[lhs.into(), rhs.into()],
&bitcode::MATH_POW_INT,
),
_ => { _ => {
unreachable!("Unrecognized int binary operation: {:?}", op); unreachable!("Unrecognized int binary operation: {:?}", op);
} }
@ -2589,7 +2583,8 @@ fn build_float_binop<'a, 'ctx, 'env>(
let result = bd.build_float_add(lhs, rhs, "add_float"); let result = bd.build_float_add(lhs, rhs, "add_float");
let is_finite = let is_finite =
call_bitcode_fn(NumIsFinite, env, &[result.into()], "is_finite_").into_int_value(); call_bitcode_fn(NumIsFinite, env, &[result.into()], &bitcode::MATH_IS_FINITE)
.into_int_value();
let then_block = context.append_basic_block(parent, "then_block"); let then_block = context.append_basic_block(parent, "then_block");
let throw_block = context.append_basic_block(parent, "throw_block"); let throw_block = context.append_basic_block(parent, "throw_block");
@ -2610,7 +2605,8 @@ fn build_float_binop<'a, 'ctx, 'env>(
let result = bd.build_float_add(lhs, rhs, "add_float"); let result = bd.build_float_add(lhs, rhs, "add_float");
let is_finite = let is_finite =
call_bitcode_fn(NumIsFinite, env, &[result.into()], "is_finite_").into_int_value(); call_bitcode_fn(NumIsFinite, env, &[result.into()], &bitcode::MATH_IS_FINITE)
.into_int_value();
let is_infinite = bd.build_not(is_finite, "negate"); let is_infinite = bd.build_not(is_finite, "negate");
let struct_type = context.struct_type( let struct_type = context.struct_type(
@ -2739,8 +2735,8 @@ fn build_float_unary_op<'a, 'ctx, 'env>(
env.context.i64_type(), env.context.i64_type(),
"num_floor", "num_floor",
), ),
NumIsFinite => call_bitcode_fn(NumIsFinite, env, &[arg.into()], "is_finite_"), NumIsFinite => call_bitcode_fn(NumIsFinite, env, &[arg.into()], &bitcode::MATH_IS_FINITE),
NumAtan => call_bitcode_fn(NumAtan, env, &[arg.into()], "atan_"), NumAtan => call_bitcode_fn(NumAtan, env, &[arg.into()], &bitcode::MATH_ATAN),
_ => { _ => {
unreachable!("Unrecognized int unary operation: {:?}", op); unreachable!("Unrecognized int unary operation: {:?}", op);
} }

View file

@ -788,8 +788,7 @@ pub fn list_walk_right<'a, 'ctx, 'env>(
let new_current = call_site_value let new_current = call_site_value
.try_as_basic_value() .try_as_basic_value()
.left() .left()
.unwrap_or_else(|| panic!("LLVM error: Invalid call by pointer.")) .unwrap_or_else(|| panic!("LLVM error: Invalid call by pointer."));
.into_int_value();
builder.build_store(accum_alloca, new_current); builder.build_store(accum_alloca, new_current);
}; };

View file

@ -14,8 +14,7 @@ mod helpers;
#[cfg(test)] #[cfg(test)]
mod gen_list { mod gen_list {
use crate::helpers::with_larger_debug_stack; use crate::helpers::with_larger_debug_stack;
//use roc_std::roclist; use roc_std::{RocList, RocStr};
use roc_std::RocList;
#[test] #[test]
fn roc_list_construction() { fn roc_list_construction() {
@ -264,6 +263,47 @@ mod gen_list {
); );
} }
#[test]
fn list_walk_right_with_str() {
assert_evals_to!(
r#"List.walkRight [ "x", "y", "z" ] Str.concat "<""#,
RocStr::from("zyx<"),
RocStr
);
assert_evals_to!(
r#"List.walkRight [ "Third", "Second", "First" ] Str.concat "Fourth""#,
RocStr::from("FirstSecondThirdFourth"),
RocStr
);
}
#[test]
fn list_walk_right_with_record() {
assert_evals_to!(
indoc!(
r#"
Bit : [ Zero, One ]
byte = [ Zero, One, Zero, One, Zero, Zero, One, Zero ]
initialCounts = { zeroes: 0, ones: 0 }
acc = \b, r ->
when b is
Zero -> { r & zeroes: r.zeroes + 1 }
One -> { r & ones: r.ones + 1 }
finalCounts = List.walkRight byte acc initialCounts
finalCounts.ones * 10 + finalCounts.zeroes
"#
),
35,
i64
);
}
#[test] #[test]
fn list_keep_if_empty_list_of_int() { fn list_keep_if_empty_list_of_int() {
assert_evals_to!( assert_evals_to!(

View file

@ -686,15 +686,7 @@ mod gen_num {
assert_evals_to!("Num.powInt 2 3", 8, i64); assert_evals_to!("Num.powInt 2 3", 8, i64);
} }
// For some reason, libm's atan has slightly different results on macos vs non-macos
#[test] #[test]
#[cfg(target_os = "macos")]
fn atan_macos() {
assert_evals_to!("Num.atan 10", 1.4711276743037345, f64);
}
#[test]
#[cfg(not(target_os = "macos"))]
fn atan() { fn atan() {
assert_evals_to!("Num.atan 10", 1.4711276743037347, f64); assert_evals_to!("Num.atan 10", 1.4711276743037347, f64);
} }

View file

@ -30,7 +30,7 @@ pub struct DocEntry {
pub fn generate_module_docs<'a>( pub fn generate_module_docs<'a>(
module_name: ModuleName, module_name: ModuleName,
exposed_ident_ids: &'a IdentIds, exposed_ident_ids: &'a IdentIds,
parsed_defs: &'a bumpalo::collections::Vec<'a, Located<Def<'a>>>, parsed_defs: &'a [Located<Def<'a>>],
) -> ModuleDocumentation { ) -> ModuleDocumentation {
let (entries, _) = let (entries, _) =
parsed_defs parsed_defs

View file

@ -2031,7 +2031,7 @@ fn parse_and_constrain<'a>(
let mut var_store = VarStore::default(); let mut var_store = VarStore::default();
let canonicalized = canonicalize_module_defs( let canonicalized = canonicalize_module_defs(
&arena, &arena,
parsed_defs, &parsed_defs,
module_id, module_id,
module_ids, module_ids,
header.exposed_ident_ids, header.exposed_ident_ids,

View file

@ -78,8 +78,8 @@ mod test_mono {
println!("Ignoring {} canonicalization problems", can_problems.len()); println!("Ignoring {} canonicalization problems", can_problems.len());
} }
assert!(type_problems.is_empty()); assert_eq!(type_problems, Vec::new());
assert!(mono_problems.is_empty()); assert_eq!(mono_problems, Vec::new());
debug_assert_eq!(exposed_to_host.len(), 1); debug_assert_eq!(exposed_to_host.len(), 1);

View file

@ -29,7 +29,7 @@ pub struct InterfaceHeader<'a> {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct WhenBranch<'a> { pub struct WhenBranch<'a> {
pub patterns: Vec<'a, Loc<Pattern<'a>>>, pub patterns: &'a [Loc<Pattern<'a>>],
pub value: Loc<Expr<'a>>, pub value: Loc<Expr<'a>>,
pub guard: Option<Loc<Expr<'a>>>, pub guard: Option<Loc<Expr<'a>>>,
} }
@ -188,10 +188,10 @@ pub enum Expr<'a> {
AccessorFunction(&'a str), AccessorFunction(&'a str),
// Collection Literals // Collection Literals
List(Vec<'a, &'a Loc<Expr<'a>>>), List(&'a [&'a Loc<Expr<'a>>]),
Record { Record {
update: Option<&'a Loc<Expr<'a>>>, update: Option<&'a Loc<Expr<'a>>>,
fields: Vec<'a, Loc<AssignedField<'a, Expr<'a>>>>, fields: &'a [Loc<AssignedField<'a, Expr<'a>>>],
}, },
// Lookups // Lookups
@ -205,14 +205,14 @@ pub enum Expr<'a> {
PrivateTag(&'a str), PrivateTag(&'a str),
// Pattern Matching // 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 /// 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 // Application
/// To apply by name, do Apply(Var(...), ...) /// To apply by name, do Apply(Var(...), ...)
/// To apply a tag by name, do Apply(Tag(...), ...) /// 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>>)), BinOp(&'a (Loc<Expr<'a>>, Loc<BinOp>, Loc<Expr<'a>>)),
UnaryOp(&'a Loc<Expr<'a>>, Loc<UnaryOp>), 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 /// Vec, because there may be many patterns, and the guard
/// is Option<Expr> because each branch may be preceded by /// is Option<Expr> because each branch may be preceded by
/// a guard (".. if .."). /// a guard (".. if ..").
Vec<'a, &'a WhenBranch<'a>>, &'a [&'a WhenBranch<'a>],
), ),
// Blank Space (e.g. comments, spaces, newlines) before or after an expression. // Blank Space (e.g. comments, spaces, newlines) before or after an expression.

View file

@ -82,7 +82,7 @@ macro_rules! loc_parenthetical_expr {
region: loc_expr_with_extras.region, region: loc_expr_with_extras.region,
value: Expr::Apply( value: Expr::Apply(
arena.alloc(loc_expr), arena.alloc(loc_expr),
allocated_args, allocated_args.into_bump_slice(),
CalledVia::Space, 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); 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 region = loc_arg.region;
let value = expr_to_pattern(arena, &loc_arg.value)?; 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); 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 region = loc_assigned_field.region;
let value = assigned_expr_field_to_pattern(arena, &loc_assigned_field.value)?; 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! // for formatting reasons, we must insert the first def first!
defs.insert(0, arena.alloc(loc_first_def)); 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) .parse(arena, state)
@ -766,6 +769,8 @@ fn parse_def_signature<'a>(
// corresponding definition (the one with the body). // corresponding definition (the one with the body).
defs.insert(0, arena.alloc(loc_first_def)); defs.insert(0, arena.alloc(loc_first_def));
let defs = defs.into_bump_slice();
Ok((Expr::Defs(defs, arena.alloc(loc_ret)), state)) 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 { |arena: &'a Bump, opt_contents| match opt_contents {
None => Expr::MalformedClosure, 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) = let (branches, state) =
attempt!(Attempting::WhenBranch, branches(min_indent)).parse(arena, 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. // Record this as the first branch, then optionally parse additional branches.
branches.push(arena.alloc(WhenBranch { branches.push(arena.alloc(WhenBranch {
patterns: loc_first_patterns, patterns: loc_first_patterns.into_bump_slice(),
value: loc_first_expr, value: loc_first_expr,
guard: loc_first_guard, guard: loc_first_guard,
})); }));
@ -1173,10 +1186,13 @@ mod when {
), ),
branch_result(indented_more) branch_result(indented_more)
), ),
|((patterns, guard), expr)| WhenBranch { |((patterns, guard), expr)| {
patterns, let patterns: Vec<'a, _> = patterns;
value: expr, WhenBranch {
guard patterns: patterns.into_bump_slice(),
value: expr,
guard,
}
} }
); );
@ -1444,7 +1460,11 @@ fn ident_etc<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
} }
Ok(( 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, state,
)) ))
} }
@ -1638,7 +1658,7 @@ pub fn list_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
allocated.push(&*arena.alloc(parsed_elem)); 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. // This is a record literal, not a destructure.
let mut value = Expr::Record { let mut value = Expr::Record {
update: opt_update.map(|loc_expr| &*arena.alloc(loc_expr)), 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` // there can be field access, e.g. `{ x : 4 }.x`

View file

@ -54,8 +54,8 @@ mod test_parse {
fn assert_segments<E: Fn(&Bump) -> Vec<'_, ast::StrSegment<'_>>>(input: &str, to_expected: E) { fn assert_segments<E: Fn(&Bump) -> Vec<'_, ast::StrSegment<'_>>>(input: &str, to_expected: E) {
let arena = Bump::new(); let arena = Bump::new();
let actual = parse_with(&arena, arena.alloc(input)); let actual = parse_with(&arena, arena.alloc(input));
let expected_slice = to_expected(&arena).into_bump_slice(); let expected_slice = to_expected(&arena);
let expected_expr = Expr::Str(Line(expected_slice)); let expected_expr = Expr::Str(Line(&expected_slice));
assert_eq!(Ok(expected_expr), actual); assert_eq!(Ok(expected_expr), actual);
} }
@ -78,8 +78,8 @@ mod test_parse {
("\\\"", EscapedChar::Quote), ("\\\"", EscapedChar::Quote),
] { ] {
let actual = parse_with(&arena, arena.alloc(to_input(string))); let actual = parse_with(&arena, arena.alloc(to_input(string)));
let expected_slice = to_expected(*escaped, &arena).into_bump_slice(); let expected_slice = to_expected(*escaped, &arena);
let expected_expr = Expr::Str(Line(expected_slice)); let expected_expr = Expr::Str(Line(&expected_slice));
assert_eq!(Ok(expected_expr), actual); assert_eq!(Ok(expected_expr), actual);
} }
@ -420,7 +420,7 @@ mod test_parse {
fn empty_record() { fn empty_record() {
let arena = Bump::new(); let arena = Bump::new();
let expected = Record { let expected = Record {
fields: Vec::new_in(&arena), fields: &[],
update: None, update: None,
}; };
let actual = parse_with(&arena, "{}"); let actual = parse_with(&arena, "{}");
@ -441,9 +441,9 @@ mod test_parse {
&[], &[],
arena.alloc(Located::new(0, 0, 25, 26, Num("0"))), 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, 16, 20, label1),
Located::new(0, 0, 22, 26, label2) Located::new(0, 0, 22, 26, label2),
]; ];
let var = Var { let var = Var {
module_name: "Foo.Bar", module_name: "Foo.Bar",
@ -565,10 +565,7 @@ mod test_parse {
#[test] #[test]
fn newline_before_add() { fn newline_before_add() {
let arena = Bump::new(); let arena = Bump::new();
let spaced_int = Expr::SpaceAfter( let spaced_int = Expr::SpaceAfter(arena.alloc(Num("3")), &[Newline]);
arena.alloc(Num("3")),
bumpalo::vec![in &arena; Newline].into_bump_slice(),
);
let tuple = arena.alloc(( let tuple = arena.alloc((
Located::new(0, 0, 0, 1, spaced_int), Located::new(0, 0, 0, 1, spaced_int),
Located::new(1, 1, 0, 1, Plus), Located::new(1, 1, 0, 1, Plus),
@ -583,10 +580,7 @@ mod test_parse {
#[test] #[test]
fn newline_before_sub() { fn newline_before_sub() {
let arena = Bump::new(); let arena = Bump::new();
let spaced_int = Expr::SpaceAfter( let spaced_int = Expr::SpaceAfter(arena.alloc(Num("3")), &[Newline]);
arena.alloc(Num("3")),
bumpalo::vec![in &arena; Newline].into_bump_slice(),
);
let tuple = arena.alloc(( let tuple = arena.alloc((
Located::new(0, 0, 0, 1, spaced_int), Located::new(0, 0, 0, 1, spaced_int),
Located::new(1, 1, 0, 1, Minus), Located::new(1, 1, 0, 1, Minus),
@ -601,9 +595,7 @@ mod test_parse {
#[test] #[test]
fn newline_after_mul() { fn newline_after_mul() {
let arena = Bump::new(); let arena = Bump::new();
let spaced_int = arena let spaced_int = arena.alloc(Num("4")).before(&[Newline]);
.alloc(Num("4"))
.before(bumpalo::vec![in &arena; Newline].into_bump_slice());
let tuple = arena.alloc(( let tuple = arena.alloc((
Located::new(0, 0, 0, 1, Num("3")), Located::new(0, 0, 0, 1, Num("3")),
Located::new(0, 0, 3, 4, Star), Located::new(0, 0, 3, 4, Star),
@ -618,9 +610,7 @@ mod test_parse {
#[test] #[test]
fn newline_after_sub() { fn newline_after_sub() {
let arena = Bump::new(); let arena = Bump::new();
let spaced_int = arena let spaced_int = arena.alloc(Num("4")).before(&[Newline]);
.alloc(Num("4"))
.before(bumpalo::vec![in &arena; Newline].into_bump_slice());
let tuple = arena.alloc(( let tuple = arena.alloc((
Located::new(0, 0, 0, 1, Num("3")), Located::new(0, 0, 0, 1, Num("3")),
Located::new(0, 0, 3, 4, Minus), Located::new(0, 0, 3, 4, Minus),
@ -635,9 +625,7 @@ mod test_parse {
#[test] #[test]
fn comment_with_non_ascii() { fn comment_with_non_ascii() {
let arena = Bump::new(); let arena = Bump::new();
let spaced_int = arena let spaced_int = arena.alloc(Num("3")).after(&[LineComment(" 2 × 2")]);
.alloc(Num("3"))
.after(bumpalo::vec![in &arena; LineComment(" 2 × 2")].into_bump_slice());
let tuple = arena.alloc(( let tuple = arena.alloc((
Located::new(0, 0, 0, 1, spaced_int), Located::new(0, 0, 0, 1, spaced_int),
Located::new(1, 1, 0, 1, Plus), Located::new(1, 1, 0, 1, Plus),
@ -652,9 +640,7 @@ mod test_parse {
#[test] #[test]
fn comment_before_op() { fn comment_before_op() {
let arena = Bump::new(); let arena = Bump::new();
let spaced_int = arena let spaced_int = arena.alloc(Num("3")).after(&[LineComment(" test!")]);
.alloc(Num("3"))
.after(bumpalo::vec![in &arena; LineComment(" test!")].into_bump_slice());
let tuple = arena.alloc(( let tuple = arena.alloc((
Located::new(0, 0, 0, 1, spaced_int), Located::new(0, 0, 0, 1, spaced_int),
Located::new(1, 1, 0, 1, Plus), Located::new(1, 1, 0, 1, Plus),
@ -669,9 +655,7 @@ mod test_parse {
#[test] #[test]
fn comment_after_op() { fn comment_after_op() {
let arena = Bump::new(); let arena = Bump::new();
let spaced_int = arena let spaced_int = arena.alloc(Num("92")).before(&[LineComment(" test!")]);
.alloc(Num("92"))
.before(bumpalo::vec![in &arena; LineComment(" test!")].into_bump_slice());
let tuple = arena.alloc(( let tuple = arena.alloc((
Located::new(0, 0, 0, 2, Num("12")), Located::new(0, 0, 0, 2, Num("12")),
Located::new(0, 0, 4, 5, Star), Located::new(0, 0, 4, 5, Star),
@ -686,12 +670,8 @@ mod test_parse {
#[test] #[test]
fn ops_with_newlines() { fn ops_with_newlines() {
let arena = Bump::new(); let arena = Bump::new();
let spaced_int1 = arena let spaced_int1 = arena.alloc(Num("3")).after(&[Newline]);
.alloc(Num("3")) let spaced_int2 = arena.alloc(Num("4")).before(&[Newline, Newline]);
.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 tuple = arena.alloc(( let tuple = arena.alloc((
Located::new(0, 0, 0, 1, spaced_int1), Located::new(0, 0, 0, 1, spaced_int1),
Located::new(1, 1, 0, 1, Plus), Located::new(1, 1, 0, 1, Plus),
@ -881,7 +861,7 @@ mod test_parse {
let arena = Bump::new(); let arena = Bump::new();
let arg1 = arena.alloc(Located::new(0, 0, 6, 8, Num("12"))); 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 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( let expected = Expr::Apply(
arena.alloc(Located::new(0, 0, 0, 5, Expr::PrivateTag("@Whee"))), arena.alloc(Located::new(0, 0, 0, 5, Expr::PrivateTag("@Whee"))),
args, args,
@ -897,7 +877,7 @@ mod test_parse {
let arena = Bump::new(); let arena = Bump::new();
let arg1 = arena.alloc(Located::new(0, 0, 5, 7, Num("12"))); 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 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( let expected = Expr::Apply(
arena.alloc(Located::new(0, 0, 0, 4, Expr::GlobalTag("Whee"))), arena.alloc(Located::new(0, 0, 0, 4, Expr::GlobalTag("Whee"))),
args, args,
@ -915,7 +895,7 @@ mod test_parse {
let int2 = ParensAround(arena.alloc(Num("34"))); let int2 = ParensAround(arena.alloc(Num("34")));
let arg1 = arena.alloc(Located::new(0, 0, 6, 8, int1)); let arg1 = arena.alloc(Located::new(0, 0, 6, 8, int1));
let arg2 = arena.alloc(Located::new(0, 0, 11, 13, int2)); 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( let expected = Expr::Apply(
arena.alloc(Located::new(0, 0, 0, 4, Expr::GlobalTag("Whee"))), arena.alloc(Located::new(0, 0, 0, 4, Expr::GlobalTag("Whee"))),
args, args,
@ -949,11 +929,8 @@ mod test_parse {
fn tag_pattern() { fn tag_pattern() {
let arena = Bump::new(); let arena = Bump::new();
let pattern = Located::new(0, 0, 1, 6, Pattern::GlobalTag("Thing")); let pattern = Located::new(0, 0, 1, 6, Pattern::GlobalTag("Thing"));
let patterns = bumpalo::vec![in &arena; pattern]; let patterns = &[pattern];
let expected = Closure( let expected = Closure(patterns, arena.alloc(Located::new(0, 0, 10, 12, Num("42"))));
arena.alloc(patterns),
arena.alloc(Located::new(0, 0, 10, 12, Num("42"))),
);
let actual = parse_with(&arena, "\\Thing -> 42"); let actual = parse_with(&arena, "\\Thing -> 42");
assert_eq!(Ok(expected), actual); assert_eq!(Ok(expected), actual);
@ -973,7 +950,7 @@ mod test_parse {
#[test] #[test]
fn empty_list() { fn empty_list() {
let arena = Bump::new(); let arena = Bump::new();
let elems = Vec::new_in(&arena); let elems = &[];
let expected = List(elems); let expected = List(elems);
let actual = parse_with(&arena, "[]"); let actual = parse_with(&arena, "[]");
@ -984,7 +961,7 @@ mod test_parse {
fn spaces_inside_empty_list() { fn spaces_inside_empty_list() {
// This is a regression test! // This is a regression test!
let arena = Bump::new(); let arena = Bump::new();
let elems = Vec::new_in(&arena); let elems = &[];
let expected = List(elems); let expected = List(elems);
let actual = parse_with(&arena, "[ ]"); let actual = parse_with(&arena, "[ ]");
@ -994,7 +971,7 @@ mod test_parse {
#[test] #[test]
fn packed_singleton_list() { fn packed_singleton_list() {
let arena = Bump::new(); 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 expected = List(elems);
let actual = parse_with(&arena, "[1]"); let actual = parse_with(&arena, "[1]");
@ -1004,7 +981,7 @@ mod test_parse {
#[test] #[test]
fn spaced_singleton_list() { fn spaced_singleton_list() {
let arena = Bump::new(); 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 expected = List(elems);
let actual = parse_with(&arena, "[ 1 ]"); let actual = parse_with(&arena, "[ 1 ]");
@ -1090,7 +1067,7 @@ mod test_parse {
fn basic_apply() { fn basic_apply() {
let arena = Bump::new(); let arena = Bump::new();
let arg = arena.alloc(Located::new(0, 0, 5, 6, Num("1"))); 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 { let expr = Var {
module_name: "", module_name: "",
ident: "whee", ident: "whee",
@ -1110,7 +1087,7 @@ mod test_parse {
let arena = Bump::new(); let arena = Bump::new();
let arg1 = arena.alloc(Located::new(0, 0, 6, 8, Num("12"))); 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 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( let expected = Expr::Apply(
arena.alloc(Located::new( arena.alloc(Located::new(
0, 0,
@ -1163,7 +1140,7 @@ mod test_parse {
ident: "d", ident: "d",
}, },
)); ));
let args = bumpalo::vec![in &arena; &*arg1, &*arg2, &*arg3]; let args = &[&*arg1, &*arg2, &*arg3];
let expected = Expr::Apply( let expected = Expr::Apply(
arena.alloc(Located::new( arena.alloc(Located::new(
0, 0,
@ -1187,7 +1164,7 @@ mod test_parse {
fn parenthetical_apply() { fn parenthetical_apply() {
let arena = Bump::new(); let arena = Bump::new();
let arg = arena.alloc(Located::new(0, 0, 7, 8, Num("1"))); 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 { let parens_var = Expr::ParensAround(arena.alloc(Var {
module_name: "", module_name: "",
ident: "whee", ident: "whee",
@ -1249,7 +1226,7 @@ mod test_parse {
ident: "foo", ident: "foo",
}, },
)); ));
let args = bumpalo::vec![in &arena; &*arg1, &*arg2]; let args = &[&*arg1, &*arg2];
let apply_expr = Expr::Apply( let apply_expr = Expr::Apply(
arena.alloc(Located::new( arena.alloc(Located::new(
0, 0,
@ -1285,7 +1262,7 @@ mod test_parse {
ident: "foo", ident: "foo",
}, },
)); ));
let args = bumpalo::vec![in &arena; &*arg1, &*arg2]; let args = &[&*arg1, &*arg2];
let apply_expr = Expr::Apply( let apply_expr = Expr::Apply(
arena.alloc(Located::new( arena.alloc(Located::new(
0, 0,
@ -1321,7 +1298,7 @@ mod test_parse {
ident: "foo", ident: "foo",
}, },
)); ));
let args = bumpalo::vec![in &arena; &*arg1, &*arg2]; let args = &[&*arg1, &*arg2];
let apply_expr = Expr::ParensAround(arena.alloc(Expr::Apply( let apply_expr = Expr::ParensAround(arena.alloc(Expr::Apply(
arena.alloc(Located::new( arena.alloc(Located::new(
0, 0,
@ -1357,7 +1334,7 @@ mod test_parse {
ident: "foo", ident: "foo",
}, },
)); ));
let args = bumpalo::vec![in &arena; &*arg1, &*arg2]; let args = &[&*arg1, &*arg2];
let apply_expr = Expr::ParensAround(arena.alloc(Expr::Apply( let apply_expr = Expr::ParensAround(arena.alloc(Expr::Apply(
arena.alloc(Located::new( arena.alloc(Located::new(
0, 0,
@ -1390,7 +1367,7 @@ mod test_parse {
let loc_arg1_expr = Located::new(0, 0, 10, 13, var1); let loc_arg1_expr = Located::new(0, 0, 10, 13, var1);
let arg_op = UnaryOp(arena.alloc(loc_arg1_expr), loc_op); let arg_op = UnaryOp(arena.alloc(loc_arg1_expr), loc_op);
let arg2 = arena.alloc(Located::new(0, 0, 9, 13, arg_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 { let var2 = Var {
module_name: "", module_name: "",
ident: "whee", ident: "whee",
@ -1428,11 +1405,8 @@ mod test_parse {
fn single_arg_closure() { fn single_arg_closure() {
let arena = Bump::new(); let arena = Bump::new();
let pattern = Located::new(0, 0, 1, 2, Identifier("a")); let pattern = Located::new(0, 0, 1, 2, Identifier("a"));
let patterns = bumpalo::vec![in &arena; pattern]; let patterns = &[pattern];
let expected = Closure( let expected = Closure(patterns, arena.alloc(Located::new(0, 0, 6, 8, Num("42"))));
arena.alloc(patterns),
arena.alloc(Located::new(0, 0, 6, 8, Num("42"))),
);
let actual = parse_with(&arena, "\\a -> 42"); let actual = parse_with(&arena, "\\a -> 42");
assert_eq!(Ok(expected), actual); assert_eq!(Ok(expected), actual);
@ -1442,11 +1416,8 @@ mod test_parse {
fn single_underscore_closure() { fn single_underscore_closure() {
let arena = Bump::new(); let arena = Bump::new();
let pattern = Located::new(0, 0, 1, 2, Underscore); let pattern = Located::new(0, 0, 1, 2, Underscore);
let patterns = bumpalo::vec![in &arena; pattern]; let patterns = &[pattern];
let expected = Closure( let expected = Closure(patterns, arena.alloc(Located::new(0, 0, 6, 8, Num("42"))));
arena.alloc(patterns),
arena.alloc(Located::new(0, 0, 6, 8, Num("42"))),
);
let actual = parse_with(&arena, "\\_ -> 42"); let actual = parse_with(&arena, "\\_ -> 42");
assert_eq!(Ok(expected), actual); assert_eq!(Ok(expected), actual);
@ -1468,11 +1439,8 @@ mod test_parse {
let arena = Bump::new(); let arena = Bump::new();
let arg1 = Located::new(0, 0, 1, 2, Identifier("a")); let arg1 = Located::new(0, 0, 1, 2, Identifier("a"));
let arg2 = Located::new(0, 0, 4, 5, Identifier("b")); let arg2 = Located::new(0, 0, 4, 5, Identifier("b"));
let patterns = bumpalo::vec![in &arena; arg1, arg2]; let patterns = &[arg1, arg2];
let expected = Closure( let expected = Closure(patterns, arena.alloc(Located::new(0, 0, 9, 11, Num("42"))));
arena.alloc(patterns),
arena.alloc(Located::new(0, 0, 9, 11, Num("42"))),
);
let actual = parse_with(&arena, "\\a, b -> 42"); let actual = parse_with(&arena, "\\a, b -> 42");
assert_eq!(Ok(expected), actual); assert_eq!(Ok(expected), actual);
@ -1520,7 +1488,7 @@ mod test_parse {
arena.alloc(Located::new(1, 1, 2, 3, Num("5"))), arena.alloc(Located::new(1, 1, 2, 3, Num("5"))),
); );
let loc_def = &*arena.alloc(Located::new(1, 1, 0, 1, def)); 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 ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
let loc_ret = Located::new(3, 3, 0, 2, ret); let loc_ret = Located::new(3, 3, 0, 2, ret);
let reset_indentation = bumpalo::vec![in &arena; LineComment(" leading comment")]; 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"))), arena.alloc(Located::new(1, 1, 4, 5, Num("5"))),
); );
let loc_def = &*arena.alloc(Located::new(1, 1, 0, 1, def)); 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 ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
let loc_ret = Located::new(3, 3, 0, 2, ret); let loc_ret = Located::new(3, 3, 0, 2, ret);
let reset_indentation = bumpalo::vec![in &arena; LineComment(" leading comment")]; let reset_indentation = bumpalo::vec![in &arena; LineComment(" leading comment")];
@ -1589,7 +1557,7 @@ mod test_parse {
newline.into_bump_slice(), newline.into_bump_slice(),
); );
let loc_def2 = &*arena.alloc(Located::new(2, 2, 0, 5, def2)); 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 ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
let loc_ret = Located::new(4, 4, 0, 2, ret); let loc_ret = Located::new(4, 4, 0, 2, ret);
let reset_indentation = bumpalo::vec![in &arena; LineComment(" leading comment")]; 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")) Located::new(1, 1, 5, 7, Identifier("y"))
]; ];
let def1 = Def::Body( let def1 = Def::Body(
arena.alloc(Located::new( arena.alloc(Located::new(1, 1, 1, 8, RecordDestructure(&fields))),
1,
1,
1,
8,
RecordDestructure(fields.into_bump_slice()),
)),
arena.alloc(Located::new(1, 1, 11, 12, Num("5"))), arena.alloc(Located::new(1, 1, 11, 12, Num("5"))),
); );
let loc_def1 = &*arena.alloc(Located::new(1, 1, 1, 8, def1)); 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, 0, 1, Identifier("y"))),
arena.alloc(Located::new(2, 2, 4, 5, Num("6"))), 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 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 ret = Expr::SpaceBefore(arena.alloc(Num("42")), &newlines);
let loc_ret = Located::new(4, 4, 0, 2, ret); let loc_ret = Located::new(4, 4, 0, 2, ret);
let reset_indentation = bumpalo::vec![in &arena; LineComment(" leading comment")]; let reset_indentation = bumpalo::vec![in &arena; LineComment(" leading comment")];
let expected = Expr::SpaceBefore( let expected = Expr::SpaceBefore(
arena.alloc(Defs(defs, arena.alloc(loc_ret))), arena.alloc(Defs(defs, arena.alloc(loc_ret))),
reset_indentation.into_bump_slice(), &reset_indentation,
); );
assert_parses_to( 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, 0, 3, Identifier("foo"))),
arena.alloc(Located::new(1, 1, 6, 7, Num("4"))), 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_def = &*arena.alloc(Located::new(1, 1, 0, 7, spaced_def));
let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 3, signature)); 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 ret = Expr::SpaceBefore(arena.alloc(Num("42")), &newlines);
let loc_ret = Located::new(3, 3, 0, 2, ret); let loc_ret = Located::new(3, 3, 0, 2, ret);
let expected = Defs(defs, arena.alloc(loc_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 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 ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
let loc_ret = Located::new(2, 2, 0, 2, ret); let loc_ret = Located::new(2, 2, 0, 2, ret);
let expected = Defs(defs, arena.alloc(loc_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 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 ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
let loc_ret = Located::new(2, 2, 0, 2, ret); let loc_ret = Located::new(2, 2, 0, 2, ret);
let expected = Defs(defs, arena.alloc(loc_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_def = &*arena.alloc(Located::new(1, 1, 0, 17, spaced));
let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 3, signature)); 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 ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
let loc_ret = Located::new(3, 3, 0, 2, ret); let loc_ret = Located::new(3, 3, 0, 2, ret);
let expected = Defs(defs, arena.alloc(loc_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_def = &*arena.alloc(Located::new(1, 1, 0, 10, spaced_def));
let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 3, signature)); 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 ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
let loc_ret = Located::new(3, 3, 0, 2, ret); let loc_ret = Located::new(3, 3, 0, 2, ret);
let expected = Defs(defs, arena.alloc(loc_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_def = &*arena.alloc(Located::new(1, 1, 0, 10, spaced_def));
let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 3, signature)); 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 ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
let loc_ret = Located::new(3, 3, 0, 2, ret); let loc_ret = Located::new(3, 3, 0, 2, ret);
let expected = Defs(defs, arena.alloc(loc_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_def = &*arena.alloc(Located::new(1, 1, 0, 10, spaced_def));
let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 3, signature)); 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 ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
let loc_ret = Located::new(3, 3, 0, 2, ret); let loc_ret = Located::new(3, 3, 0, 2, ret);
let expected = Defs(defs, arena.alloc(loc_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_def = &*arena.alloc(Located::new(1, 1, 0, 10, spaced_def));
let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 3, signature)); 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 ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
let loc_ret = Located::new(3, 3, 0, 2, ret); let loc_ret = Located::new(3, 3, 0, 2, ret);
let expected = Defs(defs, arena.alloc(loc_ret)); let expected = Defs(defs, arena.alloc(loc_ret));
@ -2057,24 +2019,21 @@ mod test_parse {
let expr1 = Num("1"); let expr1 = Num("1");
let loc_expr1 = Located::new(1, 1, 7, 8, expr1); let loc_expr1 = Located::new(1, 1, 7, 8, expr1);
let branch1 = &*arena.alloc(WhenBranch { let branch1 = &*arena.alloc(WhenBranch {
patterns: bumpalo::vec![in &arena;loc_pattern1], patterns: arena.alloc([loc_pattern1]),
value: loc_expr1, value: loc_expr1,
guard: None, guard: None,
}); });
let newlines = bumpalo::vec![in &arena; Newline]; let newlines = &[Newline];
let pattern2 = Pattern::SpaceBefore( let pattern2 = Pattern::SpaceBefore(arena.alloc(StrLiteral(PlainLine("mise"))), newlines);
arena.alloc(StrLiteral(PlainLine("mise"))),
newlines.into_bump_slice(),
);
let loc_pattern2 = Located::new(2, 2, 1, 7, pattern2); let loc_pattern2 = Located::new(2, 2, 1, 7, pattern2);
let expr2 = Num("2"); let expr2 = Num("2");
let loc_expr2 = Located::new(2, 2, 11, 12, expr2); let loc_expr2 = Located::new(2, 2, 11, 12, expr2);
let branch2 = &*arena.alloc(WhenBranch { let branch2 = &*arena.alloc(WhenBranch {
patterns: bumpalo::vec![in &arena;loc_pattern2 ], patterns: arena.alloc([loc_pattern2]),
value: loc_expr2, value: loc_expr2,
guard: None, guard: None,
}); });
let branches = bumpalo::vec![in &arena; branch1, branch2]; let branches = &[branch1, branch2];
let var = Var { let var = Var {
module_name: "", module_name: "",
ident: "x", ident: "x",
@ -2098,29 +2057,27 @@ mod test_parse {
#[test] #[test]
fn when_with_numbers() { fn when_with_numbers() {
let arena = Bump::new(); let arena = Bump::new();
let newlines = bumpalo::vec![in &arena; Newline]; let newlines = &[Newline];
let pattern1 = let pattern1 = Pattern::SpaceBefore(arena.alloc(NumLiteral("1")), newlines);
Pattern::SpaceBefore(arena.alloc(NumLiteral("1")), newlines.into_bump_slice());
let loc_pattern1 = Located::new(1, 1, 1, 2, pattern1); let loc_pattern1 = Located::new(1, 1, 1, 2, pattern1);
let expr1 = Num("2"); let expr1 = Num("2");
let loc_expr1 = Located::new(1, 1, 6, 7, expr1); let loc_expr1 = Located::new(1, 1, 6, 7, expr1);
let branch1 = &*arena.alloc(WhenBranch { let branch1 = &*arena.alloc(WhenBranch {
patterns: bumpalo::vec![in &arena;loc_pattern1], patterns: arena.alloc([loc_pattern1]),
value: loc_expr1, value: loc_expr1,
guard: None, guard: None,
}); });
let newlines = bumpalo::vec![in &arena; Newline]; let newlines = &[Newline];
let pattern2 = let pattern2 = Pattern::SpaceBefore(arena.alloc(NumLiteral("3")), newlines);
Pattern::SpaceBefore(arena.alloc(NumLiteral("3")), newlines.into_bump_slice());
let loc_pattern2 = Located::new(2, 2, 1, 2, pattern2); let loc_pattern2 = Located::new(2, 2, 1, 2, pattern2);
let expr2 = Num("4"); let expr2 = Num("4");
let loc_expr2 = Located::new(2, 2, 6, 7, expr2); let loc_expr2 = Located::new(2, 2, 6, 7, expr2);
let branch2 = &*arena.alloc(WhenBranch { let branch2 = &*arena.alloc(WhenBranch {
patterns: bumpalo::vec![in &arena;loc_pattern2], patterns: arena.alloc([loc_pattern2]),
value: loc_expr2, value: loc_expr2,
guard: None, guard: None,
}); });
let branches = bumpalo::vec![in &arena; branch1, branch2]; let branches = &[branch1, branch2];
let var = Var { let var = Var {
module_name: "", module_name: "",
ident: "x", ident: "x",
@ -2144,35 +2101,32 @@ mod test_parse {
#[test] #[test]
fn when_with_records() { fn when_with_records() {
let arena = Bump::new(); let arena = Bump::new();
let newlines = bumpalo::vec![in &arena; Newline]; let newlines = &[Newline];
let identifiers1 = bumpalo::vec![in &arena; Located::new(1, 1, 3, 4, Identifier("y")) ]; let identifiers1 = &[Located::new(1, 1, 3, 4, Identifier("y"))];
let pattern1 = Pattern::SpaceBefore( let pattern1 = Pattern::SpaceBefore(arena.alloc(RecordDestructure(identifiers1)), newlines);
arena.alloc(RecordDestructure(identifiers1.into_bump_slice())),
newlines.into_bump_slice(),
);
let loc_pattern1 = Located::new(1, 1, 1, 6, pattern1); let loc_pattern1 = Located::new(1, 1, 1, 6, pattern1);
let expr1 = Num("2"); let expr1 = Num("2");
let loc_expr1 = Located::new(1, 1, 10, 11, expr1); let loc_expr1 = Located::new(1, 1, 10, 11, expr1);
let branch1 = &*arena.alloc(WhenBranch { let branch1 = &*arena.alloc(WhenBranch {
patterns: bumpalo::vec![in &arena;loc_pattern1 ], patterns: arena.alloc([loc_pattern1]),
value: loc_expr1, value: loc_expr1,
guard: None, guard: None,
}); });
let newlines = bumpalo::vec![in &arena; Newline]; let newlines = &[Newline];
let identifiers2 = bumpalo::vec![in &arena; Located::new(2, 2, 3, 4, Identifier("z")), Located::new(2, 2, 6, 7, Identifier("w")) ]; let identifiers2 = &[
let pattern2 = Pattern::SpaceBefore( Located::new(2, 2, 3, 4, Identifier("z")),
arena.alloc(RecordDestructure(identifiers2.into_bump_slice())), Located::new(2, 2, 6, 7, Identifier("w")),
newlines.into_bump_slice(), ];
); let pattern2 = Pattern::SpaceBefore(arena.alloc(RecordDestructure(identifiers2)), newlines);
let loc_pattern2 = Located::new(2, 2, 1, 9, pattern2); let loc_pattern2 = Located::new(2, 2, 1, 9, pattern2);
let expr2 = Num("4"); let expr2 = Num("4");
let loc_expr2 = Located::new(2, 2, 13, 14, expr2); let loc_expr2 = Located::new(2, 2, 13, 14, expr2);
let branch2 = &*arena.alloc(WhenBranch { let branch2 = &*arena.alloc(WhenBranch {
patterns: bumpalo::vec![in &arena;loc_pattern2 ], patterns: arena.alloc([loc_pattern2]),
value: loc_expr2, value: loc_expr2,
guard: None, guard: None,
}); });
let branches = bumpalo::vec![in &arena; branch1, branch2]; let branches = &[branch1, branch2];
let var = Var { let var = Var {
module_name: "", module_name: "",
ident: "x", ident: "x",
@ -2196,41 +2150,33 @@ mod test_parse {
#[test] #[test]
fn when_with_alternative_patterns() { fn when_with_alternative_patterns() {
let arena = Bump::new(); let arena = Bump::new();
let newlines = bumpalo::vec![in &arena; Newline]; let newlines = &[Newline];
let pattern1 = Pattern::SpaceBefore( let pattern1 = Pattern::SpaceBefore(arena.alloc(StrLiteral(PlainLine("blah"))), newlines);
arena.alloc(StrLiteral(PlainLine("blah"))),
newlines.into_bump_slice(),
);
let pattern1_alt = StrLiteral(PlainLine("blop")); let pattern1_alt = StrLiteral(PlainLine("blop"));
let loc_pattern1 = Located::new(1, 1, 1, 7, pattern1); let loc_pattern1 = Located::new(1, 1, 1, 7, pattern1);
let loc_pattern1_alt = Located::new(1, 1, 10, 16, pattern1_alt); let loc_pattern1_alt = Located::new(1, 1, 10, 16, pattern1_alt);
let expr1 = Num("1"); let expr1 = Num("1");
let loc_expr1 = Located::new(1, 1, 20, 21, expr1); let loc_expr1 = Located::new(1, 1, 20, 21, expr1);
let branch1 = &*arena.alloc(WhenBranch { 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, value: loc_expr1,
guard: None, guard: None,
}); });
let newlines = bumpalo::vec![in &arena; Newline]; let newlines = &[Newline];
let pattern2 = Pattern::SpaceBefore( let pattern2 = Pattern::SpaceBefore(arena.alloc(StrLiteral(PlainLine("foo"))), newlines);
arena.alloc(StrLiteral(PlainLine("foo"))), let newlines = &[Newline];
newlines.into_bump_slice(), let pattern2_alt =
); Pattern::SpaceBefore(arena.alloc(StrLiteral(PlainLine("bar"))), newlines);
let newlines = bumpalo::vec![in &arena; Newline];
let pattern2_alt = Pattern::SpaceBefore(
arena.alloc(StrLiteral(PlainLine("bar"))),
newlines.into_bump_slice(),
);
let loc_pattern2 = Located::new(2, 2, 1, 6, pattern2); let loc_pattern2 = Located::new(2, 2, 1, 6, pattern2);
let loc_pattern2_alt = Located::new(3, 3, 1, 6, pattern2_alt); let loc_pattern2_alt = Located::new(3, 3, 1, 6, pattern2_alt);
let expr2 = Num("2"); let expr2 = Num("2");
let loc_expr2 = Located::new(3, 3, 10, 11, expr2); let loc_expr2 = Located::new(3, 3, 10, 11, expr2);
let branch2 = &*arena.alloc(WhenBranch { 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, value: loc_expr2,
guard: None, guard: None,
}); });
let branches = bumpalo::vec![in &arena; branch1, branch2]; let branches = &[branch1, branch2];
let var = Var { let var = Var {
module_name: "", module_name: "",
ident: "x", ident: "x",
@ -2317,9 +2263,9 @@ mod test_parse {
use roc_parse::ast::Def::*; use roc_parse::ast::Def::*;
let arena = Bump::new(); let arena = Bump::new();
let newlines1 = bumpalo::vec![in &arena; Newline, Newline]; let newlines1 = &[Newline, Newline];
let newlines2 = bumpalo::vec![in &arena; Newline]; let newlines2 = &[Newline];
let newlines3 = bumpalo::vec![in &arena; Newline]; let newlines3 = &[Newline];
let pattern1 = Identifier("foo"); let pattern1 = Identifier("foo");
let pattern2 = Identifier("bar"); let pattern2 = Identifier("bar");
let pattern3 = Identifier("baz"); 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, 0, 3, pattern1)),
arena.alloc(Located::new(0, 0, 6, 7, Num("1"))), arena.alloc(Located::new(0, 0, 6, 7, Num("1"))),
)), )),
newlines1.into_bump_slice(), newlines1,
); );
let def2 = SpaceAfter( let def2 = SpaceAfter(
arena.alloc(Body( arena.alloc(Body(
arena.alloc(Located::new(2, 2, 0, 3, pattern2)), arena.alloc(Located::new(2, 2, 0, 3, pattern2)),
arena.alloc(Located::new(2, 2, 6, 10, Str(PlainLine("hi")))), arena.alloc(Located::new(2, 2, 6, 10, Str(PlainLine("hi")))),
)), )),
newlines2.into_bump_slice(), newlines2,
); );
let def3 = SpaceAfter( let def3 = SpaceAfter(
arena.alloc(Body( arena.alloc(Body(
arena.alloc(Located::new(3, 3, 0, 3, pattern3)), arena.alloc(Located::new(3, 3, 0, 3, pattern3)),
arena.alloc(Located::new(3, 3, 6, 13, Str(PlainLine("stuff")))), arena.alloc(Located::new(3, 3, 6, 13, Str(PlainLine("stuff")))),
)), )),
newlines3.into_bump_slice(), newlines3,
); );
let expected = bumpalo::vec![in &arena; let expected = bumpalo::vec![in &arena;
Located::new(0, 0, 0, 7, def1), Located::new(0, 0, 0, 7, def1),
Located::new(2, 2, 0, 10, def2), Located::new(2, 2, 0, 10, def2),
Located::new(3, 3, 0, 13, def3) Located::new(3, 3, 0, 13, def3),
]; ];
let src = indoc!( let src = indoc!(
r#" r#"
@ -2369,15 +2315,15 @@ mod test_parse {
fn newline_after_equals() { fn newline_after_equals() {
// Regression test for https://github.com/rtfeldman/roc/issues/51 // Regression test for https://github.com/rtfeldman/roc/issues/51
let arena = Bump::new(); let arena = Bump::new();
let newlines = bumpalo::vec![in &arena; Newline, Newline]; let newlines = &[Newline, Newline];
let num = arena.alloc(Num("5")); let num = arena.alloc(Num("5"));
let def = Def::Body( let def = Def::Body(
arena.alloc(Located::new(0, 0, 0, 1, Identifier("x"))), arena.alloc(Located::new(0, 0, 0, 1, Identifier("x"))),
arena.alloc(Located::new(1, 1, 4, 5, Expr::SpaceBefore(num, &[Newline]))), 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 loc_def = &*arena.alloc(Located::new(0, 0, 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 ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines);
let loc_ret = Located::new(3, 3, 0, 2, ret); let loc_ret = Located::new(3, 3, 0, 2, ret);
let expected = Defs(defs, arena.alloc(loc_ret)); let expected = Defs(defs, arena.alloc(loc_ret));
@ -2399,24 +2345,24 @@ mod test_parse {
#[test] #[test]
fn basic_docs() { fn basic_docs() {
let arena = Bump::new(); let arena = Bump::new();
let newlines = bumpalo::vec![in &arena; Newline, Newline]; let newlines = &[Newline, Newline];
let def = Def::Body( let def = Def::Body(
arena.alloc(Located::new(4, 4, 0, 1, Identifier("x"))), arena.alloc(Located::new(4, 4, 0, 1, Identifier("x"))),
arena.alloc(Located::new(4, 4, 4, 5, Num("5"))), arena.alloc(Located::new(4, 4, 4, 5, Num("5"))),
); );
let loc_def = &*arena.alloc(Located::new(4, 4, 0, 1, def)); let loc_def = &*arena.alloc(Located::new(4, 4, 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 ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines);
let loc_ret = Located::new(6, 6, 0, 2, ret); 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("first line of docs"),
DocComment(" second line"), DocComment(" second line"),
DocComment(" third line"), DocComment(" third line"),
DocComment("fourth line") DocComment("fourth line"),
]; ];
let expected = Expr::SpaceBefore( let expected = Expr::SpaceBefore(
arena.alloc(Defs(defs, arena.alloc(loc_ret))), arena.alloc(Defs(defs, arena.alloc(loc_ret))),
reset_indentation.into_bump_slice(), reset_indentation,
); );
assert_parses_to( assert_parses_to(
@ -2438,16 +2384,16 @@ mod test_parse {
#[test] #[test]
fn not_docs() { fn not_docs() {
let arena = Bump::new(); let arena = Bump::new();
let newlines = bumpalo::vec![in &arena; Newline, Newline]; let newlines = &[Newline, Newline];
let def = Def::Body( let def = Def::Body(
arena.alloc(Located::new(4, 4, 0, 1, Identifier("x"))), arena.alloc(Located::new(4, 4, 0, 1, Identifier("x"))),
arena.alloc(Located::new(4, 4, 4, 5, Num("5"))), arena.alloc(Located::new(4, 4, 4, 5, Num("5"))),
); );
let loc_def = &*arena.alloc(Located::new(4, 4, 0, 1, def)); let loc_def = &*arena.alloc(Located::new(4, 4, 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 ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines);
let loc_ret = Located::new(6, 6, 0, 2, ret); let loc_ret = Located::new(6, 6, 0, 2, ret);
let reset_indentation = bumpalo::vec![in &arena; let reset_indentation = &[
LineComment("######"), LineComment("######"),
LineComment("## not docs!"), LineComment("## not docs!"),
LineComment("#still not docs"), LineComment("#still not docs"),
@ -2455,7 +2401,7 @@ mod test_parse {
]; ];
let expected = Expr::SpaceBefore( let expected = Expr::SpaceBefore(
arena.alloc(Defs(defs, arena.alloc(loc_ret))), arena.alloc(Defs(defs, arena.alloc(loc_ret))),
reset_indentation.into_bump_slice(), reset_indentation,
); );
assert_parses_to( assert_parses_to(
@ -2477,16 +2423,16 @@ mod test_parse {
#[test] #[test]
fn mixed_docs() { fn mixed_docs() {
let arena = Bump::new(); let arena = Bump::new();
let newlines = bumpalo::vec![in &arena; Newline, Newline]; let newlines = &[Newline, Newline];
let def = Def::Body( let def = Def::Body(
arena.alloc(Located::new(4, 4, 0, 1, Identifier("x"))), arena.alloc(Located::new(4, 4, 0, 1, Identifier("x"))),
arena.alloc(Located::new(4, 4, 4, 5, Num("5"))), arena.alloc(Located::new(4, 4, 4, 5, Num("5"))),
); );
let loc_def = &*arena.alloc(Located::new(4, 4, 0, 1, def)); let loc_def = &*arena.alloc(Located::new(4, 4, 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 ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines);
let loc_ret = Located::new(6, 6, 0, 2, ret); let loc_ret = Located::new(6, 6, 0, 2, ret);
let reset_indentation = bumpalo::vec![in &arena; let reset_indentation = &[
LineComment("## not docs!"), LineComment("## not docs!"),
DocComment("docs, but with a problem"), DocComment("docs, but with a problem"),
DocComment("(namely that this is a mix of docs and regular comments)"), DocComment("(namely that this is a mix of docs and regular comments)"),
@ -2494,7 +2440,7 @@ mod test_parse {
]; ];
let expected = Expr::SpaceBefore( let expected = Expr::SpaceBefore(
arena.alloc(Defs(defs, arena.alloc(loc_ret))), arena.alloc(Defs(defs, arena.alloc(loc_ret))),
reset_indentation.into_bump_slice(), reset_indentation,
); );
assert_parses_to( assert_parses_to(
@ -2517,30 +2463,27 @@ mod test_parse {
fn malformed_pattern_field_access() { fn malformed_pattern_field_access() {
// See https://github.com/rtfeldman/roc/issues/399 // See https://github.com/rtfeldman/roc/issues/399
let arena = Bump::new(); let arena = Bump::new();
let newlines = bumpalo::vec![in &arena; Newline]; let newlines = &[Newline];
let pattern1 = Pattern::SpaceBefore( let pattern1 = Pattern::SpaceBefore(arena.alloc(Pattern::Malformed("bar.and")), newlines);
arena.alloc(Pattern::Malformed("bar.and")),
newlines.into_bump_slice(),
);
let loc_pattern1 = Located::new(1, 1, 4, 11, pattern1); let loc_pattern1 = Located::new(1, 1, 4, 11, pattern1);
let expr1 = Num("1"); let expr1 = Num("1");
let loc_expr1 = Located::new(1, 1, 15, 16, expr1); let loc_expr1 = Located::new(1, 1, 15, 16, expr1);
let branch1 = &*arena.alloc(WhenBranch { let branch1 = &*arena.alloc(WhenBranch {
patterns: bumpalo::vec![in &arena;loc_pattern1], patterns: arena.alloc([loc_pattern1]),
value: loc_expr1, value: loc_expr1,
guard: None, guard: None,
}); });
let newlines = bumpalo::vec![in &arena; Newline]; let newlines = &[Newline];
let pattern2 = Pattern::SpaceBefore(arena.alloc(Underscore), newlines.into_bump_slice()); let pattern2 = Pattern::SpaceBefore(arena.alloc(Underscore), newlines);
let loc_pattern2 = Located::new(2, 2, 4, 5, pattern2); let loc_pattern2 = Located::new(2, 2, 4, 5, pattern2);
let expr2 = Num("4"); let expr2 = Num("4");
let loc_expr2 = Located::new(2, 2, 9, 10, expr2); let loc_expr2 = Located::new(2, 2, 9, 10, expr2);
let branch2 = &*arena.alloc(WhenBranch { let branch2 = &*arena.alloc(WhenBranch {
patterns: bumpalo::vec![in &arena;loc_pattern2 ], patterns: arena.alloc([loc_pattern2]),
value: loc_expr2, value: loc_expr2,
guard: None, guard: None,
}); });
let branches = bumpalo::vec![in &arena; branch1, branch2]; let branches = &[branch1, branch2];
let var = Var { let var = Var {
module_name: "", module_name: "",
ident: "x", ident: "x",
@ -2565,30 +2508,27 @@ mod test_parse {
fn malformed_pattern_module_name() { fn malformed_pattern_module_name() {
// See https://github.com/rtfeldman/roc/issues/399 // See https://github.com/rtfeldman/roc/issues/399
let arena = Bump::new(); let arena = Bump::new();
let newlines = bumpalo::vec![in &arena; Newline]; let newlines = &[Newline];
let pattern1 = Pattern::SpaceBefore( let pattern1 = Pattern::SpaceBefore(arena.alloc(Pattern::Malformed("Foo.and")), newlines);
arena.alloc(Pattern::Malformed("Foo.and")),
newlines.into_bump_slice(),
);
let loc_pattern1 = Located::new(1, 1, 4, 11, pattern1); let loc_pattern1 = Located::new(1, 1, 4, 11, pattern1);
let expr1 = Num("1"); let expr1 = Num("1");
let loc_expr1 = Located::new(1, 1, 15, 16, expr1); let loc_expr1 = Located::new(1, 1, 15, 16, expr1);
let branch1 = &*arena.alloc(WhenBranch { let branch1 = &*arena.alloc(WhenBranch {
patterns: bumpalo::vec![in &arena;loc_pattern1], patterns: arena.alloc([loc_pattern1]),
value: loc_expr1, value: loc_expr1,
guard: None, guard: None,
}); });
let newlines = bumpalo::vec![in &arena; Newline]; let newlines = &[Newline];
let pattern2 = Pattern::SpaceBefore(arena.alloc(Underscore), newlines.into_bump_slice()); let pattern2 = Pattern::SpaceBefore(arena.alloc(Underscore), newlines);
let loc_pattern2 = Located::new(2, 2, 4, 5, pattern2); let loc_pattern2 = Located::new(2, 2, 4, 5, pattern2);
let expr2 = Num("4"); let expr2 = Num("4");
let loc_expr2 = Located::new(2, 2, 9, 10, expr2); let loc_expr2 = Located::new(2, 2, 9, 10, expr2);
let branch2 = &*arena.alloc(WhenBranch { let branch2 = &*arena.alloc(WhenBranch {
patterns: bumpalo::vec![in &arena;loc_pattern2 ], patterns: arena.alloc([loc_pattern2]),
value: loc_expr2, value: loc_expr2,
guard: None, guard: None,
}); });
let branches = bumpalo::vec![in &arena; branch1, branch2]; let branches = &[branch1, branch2];
let var = Var { let var = Var {
module_name: "", module_name: "",
ident: "x", ident: "x",

View file

@ -1369,7 +1369,7 @@ mod solve_expr {
indoc!( indoc!(
r#" r#"
int : Num Integer int : Num Integer
int = 5.5 int = 5
int int
"# "#
@ -1384,7 +1384,7 @@ mod solve_expr {
indoc!( indoc!(
r#" r#"
int : Num.Num Num.Integer int : Num.Num Num.Integer
int = 5.5 int = 5
int int
"# "#
@ -2039,16 +2039,17 @@ mod solve_expr {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
Peano : [ S Peano, Z ] app Test provides [ main ] imports []
map : Peano -> Peano Peano : [ S Peano, Z ]
map = \peano ->
when peano is
Z -> Z
S rest ->
map rest |> S
map : Peano -> Peano
map = \peano ->
when peano is
Z -> Z
S rest -> S (map rest)
main =
map map
"# "#
), ),

View file

@ -328,11 +328,7 @@ impl Subs {
let l_key = self.utable.get_root_key(key); let l_key = self.utable.get_root_key(key);
self.utable.update_value(l_key, |node| { self.utable.update_value(l_key, |node| {
let mut new_desc = node.value.clone(); node.value.rank = rank;
new_desc.rank = rank;
node.value = new_desc;
}); });
} }
@ -340,11 +336,7 @@ impl Subs {
let l_key = self.utable.get_root_key(key); let l_key = self.utable.get_root_key(key);
self.utable.update_value(l_key, |node| { self.utable.update_value(l_key, |node| {
let mut new_desc = node.value.clone(); node.value.mark = mark;
new_desc.mark = mark;
node.value = new_desc;
}); });
} }
@ -352,11 +344,7 @@ impl Subs {
let l_key = self.utable.get_root_key(key); let l_key = self.utable.get_root_key(key);
self.utable.update_value(l_key, |node| { self.utable.update_value(l_key, |node| {
let mut new_desc = node.value.clone(); node.value.content = content;
new_desc.content = content;
node.value = new_desc;
}); });
} }

2
nix/bin/llvm-as-10 Executable file
View file

@ -0,0 +1,2 @@
#!/bin/sh
llvm-as "$@"

36
nix/zig.nix Normal file
View 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
View 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
'';
}

View file

@ -382,6 +382,12 @@ impl RocStr {
} }
} }
impl From<&str> for RocStr {
fn from(str: &str) -> Self {
Self::from_slice(str.as_bytes())
}
}
impl fmt::Debug for RocStr { impl fmt::Debug for RocStr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// RocStr { is_small_str: false, storage: Refcounted(3), elements: [ 1,2,3,4] } // RocStr { is_small_str: false, storage: Refcounted(3), elements: [ 1,2,3,4] }

View file

@ -9,10 +9,11 @@ with {
ref = "refs/heads/nixpkgs-unstable"; ref = "refs/heads/nixpkgs-unstable";
rev = "502845c3e31ef3de0e424f3fcb09217df2ce6df6"; rev = "502845c3e31ef3de0e424f3fcb09217df2ce6df6";
}) { }; }) { };
isMacOS = builtins.currentSystem == "x86_64-darwin";
}; };
let let
isMacOS = builtins.currentSystem == "x86_64-darwin";
darwin-frameworks = darwin-frameworks =
if isMacOS then if isMacOS then
with pkgs.darwin.apple_sdk.frameworks; [ with pkgs.darwin.apple_sdk.frameworks; [
@ -28,6 +29,7 @@ let
[ ]; [ ];
llvm = pkgs.llvm_10; llvm = pkgs.llvm_10;
lld = pkgs.lld_10; # this should match llvm's version lld = pkgs.lld_10; # this should match llvm's version
zig = import ./nix/zig.nix { inherit pkgs isMacOS; };
inputs = inputs =
[ [
# build libraries # build libraries
@ -35,6 +37,7 @@ let
pkgs.cargo pkgs.cargo
llvm llvm
pkgs.valgrind pkgs.valgrind
zig
# llb deps # llb deps
pkgs.libffi pkgs.libffi
pkgs.libxml2 pkgs.libxml2
@ -43,10 +46,16 @@ let
lld lld
# dev tools # dev tools
pkgs.rust-analyzer pkgs.rust-analyzer
# (import ./nix/zls.nix { inherit pkgs zig; })
pkgs.ccls pkgs.ccls
]; ];
in pkgs.mkShell { in pkgs.mkShell {
buildInputs = inputs ++ darwin-frameworks; buildInputs = inputs ++ darwin-frameworks;
LLVM_SYS_100_PREFIX = "${llvm}"; LLVM_SYS_100_PREFIX = "${llvm}";
# Aliases don't work cross shell, so we do this
shellHook = ''
export PATH="$PATH:$PWD/nix/bin"
'';
} }