mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 00:24:34 +00:00
Merge branch 'trunk' of github.com:rtfeldman/roc into str-split
This commit is contained in:
commit
d5dd03313a
62 changed files with 3436 additions and 678 deletions
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
|
@ -9,6 +9,9 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
|
|
||||||
|
- name: Verify compiler/builtin/bitcode/regenerate.sh was run if necessary
|
||||||
|
run: pushd compiler/builtins/bitcode && ./regenerate.sh && git diff --exit-code ../../gen/src/llvm/builtins.bc && popd
|
||||||
|
|
||||||
- name: Install LLVM
|
- name: Install LLVM
|
||||||
run: sudo ./ci/install-llvm.sh 10
|
run: sudo ./ci/install-llvm.sh 10
|
||||||
|
|
||||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,3 +1,6 @@
|
||||||
target
|
target
|
||||||
.direnv
|
.direnv
|
||||||
*.rs.bk
|
*.rs.bk
|
||||||
|
|
||||||
|
#valgrind
|
||||||
|
vgcore.*
|
|
@ -1,9 +1,9 @@
|
||||||
# Building the Roc compiler from source
|
# Building the Roc compiler from source
|
||||||
|
|
||||||
|
|
||||||
## Installing LLVM
|
## Installing LLVM and libc++abi
|
||||||
|
|
||||||
To build the compiler, you need a particular version of LLVM installed on your system.
|
To build the compiler, you need both `libc++abi` and a particular version of LLVM installed on your system. Some systems may already have `libc++abi` on them, but if not, you may need to install it. (On Ubuntu, this can be done with `apt-get install libc++abi-dev`.)
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
|
@ -24,7 +24,9 @@ That will help us improve this document for everyone who reads it in the future!
|
||||||
### LLVM installation on Linux
|
### LLVM installation on Linux
|
||||||
|
|
||||||
On some Linux systems we've seen the error "failed to run custom build command for x11".
|
On some Linux systems we've seen the error "failed to run custom build command for x11".
|
||||||
On Ubuntu, running `sudo apt-get install cmake libx11-dev` fixed this.
|
On Ubuntu, running `sudo apt install pkg-config cmake libx11-dev` fixed this.
|
||||||
|
|
||||||
|
If you encounter `cannot find -lz` run `sudo apt install zlib1g-dev`.
|
||||||
|
|
||||||
### LLVM installation on macOS
|
### LLVM installation on macOS
|
||||||
|
|
||||||
|
|
11
Cargo.lock
generated
11
Cargo.lock
generated
|
@ -579,6 +579,9 @@ dependencies = [
|
||||||
"fs_extra",
|
"fs_extra",
|
||||||
"handlebars",
|
"handlebars",
|
||||||
"pulldown-cmark",
|
"pulldown-cmark",
|
||||||
|
"roc_builtins",
|
||||||
|
"roc_collections",
|
||||||
|
"roc_load",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
@ -2322,6 +2325,7 @@ dependencies = [
|
||||||
"roc_problem",
|
"roc_problem",
|
||||||
"roc_region",
|
"roc_region",
|
||||||
"roc_solve",
|
"roc_solve",
|
||||||
|
"roc_std",
|
||||||
"roc_types",
|
"roc_types",
|
||||||
"roc_unify",
|
"roc_unify",
|
||||||
"roc_uniq",
|
"roc_uniq",
|
||||||
|
@ -2481,6 +2485,13 @@ dependencies = [
|
||||||
"roc_unify",
|
"roc_unify",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "roc_std"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "roc_types"
|
name = "roc_types"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
|
@ -26,6 +26,7 @@ members = [
|
||||||
"vendor/pretty",
|
"vendor/pretty",
|
||||||
"editor",
|
"editor",
|
||||||
"cli",
|
"cli",
|
||||||
|
"roc_std",
|
||||||
"docs"
|
"docs"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#[macro_use]
|
// #[macro_use]
|
||||||
extern crate pretty_assertions;
|
extern crate pretty_assertions;
|
||||||
|
|
||||||
extern crate bumpalo;
|
extern crate bumpalo;
|
||||||
|
@ -20,7 +20,9 @@ mod cli_run {
|
||||||
example_file("hello-world", "Hello.roc").to_str().unwrap(),
|
example_file("hello-world", "Hello.roc").to_str().unwrap(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
assert_eq!(&out.stderr, "");
|
if !out.stderr.is_empty() {
|
||||||
|
panic!(out.stderr);
|
||||||
|
}
|
||||||
assert!(&out.stdout.ends_with("Hello, World!!!!!!!!!!!!!\n"));
|
assert!(&out.stdout.ends_with("Hello, World!!!!!!!!!!!!!\n"));
|
||||||
assert!(out.status.success());
|
assert!(out.status.success());
|
||||||
}
|
}
|
||||||
|
@ -33,7 +35,9 @@ mod cli_run {
|
||||||
"--optimize",
|
"--optimize",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
assert_eq!(&out.stderr, "");
|
if !out.stderr.is_empty() {
|
||||||
|
panic!(out.stderr);
|
||||||
|
}
|
||||||
assert!(&out
|
assert!(&out
|
||||||
.stdout
|
.stdout
|
||||||
.ends_with("[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]\n"));
|
.ends_with("[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]\n"));
|
||||||
|
|
|
@ -105,11 +105,18 @@ pub fn gen(
|
||||||
Declare(def) | Builtin(def) => match def.loc_pattern.value {
|
Declare(def) | Builtin(def) => match def.loc_pattern.value {
|
||||||
Identifier(symbol) => {
|
Identifier(symbol) => {
|
||||||
match def.loc_expr.value {
|
match def.loc_expr.value {
|
||||||
Closure(annotation, _, recursivity, loc_args, boxed_body) => {
|
Closure {
|
||||||
|
function_type: annotation,
|
||||||
|
return_type: ret_var,
|
||||||
|
recursive: recursivity,
|
||||||
|
arguments: loc_args,
|
||||||
|
loc_body: boxed_body,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
let is_tail_recursive =
|
let is_tail_recursive =
|
||||||
matches!(recursivity, roc_can::expr::Recursive::TailRecursive);
|
matches!(recursivity, roc_can::expr::Recursive::TailRecursive);
|
||||||
|
|
||||||
let (loc_body, ret_var) = *boxed_body;
|
let loc_body = *boxed_body;
|
||||||
|
|
||||||
// If this is an exposed symbol, we need to
|
// If this is an exposed symbol, we need to
|
||||||
// register it as such. Otherwise, since it
|
// register it as such. Otherwise, since it
|
||||||
|
|
|
@ -17,42 +17,37 @@ The source we'll use to generate the bitcode is in `src/lib.rs` in this director
|
||||||
To generate the bitcode, `cd` into `compiler/builtins/bitcode/` and run:
|
To generate the bitcode, `cd` into `compiler/builtins/bitcode/` and run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ cargo rustc --release --lib -- --emit=llvm-bc
|
$ ./regenerate.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
Then look in the root `roc` source directory under `target/release/deps/` for a file
|
|
||||||
with a name like `roc_builtins_bitcode-8da0901c58a73ebf.bc` - except
|
|
||||||
probably with a different hash before the `.bc`. If there's more than one
|
|
||||||
`*.bc` file in that directory, delete the whole `deps/` directory and re-run
|
|
||||||
the `cargo rustc` command above to regenerate it.
|
|
||||||
|
|
||||||
> If you want to take a look at the human-readable LLVM IR rather than the
|
> If you want to take a look at the human-readable LLVM IR rather than the
|
||||||
> bitcode, run this instead and look for a `.ll` file instead of a `.bc` file:
|
> bitcode, run this instead and look for a `.ll` file instead of a `.bc` file:
|
||||||
>
|
>
|
||||||
> ```bash
|
> ```bash
|
||||||
> $ cargo rustc --release --lib -- --emit=llvm-ir
|
> $ cargo rustc --release --lib -- --emit=llvm-ir
|
||||||
> ```
|
> ```
|
||||||
|
>
|
||||||
|
> Then look in the root `roc` source directory under `target/release/deps/` for a file
|
||||||
|
> with a name like `roc_builtins_bitcode-8da0901c58a73ebf.bc` - except
|
||||||
|
> probably with a different hash before the `.bc`. If there's more than one
|
||||||
|
> `*.bc` file in that directory, delete the whole `deps/` directory and re-run
|
||||||
|
> the `cargo rustc` command above to regenerate it.
|
||||||
|
|
||||||
**Note**: In order to be able to address the bitcode functions by name, they need to be defined with the `#[no_mangle]` attribute.
|
**Note**: In order to be able to address the bitcode functions by name, they need to be defined with the `#[no_mangle]` attribute.
|
||||||
|
|
||||||
## Importing the bitcode
|
|
||||||
|
|
||||||
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
|
Since Roc is designed to be distributed as a single binary, these bytes
|
||||||
need to be included in the raw source somewhere.
|
need to be included in the raw source somewhere.
|
||||||
|
|
||||||
The `llvm/src/build.rs` file statically imports these raw bytes
|
The `llvm/src/build.rs` file statically imports these raw bytes
|
||||||
using the [`include_bytes!` macro](https://doc.rust-lang.org/std/macro.include_bytes.html),
|
using the [`include_bytes!` macro](https://doc.rust-lang.org/std/macro.include_bytes.html).
|
||||||
so we just need to move the `.bc` file from the previous step to the correct
|
|
||||||
location.
|
|
||||||
|
|
||||||
The current `.bc` file is located at:
|
The current `.bc` file is located at:
|
||||||
|
|
||||||
```
|
```
|
||||||
compiler/gen/src/llvm/builtins.bc
|
compiler/gen/src/llvm/builtins.bc
|
||||||
```
|
```
|
||||||
|
|
||||||
...so you want to overwrite it with the new `.bc` file in `target/deps/`
|
The script will automatically replace this `.bc` file with the new one.
|
||||||
|
|
||||||
Once that's done, `git status` should show that the `builtins.bc` file
|
Once that's done, `git status` should show that the `builtins.bc` file
|
||||||
has been changed. Commit that change and you're done!
|
has been changed. Commit that change and you're done!
|
||||||
|
|
19
compiler/builtins/bitcode/regenerate.sh
Executable file
19
compiler/builtins/bitcode/regenerate.sh
Executable file
|
@ -0,0 +1,19 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euxo pipefail
|
||||||
|
|
||||||
|
# Clear out any existing output files. Sometimes if these are there, rustc
|
||||||
|
# doesn't generate the .bc file - or we can end up with more than one .bc
|
||||||
|
rm -rf ../../../target/release/deps/
|
||||||
|
|
||||||
|
# Regenerate the .bc file
|
||||||
|
cargo rustc --release --lib -- --emit=llvm-bc
|
||||||
|
|
||||||
|
bc_files=$(ls ../../../target/release/deps/*.bc | wc -l)
|
||||||
|
|
||||||
|
if [[ $bc_files != 1 ]]; then
|
||||||
|
echo "More than one .bc file was emitted somehow."
|
||||||
|
exit 1;
|
||||||
|
fi
|
||||||
|
|
||||||
|
cp ../../../target/release/deps/*.bc ../../gen/src/llvm/builtins.bc
|
|
@ -4,6 +4,8 @@
|
||||||
#![crate_type = "lib"]
|
#![crate_type = "lib"]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
|
mod libm;
|
||||||
|
|
||||||
/// TODO replace this with a normal Inkwell build_cast call - this was just
|
/// TODO replace this with a normal Inkwell build_cast call - this was just
|
||||||
/// used as a proof of concept for getting bitcode importing working!
|
/// used as a proof of concept for getting bitcode importing working!
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -45,3 +47,8 @@ pub fn pow_int_(mut base: i64, mut exp: i64) -> i64 {
|
||||||
pub fn is_finite_(num: f64) -> bool {
|
pub fn is_finite_(num: f64) -> bool {
|
||||||
f64::is_finite(num)
|
f64::is_finite(num)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn atan_(x: f64) -> f64 {
|
||||||
|
libm::atan(x)
|
||||||
|
}
|
||||||
|
|
199
compiler/builtins/bitcode/src/libm.rs
Normal file
199
compiler/builtins/bitcode/src/libm.rs
Normal file
|
@ -0,0 +1,199 @@
|
||||||
|
/// Adapted from Rust's libm module, by the Rust core team,
|
||||||
|
/// licensed under the Apache License, version 2.0 - https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
/// https://github.com/rust-lang/libm/blob/master/LICENSE-APACHE
|
||||||
|
///
|
||||||
|
/// Thank you, Rust core team!
|
||||||
|
|
||||||
|
// From https://github.com/rust-lang/libm/blob/master/src/math/mod.rs
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
macro_rules! i {
|
||||||
|
($array:expr, $index:expr) => {
|
||||||
|
unsafe { *$array.get_unchecked($index) }
|
||||||
|
};
|
||||||
|
($array:expr, $index:expr, = , $rhs:expr) => {
|
||||||
|
unsafe {
|
||||||
|
*$array.get_unchecked_mut($index) = $rhs;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($array:expr, $index:expr, += , $rhs:expr) => {
|
||||||
|
unsafe {
|
||||||
|
*$array.get_unchecked_mut($index) += $rhs;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($array:expr, $index:expr, -= , $rhs:expr) => {
|
||||||
|
unsafe {
|
||||||
|
*$array.get_unchecked_mut($index) -= $rhs;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($array:expr, $index:expr, &= , $rhs:expr) => {
|
||||||
|
unsafe {
|
||||||
|
*$array.get_unchecked_mut($index) &= $rhs;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($array:expr, $index:expr, == , $rhs:expr) => {
|
||||||
|
unsafe { *$array.get_unchecked_mut($index) == $rhs }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
macro_rules! i {
|
||||||
|
($array:expr, $index:expr) => {
|
||||||
|
*$array.get($index).unwrap()
|
||||||
|
};
|
||||||
|
($array:expr, $index:expr, = , $rhs:expr) => {
|
||||||
|
*$array.get_mut($index).unwrap() = $rhs;
|
||||||
|
};
|
||||||
|
($array:expr, $index:expr, -= , $rhs:expr) => {
|
||||||
|
*$array.get_mut($index).unwrap() -= $rhs;
|
||||||
|
};
|
||||||
|
($array:expr, $index:expr, += , $rhs:expr) => {
|
||||||
|
*$array.get_mut($index).unwrap() += $rhs;
|
||||||
|
};
|
||||||
|
($array:expr, $index:expr, &= , $rhs:expr) => {
|
||||||
|
*$array.get_mut($index).unwrap() &= $rhs;
|
||||||
|
};
|
||||||
|
($array:expr, $index:expr, == , $rhs:expr) => {
|
||||||
|
*$array.get_mut($index).unwrap() == $rhs
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! llvm_intrinsically_optimized {
|
||||||
|
(#[cfg($($clause:tt)*)] $e:expr) => {
|
||||||
|
#[cfg(all(feature = "unstable", $($clause)*))]
|
||||||
|
{
|
||||||
|
if true { // thwart the dead code lint
|
||||||
|
$e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! force_eval {
|
||||||
|
($e:expr) => {
|
||||||
|
unsafe {
|
||||||
|
::core::ptr::read_volatile(&$e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// From https://github.com/rust-lang/libm/blob/master/src/math/atan.rs
|
||||||
|
|
||||||
|
// Clippy fails CI if we don't include overrides below. Since this is copied
|
||||||
|
// straight from the libm crate, I figure they had a reason to include
|
||||||
|
// the extra percision, so we just silence this warning.
|
||||||
|
|
||||||
|
#[allow(clippy::excessive_precision)]
|
||||||
|
const ATANHI: [f64; 4] = [
|
||||||
|
4.63647609000806093515e-01, /* atan(0.5)hi 0x3FDDAC67, 0x0561BB4F */
|
||||||
|
7.85398163397448278999e-01, /* atan(1.0)hi 0x3FE921FB, 0x54442D18 */
|
||||||
|
9.82793723247329054082e-01, /* atan(1.5)hi 0x3FEF730B, 0xD281F69B */
|
||||||
|
1.57079632679489655800e+00, /* atan(inf)hi 0x3FF921FB, 0x54442D18 */
|
||||||
|
];
|
||||||
|
|
||||||
|
#[allow(clippy::excessive_precision)]
|
||||||
|
const ATANLO: [f64; 4] = [
|
||||||
|
2.26987774529616870924e-17, /* atan(0.5)lo 0x3C7A2B7F, 0x222F65E2 */
|
||||||
|
3.06161699786838301793e-17, /* atan(1.0)lo 0x3C81A626, 0x33145C07 */
|
||||||
|
1.39033110312309984516e-17, /* atan(1.5)lo 0x3C700788, 0x7AF0CBBD */
|
||||||
|
6.12323399573676603587e-17, /* atan(inf)lo 0x3C91A626, 0x33145C07 */
|
||||||
|
];
|
||||||
|
|
||||||
|
#[allow(clippy::excessive_precision)]
|
||||||
|
const AT: [f64; 11] = [
|
||||||
|
3.33333333333329318027e-01, /* 0x3FD55555, 0x5555550D */
|
||||||
|
-1.99999999998764832476e-01, /* 0xBFC99999, 0x9998EBC4 */
|
||||||
|
1.42857142725034663711e-01, /* 0x3FC24924, 0x920083FF */
|
||||||
|
-1.11111104054623557880e-01, /* 0xBFBC71C6, 0xFE231671 */
|
||||||
|
9.09088713343650656196e-02, /* 0x3FB745CD, 0xC54C206E */
|
||||||
|
-7.69187620504482999495e-02, /* 0xBFB3B0F2, 0xAF749A6D */
|
||||||
|
6.66107313738753120669e-02, /* 0x3FB10D66, 0xA0D03D51 */
|
||||||
|
-5.83357013379057348645e-02, /* 0xBFADDE2D, 0x52DEFD9A */
|
||||||
|
4.97687799461593236017e-02, /* 0x3FA97B4B, 0x24760DEB */
|
||||||
|
-3.65315727442169155270e-02, /* 0xBFA2B444, 0x2C6A6C2F */
|
||||||
|
1.62858201153657823623e-02, /* 0x3F90AD3A, 0xE322DA11 */
|
||||||
|
];
|
||||||
|
|
||||||
|
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
|
||||||
|
pub fn fabs(x: f64) -> f64 {
|
||||||
|
// On wasm32 we know that LLVM's intrinsic will compile to an optimized
|
||||||
|
// `f64.abs` native instruction, so we can leverage this for both code size
|
||||||
|
// and speed.
|
||||||
|
llvm_intrinsically_optimized! {
|
||||||
|
#[cfg(target_arch = "wasm32")] {
|
||||||
|
return unsafe { ::core::intrinsics::fabsf64(x) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f64::from_bits(x.to_bits() & (u64::MAX / 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
|
||||||
|
pub fn atan(x: f64) -> f64 {
|
||||||
|
let mut x = x;
|
||||||
|
let mut ix = (x.to_bits() >> 32) as u32;
|
||||||
|
let sign = ix >> 31;
|
||||||
|
ix &= 0x7fff_ffff;
|
||||||
|
if ix >= 0x4410_0000 {
|
||||||
|
if x.is_nan() {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
let z = ATANHI[3] + f64::from_bits(0x0380_0000); // 0x1p-120f
|
||||||
|
return if sign != 0 { -z } else { z };
|
||||||
|
}
|
||||||
|
|
||||||
|
let id = if ix < 0x3fdc_0000 {
|
||||||
|
/* |x| < 0.4375 */
|
||||||
|
if ix < 0x3e40_0000 {
|
||||||
|
/* |x| < 2^-27 */
|
||||||
|
if ix < 0x0010_0000 {
|
||||||
|
/* raise underflow for subnormal x */
|
||||||
|
force_eval!(x as f32);
|
||||||
|
}
|
||||||
|
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
-1
|
||||||
|
} else {
|
||||||
|
x = fabs(x);
|
||||||
|
if ix < 0x3ff30000 {
|
||||||
|
/* |x| < 1.1875 */
|
||||||
|
if ix < 0x3fe60000 {
|
||||||
|
/* 7/16 <= |x| < 11/16 */
|
||||||
|
x = (2. * x - 1.) / (2. + x);
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
/* 11/16 <= |x| < 19/16 */
|
||||||
|
x = (x - 1.) / (x + 1.);
|
||||||
|
1
|
||||||
|
}
|
||||||
|
} else if ix < 0x40038000 {
|
||||||
|
/* |x| < 2.4375 */
|
||||||
|
x = (x - 1.5) / (1. + 1.5 * x);
|
||||||
|
2
|
||||||
|
} else {
|
||||||
|
/* 2.4375 <= |x| < 2^66 */
|
||||||
|
x = -1. / x;
|
||||||
|
3
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let z = x * x;
|
||||||
|
let w = z * z;
|
||||||
|
/* break sum from i=0 to 10 AT[i]z**(i+1) into odd and even poly */
|
||||||
|
let s1 = z * (AT[0] + w * (AT[2] + w * (AT[4] + w * (AT[6] + w * (AT[8] + w * AT[10])))));
|
||||||
|
let s2 = w * (AT[1] + w * (AT[3] + w * (AT[5] + w * (AT[7] + w * AT[9]))));
|
||||||
|
|
||||||
|
if id < 0 {
|
||||||
|
return x - x * (s1 + s2);
|
||||||
|
}
|
||||||
|
|
||||||
|
let z = i!(ATANHI, id as usize) - (x * (s1 + s2) - i!(ATANLO, id as usize) - x);
|
||||||
|
|
||||||
|
if sign != 0 {
|
||||||
|
-z
|
||||||
|
} else {
|
||||||
|
z
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
interface Bool
|
interface Bool2
|
||||||
exposes [ not, isEq, isNe ]
|
exposes [ not, and, or, xor, isEq, isNotEq ]
|
||||||
imports []
|
imports []
|
||||||
|
|
||||||
## Returns #False when given #True, and vice versa.
|
## Returns #False when given #True, and vice versa.
|
||||||
|
@ -76,7 +76,9 @@ xor : Bool, Bool -> Bool
|
||||||
##
|
##
|
||||||
## Note that `isEq` takes `'val` instead of `val`, which means `isEq` does not
|
## Note that `isEq` takes `'val` instead of `val`, which means `isEq` does not
|
||||||
## accept arguments whose types contain functions.
|
## accept arguments whose types contain functions.
|
||||||
isEq : 'val, 'val -> Bool
|
# TODO: removed `'` from signature because parser does not support it yet
|
||||||
|
# Original signature: `isEq : 'val, 'val -> Bool`
|
||||||
|
isEq : val, val -> Bool
|
||||||
|
|
||||||
## Calls #eq on the given values, then calls #not on the result.
|
## Calls #eq on the given values, then calls #not on the result.
|
||||||
##
|
##
|
||||||
|
@ -84,4 +86,6 @@ isEq : 'val, 'val -> Bool
|
||||||
##
|
##
|
||||||
## Note that `isNotEq` takes `'val` instead of `val`, which means `isNotEq` does not
|
## Note that `isNotEq` takes `'val` instead of `val`, which means `isNotEq` does not
|
||||||
## accept arguments whose types contain functions.
|
## accept arguments whose types contain functions.
|
||||||
isNotEq : 'val, 'val -> Bool
|
# TODO: removed `'` from signature because parser does not support it yet
|
||||||
|
# Original signature: `isNotEq : 'val, 'val -> Bool`
|
||||||
|
isNotEq : val, val -> Bool
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
interface List
|
interface List2
|
||||||
exposes [ List, map, fold ]
|
exposes [ List, single, empty, repeat, range, reverse, sort, map, mapWithIndex, mapOrCancel, mapOks, update, updater, allOks, append, prepend, concat, join, joinMap, oks, zip, zipMap, keepIf, dropIf, first, last, get, max, min, put, drop, append, prepend, dropLast, dropFirst, takeFirst, takeLast, split, sublist, walk, walkBackwards, walkUntil, walkBackwardsUntil, len, isEmpty, contains, all, any ]
|
||||||
imports []
|
imports []
|
||||||
|
|
||||||
## Types
|
## Types
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
interface Map
|
interface Map2
|
||||||
exposes [ Map, isEmpty ]
|
exposes [ isEmpty, map ]
|
||||||
imports []
|
imports []
|
||||||
|
|
||||||
isEmpty : Map * * -> Bool
|
isEmpty : Map * * -> Bool
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
interface Num
|
interface Num2
|
||||||
exposes [ Num, neg, abs, add, sub, mul, isOdd, isEven, isPositive, isNegative, isZero ]
|
exposes [ Num, neg, abs, add, sub, mul, isOdd, isEven, isPositive, isNegative, isZero ]
|
||||||
imports []
|
imports []
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
interface Set
|
interface Set2
|
||||||
exposes [ Set, map, isEmpty ]
|
exposes [ empty, isEmpty, len, add, drop, map ]
|
||||||
imports []
|
imports []
|
||||||
|
|
||||||
|
|
||||||
|
@ -12,10 +12,14 @@ isEmpty : Set * -> Bool
|
||||||
|
|
||||||
len : Set * -> Len
|
len : Set * -> Len
|
||||||
|
|
||||||
add : Set 'elem, 'elem -> Set 'elem
|
# TODO: removed `'` from signature because parser does not support it yet
|
||||||
|
# Original signature: `add : Set 'elem, 'elem -> Set 'elem`
|
||||||
|
add : Set elem, elem -> Set elem
|
||||||
|
|
||||||
## Drops the given element from the set.
|
## Drops the given element from the set.
|
||||||
drop : Set 'elem, 'elem -> Set 'elem
|
# TODO: removed `'` from signature because parser does not support it yet
|
||||||
|
# Original signature: `drop : Set 'elem, 'elem -> Set 'elem`
|
||||||
|
drop : Set elem, elem -> Set elem
|
||||||
|
|
||||||
## Convert each element in the set to something new, by calling a conversion
|
## Convert each element in the set to something new, by calling a conversion
|
||||||
## function on each of them. Then return a new set of the converted values.
|
## function on each of them. Then return a new set of the converted values.
|
||||||
|
@ -26,4 +30,6 @@ drop : Set 'elem, 'elem -> Set 'elem
|
||||||
##
|
##
|
||||||
## `map` functions like this are common in Roc, and they all work similarly.
|
## `map` functions like this are common in Roc, and they all work similarly.
|
||||||
## See for example #Result.map, #List.map, and #Map.map.
|
## See for example #Result.map, #List.map, and #Map.map.
|
||||||
map : Set 'elem, ('before -> 'after) -> Set 'after
|
# TODO: removed `'` from signature because parser does not support it yet
|
||||||
|
# Original signature: `map : Set 'elem, ('before -> 'after) -> Set 'after`
|
||||||
|
map : Set elem, (before -> after) -> Set after
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
interface Str exposes [ Str, isEmpty, join ] imports []
|
interface Str2
|
||||||
|
exposes [ Str2, decimal, split, isEmpty, startsWith, endsWith, contains, anyGraphemes, allGraphemes, join, joinWith, padGraphemesStart, padGraphemesEnd, graphemes, reverseGraphemes, isCaseInsensitiveEq, isCaseInsensitiveNeq, walkGraphemes, isCapitalized, isAllUppercase, isAllLowercase, toUtf8, toUtf16, toUtf32, walkUtf8, walkUtf16, walkUtf32, walkRevUtf8, walkRevUtf16, walkRevUtf32 ]
|
||||||
|
imports []
|
||||||
## Types
|
## Types
|
||||||
|
|
||||||
## Dealing with text is a deep topic, so by design, Roc's `Str` module sticks
|
## Dealing with text is a deep topic, so by design, Roc's `Str` module sticks
|
||||||
|
@ -98,7 +100,7 @@ interface Str exposes [ Str, isEmpty, join ] imports []
|
||||||
|
|
||||||
## A [Unicode](https://unicode.org) text value.
|
## A [Unicode](https://unicode.org) text value.
|
||||||
##
|
##
|
||||||
Str : [ @Str ]
|
Str2 : [ @Str ]
|
||||||
|
|
||||||
## Convert
|
## Convert
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,8 @@ const NUM_BUILTIN_IMPORTS: usize = 7;
|
||||||
const TVAR1: VarId = VarId::from_u32(1);
|
const TVAR1: VarId = VarId::from_u32(1);
|
||||||
const TVAR2: VarId = VarId::from_u32(2);
|
const TVAR2: VarId = VarId::from_u32(2);
|
||||||
const TVAR3: VarId = VarId::from_u32(3);
|
const TVAR3: VarId = VarId::from_u32(3);
|
||||||
|
const TVAR4: VarId = VarId::from_u32(4);
|
||||||
|
const TOP_LEVEL_CLOSURE_VAR: VarId = VarId::from_u32(5);
|
||||||
|
|
||||||
pub fn aliases() -> MutMap<Symbol, BuiltinAlias> {
|
pub fn aliases() -> MutMap<Symbol, BuiltinAlias> {
|
||||||
let mut aliases = HashMap::with_capacity_and_hasher(NUM_BUILTIN_IMPORTS, default_hasher());
|
let mut aliases = HashMap::with_capacity_and_hasher(NUM_BUILTIN_IMPORTS, default_hasher());
|
||||||
|
@ -181,7 +183,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// add or (+) : Num a, Num a -> Num a
|
// add or (+) : Num a, Num a -> Num a
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::NUM_ADD,
|
Symbol::NUM_ADD,
|
||||||
SolvedType::Func(
|
top_level_function(
|
||||||
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
|
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
|
||||||
Box::new(num_type(flex(TVAR1))),
|
Box::new(num_type(flex(TVAR1))),
|
||||||
),
|
),
|
||||||
|
@ -195,7 +197,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
|
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::NUM_ADD_CHECKED,
|
Symbol::NUM_ADD_CHECKED,
|
||||||
SolvedType::Func(
|
top_level_function(
|
||||||
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
|
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
|
||||||
Box::new(result_type(num_type(flex(TVAR1)), overflow)),
|
Box::new(result_type(num_type(flex(TVAR1)), overflow)),
|
||||||
),
|
),
|
||||||
|
@ -204,13 +206,13 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// addWrap : Int, Int -> Int
|
// addWrap : Int, Int -> Int
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::NUM_ADD_WRAP,
|
Symbol::NUM_ADD_WRAP,
|
||||||
SolvedType::Func(vec![int_type(), int_type()], Box::new(int_type())),
|
top_level_function(vec![int_type(), int_type()], Box::new(int_type())),
|
||||||
);
|
);
|
||||||
|
|
||||||
// sub or (-) : Num a, Num a -> Num a
|
// sub or (-) : Num a, Num a -> Num a
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::NUM_SUB,
|
Symbol::NUM_SUB,
|
||||||
SolvedType::Func(
|
top_level_function(
|
||||||
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
|
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
|
||||||
Box::new(num_type(flex(TVAR1))),
|
Box::new(num_type(flex(TVAR1))),
|
||||||
),
|
),
|
||||||
|
@ -219,7 +221,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// mul or (*) : Num a, Num a -> Num a
|
// mul or (*) : Num a, Num a -> Num a
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::NUM_MUL,
|
Symbol::NUM_MUL,
|
||||||
SolvedType::Func(
|
top_level_function(
|
||||||
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
|
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
|
||||||
Box::new(num_type(flex(TVAR1))),
|
Box::new(num_type(flex(TVAR1))),
|
||||||
),
|
),
|
||||||
|
@ -228,31 +230,31 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// abs : Num a -> Num a
|
// abs : Num a -> Num a
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::NUM_ABS,
|
Symbol::NUM_ABS,
|
||||||
SolvedType::Func(vec![num_type(flex(TVAR1))], Box::new(num_type(flex(TVAR1)))),
|
top_level_function(vec![num_type(flex(TVAR1))], Box::new(num_type(flex(TVAR1)))),
|
||||||
);
|
);
|
||||||
|
|
||||||
// neg : Num a -> Num a
|
// neg : Num a -> Num a
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::NUM_NEG,
|
Symbol::NUM_NEG,
|
||||||
SolvedType::Func(vec![num_type(flex(TVAR1))], Box::new(num_type(flex(TVAR1)))),
|
top_level_function(vec![num_type(flex(TVAR1))], Box::new(num_type(flex(TVAR1)))),
|
||||||
);
|
);
|
||||||
|
|
||||||
// isEq or (==) : a, a -> Bool
|
// isEq or (==) : a, a -> Bool
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::BOOL_EQ,
|
Symbol::BOOL_EQ,
|
||||||
SolvedType::Func(vec![flex(TVAR1), flex(TVAR1)], Box::new(bool_type())),
|
top_level_function(vec![flex(TVAR1), flex(TVAR1)], Box::new(bool_type())),
|
||||||
);
|
);
|
||||||
|
|
||||||
// isNeq or (!=) : a, a -> Bool
|
// isNeq or (!=) : a, a -> Bool
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::BOOL_NEQ,
|
Symbol::BOOL_NEQ,
|
||||||
SolvedType::Func(vec![flex(TVAR1), flex(TVAR1)], Box::new(bool_type())),
|
top_level_function(vec![flex(TVAR1), flex(TVAR1)], Box::new(bool_type())),
|
||||||
);
|
);
|
||||||
|
|
||||||
// isLt or (<) : Num a, Num a -> Bool
|
// isLt or (<) : Num a, Num a -> Bool
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::NUM_LT,
|
Symbol::NUM_LT,
|
||||||
SolvedType::Func(
|
top_level_function(
|
||||||
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
|
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
|
||||||
Box::new(bool_type()),
|
Box::new(bool_type()),
|
||||||
),
|
),
|
||||||
|
@ -261,7 +263,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// isLte or (<=) : Num a, Num a -> Bool
|
// isLte or (<=) : Num a, Num a -> Bool
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::NUM_LTE,
|
Symbol::NUM_LTE,
|
||||||
SolvedType::Func(
|
top_level_function(
|
||||||
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
|
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
|
||||||
Box::new(bool_type()),
|
Box::new(bool_type()),
|
||||||
),
|
),
|
||||||
|
@ -270,7 +272,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// isGt or (>) : Num a, Num a -> Bool
|
// isGt or (>) : Num a, Num a -> Bool
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::NUM_GT,
|
Symbol::NUM_GT,
|
||||||
SolvedType::Func(
|
top_level_function(
|
||||||
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
|
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
|
||||||
Box::new(bool_type()),
|
Box::new(bool_type()),
|
||||||
),
|
),
|
||||||
|
@ -279,7 +281,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// isGte or (>=) : Num a, Num a -> Bool
|
// isGte or (>=) : Num a, Num a -> Bool
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::NUM_GTE,
|
Symbol::NUM_GTE,
|
||||||
SolvedType::Func(
|
top_level_function(
|
||||||
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
|
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
|
||||||
Box::new(bool_type()),
|
Box::new(bool_type()),
|
||||||
),
|
),
|
||||||
|
@ -288,7 +290,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// compare : Num a, Num a -> [ LT, EQ, GT ]
|
// compare : Num a, Num a -> [ LT, EQ, GT ]
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::NUM_COMPARE,
|
Symbol::NUM_COMPARE,
|
||||||
SolvedType::Func(
|
top_level_function(
|
||||||
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
|
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
|
||||||
Box::new(ordering_type()),
|
Box::new(ordering_type()),
|
||||||
),
|
),
|
||||||
|
@ -297,37 +299,37 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// toFloat : Num a -> Float
|
// toFloat : Num a -> Float
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::NUM_TO_FLOAT,
|
Symbol::NUM_TO_FLOAT,
|
||||||
SolvedType::Func(vec![num_type(flex(TVAR1))], Box::new(float_type())),
|
top_level_function(vec![num_type(flex(TVAR1))], Box::new(float_type())),
|
||||||
);
|
);
|
||||||
|
|
||||||
// isNegative : Num a -> Bool
|
// isNegative : Num a -> Bool
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::NUM_IS_NEGATIVE,
|
Symbol::NUM_IS_NEGATIVE,
|
||||||
SolvedType::Func(vec![num_type(flex(TVAR1))], Box::new(bool_type())),
|
top_level_function(vec![num_type(flex(TVAR1))], Box::new(bool_type())),
|
||||||
);
|
);
|
||||||
|
|
||||||
// isPositive : Num a -> Bool
|
// isPositive : Num a -> Bool
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::NUM_IS_POSITIVE,
|
Symbol::NUM_IS_POSITIVE,
|
||||||
SolvedType::Func(vec![num_type(flex(TVAR1))], Box::new(bool_type())),
|
top_level_function(vec![num_type(flex(TVAR1))], Box::new(bool_type())),
|
||||||
);
|
);
|
||||||
|
|
||||||
// isZero : Num a -> Bool
|
// isZero : Num a -> Bool
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::NUM_IS_ZERO,
|
Symbol::NUM_IS_ZERO,
|
||||||
SolvedType::Func(vec![num_type(flex(TVAR1))], Box::new(bool_type())),
|
top_level_function(vec![num_type(flex(TVAR1))], Box::new(bool_type())),
|
||||||
);
|
);
|
||||||
|
|
||||||
// isEven : Num a -> Bool
|
// isEven : Num a -> Bool
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::NUM_IS_EVEN,
|
Symbol::NUM_IS_EVEN,
|
||||||
SolvedType::Func(vec![num_type(flex(TVAR1))], Box::new(bool_type())),
|
top_level_function(vec![num_type(flex(TVAR1))], Box::new(bool_type())),
|
||||||
);
|
);
|
||||||
|
|
||||||
// isOdd : Num a -> Bool
|
// isOdd : Num a -> Bool
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::NUM_IS_ODD,
|
Symbol::NUM_IS_ODD,
|
||||||
SolvedType::Func(vec![num_type(flex(TVAR1))], Box::new(bool_type())),
|
top_level_function(vec![num_type(flex(TVAR1))], Box::new(bool_type())),
|
||||||
);
|
);
|
||||||
|
|
||||||
// maxInt : Int
|
// maxInt : Int
|
||||||
|
@ -344,7 +346,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
|
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::NUM_DIV_INT,
|
Symbol::NUM_DIV_INT,
|
||||||
SolvedType::Func(
|
top_level_function(
|
||||||
vec![int_type(), int_type()],
|
vec![int_type(), int_type()],
|
||||||
Box::new(result_type(int_type(), div_by_zero.clone())),
|
Box::new(result_type(int_type(), div_by_zero.clone())),
|
||||||
),
|
),
|
||||||
|
@ -353,7 +355,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// rem : Int, Int -> Result Int [ DivByZero ]*
|
// rem : Int, Int -> Result Int [ DivByZero ]*
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::NUM_REM,
|
Symbol::NUM_REM,
|
||||||
SolvedType::Func(
|
top_level_function(
|
||||||
vec![int_type(), int_type()],
|
vec![int_type(), int_type()],
|
||||||
Box::new(result_type(int_type(), div_by_zero.clone())),
|
Box::new(result_type(int_type(), div_by_zero.clone())),
|
||||||
),
|
),
|
||||||
|
@ -362,7 +364,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// mod : Int, Int -> Result Int [ DivByZero ]*
|
// mod : Int, Int -> Result Int [ DivByZero ]*
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::NUM_MOD_INT,
|
Symbol::NUM_MOD_INT,
|
||||||
SolvedType::Func(
|
top_level_function(
|
||||||
vec![int_type(), int_type()],
|
vec![int_type(), int_type()],
|
||||||
Box::new(result_type(int_type(), div_by_zero.clone())),
|
Box::new(result_type(int_type(), div_by_zero.clone())),
|
||||||
),
|
),
|
||||||
|
@ -373,7 +375,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// div : Float, Float -> Float
|
// div : Float, Float -> Float
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::NUM_DIV_FLOAT,
|
Symbol::NUM_DIV_FLOAT,
|
||||||
SolvedType::Func(
|
top_level_function(
|
||||||
vec![float_type(), float_type()],
|
vec![float_type(), float_type()],
|
||||||
Box::new(result_type(float_type(), div_by_zero.clone())),
|
Box::new(result_type(float_type(), div_by_zero.clone())),
|
||||||
),
|
),
|
||||||
|
@ -382,7 +384,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// mod : Float, Float -> Result Int [ DivByZero ]*
|
// mod : Float, Float -> Result Int [ DivByZero ]*
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::NUM_MOD_FLOAT,
|
Symbol::NUM_MOD_FLOAT,
|
||||||
SolvedType::Func(
|
top_level_function(
|
||||||
vec![float_type(), float_type()],
|
vec![float_type(), float_type()],
|
||||||
Box::new(result_type(float_type(), div_by_zero)),
|
Box::new(result_type(float_type(), div_by_zero)),
|
||||||
),
|
),
|
||||||
|
@ -396,7 +398,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
|
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::NUM_SQRT,
|
Symbol::NUM_SQRT,
|
||||||
SolvedType::Func(
|
top_level_function(
|
||||||
vec![float_type()],
|
vec![float_type()],
|
||||||
Box::new(result_type(float_type(), sqrt_of_negative)),
|
Box::new(result_type(float_type(), sqrt_of_negative)),
|
||||||
),
|
),
|
||||||
|
@ -405,25 +407,25 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// round : Float -> Int
|
// round : Float -> Int
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::NUM_ROUND,
|
Symbol::NUM_ROUND,
|
||||||
SolvedType::Func(vec![float_type()], Box::new(int_type())),
|
top_level_function(vec![float_type()], Box::new(int_type())),
|
||||||
);
|
);
|
||||||
|
|
||||||
// sin : Float -> Float
|
// sin : Float -> Float
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::NUM_SIN,
|
Symbol::NUM_SIN,
|
||||||
SolvedType::Func(vec![float_type()], Box::new(float_type())),
|
top_level_function(vec![float_type()], Box::new(float_type())),
|
||||||
);
|
);
|
||||||
|
|
||||||
// cos : Float -> Float
|
// cos : Float -> Float
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::NUM_COS,
|
Symbol::NUM_COS,
|
||||||
SolvedType::Func(vec![float_type()], Box::new(float_type())),
|
top_level_function(vec![float_type()], Box::new(float_type())),
|
||||||
);
|
);
|
||||||
|
|
||||||
// tan : Float -> Float
|
// tan : Float -> Float
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::NUM_TAN,
|
Symbol::NUM_TAN,
|
||||||
SolvedType::Func(vec![float_type()], Box::new(float_type())),
|
top_level_function(vec![float_type()], Box::new(float_type())),
|
||||||
);
|
);
|
||||||
|
|
||||||
// maxFloat : Float
|
// maxFloat : Float
|
||||||
|
@ -435,25 +437,31 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// pow : Float, Float -> Float
|
// pow : Float, Float -> Float
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::NUM_POW,
|
Symbol::NUM_POW,
|
||||||
SolvedType::Func(vec![float_type(), float_type()], Box::new(float_type())),
|
top_level_function(vec![float_type(), float_type()], Box::new(float_type())),
|
||||||
);
|
);
|
||||||
|
|
||||||
// ceiling : Float -> Int
|
// ceiling : Float -> Int
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::NUM_CEILING,
|
Symbol::NUM_CEILING,
|
||||||
SolvedType::Func(vec![float_type()], Box::new(int_type())),
|
top_level_function(vec![float_type()], Box::new(int_type())),
|
||||||
);
|
);
|
||||||
|
|
||||||
// powInt : Int, Int -> Int
|
// powInt : Int, Int -> Int
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::NUM_POW_INT,
|
Symbol::NUM_POW_INT,
|
||||||
SolvedType::Func(vec![int_type(), int_type()], Box::new(int_type())),
|
top_level_function(vec![int_type(), int_type()], Box::new(int_type())),
|
||||||
);
|
);
|
||||||
|
|
||||||
// floor : Float -> Int
|
// floor : Float -> Int
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::NUM_FLOOR,
|
Symbol::NUM_FLOOR,
|
||||||
SolvedType::Func(vec![float_type()], Box::new(int_type())),
|
top_level_function(vec![float_type()], Box::new(int_type())),
|
||||||
|
);
|
||||||
|
|
||||||
|
// atan : Float -> Float
|
||||||
|
add_type(
|
||||||
|
Symbol::NUM_ATAN,
|
||||||
|
top_level_function(vec![float_type()], Box::new(float_type())),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Bool module
|
// Bool module
|
||||||
|
@ -461,25 +469,25 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// and : Bool, Bool -> Bool
|
// and : Bool, Bool -> Bool
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::BOOL_AND,
|
Symbol::BOOL_AND,
|
||||||
SolvedType::Func(vec![bool_type(), bool_type()], Box::new(bool_type())),
|
top_level_function(vec![bool_type(), bool_type()], Box::new(bool_type())),
|
||||||
);
|
);
|
||||||
|
|
||||||
// or : Bool, Bool -> Bool
|
// or : Bool, Bool -> Bool
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::BOOL_OR,
|
Symbol::BOOL_OR,
|
||||||
SolvedType::Func(vec![bool_type(), bool_type()], Box::new(bool_type())),
|
top_level_function(vec![bool_type(), bool_type()], Box::new(bool_type())),
|
||||||
);
|
);
|
||||||
|
|
||||||
// xor : Bool, Bool -> Bool
|
// xor : Bool, Bool -> Bool
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::BOOL_XOR,
|
Symbol::BOOL_XOR,
|
||||||
SolvedType::Func(vec![bool_type(), bool_type()], Box::new(bool_type())),
|
top_level_function(vec![bool_type(), bool_type()], Box::new(bool_type())),
|
||||||
);
|
);
|
||||||
|
|
||||||
// not : Bool -> Bool
|
// not : Bool -> Bool
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::BOOL_NOT,
|
Symbol::BOOL_NOT,
|
||||||
SolvedType::Func(vec![bool_type()], Box::new(bool_type())),
|
top_level_function(vec![bool_type()], Box::new(bool_type())),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Str module
|
// Str module
|
||||||
|
@ -496,13 +504,13 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// Str.concat : Str, Str -> Str
|
// Str.concat : Str, Str -> Str
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::STR_CONCAT,
|
Symbol::STR_CONCAT,
|
||||||
SolvedType::Func(vec![str_type(), str_type()], Box::new(str_type())),
|
top_level_function(vec![str_type(), str_type()], Box::new(str_type())),
|
||||||
);
|
);
|
||||||
|
|
||||||
// isEmpty : Str -> Bool
|
// isEmpty : Str -> Bool
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::STR_IS_EMPTY,
|
Symbol::STR_IS_EMPTY,
|
||||||
SolvedType::Func(vec![str_type()], Box::new(bool_type())),
|
top_level_function(vec![str_type()], Box::new(bool_type())),
|
||||||
);
|
);
|
||||||
|
|
||||||
// List module
|
// List module
|
||||||
|
@ -515,7 +523,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
|
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::LIST_GET,
|
Symbol::LIST_GET,
|
||||||
SolvedType::Func(
|
top_level_function(
|
||||||
vec![list_type(flex(TVAR1)), int_type()],
|
vec![list_type(flex(TVAR1)), int_type()],
|
||||||
Box::new(result_type(flex(TVAR1), index_out_of_bounds)),
|
Box::new(result_type(flex(TVAR1), index_out_of_bounds)),
|
||||||
),
|
),
|
||||||
|
@ -529,7 +537,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
|
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::LIST_FIRST,
|
Symbol::LIST_FIRST,
|
||||||
SolvedType::Func(
|
top_level_function(
|
||||||
vec![list_type(flex(TVAR1))],
|
vec![list_type(flex(TVAR1))],
|
||||||
Box::new(result_type(flex(TVAR1), list_was_empty)),
|
Box::new(result_type(flex(TVAR1), list_was_empty)),
|
||||||
),
|
),
|
||||||
|
@ -538,7 +546,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// set : List elem, Int, elem -> List elem
|
// set : List elem, Int, elem -> List elem
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::LIST_SET,
|
Symbol::LIST_SET,
|
||||||
SolvedType::Func(
|
top_level_function(
|
||||||
vec![list_type(flex(TVAR1)), int_type(), flex(TVAR1)],
|
vec![list_type(flex(TVAR1)), int_type(), flex(TVAR1)],
|
||||||
Box::new(list_type(flex(TVAR1))),
|
Box::new(list_type(flex(TVAR1))),
|
||||||
),
|
),
|
||||||
|
@ -547,7 +555,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// concat : List elem, List elem -> List elem
|
// concat : List elem, List elem -> List elem
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::LIST_CONCAT,
|
Symbol::LIST_CONCAT,
|
||||||
SolvedType::Func(
|
top_level_function(
|
||||||
vec![list_type(flex(TVAR1)), list_type(flex(TVAR1))],
|
vec![list_type(flex(TVAR1)), list_type(flex(TVAR1))],
|
||||||
Box::new(list_type(flex(TVAR1))),
|
Box::new(list_type(flex(TVAR1))),
|
||||||
),
|
),
|
||||||
|
@ -556,10 +564,10 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// walkRight : List elem, (elem -> accum -> accum), accum -> accum
|
// walkRight : List elem, (elem -> accum -> accum), accum -> accum
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::LIST_WALK_RIGHT,
|
Symbol::LIST_WALK_RIGHT,
|
||||||
SolvedType::Func(
|
top_level_function(
|
||||||
vec![
|
vec![
|
||||||
list_type(flex(TVAR1)),
|
list_type(flex(TVAR1)),
|
||||||
SolvedType::Func(vec![flex(TVAR1), flex(TVAR2)], Box::new(flex(TVAR2))),
|
closure(vec![flex(TVAR1), flex(TVAR2)], TVAR3, Box::new(flex(TVAR2))),
|
||||||
flex(TVAR2),
|
flex(TVAR2),
|
||||||
],
|
],
|
||||||
Box::new(flex(TVAR2)),
|
Box::new(flex(TVAR2)),
|
||||||
|
@ -569,10 +577,10 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// keepIf : List elem, (elem -> Bool) -> List elem
|
// keepIf : List elem, (elem -> Bool) -> List elem
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::LIST_KEEP_IF,
|
Symbol::LIST_KEEP_IF,
|
||||||
SolvedType::Func(
|
top_level_function(
|
||||||
vec![
|
vec![
|
||||||
list_type(flex(TVAR1)),
|
list_type(flex(TVAR1)),
|
||||||
SolvedType::Func(vec![flex(TVAR1)], Box::new(bool_type())),
|
closure(vec![flex(TVAR1)], TVAR2, Box::new(bool_type())),
|
||||||
],
|
],
|
||||||
Box::new(list_type(flex(TVAR1))),
|
Box::new(list_type(flex(TVAR1))),
|
||||||
),
|
),
|
||||||
|
@ -581,10 +589,10 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// map : List before, (before -> after) -> List after
|
// map : List before, (before -> after) -> List after
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::LIST_MAP,
|
Symbol::LIST_MAP,
|
||||||
SolvedType::Func(
|
top_level_function(
|
||||||
vec![
|
vec![
|
||||||
list_type(flex(TVAR1)),
|
list_type(flex(TVAR1)),
|
||||||
SolvedType::Func(vec![flex(TVAR1)], Box::new(flex(TVAR2))),
|
closure(vec![flex(TVAR1)], TVAR3, Box::new(flex(TVAR2))),
|
||||||
],
|
],
|
||||||
Box::new(list_type(flex(TVAR2))),
|
Box::new(list_type(flex(TVAR2))),
|
||||||
),
|
),
|
||||||
|
@ -593,7 +601,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// append : List elem, elem -> List elem
|
// append : List elem, elem -> List elem
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::LIST_APPEND,
|
Symbol::LIST_APPEND,
|
||||||
SolvedType::Func(
|
top_level_function(
|
||||||
vec![list_type(flex(TVAR1)), flex(TVAR1)],
|
vec![list_type(flex(TVAR1)), flex(TVAR1)],
|
||||||
Box::new(list_type(flex(TVAR1))),
|
Box::new(list_type(flex(TVAR1))),
|
||||||
),
|
),
|
||||||
|
@ -602,7 +610,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// prepend : List elem, elem -> List elem
|
// prepend : List elem, elem -> List elem
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::LIST_PREPEND,
|
Symbol::LIST_PREPEND,
|
||||||
SolvedType::Func(
|
top_level_function(
|
||||||
vec![list_type(flex(TVAR1)), flex(TVAR1)],
|
vec![list_type(flex(TVAR1)), flex(TVAR1)],
|
||||||
Box::new(list_type(flex(TVAR1))),
|
Box::new(list_type(flex(TVAR1))),
|
||||||
),
|
),
|
||||||
|
@ -611,7 +619,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// join : List (List elem) -> List elem
|
// join : List (List elem) -> List elem
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::LIST_JOIN,
|
Symbol::LIST_JOIN,
|
||||||
SolvedType::Func(
|
top_level_function(
|
||||||
vec![list_type(list_type(flex(TVAR1)))],
|
vec![list_type(list_type(flex(TVAR1)))],
|
||||||
Box::new(list_type(flex(TVAR1))),
|
Box::new(list_type(flex(TVAR1))),
|
||||||
),
|
),
|
||||||
|
@ -620,13 +628,13 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// single : a -> List a
|
// single : a -> List a
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::LIST_SINGLE,
|
Symbol::LIST_SINGLE,
|
||||||
SolvedType::Func(vec![flex(TVAR1)], Box::new(list_type(flex(TVAR1)))),
|
top_level_function(vec![flex(TVAR1)], Box::new(list_type(flex(TVAR1)))),
|
||||||
);
|
);
|
||||||
|
|
||||||
// repeat : Int, elem -> List elem
|
// repeat : Int, elem -> List elem
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::LIST_REPEAT,
|
Symbol::LIST_REPEAT,
|
||||||
SolvedType::Func(
|
top_level_function(
|
||||||
vec![int_type(), flex(TVAR1)],
|
vec![int_type(), flex(TVAR1)],
|
||||||
Box::new(list_type(flex(TVAR1))),
|
Box::new(list_type(flex(TVAR1))),
|
||||||
),
|
),
|
||||||
|
@ -635,7 +643,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// reverse : List elem -> List elem
|
// reverse : List elem -> List elem
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::LIST_REVERSE,
|
Symbol::LIST_REVERSE,
|
||||||
SolvedType::Func(
|
top_level_function(
|
||||||
vec![list_type(flex(TVAR1))],
|
vec![list_type(flex(TVAR1))],
|
||||||
Box::new(list_type(flex(TVAR1))),
|
Box::new(list_type(flex(TVAR1))),
|
||||||
),
|
),
|
||||||
|
@ -644,13 +652,13 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// len : List * -> Int
|
// len : List * -> Int
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::LIST_LEN,
|
Symbol::LIST_LEN,
|
||||||
SolvedType::Func(vec![list_type(flex(TVAR1))], Box::new(int_type())),
|
top_level_function(vec![list_type(flex(TVAR1))], Box::new(int_type())),
|
||||||
);
|
);
|
||||||
|
|
||||||
// isEmpty : List * -> Bool
|
// isEmpty : List * -> Bool
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::LIST_IS_EMPTY,
|
Symbol::LIST_IS_EMPTY,
|
||||||
SolvedType::Func(vec![list_type(flex(TVAR1))], Box::new(bool_type())),
|
top_level_function(vec![list_type(flex(TVAR1))], Box::new(bool_type())),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Map module
|
// Map module
|
||||||
|
@ -661,7 +669,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// singleton : k, v -> Map k v
|
// singleton : k, v -> Map k v
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::MAP_SINGLETON,
|
Symbol::MAP_SINGLETON,
|
||||||
SolvedType::Func(
|
top_level_function(
|
||||||
vec![flex(TVAR1), flex(TVAR2)],
|
vec![flex(TVAR1), flex(TVAR2)],
|
||||||
Box::new(map_type(flex(TVAR1), flex(TVAR2))),
|
Box::new(map_type(flex(TVAR1), flex(TVAR2))),
|
||||||
),
|
),
|
||||||
|
@ -675,7 +683,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
|
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::MAP_GET,
|
Symbol::MAP_GET,
|
||||||
SolvedType::Func(
|
top_level_function(
|
||||||
vec![map_type(flex(TVAR1), flex(TVAR2)), flex(TVAR1)],
|
vec![map_type(flex(TVAR1), flex(TVAR2)), flex(TVAR1)],
|
||||||
Box::new(result_type(flex(TVAR2), key_not_found)),
|
Box::new(result_type(flex(TVAR2), key_not_found)),
|
||||||
),
|
),
|
||||||
|
@ -683,7 +691,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
|
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::MAP_INSERT,
|
Symbol::MAP_INSERT,
|
||||||
SolvedType::Func(
|
top_level_function(
|
||||||
vec![map_type(flex(TVAR1), flex(TVAR2)), flex(TVAR1), flex(TVAR2)],
|
vec![map_type(flex(TVAR1), flex(TVAR2)), flex(TVAR1), flex(TVAR2)],
|
||||||
Box::new(map_type(flex(TVAR1), flex(TVAR2))),
|
Box::new(map_type(flex(TVAR1), flex(TVAR2))),
|
||||||
),
|
),
|
||||||
|
@ -697,13 +705,13 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// singleton : a -> Set a
|
// singleton : a -> Set a
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::SET_SINGLETON,
|
Symbol::SET_SINGLETON,
|
||||||
SolvedType::Func(vec![flex(TVAR1)], Box::new(set_type(flex(TVAR1)))),
|
top_level_function(vec![flex(TVAR1)], Box::new(set_type(flex(TVAR1)))),
|
||||||
);
|
);
|
||||||
|
|
||||||
// union : Set a, Set a -> Set a
|
// union : Set a, Set a -> Set a
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::SET_UNION,
|
Symbol::SET_UNION,
|
||||||
SolvedType::Func(
|
top_level_function(
|
||||||
vec![set_type(flex(TVAR1)), set_type(flex(TVAR1))],
|
vec![set_type(flex(TVAR1)), set_type(flex(TVAR1))],
|
||||||
Box::new(set_type(flex(TVAR1))),
|
Box::new(set_type(flex(TVAR1))),
|
||||||
),
|
),
|
||||||
|
@ -712,7 +720,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// diff : Set a, Set a -> Set a
|
// diff : Set a, Set a -> Set a
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::SET_DIFF,
|
Symbol::SET_DIFF,
|
||||||
SolvedType::Func(
|
top_level_function(
|
||||||
vec![set_type(flex(TVAR1)), set_type(flex(TVAR1))],
|
vec![set_type(flex(TVAR1)), set_type(flex(TVAR1))],
|
||||||
Box::new(set_type(flex(TVAR1))),
|
Box::new(set_type(flex(TVAR1))),
|
||||||
),
|
),
|
||||||
|
@ -721,10 +729,10 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// foldl : Set a, (a -> b -> b), b -> b
|
// foldl : Set a, (a -> b -> b), b -> b
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::SET_FOLDL,
|
Symbol::SET_FOLDL,
|
||||||
SolvedType::Func(
|
top_level_function(
|
||||||
vec![
|
vec![
|
||||||
set_type(flex(TVAR1)),
|
set_type(flex(TVAR1)),
|
||||||
SolvedType::Func(vec![flex(TVAR1), flex(TVAR2)], Box::new(flex(TVAR2))),
|
closure(vec![flex(TVAR1), flex(TVAR2)], TVAR3, Box::new(flex(TVAR2))),
|
||||||
flex(TVAR2),
|
flex(TVAR2),
|
||||||
],
|
],
|
||||||
Box::new(flex(TVAR2)),
|
Box::new(flex(TVAR2)),
|
||||||
|
@ -733,7 +741,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
|
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::SET_INSERT,
|
Symbol::SET_INSERT,
|
||||||
SolvedType::Func(
|
top_level_function(
|
||||||
vec![set_type(flex(TVAR1)), flex(TVAR1)],
|
vec![set_type(flex(TVAR1)), flex(TVAR1)],
|
||||||
Box::new(set_type(flex(TVAR1))),
|
Box::new(set_type(flex(TVAR1))),
|
||||||
),
|
),
|
||||||
|
@ -741,7 +749,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
|
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::SET_REMOVE,
|
Symbol::SET_REMOVE,
|
||||||
SolvedType::Func(
|
top_level_function(
|
||||||
vec![set_type(flex(TVAR1)), flex(TVAR1)],
|
vec![set_type(flex(TVAR1)), flex(TVAR1)],
|
||||||
Box::new(set_type(flex(TVAR1))),
|
Box::new(set_type(flex(TVAR1))),
|
||||||
),
|
),
|
||||||
|
@ -752,10 +760,10 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// map : Result a err, (a -> b) -> Result b err
|
// map : Result a err, (a -> b) -> Result b err
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::RESULT_MAP,
|
Symbol::RESULT_MAP,
|
||||||
SolvedType::Func(
|
top_level_function(
|
||||||
vec![
|
vec![
|
||||||
result_type(flex(TVAR1), flex(TVAR3)),
|
result_type(flex(TVAR1), flex(TVAR3)),
|
||||||
SolvedType::Func(vec![flex(TVAR1)], Box::new(flex(TVAR2))),
|
closure(vec![flex(TVAR1)], TVAR4, Box::new(flex(TVAR2))),
|
||||||
],
|
],
|
||||||
Box::new(result_type(flex(TVAR2), flex(TVAR3))),
|
Box::new(result_type(flex(TVAR2), flex(TVAR3))),
|
||||||
),
|
),
|
||||||
|
@ -769,6 +777,20 @@ fn flex(tvar: VarId) -> SolvedType {
|
||||||
SolvedType::Flex(tvar)
|
SolvedType::Flex(tvar)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn top_level_function(arguments: Vec<SolvedType>, ret: Box<SolvedType>) -> SolvedType {
|
||||||
|
SolvedType::Func(
|
||||||
|
arguments,
|
||||||
|
Box::new(SolvedType::Flex(TOP_LEVEL_CLOSURE_VAR)),
|
||||||
|
ret,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn closure(arguments: Vec<SolvedType>, closure_var: VarId, ret: Box<SolvedType>) -> SolvedType {
|
||||||
|
SolvedType::Func(arguments, Box::new(SolvedType::Flex(closure_var)), ret)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn float_type() -> SolvedType {
|
fn float_type() -> SolvedType {
|
||||||
SolvedType::Apply(Symbol::NUM_FLOAT, Vec::new())
|
SolvedType::Apply(Symbol::NUM_FLOAT, Vec::new())
|
||||||
|
|
|
@ -37,6 +37,7 @@ const NUM_BUILTIN_IMPORTS: usize = 7;
|
||||||
|
|
||||||
/// These can be shared between definitions, they will get instantiated when converted to Type
|
/// These can be shared between definitions, they will get instantiated when converted to Type
|
||||||
const FUVAR: VarId = VarId::from_u32(1000);
|
const FUVAR: VarId = VarId::from_u32(1000);
|
||||||
|
const TOP_LEVEL_CLOSURE_VAR: VarId = VarId::from_u32(1001);
|
||||||
|
|
||||||
fn shared(base: SolvedType) -> SolvedType {
|
fn shared(base: SolvedType) -> SolvedType {
|
||||||
SolvedType::Apply(
|
SolvedType::Apply(
|
||||||
|
@ -495,6 +496,12 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
unique_function(vec![float_type(star1)], int_type(star2))
|
unique_function(vec![float_type(star1)], int_type(star2))
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// atan : Float -> Float
|
||||||
|
add_type(Symbol::NUM_ATAN, {
|
||||||
|
let_tvars! { star1, star2 };
|
||||||
|
unique_function(vec![float_type(star1)], float_type(star2))
|
||||||
|
});
|
||||||
|
|
||||||
// Bool module
|
// Bool module
|
||||||
|
|
||||||
// isEq or (==) : Attr * a, Attr * a -> Attr * Bool
|
// isEq or (==) : Attr * a, Attr * a -> Attr * Bool
|
||||||
|
@ -825,12 +832,16 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// , Attr Shared (a -> b)
|
// , Attr Shared (a -> b)
|
||||||
// -> Attr * (List b)
|
// -> Attr * (List b)
|
||||||
add_type(Symbol::LIST_MAP, {
|
add_type(Symbol::LIST_MAP, {
|
||||||
let_tvars! { a, b, star1, star2 };
|
let_tvars! { a, b, star1, star2, closure };
|
||||||
|
|
||||||
unique_function(
|
unique_function(
|
||||||
vec![
|
vec![
|
||||||
list_type(star1, a),
|
list_type(star1, a),
|
||||||
shared(SolvedType::Func(vec![flex(a)], Box::new(flex(b)))),
|
shared(SolvedType::Func(
|
||||||
|
vec![flex(a)],
|
||||||
|
Box::new(flex(closure)),
|
||||||
|
Box::new(flex(b)),
|
||||||
|
)),
|
||||||
],
|
],
|
||||||
list_type(star2, b),
|
list_type(star2, b),
|
||||||
)
|
)
|
||||||
|
@ -840,12 +851,16 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// , Attr Shared (a -> Attr * Bool)
|
// , Attr Shared (a -> Attr * Bool)
|
||||||
// -> Attr * (List a)
|
// -> Attr * (List a)
|
||||||
add_type(Symbol::LIST_KEEP_IF, {
|
add_type(Symbol::LIST_KEEP_IF, {
|
||||||
let_tvars! { a, star1, star2, star3 };
|
let_tvars! { a, star1, star2, star3, closure };
|
||||||
|
|
||||||
unique_function(
|
unique_function(
|
||||||
vec![
|
vec![
|
||||||
list_type(star1, a),
|
list_type(star1, a),
|
||||||
shared(SolvedType::Func(vec![flex(a)], Box::new(bool_type(star2)))),
|
shared(SolvedType::Func(
|
||||||
|
vec![flex(a)],
|
||||||
|
Box::new(flex(closure)),
|
||||||
|
Box::new(bool_type(star2)),
|
||||||
|
)),
|
||||||
],
|
],
|
||||||
list_type(star3, a),
|
list_type(star3, a),
|
||||||
)
|
)
|
||||||
|
@ -856,7 +871,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// , b
|
// , b
|
||||||
// -> b
|
// -> b
|
||||||
add_type(Symbol::LIST_WALK_RIGHT, {
|
add_type(Symbol::LIST_WALK_RIGHT, {
|
||||||
let_tvars! { u, a, b, star1 };
|
let_tvars! { u, a, b, star1, closure };
|
||||||
|
|
||||||
unique_function(
|
unique_function(
|
||||||
vec![
|
vec![
|
||||||
|
@ -869,6 +884,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
),
|
),
|
||||||
shared(SolvedType::Func(
|
shared(SolvedType::Func(
|
||||||
vec![attr_type(u, a), flex(b)],
|
vec![attr_type(u, a), flex(b)],
|
||||||
|
Box::new(flex(closure)),
|
||||||
Box::new(flex(b)),
|
Box::new(flex(b)),
|
||||||
)),
|
)),
|
||||||
flex(b),
|
flex(b),
|
||||||
|
@ -1026,7 +1042,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// , b
|
// , b
|
||||||
// -> b
|
// -> b
|
||||||
add_type(Symbol::SET_FOLDL, {
|
add_type(Symbol::SET_FOLDL, {
|
||||||
let_tvars! { star, u, a, b };
|
let_tvars! { star, u, a, b, closure };
|
||||||
|
|
||||||
unique_function(
|
unique_function(
|
||||||
vec![
|
vec![
|
||||||
|
@ -1039,6 +1055,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
),
|
),
|
||||||
shared(SolvedType::Func(
|
shared(SolvedType::Func(
|
||||||
vec![attr_type(u, a), flex(b)],
|
vec![attr_type(u, a), flex(b)],
|
||||||
|
Box::new(flex(closure)),
|
||||||
Box::new(flex(b)),
|
Box::new(flex(b)),
|
||||||
)),
|
)),
|
||||||
flex(b),
|
flex(b),
|
||||||
|
@ -1141,7 +1158,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// , Attr * (a -> b)
|
// , Attr * (a -> b)
|
||||||
// -> Attr * (Result b e)
|
// -> Attr * (Result b e)
|
||||||
add_type(Symbol::RESULT_MAP, {
|
add_type(Symbol::RESULT_MAP, {
|
||||||
let_tvars! { star1, star2, star3, a, b, e };
|
let_tvars! { star1, star2, star3, a, b, e, closure };
|
||||||
unique_function(
|
unique_function(
|
||||||
vec![
|
vec![
|
||||||
SolvedType::Apply(
|
SolvedType::Apply(
|
||||||
|
@ -1155,7 +1172,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
Symbol::ATTR_ATTR,
|
Symbol::ATTR_ATTR,
|
||||||
vec![
|
vec![
|
||||||
flex(star2),
|
flex(star2),
|
||||||
SolvedType::Func(vec![flex(a)], Box::new(flex(b))),
|
SolvedType::Func(vec![flex(a)], Box::new(flex(closure)), Box::new(flex(b))),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -1181,7 +1198,14 @@ fn flex(tvar: VarId) -> SolvedType {
|
||||||
fn unique_function(args: Vec<SolvedType>, ret: SolvedType) -> SolvedType {
|
fn unique_function(args: Vec<SolvedType>, ret: SolvedType) -> SolvedType {
|
||||||
SolvedType::Apply(
|
SolvedType::Apply(
|
||||||
Symbol::ATTR_ATTR,
|
Symbol::ATTR_ATTR,
|
||||||
vec![flex(FUVAR), SolvedType::Func(args, Box::new(ret))],
|
vec![
|
||||||
|
flex(FUVAR),
|
||||||
|
SolvedType::Func(
|
||||||
|
args,
|
||||||
|
Box::new(SolvedType::Flex(TOP_LEVEL_CLOSURE_VAR)),
|
||||||
|
Box::new(ret),
|
||||||
|
),
|
||||||
|
],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -130,7 +130,9 @@ fn can_annotation_help(
|
||||||
references,
|
references,
|
||||||
);
|
);
|
||||||
|
|
||||||
Type::Function(args, Box::new(ret))
|
let closure = Type::Variable(var_store.fresh());
|
||||||
|
|
||||||
|
Type::Function(args, Box::new(closure), Box::new(ret))
|
||||||
}
|
}
|
||||||
Apply(module_name, ident, type_arguments) => {
|
Apply(module_name, ident, type_arguments) => {
|
||||||
let symbol = if module_name.is_empty() {
|
let symbol = if module_name.is_empty() {
|
||||||
|
|
|
@ -98,6 +98,7 @@ pub fn builtin_defs(var_store: &mut VarStore) -> MutMap<Symbol, Def> {
|
||||||
Symbol::NUM_CEILING => num_ceiling,
|
Symbol::NUM_CEILING => num_ceiling,
|
||||||
Symbol::NUM_POW_INT => num_pow_int,
|
Symbol::NUM_POW_INT => num_pow_int,
|
||||||
Symbol::NUM_FLOOR => num_floor,
|
Symbol::NUM_FLOOR => num_floor,
|
||||||
|
Symbol::NUM_ATAN => num_atan,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -766,6 +767,26 @@ fn num_floor(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Num.atan : Float -> Float
|
||||||
|
fn num_atan(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
|
let arg_float_var = var_store.fresh();
|
||||||
|
let ret_float_var = var_store.fresh();
|
||||||
|
|
||||||
|
let body = RunLowLevel {
|
||||||
|
op: LowLevel::NumAtan,
|
||||||
|
args: vec![(arg_float_var, Var(Symbol::ARG_1))],
|
||||||
|
ret_var: ret_float_var,
|
||||||
|
};
|
||||||
|
|
||||||
|
defn(
|
||||||
|
symbol,
|
||||||
|
vec![(arg_float_var, Symbol::ARG_1)],
|
||||||
|
var_store,
|
||||||
|
body,
|
||||||
|
ret_float_var,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// List.isEmpty : List * -> Bool
|
/// List.isEmpty : List * -> Bool
|
||||||
fn list_is_empty(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
fn list_is_empty(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
let list_var = var_store.fresh();
|
let list_var = var_store.fresh();
|
||||||
|
@ -1590,13 +1611,15 @@ fn defn(
|
||||||
.map(|(var, symbol)| (var, no_region(Identifier(symbol))))
|
.map(|(var, symbol)| (var, no_region(Identifier(symbol))))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let expr = Closure(
|
let expr = Closure {
|
||||||
var_store.fresh(),
|
function_type: var_store.fresh(),
|
||||||
fn_name,
|
closure_type: var_store.fresh(),
|
||||||
Recursive::NotRecursive,
|
return_type: ret_var,
|
||||||
closure_args,
|
name: fn_name,
|
||||||
Box::new((no_region(body), ret_var)),
|
recursive: Recursive::NotRecursive,
|
||||||
);
|
arguments: closure_args,
|
||||||
|
loc_body: Box::new(no_region(body)),
|
||||||
|
};
|
||||||
|
|
||||||
Def {
|
Def {
|
||||||
loc_pattern: Located {
|
loc_pattern: Located {
|
||||||
|
|
|
@ -650,12 +650,12 @@ fn group_to_declaration(
|
||||||
let mut new_def = can_def.clone();
|
let mut new_def = can_def.clone();
|
||||||
|
|
||||||
// Determine recursivity of closures that are not tail-recursive
|
// Determine recursivity of closures that are not tail-recursive
|
||||||
if let Closure(fn_var, name, Recursive::NotRecursive, args, body) =
|
if let Closure {
|
||||||
new_def.loc_expr.value
|
recursive: recursive @ Recursive::NotRecursive,
|
||||||
|
..
|
||||||
|
} = &mut new_def.loc_expr.value
|
||||||
{
|
{
|
||||||
let recursion = closure_recursivity(*symbol, closures);
|
*recursive = closure_recursivity(*symbol, closures);
|
||||||
|
|
||||||
new_def.loc_expr.value = Closure(fn_var, name, recursion, args, body);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let is_recursive = successors(&symbol).contains(&symbol);
|
let is_recursive = successors(&symbol).contains(&symbol);
|
||||||
|
@ -678,12 +678,12 @@ fn group_to_declaration(
|
||||||
let mut new_def = can_def.clone();
|
let mut new_def = can_def.clone();
|
||||||
|
|
||||||
// Determine recursivity of closures that are not tail-recursive
|
// Determine recursivity of closures that are not tail-recursive
|
||||||
if let Closure(fn_var, name, Recursive::NotRecursive, args, body) =
|
if let Closure {
|
||||||
new_def.loc_expr.value
|
recursive: recursive @ Recursive::NotRecursive,
|
||||||
|
..
|
||||||
|
} = &mut new_def.loc_expr.value
|
||||||
{
|
{
|
||||||
let recursion = closure_recursivity(symbol, closures);
|
*recursive = closure_recursivity(symbol, closures);
|
||||||
|
|
||||||
new_def.loc_expr.value = Closure(fn_var, name, recursion, args, body);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !seen_pattern_regions.contains(&new_def.loc_pattern.region) {
|
if !seen_pattern_regions.contains(&new_def.loc_pattern.region) {
|
||||||
|
@ -808,16 +808,16 @@ fn canonicalize_pending_def<'a>(
|
||||||
region: loc_ann.region,
|
region: loc_ann.region,
|
||||||
};
|
};
|
||||||
|
|
||||||
let body = Box::new((body_expr, var_store.fresh()));
|
|
||||||
|
|
||||||
Located {
|
Located {
|
||||||
value: Closure(
|
value: Closure {
|
||||||
var_store.fresh(),
|
function_type: var_store.fresh(),
|
||||||
symbol,
|
closure_type: var_store.fresh(),
|
||||||
Recursive::NotRecursive,
|
return_type: var_store.fresh(),
|
||||||
underscores,
|
name: symbol,
|
||||||
body,
|
recursive: Recursive::NotRecursive,
|
||||||
),
|
arguments: underscores,
|
||||||
|
loc_body: Box::new(body_expr),
|
||||||
|
},
|
||||||
region: loc_ann.region,
|
region: loc_ann.region,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -962,7 +962,15 @@ fn canonicalize_pending_def<'a>(
|
||||||
if let (
|
if let (
|
||||||
&ast::Pattern::Identifier(ref _name),
|
&ast::Pattern::Identifier(ref _name),
|
||||||
&Pattern::Identifier(ref defined_symbol),
|
&Pattern::Identifier(ref defined_symbol),
|
||||||
&Closure(fn_var, ref symbol, _, ref arguments, ref body),
|
&Closure {
|
||||||
|
function_type,
|
||||||
|
closure_type,
|
||||||
|
return_type,
|
||||||
|
name: ref symbol,
|
||||||
|
ref arguments,
|
||||||
|
loc_body: ref body,
|
||||||
|
..
|
||||||
|
},
|
||||||
) = (
|
) = (
|
||||||
&loc_pattern.value,
|
&loc_pattern.value,
|
||||||
&loc_can_pattern.value,
|
&loc_can_pattern.value,
|
||||||
|
@ -1000,13 +1008,15 @@ fn canonicalize_pending_def<'a>(
|
||||||
});
|
});
|
||||||
|
|
||||||
// renamed_closure_def = Some(&defined_symbol);
|
// renamed_closure_def = Some(&defined_symbol);
|
||||||
loc_can_expr.value = Closure(
|
loc_can_expr.value = Closure {
|
||||||
fn_var,
|
function_type,
|
||||||
*symbol,
|
closure_type,
|
||||||
is_recursive,
|
return_type,
|
||||||
arguments.clone(),
|
name: *symbol,
|
||||||
body.clone(),
|
recursive: is_recursive,
|
||||||
);
|
arguments: arguments.clone(),
|
||||||
|
loc_body: body.clone(),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the referenced locals in the refs_by_symbol map, so we can later figure out
|
// Store the referenced locals in the refs_by_symbol map, so we can later figure out
|
||||||
|
@ -1086,7 +1096,15 @@ fn canonicalize_pending_def<'a>(
|
||||||
if let (
|
if let (
|
||||||
&ast::Pattern::Identifier(ref _name),
|
&ast::Pattern::Identifier(ref _name),
|
||||||
&Pattern::Identifier(ref defined_symbol),
|
&Pattern::Identifier(ref defined_symbol),
|
||||||
&Closure(fn_var, ref symbol, _, ref arguments, ref body),
|
&Closure {
|
||||||
|
function_type,
|
||||||
|
closure_type,
|
||||||
|
return_type,
|
||||||
|
name: ref symbol,
|
||||||
|
ref arguments,
|
||||||
|
loc_body: ref body,
|
||||||
|
..
|
||||||
|
},
|
||||||
) = (
|
) = (
|
||||||
&loc_pattern.value,
|
&loc_pattern.value,
|
||||||
&loc_can_pattern.value,
|
&loc_can_pattern.value,
|
||||||
|
@ -1123,13 +1141,15 @@ fn canonicalize_pending_def<'a>(
|
||||||
refs.lookups = refs.lookups.without(defined_symbol);
|
refs.lookups = refs.lookups.without(defined_symbol);
|
||||||
});
|
});
|
||||||
|
|
||||||
loc_can_expr.value = Closure(
|
loc_can_expr.value = Closure {
|
||||||
fn_var,
|
function_type,
|
||||||
*symbol,
|
closure_type,
|
||||||
is_recursive,
|
return_type,
|
||||||
arguments.clone(),
|
name: *symbol,
|
||||||
body.clone(),
|
recursive: is_recursive,
|
||||||
);
|
arguments: arguments.clone(),
|
||||||
|
loc_body: body.clone(),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the referenced locals in the refs_by_symbol map, so we can later figure out
|
// Store the referenced locals in the refs_by_symbol map, so we can later figure out
|
||||||
|
|
|
@ -87,7 +87,7 @@ pub enum Expr {
|
||||||
/// This is *only* for calling functions, not for tag application.
|
/// This is *only* for calling functions, not for tag application.
|
||||||
/// The Tag variant contains any applied values inside it.
|
/// The Tag variant contains any applied values inside it.
|
||||||
Call(
|
Call(
|
||||||
Box<(Variable, Located<Expr>, Variable)>,
|
Box<(Variable, Located<Expr>, Variable, Variable)>,
|
||||||
Vec<(Variable, Located<Expr>)>,
|
Vec<(Variable, Located<Expr>)>,
|
||||||
CalledVia,
|
CalledVia,
|
||||||
),
|
),
|
||||||
|
@ -97,13 +97,15 @@ pub enum Expr {
|
||||||
ret_var: Variable,
|
ret_var: Variable,
|
||||||
},
|
},
|
||||||
|
|
||||||
Closure(
|
Closure {
|
||||||
Variable,
|
function_type: Variable,
|
||||||
Symbol,
|
closure_type: Variable,
|
||||||
Recursive,
|
return_type: Variable,
|
||||||
Vec<(Variable, Located<Pattern>)>,
|
name: Symbol,
|
||||||
Box<(Located<Expr>, Variable)>,
|
recursive: Recursive,
|
||||||
),
|
arguments: Vec<(Variable, Located<Pattern>)>,
|
||||||
|
loc_body: Box<Located<Expr>>,
|
||||||
|
},
|
||||||
|
|
||||||
// Product Types
|
// Product Types
|
||||||
Record {
|
Record {
|
||||||
|
@ -125,6 +127,7 @@ pub enum Expr {
|
||||||
/// field accessor as a function, e.g. (.foo) expr
|
/// field accessor as a function, e.g. (.foo) expr
|
||||||
Accessor {
|
Accessor {
|
||||||
record_var: Variable,
|
record_var: Variable,
|
||||||
|
closure_var: Variable,
|
||||||
ext_var: Variable,
|
ext_var: Variable,
|
||||||
field_var: Variable,
|
field_var: Variable,
|
||||||
field: Lowercase,
|
field: Lowercase,
|
||||||
|
@ -323,7 +326,12 @@ pub fn canonicalize_expr<'a>(
|
||||||
};
|
};
|
||||||
|
|
||||||
Call(
|
Call(
|
||||||
Box::new((var_store.fresh(), fn_expr, var_store.fresh())),
|
Box::new((
|
||||||
|
var_store.fresh(),
|
||||||
|
fn_expr,
|
||||||
|
var_store.fresh(),
|
||||||
|
var_store.fresh(),
|
||||||
|
)),
|
||||||
args,
|
args,
|
||||||
*application_style,
|
*application_style,
|
||||||
)
|
)
|
||||||
|
@ -346,7 +354,12 @@ pub fn canonicalize_expr<'a>(
|
||||||
_ => {
|
_ => {
|
||||||
// This could be something like ((if True then fn1 else fn2) arg1 arg2).
|
// This could be something like ((if True then fn1 else fn2) arg1 arg2).
|
||||||
Call(
|
Call(
|
||||||
Box::new((var_store.fresh(), fn_expr, var_store.fresh())),
|
Box::new((
|
||||||
|
var_store.fresh(),
|
||||||
|
fn_expr,
|
||||||
|
var_store.fresh(),
|
||||||
|
var_store.fresh(),
|
||||||
|
)),
|
||||||
args,
|
args,
|
||||||
*application_style,
|
*application_style,
|
||||||
)
|
)
|
||||||
|
@ -471,13 +484,15 @@ pub fn canonicalize_expr<'a>(
|
||||||
env.register_closure(symbol, output.references.clone());
|
env.register_closure(symbol, output.references.clone());
|
||||||
|
|
||||||
(
|
(
|
||||||
Closure(
|
Closure {
|
||||||
var_store.fresh(),
|
function_type: var_store.fresh(),
|
||||||
symbol,
|
closure_type: var_store.fresh(),
|
||||||
Recursive::NotRecursive,
|
return_type: var_store.fresh(),
|
||||||
can_args,
|
name: symbol,
|
||||||
Box::new((loc_body_expr, var_store.fresh())),
|
recursive: Recursive::NotRecursive,
|
||||||
),
|
arguments: can_args,
|
||||||
|
loc_body: Box::new(loc_body_expr),
|
||||||
|
},
|
||||||
output,
|
output,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -537,6 +552,7 @@ pub fn canonicalize_expr<'a>(
|
||||||
Accessor {
|
Accessor {
|
||||||
record_var: var_store.fresh(),
|
record_var: var_store.fresh(),
|
||||||
ext_var: var_store.fresh(),
|
ext_var: var_store.fresh(),
|
||||||
|
closure_var: var_store.fresh(),
|
||||||
field_var: var_store.fresh(),
|
field_var: var_store.fresh(),
|
||||||
field: (*field).into(),
|
field: (*field).into(),
|
||||||
},
|
},
|
||||||
|
@ -1193,20 +1209,30 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
|
||||||
LetNonRec(Box::new(def), Box::new(loc_expr), var, aliases)
|
LetNonRec(Box::new(def), Box::new(loc_expr), var, aliases)
|
||||||
}
|
}
|
||||||
|
|
||||||
Closure(var, symbol, recursive, patterns, boxed_expr) => {
|
Closure {
|
||||||
let (loc_expr, expr_var) = *boxed_expr;
|
function_type,
|
||||||
|
closure_type,
|
||||||
|
return_type,
|
||||||
|
recursive,
|
||||||
|
name,
|
||||||
|
arguments,
|
||||||
|
loc_body,
|
||||||
|
} => {
|
||||||
|
let loc_expr = *loc_body;
|
||||||
let loc_expr = Located {
|
let loc_expr = Located {
|
||||||
value: inline_calls(var_store, scope, loc_expr.value),
|
value: inline_calls(var_store, scope, loc_expr.value),
|
||||||
region: loc_expr.region,
|
region: loc_expr.region,
|
||||||
};
|
};
|
||||||
|
|
||||||
Closure(
|
Closure {
|
||||||
var,
|
function_type,
|
||||||
symbol,
|
closure_type,
|
||||||
|
return_type,
|
||||||
recursive,
|
recursive,
|
||||||
patterns,
|
name,
|
||||||
Box::new((loc_expr, expr_var)),
|
arguments,
|
||||||
)
|
loc_body: Box::new(loc_expr),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Record { record_var, fields } => {
|
Record { record_var, fields } => {
|
||||||
|
@ -1243,14 +1269,20 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
|
||||||
}
|
}
|
||||||
|
|
||||||
Call(boxed_tuple, args, called_via) => {
|
Call(boxed_tuple, args, called_via) => {
|
||||||
let (fn_var, loc_expr, expr_var) = *boxed_tuple;
|
let (fn_var, loc_expr, closure_var, expr_var) = *boxed_tuple;
|
||||||
|
|
||||||
match loc_expr.value {
|
match loc_expr.value {
|
||||||
Var(symbol) if symbol.is_builtin() => match builtin_defs(var_store).get(&symbol) {
|
Var(symbol) if symbol.is_builtin() => match builtin_defs(var_store).get(&symbol) {
|
||||||
Some(Def {
|
Some(Def {
|
||||||
loc_expr:
|
loc_expr:
|
||||||
Located {
|
Located {
|
||||||
value: Closure(_var, _, recursive, params, boxed_body),
|
value:
|
||||||
|
Closure {
|
||||||
|
recursive,
|
||||||
|
arguments: params,
|
||||||
|
loc_body: boxed_body,
|
||||||
|
..
|
||||||
|
},
|
||||||
..
|
..
|
||||||
},
|
},
|
||||||
..
|
..
|
||||||
|
@ -1263,7 +1295,7 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
|
||||||
debug_assert_eq!(params.len(), args.len());
|
debug_assert_eq!(params.len(), args.len());
|
||||||
|
|
||||||
// Start with the function's body as the answer.
|
// Start with the function's body as the answer.
|
||||||
let (mut loc_answer, _body_var) = *boxed_body.clone();
|
let mut loc_answer = *boxed_body.clone();
|
||||||
|
|
||||||
// Wrap the body in one LetNonRec for each argument,
|
// Wrap the body in one LetNonRec for each argument,
|
||||||
// such that at the end we have all the arguments in
|
// such that at the end we have all the arguments in
|
||||||
|
@ -1311,7 +1343,11 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
// For now, we only inline calls to builtins. Leave this alone!
|
// For now, we only inline calls to builtins. Leave this alone!
|
||||||
Call(Box::new((fn_var, loc_expr, expr_var)), args, called_via)
|
Call(
|
||||||
|
Box::new((fn_var, loc_expr, closure_var, expr_var)),
|
||||||
|
args,
|
||||||
|
called_via,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1458,7 +1494,12 @@ fn desugar_str_segments(var_store: &mut VarStore, segments: Vec<StrSegment>) ->
|
||||||
|
|
||||||
let fn_expr = Located::new(0, 0, 0, 0, Expr::Var(Symbol::STR_CONCAT));
|
let fn_expr = Located::new(0, 0, 0, 0, Expr::Var(Symbol::STR_CONCAT));
|
||||||
let expr = Expr::Call(
|
let expr = Expr::Call(
|
||||||
Box::new((var_store.fresh(), fn_expr, var_store.fresh())),
|
Box::new((
|
||||||
|
var_store.fresh(),
|
||||||
|
fn_expr,
|
||||||
|
var_store.fresh(),
|
||||||
|
var_store.fresh(),
|
||||||
|
)),
|
||||||
vec![
|
vec![
|
||||||
(var_store.fresh(), loc_new_expr),
|
(var_store.fresh(), loc_new_expr),
|
||||||
(var_store.fresh(), loc_expr),
|
(var_store.fresh(), loc_expr),
|
||||||
|
|
|
@ -309,7 +309,10 @@ mod test_can {
|
||||||
match expr {
|
match expr {
|
||||||
LetRec(assignments, body, _, _) => {
|
LetRec(assignments, body, _, _) => {
|
||||||
match &assignments.get(i).map(|def| &def.loc_expr.value) {
|
match &assignments.get(i).map(|def| &def.loc_expr.value) {
|
||||||
Some(Closure(_, _, recursion, _, _)) => recursion.clone(),
|
Some(Closure {
|
||||||
|
recursive: recursion,
|
||||||
|
..
|
||||||
|
}) => recursion.clone(),
|
||||||
Some(other @ _) => {
|
Some(other @ _) => {
|
||||||
panic!("assignment at {} is not a closure, but a {:?}", i, other)
|
panic!("assignment at {} is not a closure, but a {:?}", i, other)
|
||||||
}
|
}
|
||||||
|
@ -328,7 +331,10 @@ mod test_can {
|
||||||
get_closure(&body.value, i - 1)
|
get_closure(&body.value, i - 1)
|
||||||
} else {
|
} else {
|
||||||
match &def.loc_expr.value {
|
match &def.loc_expr.value {
|
||||||
Closure(_, _, recursion, _, _) => recursion.clone(),
|
Closure {
|
||||||
|
recursive: recursion,
|
||||||
|
..
|
||||||
|
} => recursion.clone(),
|
||||||
other @ _ => {
|
other @ _ => {
|
||||||
panic!("assignment at {} is not a closure, but a {:?}", i, other)
|
panic!("assignment at {} is not a closure, but a {:?}", i, other)
|
||||||
}
|
}
|
||||||
|
|
|
@ -239,7 +239,7 @@ pub fn constrain_expr(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Call(boxed, loc_args, _application_style) => {
|
Call(boxed, loc_args, _application_style) => {
|
||||||
let (fn_var, loc_fn, ret_var) = &**boxed;
|
let (fn_var, loc_fn, closure_var, ret_var) = &**boxed;
|
||||||
// The expression that evaluates to the function being called, e.g. `foo` in
|
// The expression that evaluates to the function being called, e.g. `foo` in
|
||||||
// (foo) bar baz
|
// (foo) bar baz
|
||||||
let opt_symbol = if let Var(symbol) = loc_fn.value {
|
let opt_symbol = if let Var(symbol) = loc_fn.value {
|
||||||
|
@ -262,11 +262,15 @@ pub fn constrain_expr(
|
||||||
// The function's return type
|
// The function's return type
|
||||||
let ret_type = Variable(*ret_var);
|
let ret_type = Variable(*ret_var);
|
||||||
|
|
||||||
|
// type of values captured in the closure
|
||||||
|
let closure_type = Variable(*closure_var);
|
||||||
|
|
||||||
// This will be used in the occurs check
|
// This will be used in the occurs check
|
||||||
let mut vars = Vec::with_capacity(2 + loc_args.len());
|
let mut vars = Vec::with_capacity(2 + loc_args.len());
|
||||||
|
|
||||||
vars.push(*fn_var);
|
vars.push(*fn_var);
|
||||||
vars.push(*ret_var);
|
vars.push(*ret_var);
|
||||||
|
vars.push(*closure_var);
|
||||||
|
|
||||||
let mut arg_types = Vec::with_capacity(loc_args.len());
|
let mut arg_types = Vec::with_capacity(loc_args.len());
|
||||||
let mut arg_cons = Vec::with_capacity(loc_args.len());
|
let mut arg_cons = Vec::with_capacity(loc_args.len());
|
||||||
|
@ -289,7 +293,11 @@ pub fn constrain_expr(
|
||||||
|
|
||||||
let expected_fn_type = ForReason(
|
let expected_fn_type = ForReason(
|
||||||
fn_reason,
|
fn_reason,
|
||||||
Function(arg_types, Box::new(ret_type.clone())),
|
Function(
|
||||||
|
arg_types,
|
||||||
|
Box::new(closure_type),
|
||||||
|
Box::new(ret_type.clone()),
|
||||||
|
),
|
||||||
region,
|
region,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -306,21 +314,31 @@ pub fn constrain_expr(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Var(symbol) => Lookup(*symbol, expected, region),
|
Var(symbol) => Lookup(*symbol, expected, region),
|
||||||
Closure(fn_var, _symbol, _recursive, args, boxed) => {
|
Closure {
|
||||||
let (loc_body_expr, ret_var) = boxed.as_ref();
|
function_type: fn_var,
|
||||||
|
closure_type: closure_var,
|
||||||
|
return_type: ret_var,
|
||||||
|
arguments,
|
||||||
|
loc_body: boxed,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let loc_body_expr = &**boxed;
|
||||||
let mut state = PatternState {
|
let mut state = PatternState {
|
||||||
headers: SendMap::default(),
|
headers: SendMap::default(),
|
||||||
vars: Vec::with_capacity(args.len()),
|
vars: Vec::with_capacity(arguments.len()),
|
||||||
constraints: Vec::with_capacity(1),
|
constraints: Vec::with_capacity(1),
|
||||||
};
|
};
|
||||||
let mut vars = Vec::with_capacity(state.vars.capacity() + 1);
|
let mut vars = Vec::with_capacity(state.vars.capacity() + 1);
|
||||||
let mut pattern_types = Vec::with_capacity(state.vars.capacity());
|
let mut pattern_types = Vec::with_capacity(state.vars.capacity());
|
||||||
let ret_var = *ret_var;
|
let ret_var = *ret_var;
|
||||||
|
let closure_var = *closure_var;
|
||||||
let ret_type = Type::Variable(ret_var);
|
let ret_type = Type::Variable(ret_var);
|
||||||
|
let closure_type = Type::Variable(closure_var);
|
||||||
|
|
||||||
vars.push(ret_var);
|
vars.push(ret_var);
|
||||||
|
vars.push(closure_var);
|
||||||
|
|
||||||
for (pattern_var, loc_pattern) in args {
|
for (pattern_var, loc_pattern) in arguments {
|
||||||
let pattern_type = Type::Variable(*pattern_var);
|
let pattern_type = Type::Variable(*pattern_var);
|
||||||
let pattern_expected = PExpected::NoExpectation(pattern_type.clone());
|
let pattern_expected = PExpected::NoExpectation(pattern_type.clone());
|
||||||
|
|
||||||
|
@ -337,7 +355,11 @@ pub fn constrain_expr(
|
||||||
vars.push(*pattern_var);
|
vars.push(*pattern_var);
|
||||||
}
|
}
|
||||||
|
|
||||||
let fn_type = Type::Function(pattern_types, Box::new(ret_type.clone()));
|
let fn_type = Type::Function(
|
||||||
|
pattern_types,
|
||||||
|
Box::new(closure_type),
|
||||||
|
Box::new(ret_type.clone()),
|
||||||
|
);
|
||||||
let body_type = NoExpectation(ret_type);
|
let body_type = NoExpectation(ret_type);
|
||||||
let ret_constraint =
|
let ret_constraint =
|
||||||
constrain_expr(env, loc_body_expr.region, &loc_body_expr.value, body_type);
|
constrain_expr(env, loc_body_expr.region, &loc_body_expr.value, body_type);
|
||||||
|
@ -655,6 +677,7 @@ pub fn constrain_expr(
|
||||||
Accessor {
|
Accessor {
|
||||||
field,
|
field,
|
||||||
record_var,
|
record_var,
|
||||||
|
closure_var,
|
||||||
ext_var,
|
ext_var,
|
||||||
field_var,
|
field_var,
|
||||||
} => {
|
} => {
|
||||||
|
@ -679,10 +702,14 @@ pub fn constrain_expr(
|
||||||
);
|
);
|
||||||
|
|
||||||
exists(
|
exists(
|
||||||
vec![*record_var, field_var, ext_var],
|
vec![*record_var, *closure_var, field_var, ext_var],
|
||||||
And(vec![
|
And(vec![
|
||||||
Eq(
|
Eq(
|
||||||
Type::Function(vec![record_type], Box::new(field_type)),
|
Type::Function(
|
||||||
|
vec![record_type],
|
||||||
|
Box::new(Type::Variable(*closure_var)),
|
||||||
|
Box::new(field_type),
|
||||||
|
),
|
||||||
expected,
|
expected,
|
||||||
category,
|
category,
|
||||||
region,
|
region,
|
||||||
|
@ -1032,26 +1059,36 @@ fn constrain_def(env: &Env, def: &Def, body_con: Constraint) -> Constraint {
|
||||||
// instead of the more generic "something is wrong with the body of `f`"
|
// instead of the more generic "something is wrong with the body of `f`"
|
||||||
match (&def.loc_expr.value, &signature) {
|
match (&def.loc_expr.value, &signature) {
|
||||||
(
|
(
|
||||||
Closure(fn_var, _symbol, _recursive, args, boxed),
|
Closure {
|
||||||
Type::Function(arg_types, _),
|
function_type: fn_var,
|
||||||
|
closure_type: closure_var,
|
||||||
|
return_type: ret_var,
|
||||||
|
arguments,
|
||||||
|
loc_body,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
Type::Function(arg_types, _, _),
|
||||||
) => {
|
) => {
|
||||||
let expected = annotation_expected;
|
let expected = annotation_expected;
|
||||||
let region = def.loc_expr.region;
|
let region = def.loc_expr.region;
|
||||||
|
|
||||||
let (loc_body_expr, ret_var) = boxed.as_ref();
|
let loc_body_expr = &**loc_body;
|
||||||
let mut state = PatternState {
|
let mut state = PatternState {
|
||||||
headers: SendMap::default(),
|
headers: SendMap::default(),
|
||||||
vars: Vec::with_capacity(args.len()),
|
vars: Vec::with_capacity(arguments.len()),
|
||||||
constraints: Vec::with_capacity(1),
|
constraints: Vec::with_capacity(1),
|
||||||
};
|
};
|
||||||
let mut vars = Vec::with_capacity(state.vars.capacity() + 1);
|
let mut vars = Vec::with_capacity(state.vars.capacity() + 1);
|
||||||
let mut pattern_types = Vec::with_capacity(state.vars.capacity());
|
let mut pattern_types = Vec::with_capacity(state.vars.capacity());
|
||||||
let ret_var = *ret_var;
|
let ret_var = *ret_var;
|
||||||
|
let closure_var = *closure_var;
|
||||||
let ret_type = Type::Variable(ret_var);
|
let ret_type = Type::Variable(ret_var);
|
||||||
|
let closure_type = Type::Variable(closure_var);
|
||||||
|
|
||||||
vars.push(ret_var);
|
vars.push(ret_var);
|
||||||
|
vars.push(closure_var);
|
||||||
|
|
||||||
let it = args.iter().zip(arg_types.iter()).enumerate();
|
let it = arguments.iter().zip(arg_types.iter()).enumerate();
|
||||||
for (index, ((pattern_var, loc_pattern), loc_ann)) in it {
|
for (index, ((pattern_var, loc_pattern), loc_ann)) in it {
|
||||||
{
|
{
|
||||||
// ensure type matches the one in the annotation
|
// ensure type matches the one in the annotation
|
||||||
|
@ -1098,7 +1135,11 @@ fn constrain_def(env: &Env, def: &Def, body_con: Constraint) -> Constraint {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let fn_type = Type::Function(pattern_types, Box::new(ret_type.clone()));
|
let fn_type = Type::Function(
|
||||||
|
pattern_types,
|
||||||
|
Box::new(closure_type),
|
||||||
|
Box::new(ret_type.clone()),
|
||||||
|
);
|
||||||
let body_type = NoExpectation(ret_type);
|
let body_type = NoExpectation(ret_type);
|
||||||
let ret_constraint =
|
let ret_constraint =
|
||||||
constrain_expr(env, loc_body_expr.region, &loc_body_expr.value, body_type);
|
constrain_expr(env, loc_body_expr.region, &loc_body_expr.value, body_type);
|
||||||
|
|
|
@ -178,7 +178,7 @@ fn to_type(solved_type: &SolvedType, free_vars: &mut FreeVars, var_store: &mut V
|
||||||
use roc_types::solved_types::SolvedType::*;
|
use roc_types::solved_types::SolvedType::*;
|
||||||
|
|
||||||
match solved_type {
|
match solved_type {
|
||||||
Func(args, ret) => {
|
Func(args, closure, ret) => {
|
||||||
let mut new_args = Vec::with_capacity(args.len());
|
let mut new_args = Vec::with_capacity(args.len());
|
||||||
|
|
||||||
for arg in args {
|
for arg in args {
|
||||||
|
@ -186,8 +186,9 @@ fn to_type(solved_type: &SolvedType, free_vars: &mut FreeVars, var_store: &mut V
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_ret = to_type(&ret, free_vars, var_store);
|
let new_ret = to_type(&ret, free_vars, var_store);
|
||||||
|
let new_closure = to_type(&closure, free_vars, var_store);
|
||||||
|
|
||||||
Type::Function(new_args, Box::new(new_ret))
|
Type::Function(new_args, Box::new(new_closure), Box::new(new_ret))
|
||||||
}
|
}
|
||||||
Apply(symbol, args) => {
|
Apply(symbol, args) => {
|
||||||
let mut new_args = Vec::with_capacity(args.len());
|
let mut new_args = Vec::with_capacity(args.len());
|
||||||
|
|
|
@ -707,24 +707,35 @@ pub fn constrain_expr(
|
||||||
expected,
|
expected,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Closure(fn_var, _symbol, recursion, args, boxed) => {
|
Closure {
|
||||||
|
function_type: fn_var,
|
||||||
|
return_type: ret_var,
|
||||||
|
closure_type: closure_var,
|
||||||
|
recursive: recursion,
|
||||||
|
arguments,
|
||||||
|
loc_body: boxed,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
use roc_can::expr::Recursive;
|
use roc_can::expr::Recursive;
|
||||||
|
|
||||||
let (loc_body_expr, ret_var) = &**boxed;
|
let loc_body_expr = &**boxed;
|
||||||
let mut state = PatternState {
|
let mut state = PatternState {
|
||||||
headers: SendMap::default(),
|
headers: SendMap::default(),
|
||||||
vars: Vec::with_capacity(args.len()),
|
vars: Vec::with_capacity(arguments.len()),
|
||||||
constraints: Vec::with_capacity(1),
|
constraints: Vec::with_capacity(1),
|
||||||
};
|
};
|
||||||
let mut vars = Vec::with_capacity(state.vars.capacity() + 1);
|
let mut vars = Vec::with_capacity(state.vars.capacity() + 1);
|
||||||
let mut pattern_types = Vec::with_capacity(state.vars.capacity());
|
let mut pattern_types = Vec::with_capacity(state.vars.capacity());
|
||||||
let ret_var = *ret_var;
|
let ret_var = *ret_var;
|
||||||
let ret_type = Type::Variable(ret_var);
|
let ret_type = Type::Variable(ret_var);
|
||||||
|
let closure_var = *closure_var;
|
||||||
|
let closure_type = Type::Variable(closure_var);
|
||||||
|
|
||||||
vars.push(ret_var);
|
vars.push(ret_var);
|
||||||
|
vars.push(closure_var);
|
||||||
vars.push(*fn_var);
|
vars.push(*fn_var);
|
||||||
|
|
||||||
for (pattern_var, loc_pattern) in args {
|
for (pattern_var, loc_pattern) in arguments {
|
||||||
let pattern_type = Type::Variable(*pattern_var);
|
let pattern_type = Type::Variable(*pattern_var);
|
||||||
let pattern_expected = PExpected::NoExpectation(pattern_type.clone());
|
let pattern_expected = PExpected::NoExpectation(pattern_type.clone());
|
||||||
|
|
||||||
|
@ -755,7 +766,11 @@ pub fn constrain_expr(
|
||||||
|
|
||||||
let fn_type = attr_type(
|
let fn_type = attr_type(
|
||||||
fn_uniq_type,
|
fn_uniq_type,
|
||||||
Type::Function(pattern_types, Box::new(ret_type.clone())),
|
Type::Function(
|
||||||
|
pattern_types,
|
||||||
|
Box::new(closure_type),
|
||||||
|
Box::new(ret_type.clone()),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
let body_type = Expected::NoExpectation(ret_type);
|
let body_type = Expected::NoExpectation(ret_type);
|
||||||
let ret_constraint = constrain_expr(
|
let ret_constraint = constrain_expr(
|
||||||
|
@ -795,9 +810,10 @@ pub fn constrain_expr(
|
||||||
}
|
}
|
||||||
|
|
||||||
Call(boxed, loc_args, _) => {
|
Call(boxed, loc_args, _) => {
|
||||||
let (fn_var, fn_expr, ret_var) = &**boxed;
|
let (fn_var, fn_expr, closure_var, ret_var) = &**boxed;
|
||||||
let fn_type = Variable(*fn_var);
|
let fn_type = Variable(*fn_var);
|
||||||
let ret_type = Variable(*ret_var);
|
let ret_type = Variable(*ret_var);
|
||||||
|
let closure_type = Variable(*closure_var);
|
||||||
let fn_expected = Expected::NoExpectation(fn_type.clone());
|
let fn_expected = Expected::NoExpectation(fn_type.clone());
|
||||||
let fn_region = fn_expr.region;
|
let fn_region = fn_expr.region;
|
||||||
|
|
||||||
|
@ -811,6 +827,7 @@ pub fn constrain_expr(
|
||||||
|
|
||||||
vars.push(*fn_var);
|
vars.push(*fn_var);
|
||||||
vars.push(*ret_var);
|
vars.push(*ret_var);
|
||||||
|
vars.push(*closure_var);
|
||||||
|
|
||||||
// Canonicalize the function expression and its arguments
|
// Canonicalize the function expression and its arguments
|
||||||
let fn_con = constrain_expr(
|
let fn_con = constrain_expr(
|
||||||
|
@ -862,7 +879,11 @@ pub fn constrain_expr(
|
||||||
fn_reason,
|
fn_reason,
|
||||||
attr_type(
|
attr_type(
|
||||||
Bool::variable(expected_uniq_type),
|
Bool::variable(expected_uniq_type),
|
||||||
Function(arg_types, Box::new(ret_type.clone())),
|
Function(
|
||||||
|
arg_types,
|
||||||
|
Box::new(closure_type),
|
||||||
|
Box::new(ret_type.clone()),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
region,
|
region,
|
||||||
);
|
);
|
||||||
|
@ -1426,6 +1447,7 @@ pub fn constrain_expr(
|
||||||
Accessor {
|
Accessor {
|
||||||
field,
|
field,
|
||||||
record_var,
|
record_var,
|
||||||
|
closure_var,
|
||||||
field_var,
|
field_var,
|
||||||
ext_var,
|
ext_var,
|
||||||
} => {
|
} => {
|
||||||
|
@ -1455,14 +1477,20 @@ pub fn constrain_expr(
|
||||||
);
|
);
|
||||||
|
|
||||||
let fn_uniq_var = var_store.fresh();
|
let fn_uniq_var = var_store.fresh();
|
||||||
|
let closure_type = Type::Variable(*closure_var);
|
||||||
let fn_type = attr_type(
|
let fn_type = attr_type(
|
||||||
Bool::variable(fn_uniq_var),
|
Bool::variable(fn_uniq_var),
|
||||||
Type::Function(vec![record_type], Box::new(field_type)),
|
Type::Function(
|
||||||
|
vec![record_type],
|
||||||
|
Box::new(closure_type),
|
||||||
|
Box::new(field_type),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
exists(
|
exists(
|
||||||
vec![
|
vec![
|
||||||
*record_var,
|
*record_var,
|
||||||
|
*closure_var,
|
||||||
*field_var,
|
*field_var,
|
||||||
*ext_var,
|
*ext_var,
|
||||||
fn_uniq_var,
|
fn_uniq_var,
|
||||||
|
@ -1873,21 +1901,33 @@ fn annotation_to_attr_type(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Function(arguments, result) => {
|
Function(arguments, closure, result) => {
|
||||||
let uniq_var = var_store.fresh();
|
let uniq_var = var_store.fresh();
|
||||||
let (mut arg_vars, args_lifted) =
|
let (mut arg_vars, args_lifted) =
|
||||||
annotation_to_attr_type_many(var_store, arguments, rigids, change_var_kind);
|
annotation_to_attr_type_many(var_store, arguments, rigids, change_var_kind);
|
||||||
|
let (closure_vars, closure_lifted) =
|
||||||
|
annotation_to_attr_type(var_store, closure, rigids, change_var_kind);
|
||||||
let (result_vars, result_lifted) =
|
let (result_vars, result_lifted) =
|
||||||
annotation_to_attr_type(var_store, result, rigids, change_var_kind);
|
annotation_to_attr_type(var_store, result, rigids, change_var_kind);
|
||||||
|
|
||||||
arg_vars.extend(result_vars);
|
arg_vars.extend(result_vars);
|
||||||
|
arg_vars.extend(closure_vars);
|
||||||
arg_vars.push(uniq_var);
|
arg_vars.push(uniq_var);
|
||||||
|
|
||||||
|
match **closure {
|
||||||
|
Type::Variable(c) => arg_vars.push(c),
|
||||||
|
_ => unreachable!("closure must contain a type variable"),
|
||||||
|
}
|
||||||
|
|
||||||
(
|
(
|
||||||
arg_vars,
|
arg_vars,
|
||||||
attr_type(
|
attr_type(
|
||||||
Bool::variable(uniq_var),
|
Bool::variable(uniq_var),
|
||||||
Type::Function(args_lifted, Box::new(result_lifted)),
|
Type::Function(
|
||||||
|
args_lifted,
|
||||||
|
Box::new(closure_lifted),
|
||||||
|
Box::new(result_lifted),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -2542,8 +2582,9 @@ fn fix_mutual_recursive_alias_help_help(rec_var: Variable, attribute: &Type, int
|
||||||
use Type::*;
|
use Type::*;
|
||||||
|
|
||||||
match into_type {
|
match into_type {
|
||||||
Function(args, ret) => {
|
Function(args, closure, ret) => {
|
||||||
fix_mutual_recursive_alias_help(rec_var, attribute, ret);
|
fix_mutual_recursive_alias_help(rec_var, attribute, ret);
|
||||||
|
fix_mutual_recursive_alias_help(rec_var, attribute, closure);
|
||||||
args.iter_mut()
|
args.iter_mut()
|
||||||
.for_each(|arg| fix_mutual_recursive_alias_help(rec_var, attribute, arg));
|
.for_each(|arg| fix_mutual_recursive_alias_help(rec_var, attribute, arg));
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,3 +52,4 @@ quickcheck_macros = "0.8"
|
||||||
tokio = { version = "0.2", features = ["blocking", "fs", "sync", "rt-threaded"] }
|
tokio = { version = "0.2", features = ["blocking", "fs", "sync", "rt-threaded"] }
|
||||||
bumpalo = { version = "3.2", features = ["collections"] }
|
bumpalo = { version = "3.2", features = ["collections"] }
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
|
roc_std = { path = "../../roc_std" }
|
||||||
|
|
|
@ -288,7 +288,7 @@ static LLVM_COS_F64: &str = "llvm.cos.f64";
|
||||||
static LLVM_POW_F64: &str = "llvm.pow.f64";
|
static LLVM_POW_F64: &str = "llvm.pow.f64";
|
||||||
static LLVM_CEILING_F64: &str = "llvm.ceil.f64";
|
static LLVM_CEILING_F64: &str = "llvm.ceil.f64";
|
||||||
static LLVM_FLOOR_F64: &str = "llvm.floor.f64";
|
static LLVM_FLOOR_F64: &str = "llvm.floor.f64";
|
||||||
static LLVM_SADD_WITH_OVERFLOW_I64: &str = "llvm.sadd.with.overflow.i64";
|
pub static LLVM_SADD_WITH_OVERFLOW_I64: &str = "llvm.sadd.with.overflow.i64";
|
||||||
|
|
||||||
fn add_intrinsic<'ctx>(
|
fn add_intrinsic<'ctx>(
|
||||||
module: &Module<'ctx>,
|
module: &Module<'ctx>,
|
||||||
|
@ -1210,10 +1210,10 @@ pub fn allocate_with_refcount<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
// the refcount of a new allocation is initially 1
|
// the refcount of a new allocation is initially 1
|
||||||
// we assume that the allocation is indeed used (dead variables are eliminated)
|
// we assume that the allocation is indeed used (dead variables are eliminated)
|
||||||
let ref_count_one = ctx
|
builder.build_store(
|
||||||
.i64_type()
|
refcount_ptr,
|
||||||
.const_int(crate::llvm::refcounting::REFCOUNT_1 as _, false);
|
crate::llvm::refcounting::refcount_1(ctx, env.ptr_bytes),
|
||||||
builder.build_store(refcount_ptr, ref_count_one);
|
);
|
||||||
|
|
||||||
// store the value in the pointer
|
// store the value in the pointer
|
||||||
builder.build_store(list_element_ptr, value);
|
builder.build_store(list_element_ptr, value);
|
||||||
|
@ -2114,7 +2114,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||||
list_join(env, inplace, parent, list, outer_list_layout)
|
list_join(env, inplace, parent, list, outer_list_layout)
|
||||||
}
|
}
|
||||||
NumAbs | NumNeg | NumRound | NumSqrtUnchecked | NumSin | NumCos | NumCeiling | NumFloor
|
NumAbs | NumNeg | NumRound | NumSqrtUnchecked | NumSin | NumCos | NumCeiling | NumFloor
|
||||||
| NumToFloat | NumIsFinite => {
|
| NumToFloat | NumIsFinite | NumAtan => {
|
||||||
debug_assert_eq!(args.len(), 1);
|
debug_assert_eq!(args.len(), 1);
|
||||||
|
|
||||||
let (arg, arg_layout) = load_symbol_and_layout(env, scope, &args[0]);
|
let (arg, arg_layout) = load_symbol_and_layout(env, scope, &args[0]);
|
||||||
|
@ -2396,7 +2396,6 @@ where
|
||||||
Layout::Builtin(Builtin::List(MemoryMode::Refcounted, _)) => {
|
Layout::Builtin(Builtin::List(MemoryMode::Refcounted, _)) => {
|
||||||
// no static guarantees, but all is not lost: we can check the refcount
|
// no static guarantees, but all is not lost: we can check the refcount
|
||||||
// if it is one, we hold the final reference, and can mutate it in-place!
|
// if it is one, we hold the final reference, and can mutate it in-place!
|
||||||
let builder = env.builder;
|
|
||||||
let ctx = env.context;
|
let ctx = env.context;
|
||||||
|
|
||||||
let ret_type = basic_type_from_layout(env.arena, ctx, list_layout, env.ptr_bytes);
|
let ret_type = basic_type_from_layout(env.arena, ctx, list_layout, env.ptr_bytes);
|
||||||
|
@ -2408,7 +2407,7 @@ where
|
||||||
.build_load(refcount_ptr, "get_refcount")
|
.build_load(refcount_ptr, "get_refcount")
|
||||||
.into_int_value();
|
.into_int_value();
|
||||||
|
|
||||||
let comparison = refcount_is_one_comparison(builder, env.context, refcount);
|
let comparison = refcount_is_one_comparison(env, refcount);
|
||||||
|
|
||||||
crate::llvm::build_list::build_basic_phi2(
|
crate::llvm::build_list::build_basic_phi2(
|
||||||
env, parent, comparison, in_place, clone, ret_type,
|
env, parent, comparison, in_place, clone, ret_type,
|
||||||
|
@ -2667,6 +2666,7 @@ fn build_float_unary_op<'a, 'ctx, 'env>(
|
||||||
"num_floor",
|
"num_floor",
|
||||||
),
|
),
|
||||||
NumIsFinite => call_bitcode_fn(NumIsFinite, env, &[arg.into()], "is_finite_"),
|
NumIsFinite => call_bitcode_fn(NumIsFinite, env, &[arg.into()], "is_finite_"),
|
||||||
|
NumAtan => call_bitcode_fn(NumAtan, env, &[arg.into()], "atan_"),
|
||||||
_ => {
|
_ => {
|
||||||
unreachable!("Unrecognized int unary operation: {:?}", op);
|
unreachable!("Unrecognized int unary operation: {:?}", op);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1757,8 +1757,7 @@ pub fn allocate_list<'a, 'ctx, 'env>(
|
||||||
InPlace::Clone => {
|
InPlace::Clone => {
|
||||||
// the refcount of a new list is initially 1
|
// the refcount of a new list is initially 1
|
||||||
// we assume that the list is indeed used (dead variables are eliminated)
|
// we assume that the list is indeed used (dead variables are eliminated)
|
||||||
ctx.i64_type()
|
crate::llvm::refcounting::refcount_1(ctx, env.ptr_bytes)
|
||||||
.const_int(crate::llvm::refcounting::REFCOUNT_1 as _, false)
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Binary file not shown.
|
@ -200,7 +200,6 @@ pub fn ptr_int(ctx: &Context, ptr_bytes: u32) -> IntType<'_> {
|
||||||
2 => ctx.i16_type(),
|
2 => ctx.i16_type(),
|
||||||
4 => ctx.i32_type(),
|
4 => ctx.i32_type(),
|
||||||
8 => ctx.i64_type(),
|
8 => ctx.i64_type(),
|
||||||
16 => ctx.i128_type(),
|
|
||||||
_ => panic!(
|
_ => panic!(
|
||||||
"Invalid target: Roc does't support compiling to {}-bit systems.",
|
"Invalid target: Roc does't support compiling to {}-bit systems.",
|
||||||
ptr_bytes * 8
|
ptr_bytes * 8
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
use crate::layout_id::LayoutIds;
|
use crate::layout_id::LayoutIds;
|
||||||
use crate::llvm::build::{
|
use crate::llvm::build::{
|
||||||
cast_basic_basic, cast_struct_struct, create_entry_block_alloca, set_name, Env, Scope,
|
cast_basic_basic, cast_struct_struct, create_entry_block_alloca, set_name, Env, Scope,
|
||||||
FAST_CALL_CONV,
|
FAST_CALL_CONV, LLVM_SADD_WITH_OVERFLOW_I64,
|
||||||
};
|
};
|
||||||
use crate::llvm::build_list::list_len;
|
use crate::llvm::build_list::list_len;
|
||||||
use crate::llvm::convert::{basic_type_from_layout, block_of_memory, ptr_int};
|
use crate::llvm::convert::{basic_type_from_layout, block_of_memory, ptr_int};
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
use inkwell::builder::Builder;
|
use inkwell::basic_block::BasicBlock;
|
||||||
use inkwell::context::Context;
|
use inkwell::context::Context;
|
||||||
use inkwell::module::Linkage;
|
use inkwell::module::Linkage;
|
||||||
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue};
|
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue};
|
||||||
|
@ -14,8 +14,20 @@ use inkwell::{AddressSpace, IntPredicate};
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_mono::layout::{Builtin, Layout, MemoryMode};
|
use roc_mono::layout::{Builtin, Layout, MemoryMode};
|
||||||
|
|
||||||
pub const REFCOUNT_0: usize = std::usize::MAX;
|
pub const REFCOUNT_MAX: usize = 0 as usize;
|
||||||
pub const REFCOUNT_1: usize = REFCOUNT_0 - 1;
|
|
||||||
|
pub fn refcount_1(ctx: &Context, ptr_bytes: u32) -> IntValue<'_> {
|
||||||
|
match ptr_bytes {
|
||||||
|
1 => ctx.i8_type().const_int(i8::MIN as u64, false),
|
||||||
|
2 => ctx.i16_type().const_int(i16::MIN as u64, false),
|
||||||
|
4 => ctx.i32_type().const_int(i32::MIN as u64, false),
|
||||||
|
8 => ctx.i64_type().const_int(i64::MIN as u64, false),
|
||||||
|
_ => panic!(
|
||||||
|
"Invalid target: Roc does't support compiling to {}-bit systems.",
|
||||||
|
ptr_bytes * 8
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn decrement_refcount_layout<'a, 'ctx, 'env>(
|
pub fn decrement_refcount_layout<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
@ -137,10 +149,9 @@ fn decrement_refcount_builtin<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
|
|
||||||
if let MemoryMode::Refcounted = memory_mode {
|
if let MemoryMode::Refcounted = memory_mode {
|
||||||
build_inc_list(env, layout_ids, layout, wrapper_struct);
|
|
||||||
}
|
|
||||||
build_dec_list(env, layout_ids, layout, wrapper_struct);
|
build_dec_list(env, layout_ids, layout, wrapper_struct);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Set(element_layout) => {
|
Set(element_layout) => {
|
||||||
if element_layout.contains_refcounted() {
|
if element_layout.contains_refcounted() {
|
||||||
// TODO decrement all values
|
// TODO decrement all values
|
||||||
|
@ -154,6 +165,10 @@ fn decrement_refcount_builtin<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
todo!();
|
todo!();
|
||||||
}
|
}
|
||||||
|
Str => {
|
||||||
|
let wrapper_struct = value.into_struct_value();
|
||||||
|
build_dec_str(env, layout_ids, layout, wrapper_struct);
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -234,6 +249,10 @@ fn increment_refcount_builtin<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
todo!();
|
todo!();
|
||||||
}
|
}
|
||||||
|
Str => {
|
||||||
|
let wrapper_struct = value.into_struct_value();
|
||||||
|
build_inc_str(env, layout_ids, layout, wrapper_struct);
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -265,7 +284,7 @@ pub fn build_inc_list<'a, 'ctx, 'env>(
|
||||||
env.builder.position_at_end(block);
|
env.builder.position_at_end(block);
|
||||||
let call = env
|
let call = env
|
||||||
.builder
|
.builder
|
||||||
.build_call(function, &[original_wrapper.into()], "decrement_union");
|
.build_call(function, &[original_wrapper.into()], "increment_list");
|
||||||
|
|
||||||
call.set_call_convention(FAST_CALL_CONV);
|
call.set_call_convention(FAST_CALL_CONV);
|
||||||
}
|
}
|
||||||
|
@ -324,21 +343,8 @@ fn build_inc_list_help<'a, 'ctx, 'env>(
|
||||||
builder.position_at_end(increment_block);
|
builder.position_at_end(increment_block);
|
||||||
|
|
||||||
let refcount_ptr = list_get_refcount_ptr(env, layout, original_wrapper);
|
let refcount_ptr = list_get_refcount_ptr(env, layout, original_wrapper);
|
||||||
|
increment_refcount_help(env, refcount_ptr);
|
||||||
|
|
||||||
let refcount = env
|
|
||||||
.builder
|
|
||||||
.build_load(refcount_ptr, "get_refcount")
|
|
||||||
.into_int_value();
|
|
||||||
|
|
||||||
// our refcount 0 is actually usize::MAX, so incrementing the refcount means decrementing this value.
|
|
||||||
let decremented = env.builder.build_int_sub(
|
|
||||||
refcount,
|
|
||||||
ctx.i64_type().const_int(1 as u64, false),
|
|
||||||
"incremented_refcount",
|
|
||||||
);
|
|
||||||
|
|
||||||
// Mutate the new array in-place to change the element.
|
|
||||||
builder.build_store(refcount_ptr, decremented);
|
|
||||||
builder.build_unconditional_branch(cont_block);
|
builder.build_unconditional_branch(cont_block);
|
||||||
|
|
||||||
builder.position_at_end(cont_block);
|
builder.position_at_end(cont_block);
|
||||||
|
@ -374,7 +380,7 @@ pub fn build_dec_list<'a, 'ctx, 'env>(
|
||||||
env.builder.position_at_end(block);
|
env.builder.position_at_end(block);
|
||||||
let call = env
|
let call = env
|
||||||
.builder
|
.builder
|
||||||
.build_call(function, &[original_wrapper.into()], "decrement_union");
|
.build_call(function, &[original_wrapper.into()], "decrement_list");
|
||||||
call.set_call_convention(FAST_CALL_CONV);
|
call.set_call_convention(FAST_CALL_CONV);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -435,81 +441,243 @@ fn build_dec_list_help<'a, 'ctx, 'env>(
|
||||||
builder.build_conditional_branch(is_non_empty, decrement_block, cont_block);
|
builder.build_conditional_branch(is_non_empty, decrement_block, cont_block);
|
||||||
builder.position_at_end(decrement_block);
|
builder.position_at_end(decrement_block);
|
||||||
|
|
||||||
// build blocks
|
|
||||||
let then_block = ctx.append_basic_block(parent, "then");
|
|
||||||
let else_block = ctx.append_basic_block(parent, "else");
|
|
||||||
|
|
||||||
let refcount_ptr = list_get_refcount_ptr(env, layout, original_wrapper);
|
let refcount_ptr = list_get_refcount_ptr(env, layout, original_wrapper);
|
||||||
|
|
||||||
let refcount = env
|
decrement_refcount_help(env, parent, refcount_ptr, cont_block);
|
||||||
|
|
||||||
|
// this function returns void
|
||||||
|
builder.build_return(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_inc_str<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
|
layout: &Layout<'a>,
|
||||||
|
original_wrapper: StructValue<'ctx>,
|
||||||
|
) {
|
||||||
|
let block = env.builder.get_insert_block().expect("to be in a function");
|
||||||
|
|
||||||
|
let symbol = Symbol::INC;
|
||||||
|
let fn_name = layout_ids
|
||||||
|
.get(symbol, &layout)
|
||||||
|
.to_symbol_string(symbol, &env.interns);
|
||||||
|
|
||||||
|
let function = match env.module.get_function(fn_name.as_str()) {
|
||||||
|
Some(function_value) => function_value,
|
||||||
|
None => {
|
||||||
|
let function_value = build_header(env, &layout, fn_name);
|
||||||
|
|
||||||
|
build_inc_str_help(env, layout_ids, layout, function_value);
|
||||||
|
|
||||||
|
function_value
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
env.builder.position_at_end(block);
|
||||||
|
let call = env
|
||||||
.builder
|
.builder
|
||||||
.build_load(refcount_ptr, "get_refcount")
|
.build_call(function, &[original_wrapper.into()], "increment_str");
|
||||||
.into_int_value();
|
call.set_call_convention(FAST_CALL_CONV);
|
||||||
|
}
|
||||||
|
|
||||||
let comparison = refcount_is_one_comparison(builder, env.context, refcount);
|
fn build_inc_str_help<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
_layout_ids: &mut LayoutIds<'a>,
|
||||||
|
layout: &Layout<'a>,
|
||||||
|
fn_val: FunctionValue<'ctx>,
|
||||||
|
) {
|
||||||
|
let builder = env.builder;
|
||||||
|
let ctx = env.context;
|
||||||
|
|
||||||
// TODO what would be most optimial for the branch predictor
|
// Add a basic block for the entry point
|
||||||
//
|
let entry = ctx.append_basic_block(fn_val, "entry");
|
||||||
// are most refcounts 1 most of the time? or not?
|
|
||||||
builder.build_conditional_branch(comparison, then_block, else_block);
|
|
||||||
|
|
||||||
// build then block
|
builder.position_at_end(entry);
|
||||||
{
|
|
||||||
builder.position_at_end(then_block);
|
|
||||||
if !env.leak {
|
|
||||||
builder.build_free(refcount_ptr);
|
|
||||||
}
|
|
||||||
builder.build_unconditional_branch(cont_block);
|
|
||||||
}
|
|
||||||
|
|
||||||
// build else block
|
let mut scope = Scope::default();
|
||||||
{
|
|
||||||
builder.position_at_end(else_block);
|
// Add args to scope
|
||||||
// our refcount 0 is actually usize::MAX, so decrementing the refcount means incrementing this value.
|
let arg_symbol = Symbol::ARG_1;
|
||||||
let decremented = env.builder.build_int_add(
|
let arg_val = fn_val.get_param_iter().next().unwrap();
|
||||||
ctx.i64_type().const_int(1 as u64, false),
|
|
||||||
refcount,
|
set_name(arg_val, arg_symbol.ident_string(&env.interns));
|
||||||
"decremented_refcount",
|
|
||||||
|
let alloca = create_entry_block_alloca(
|
||||||
|
env,
|
||||||
|
fn_val,
|
||||||
|
arg_val.get_type(),
|
||||||
|
arg_symbol.ident_string(&env.interns),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Mutate the new array in-place to change the element.
|
builder.build_store(alloca, arg_val);
|
||||||
builder.build_store(refcount_ptr, decremented);
|
|
||||||
|
|
||||||
|
scope.insert(arg_symbol, (layout.clone(), alloca));
|
||||||
|
|
||||||
|
let parent = fn_val;
|
||||||
|
|
||||||
|
let str_wrapper = arg_val.into_struct_value();
|
||||||
|
let len = builder
|
||||||
|
.build_extract_value(str_wrapper, Builtin::WRAPPER_LEN, "read_str_ptr")
|
||||||
|
.unwrap()
|
||||||
|
.into_int_value();
|
||||||
|
|
||||||
|
// Small strings have 1 as the first bit of length, making them negative.
|
||||||
|
// Thus, to check for big and non empty, just needs a signed len > 0.
|
||||||
|
let is_big_and_non_empty = builder.build_int_compare(
|
||||||
|
IntPredicate::SGT,
|
||||||
|
len,
|
||||||
|
ptr_int(ctx, env.ptr_bytes).const_zero(),
|
||||||
|
"len > 0",
|
||||||
|
);
|
||||||
|
|
||||||
|
// the block we'll always jump to when we're done
|
||||||
|
let cont_block = ctx.append_basic_block(parent, "after_increment_block");
|
||||||
|
let decrement_block = ctx.append_basic_block(parent, "increment_block");
|
||||||
|
|
||||||
|
builder.build_conditional_branch(is_big_and_non_empty, decrement_block, cont_block);
|
||||||
|
builder.position_at_end(decrement_block);
|
||||||
|
|
||||||
|
let refcount_ptr = list_get_refcount_ptr(env, layout, str_wrapper);
|
||||||
|
increment_refcount_help(env, refcount_ptr);
|
||||||
builder.build_unconditional_branch(cont_block);
|
builder.build_unconditional_branch(cont_block);
|
||||||
}
|
|
||||||
|
|
||||||
// emit merge block
|
|
||||||
builder.position_at_end(cont_block);
|
builder.position_at_end(cont_block);
|
||||||
|
|
||||||
// this function returns void
|
// this function returns void
|
||||||
builder.build_return(None);
|
builder.build_return(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn build_dec_str<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
|
layout: &Layout<'a>,
|
||||||
|
original_wrapper: StructValue<'ctx>,
|
||||||
|
) {
|
||||||
|
let block = env.builder.get_insert_block().expect("to be in a function");
|
||||||
|
|
||||||
|
let symbol = Symbol::DEC;
|
||||||
|
let fn_name = layout_ids
|
||||||
|
.get(symbol, &layout)
|
||||||
|
.to_symbol_string(symbol, &env.interns);
|
||||||
|
|
||||||
|
let function = match env.module.get_function(fn_name.as_str()) {
|
||||||
|
Some(function_value) => function_value,
|
||||||
|
None => {
|
||||||
|
let function_value = build_header(env, &layout, fn_name);
|
||||||
|
|
||||||
|
build_dec_str_help(env, layout_ids, layout, function_value);
|
||||||
|
|
||||||
|
function_value
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
env.builder.position_at_end(block);
|
||||||
|
let call = env
|
||||||
|
.builder
|
||||||
|
.build_call(function, &[original_wrapper.into()], "decrement_str");
|
||||||
|
call.set_call_convention(FAST_CALL_CONV);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_dec_str_help<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
_layout_ids: &mut LayoutIds<'a>,
|
||||||
|
layout: &Layout<'a>,
|
||||||
|
fn_val: FunctionValue<'ctx>,
|
||||||
|
) {
|
||||||
|
let builder = env.builder;
|
||||||
|
let ctx = env.context;
|
||||||
|
|
||||||
|
// Add a basic block for the entry point
|
||||||
|
let entry = ctx.append_basic_block(fn_val, "entry");
|
||||||
|
|
||||||
|
builder.position_at_end(entry);
|
||||||
|
|
||||||
|
let mut scope = Scope::default();
|
||||||
|
|
||||||
|
// Add args to scope
|
||||||
|
let arg_symbol = Symbol::ARG_1;
|
||||||
|
let arg_val = fn_val.get_param_iter().next().unwrap();
|
||||||
|
|
||||||
|
set_name(arg_val, arg_symbol.ident_string(&env.interns));
|
||||||
|
|
||||||
|
let alloca = create_entry_block_alloca(
|
||||||
|
env,
|
||||||
|
fn_val,
|
||||||
|
arg_val.get_type(),
|
||||||
|
arg_symbol.ident_string(&env.interns),
|
||||||
|
);
|
||||||
|
|
||||||
|
builder.build_store(alloca, arg_val);
|
||||||
|
|
||||||
|
scope.insert(arg_symbol, (layout.clone(), alloca));
|
||||||
|
|
||||||
|
let parent = fn_val;
|
||||||
|
|
||||||
|
let str_wrapper = arg_val.into_struct_value();
|
||||||
|
let len = builder
|
||||||
|
.build_extract_value(str_wrapper, Builtin::WRAPPER_LEN, "read_str_ptr")
|
||||||
|
.unwrap()
|
||||||
|
.into_int_value();
|
||||||
|
|
||||||
|
// Small strings have 1 as the first bit of length, making them negative.
|
||||||
|
// Thus, to check for big and non empty, just needs a signed len > 0.
|
||||||
|
let is_big_and_non_empty = builder.build_int_compare(
|
||||||
|
IntPredicate::SGT,
|
||||||
|
len,
|
||||||
|
ptr_int(ctx, env.ptr_bytes).const_zero(),
|
||||||
|
"len > 0",
|
||||||
|
);
|
||||||
|
|
||||||
|
// the block we'll always jump to when we're done
|
||||||
|
let cont_block = ctx.append_basic_block(parent, "after_decrement_block");
|
||||||
|
let decrement_block = ctx.append_basic_block(parent, "decrement_block");
|
||||||
|
|
||||||
|
builder.build_conditional_branch(is_big_and_non_empty, decrement_block, cont_block);
|
||||||
|
builder.position_at_end(decrement_block);
|
||||||
|
|
||||||
|
let refcount_ptr = list_get_refcount_ptr(env, layout, str_wrapper);
|
||||||
|
decrement_refcount_help(env, parent, refcount_ptr, cont_block);
|
||||||
|
|
||||||
|
// this function returns void
|
||||||
|
builder.build_return(None);
|
||||||
|
}
|
||||||
|
|
||||||
fn increment_refcount_ptr<'a, 'ctx, 'env>(
|
fn increment_refcount_ptr<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
_parent: FunctionValue<'ctx>,
|
|
||||||
layout: &Layout<'a>,
|
layout: &Layout<'a>,
|
||||||
field_ptr: PointerValue<'ctx>,
|
field_ptr: PointerValue<'ctx>,
|
||||||
|
) {
|
||||||
|
let refcount_ptr = get_refcount_ptr(env, layout, field_ptr);
|
||||||
|
increment_refcount_help(env, refcount_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn increment_refcount_help<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
refcount_ptr: PointerValue<'ctx>,
|
||||||
) {
|
) {
|
||||||
let builder = env.builder;
|
let builder = env.builder;
|
||||||
let ctx = env.context;
|
let ctx = env.context;
|
||||||
|
let refcount_type = ptr_int(ctx, env.ptr_bytes);
|
||||||
let refcount_ptr = get_refcount_ptr(env, layout, field_ptr);
|
|
||||||
|
|
||||||
let refcount = env
|
let refcount = env
|
||||||
.builder
|
.builder
|
||||||
.build_load(refcount_ptr, "get_refcount")
|
.build_load(refcount_ptr, "get_refcount")
|
||||||
.into_int_value();
|
.into_int_value();
|
||||||
|
|
||||||
// our refcount 0 is actually usize::MAX, so incrementing the refcount means decrementing this value.
|
let max = builder.build_int_compare(
|
||||||
let incremented = env.builder.build_int_sub(
|
IntPredicate::EQ,
|
||||||
refcount,
|
refcount,
|
||||||
ctx.i64_type().const_int(1 as u64, false),
|
refcount_type.const_int(REFCOUNT_MAX as u64, false),
|
||||||
|
"refcount_max_check",
|
||||||
|
);
|
||||||
|
let incremented = builder.build_int_add(
|
||||||
|
refcount,
|
||||||
|
refcount_type.const_int(1 as u64, false),
|
||||||
"increment_refcount",
|
"increment_refcount",
|
||||||
);
|
);
|
||||||
|
let selected = builder.build_select(max, refcount, incremented, "select_refcount");
|
||||||
|
|
||||||
// Mutate the new array in-place to change the element.
|
builder.build_store(refcount_ptr, selected);
|
||||||
builder.build_store(refcount_ptr, incremented);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decrement_refcount_ptr<'a, 'ctx, 'env>(
|
fn decrement_refcount_ptr<'a, 'ctx, 'env>(
|
||||||
|
@ -518,21 +686,49 @@ fn decrement_refcount_ptr<'a, 'ctx, 'env>(
|
||||||
layout: &Layout<'a>,
|
layout: &Layout<'a>,
|
||||||
field_ptr: PointerValue<'ctx>,
|
field_ptr: PointerValue<'ctx>,
|
||||||
) {
|
) {
|
||||||
let builder = env.builder;
|
|
||||||
let ctx = env.context;
|
let ctx = env.context;
|
||||||
|
|
||||||
// the block we'll always jump to when we're done
|
// the block we'll always jump to when we're done
|
||||||
let cont_block = ctx.append_basic_block(parent, "after_decrement_block");
|
let cont_block = ctx.append_basic_block(parent, "after_decrement_block");
|
||||||
|
|
||||||
let refcount_ptr = get_refcount_ptr(env, layout, field_ptr);
|
let refcount_ptr = get_refcount_ptr(env, layout, field_ptr);
|
||||||
|
decrement_refcount_help(env, parent, refcount_ptr, cont_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decrement_refcount_help<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
parent: FunctionValue<'ctx>,
|
||||||
|
refcount_ptr: PointerValue<'ctx>,
|
||||||
|
cont_block: BasicBlock,
|
||||||
|
) {
|
||||||
|
let builder = env.builder;
|
||||||
|
let ctx = env.context;
|
||||||
|
let refcount_type = ptr_int(ctx, env.ptr_bytes);
|
||||||
|
|
||||||
let refcount = env
|
let refcount = env
|
||||||
.builder
|
.builder
|
||||||
.build_load(refcount_ptr, "get_refcount")
|
.build_load(refcount_ptr, "get_refcount")
|
||||||
.into_int_value();
|
.into_int_value();
|
||||||
|
|
||||||
let comparison = refcount_is_one_comparison(builder, env.context, refcount);
|
let add_with_overflow = env
|
||||||
|
.call_intrinsic(
|
||||||
|
LLVM_SADD_WITH_OVERFLOW_I64,
|
||||||
|
&[
|
||||||
|
refcount.into(),
|
||||||
|
refcount_type.const_int((-1 as i64) as u64, true).into(),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.into_struct_value();
|
||||||
|
let has_overflowed = builder
|
||||||
|
.build_extract_value(add_with_overflow, 1, "has_overflowed")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let has_overflowed_comparison = builder.build_int_compare(
|
||||||
|
IntPredicate::EQ,
|
||||||
|
has_overflowed.into_int_value(),
|
||||||
|
ctx.bool_type().const_int(1 as u64, false),
|
||||||
|
"has_overflowed",
|
||||||
|
);
|
||||||
// build blocks
|
// build blocks
|
||||||
let then_block = ctx.append_basic_block(parent, "then");
|
let then_block = ctx.append_basic_block(parent, "then");
|
||||||
let else_block = ctx.append_basic_block(parent, "else");
|
let else_block = ctx.append_basic_block(parent, "else");
|
||||||
|
@ -540,7 +736,7 @@ fn decrement_refcount_ptr<'a, 'ctx, 'env>(
|
||||||
// TODO what would be most optimial for the branch predictor
|
// TODO what would be most optimial for the branch predictor
|
||||||
//
|
//
|
||||||
// are most refcounts 1 most of the time? or not?
|
// are most refcounts 1 most of the time? or not?
|
||||||
builder.build_conditional_branch(comparison, then_block, else_block);
|
builder.build_conditional_branch(has_overflowed_comparison, then_block, else_block);
|
||||||
|
|
||||||
// build then block
|
// build then block
|
||||||
{
|
{
|
||||||
|
@ -554,15 +750,18 @@ fn decrement_refcount_ptr<'a, 'ctx, 'env>(
|
||||||
// build else block
|
// build else block
|
||||||
{
|
{
|
||||||
builder.position_at_end(else_block);
|
builder.position_at_end(else_block);
|
||||||
// our refcount 0 is actually usize::MAX, so decrementing the refcount means incrementing this value.
|
let max = builder.build_int_compare(
|
||||||
let decremented = env.builder.build_int_add(
|
IntPredicate::EQ,
|
||||||
ctx.i64_type().const_int(1 as u64, false),
|
|
||||||
refcount,
|
refcount,
|
||||||
"decremented_refcount",
|
refcount_type.const_int(REFCOUNT_MAX as u64, false),
|
||||||
|
"refcount_max_check",
|
||||||
);
|
);
|
||||||
|
let decremented = builder
|
||||||
// Mutate the new array in-place to change the element.
|
.build_extract_value(add_with_overflow, 0, "decrement_refcount")
|
||||||
builder.build_store(refcount_ptr, decremented);
|
.unwrap()
|
||||||
|
.into_int_value();
|
||||||
|
let selected = builder.build_select(max, refcount, decremented, "select_refcount");
|
||||||
|
builder.build_store(refcount_ptr, selected);
|
||||||
|
|
||||||
builder.build_unconditional_branch(cont_block);
|
builder.build_unconditional_branch(cont_block);
|
||||||
}
|
}
|
||||||
|
@ -924,7 +1123,7 @@ pub fn build_inc_union_help<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
// TODO do this increment before the recursive call?
|
// TODO do this increment before the recursive call?
|
||||||
// Then the recursive call is potentially TCE'd
|
// Then the recursive call is potentially TCE'd
|
||||||
increment_refcount_ptr(env, parent, &layout, field_ptr.into_pointer_value());
|
increment_refcount_ptr(env, &layout, field_ptr.into_pointer_value());
|
||||||
} else if field_layout.contains_refcounted() {
|
} else if field_layout.contains_refcounted() {
|
||||||
let field_ptr = env
|
let field_ptr = env
|
||||||
.builder
|
.builder
|
||||||
|
@ -953,16 +1152,14 @@ pub fn build_inc_union_help<'a, 'ctx, 'env>(
|
||||||
builder.build_return(None);
|
builder.build_return(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn refcount_is_one_comparison<'ctx>(
|
pub fn refcount_is_one_comparison<'a, 'ctx, 'env>(
|
||||||
builder: &Builder<'ctx>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
context: &'ctx Context,
|
|
||||||
refcount: IntValue<'ctx>,
|
refcount: IntValue<'ctx>,
|
||||||
) -> IntValue<'ctx> {
|
) -> IntValue<'ctx> {
|
||||||
let refcount_one: IntValue<'ctx> = context.i64_type().const_int(REFCOUNT_1 as _, false);
|
env.builder.build_int_compare(
|
||||||
builder.build_int_compare(
|
|
||||||
IntPredicate::EQ,
|
IntPredicate::EQ,
|
||||||
refcount,
|
refcount,
|
||||||
refcount_one,
|
refcount_1(env.context, env.ptr_bytes),
|
||||||
"refcount_one_check",
|
"refcount_one_check",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -987,8 +1184,9 @@ fn get_refcount_ptr<'a, 'ctx, 'env>(
|
||||||
layout: &Layout<'a>,
|
layout: &Layout<'a>,
|
||||||
ptr: PointerValue<'ctx>,
|
ptr: PointerValue<'ctx>,
|
||||||
) -> PointerValue<'ctx> {
|
) -> PointerValue<'ctx> {
|
||||||
|
let refcount_type = ptr_int(env.context, env.ptr_bytes);
|
||||||
let ptr_as_int =
|
let ptr_as_int =
|
||||||
cast_basic_basic(env.builder, ptr.into(), env.context.i64_type().into()).into_int_value();
|
cast_basic_basic(env.builder, ptr.into(), refcount_type.into()).into_int_value();
|
||||||
|
|
||||||
get_refcount_ptr_help(env, layout, ptr_as_int)
|
get_refcount_ptr_help(env, layout, ptr_as_int)
|
||||||
}
|
}
|
||||||
|
@ -1004,23 +1202,23 @@ fn get_refcount_ptr_help<'a, 'ctx, 'env>(
|
||||||
let value_bytes = layout.stack_size(env.ptr_bytes) as u64;
|
let value_bytes = layout.stack_size(env.ptr_bytes) as u64;
|
||||||
let offset = match layout {
|
let offset = match layout {
|
||||||
Layout::Builtin(Builtin::List(_, _)) => env.ptr_bytes as u64,
|
Layout::Builtin(Builtin::List(_, _)) => env.ptr_bytes as u64,
|
||||||
|
Layout::Builtin(Builtin::Str) => env.ptr_bytes as u64,
|
||||||
_ => (env.ptr_bytes as u64).max(value_bytes),
|
_ => (env.ptr_bytes as u64).max(value_bytes),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// pointer to usize
|
||||||
|
let refcount_type = ptr_int(ctx, env.ptr_bytes);
|
||||||
|
|
||||||
// subtract offset, to access the refcount
|
// subtract offset, to access the refcount
|
||||||
let refcount_ptr = builder.build_int_sub(
|
let refcount_ptr = builder.build_int_sub(
|
||||||
ptr_as_int,
|
ptr_as_int,
|
||||||
ctx.i64_type().const_int(offset, false),
|
refcount_type.const_int(offset, false),
|
||||||
"make_refcount_ptr",
|
"make_refcount_ptr",
|
||||||
);
|
);
|
||||||
|
|
||||||
// pointer to usize
|
|
||||||
let ptr_bytes = env.ptr_bytes;
|
|
||||||
let int_type = ptr_int(ctx, ptr_bytes);
|
|
||||||
|
|
||||||
builder.build_int_to_ptr(
|
builder.build_int_to_ptr(
|
||||||
refcount_ptr,
|
refcount_ptr,
|
||||||
int_type.ptr_type(AddressSpace::Generic),
|
refcount_type.ptr_type(AddressSpace::Generic),
|
||||||
"get_refcount_ptr",
|
"get_refcount_ptr",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,19 @@
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::os::raw::c_char;
|
use std::os::raw::c_char;
|
||||||
|
use RocCallResult::*;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(u64)]
|
||||||
union Payload<T: Copy> {
|
pub enum RocCallResult<T> {
|
||||||
success: T,
|
Success(T),
|
||||||
failure: *mut c_char,
|
Failure(*mut c_char),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
impl<T: Sized> Into<Result<T, String>> for RocCallResult<T> {
|
||||||
pub struct RocCallResult<T: Copy> {
|
|
||||||
pub flag: u64,
|
|
||||||
payload: Payload<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Copy> Into<Result<T, String>> for RocCallResult<T> {
|
|
||||||
fn into(self) -> Result<T, String> {
|
fn into(self) -> Result<T, String> {
|
||||||
if self.flag == 0 {
|
match self {
|
||||||
Ok(unsafe { self.payload.success })
|
Success(value) => Ok(value),
|
||||||
} else {
|
Failure(failure) => Err({
|
||||||
Err(unsafe {
|
let raw = unsafe { CString::from_raw(failure) };
|
||||||
let raw = CString::from_raw(self.payload.failure);
|
|
||||||
|
|
||||||
let result = format!("{:?}", raw);
|
let result = format!("{:?}", raw);
|
||||||
|
|
||||||
|
@ -27,7 +21,7 @@ impl<T: Copy> Into<Result<T, String>> for RocCallResult<T> {
|
||||||
std::mem::forget(raw);
|
std::mem::forget(raw);
|
||||||
|
|
||||||
result
|
result
|
||||||
})
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,31 +14,148 @@ 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;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn roc_list_construction() {
|
||||||
|
let list = RocList::from_slice(&vec![1i64; 23]);
|
||||||
|
assert_eq!(&list, &list);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty_list_literal() {
|
fn empty_list_literal() {
|
||||||
assert_evals_to!("[]", &[], &'static [i64]);
|
assert_evals_to!("[]", RocList::from_slice(&[]), RocList<i64>);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn int_singleton_list_literal() {
|
fn int_singleton_list_literal() {
|
||||||
assert_evals_to!("[1]", &[1], &'static [i64]);
|
assert_evals_to!("[1, 2]", RocList::from_slice(&[1, 2]), RocList<i64>);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn int_list_literal() {
|
fn int_list_literal() {
|
||||||
assert_evals_to!("[ 12, 9, 6, 3 ]", &[12, 9, 6, 3], &'static [i64]);
|
assert_evals_to!("[ 12, 9 ]", RocList::from_slice(&[12, 9]), RocList<i64>);
|
||||||
|
assert_evals_to!(
|
||||||
|
"[ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 ]",
|
||||||
|
RocList::from_slice(&(vec![1i64; 23])),
|
||||||
|
RocList<i64>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bool_list_literal() {
|
||||||
|
// NOTE: make sure to explicitly declare the elements to be of type bool, or
|
||||||
|
// use both True and False; only using one of them causes the list to in practice be
|
||||||
|
// of type `List [ True ]` or `List [ False ]`, those are tag unions with one constructor
|
||||||
|
// and not fields, and don't have a runtime representation.
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
false : Bool
|
||||||
|
false = False
|
||||||
|
|
||||||
|
[ false ]
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
RocList::from_slice(&(vec![false; 1])),
|
||||||
|
RocList<bool>
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_evals_to!(
|
||||||
|
"[ True, False, True ]",
|
||||||
|
RocList::from_slice(&[true, false, true]),
|
||||||
|
RocList<bool>
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
false : Bool
|
||||||
|
false = False
|
||||||
|
|
||||||
|
[false ]
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
RocList::from_slice(&(vec![false; 1])),
|
||||||
|
RocList<bool>
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
true : Bool
|
||||||
|
true = True
|
||||||
|
|
||||||
|
List.repeat 23 true
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
RocList::from_slice(&(vec![true; 23])),
|
||||||
|
RocList<bool>
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
true : Bool
|
||||||
|
true = True
|
||||||
|
|
||||||
|
List.repeat 23 { x: true, y: true }
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
RocList::from_slice(&(vec![[true, true]; 23])),
|
||||||
|
RocList<[bool; 2]>
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
true : Bool
|
||||||
|
true = True
|
||||||
|
|
||||||
|
List.repeat 23 { x: true, y: true, a: true, b: true, c: true, d : true, e: true, f: true }
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
RocList::from_slice(&(vec![[true, true, true, true, true, true, true, true]; 23])),
|
||||||
|
RocList<[bool; 8]>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn variously_sized_list_literals() {
|
||||||
|
assert_evals_to!("[]", RocList::from_slice(&[]), RocList<i64>);
|
||||||
|
assert_evals_to!("[1]", RocList::from_slice(&[1]), RocList<i64>);
|
||||||
|
assert_evals_to!("[1, 2]", RocList::from_slice(&[1, 2]), RocList<i64>);
|
||||||
|
assert_evals_to!("[1, 2, 3]", RocList::from_slice(&[1, 2, 3]), RocList<i64>);
|
||||||
|
assert_evals_to!(
|
||||||
|
"[1, 2, 3, 4]",
|
||||||
|
RocList::from_slice(&[1, 2, 3, 4]),
|
||||||
|
RocList<i64>
|
||||||
|
);
|
||||||
|
assert_evals_to!(
|
||||||
|
"[1, 2, 3, 4, 5]",
|
||||||
|
RocList::from_slice(&[1, 2, 3, 4, 5]),
|
||||||
|
RocList<i64>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn list_append() {
|
fn list_append() {
|
||||||
assert_evals_to!("List.append [1] 2", &[1, 2], &'static [i64]);
|
assert_evals_to!(
|
||||||
assert_evals_to!("List.append [1, 1] 2", &[1, 1, 2], &'static [i64]);
|
"List.append [1] 2",
|
||||||
|
RocList::from_slice(&[1, 2]),
|
||||||
|
RocList<i64>
|
||||||
|
);
|
||||||
|
assert_evals_to!(
|
||||||
|
"List.append [1, 1] 2",
|
||||||
|
RocList::from_slice(&[1, 1, 2]),
|
||||||
|
RocList<i64>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn list_append_to_empty_list() {
|
fn list_append_to_empty_list() {
|
||||||
assert_evals_to!("List.append [] 3", &[3], &'static [i64]);
|
assert_evals_to!("List.append [] 3", RocList::from_slice(&[3]), RocList<i64>);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -53,8 +170,8 @@ mod gen_list {
|
||||||
List.append (List.append initThrees 3) 3
|
List.append (List.append initThrees 3) 3
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
&[3, 3],
|
RocList::from_slice(&[3, 3]),
|
||||||
&'static [i64]
|
RocList<i64>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,8 +179,8 @@ mod gen_list {
|
||||||
fn list_append_bools() {
|
fn list_append_bools() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
"List.append [ True, False ] True",
|
"List.append [ True, False ] True",
|
||||||
&[true, false, true],
|
RocList::from_slice(&[true, false, true]),
|
||||||
&'static [bool]
|
RocList<bool>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,15 +188,19 @@ mod gen_list {
|
||||||
fn list_append_longer_list() {
|
fn list_append_longer_list() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
"List.append [ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 ] 23",
|
"List.append [ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 ] 23",
|
||||||
&[11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23],
|
RocList::from_slice(&[11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]),
|
||||||
&'static [i64]
|
RocList<i64>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn list_prepend() {
|
fn list_prepend() {
|
||||||
assert_evals_to!("List.prepend [] 1", &[1], &'static [i64]);
|
assert_evals_to!("List.prepend [] 1", RocList::from_slice(&[1]), RocList<i64>);
|
||||||
assert_evals_to!("List.prepend [2] 1", &[1, 2], &'static [i64]);
|
assert_evals_to!(
|
||||||
|
"List.prepend [2] 1",
|
||||||
|
RocList::from_slice(&[1, 2]),
|
||||||
|
RocList<i64>
|
||||||
|
);
|
||||||
|
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -91,8 +212,8 @@ mod gen_list {
|
||||||
List.prepend (List.prepend init 4) 6
|
List.prepend (List.prepend init 4) 6
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
&[6, 4],
|
RocList::from_slice(&[6, 4]),
|
||||||
&'static [i64]
|
RocList<i64>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,8 +221,8 @@ mod gen_list {
|
||||||
fn list_prepend_bools() {
|
fn list_prepend_bools() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
"List.prepend [ True, False ] True",
|
"List.prepend [ True, False ] True",
|
||||||
&[true, true, false],
|
RocList::from_slice(&[true, true, false]),
|
||||||
&'static [bool]
|
RocList<bool>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,8 +230,10 @@ mod gen_list {
|
||||||
fn list_prepend_big_list() {
|
fn list_prepend_big_list() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
"List.prepend [ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 100, 100, 100, 100 ] 9",
|
"List.prepend [ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 100, 100, 100, 100 ] 9",
|
||||||
&[9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 100, 100, 100, 100],
|
RocList::from_slice(&[
|
||||||
&'static [i64]
|
9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 100, 100, 100, 100
|
||||||
|
]),
|
||||||
|
RocList<i64>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,8 +276,8 @@ mod gen_list {
|
||||||
List.keepIf empty (\x -> True)
|
List.keepIf empty (\x -> True)
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
&[],
|
RocList::from_slice(&[]),
|
||||||
&'static [i64]
|
RocList<i64>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,8 +294,8 @@ mod gen_list {
|
||||||
List.keepIf [] alwaysTrue
|
List.keepIf [] alwaysTrue
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
&[],
|
RocList::from_slice(&[]),
|
||||||
&'static [i64]
|
RocList<i64>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,8 +315,8 @@ mod gen_list {
|
||||||
List.keepIf oneThroughEight alwaysTrue
|
List.keepIf oneThroughEight alwaysTrue
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
&[1, 2, 3, 4, 5, 6, 7, 8],
|
RocList::from_slice(&[1, 2, 3, 4, 5, 6, 7, 8]),
|
||||||
&'static [i64]
|
RocList<i64>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,8 +332,8 @@ mod gen_list {
|
||||||
List.keepIf [1,2,3,4,5,6,7,8] alwaysFalse
|
List.keepIf [1,2,3,4,5,6,7,8] alwaysFalse
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
&[],
|
RocList::from_slice(&[]),
|
||||||
&'static [i64]
|
RocList<i64>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,8 +349,8 @@ mod gen_list {
|
||||||
List.keepIf [1,2,3,4,5,6,7,8] intIsLessThanThree
|
List.keepIf [1,2,3,4,5,6,7,8] intIsLessThanThree
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
&[1, 2],
|
RocList::from_slice(&[1, 2]),
|
||||||
&'static [i64]
|
RocList<i64>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,8 +369,8 @@ mod gen_list {
|
||||||
// List.keepIf ["Hello", "Hello", "Goodbye"] strIsHello
|
// List.keepIf ["Hello", "Hello", "Goodbye"] strIsHello
|
||||||
// "#
|
// "#
|
||||||
// ),
|
// ),
|
||||||
// &["Hello", "Hello"],
|
// RocList::from_slice(&["Hello", "Hello"]),
|
||||||
// &'static [&'static str]
|
// RocList<&'static str>
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
@ -263,8 +386,8 @@ mod gen_list {
|
||||||
List.map empty (\x -> x)
|
List.map empty (\x -> x)
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
&[],
|
RocList::from_slice(&[]),
|
||||||
&'static [i64]
|
RocList<i64>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,8 +403,8 @@ mod gen_list {
|
||||||
List.map nonEmpty (\x -> x)
|
List.map nonEmpty (\x -> x)
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
&[1],
|
RocList::from_slice(&[1]),
|
||||||
&'static [i64]
|
RocList<i64>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -297,8 +420,8 @@ mod gen_list {
|
||||||
List.map nonEmpty (\x -> x + 1)
|
List.map nonEmpty (\x -> x + 1)
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
&[2],
|
RocList::from_slice(&[2]),
|
||||||
&'static [i64]
|
RocList<i64>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -314,8 +437,10 @@ mod gen_list {
|
||||||
List.map nonEmpty (\x -> x * 2)
|
List.map nonEmpty (\x -> x * 2)
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
&[2, 4, 6, 8, 10, 2, 4, 6, 8, 10, 2, 4, 6, 8, 10, 2, 4, 6, 8, 10, 2, 4, 6, 8, 10],
|
RocList::from_slice(&[
|
||||||
&'static [i64]
|
2, 4, 6, 8, 10, 2, 4, 6, 8, 10, 2, 4, 6, 8, 10, 2, 4, 6, 8, 10, 2, 4, 6, 8, 10
|
||||||
|
]),
|
||||||
|
RocList<i64>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,8 +457,8 @@ mod gen_list {
|
||||||
List.map nonEmpty (\x -> x > 0)
|
List.map nonEmpty (\x -> x > 0)
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
&[true, true, false, true, true],
|
RocList::from_slice(&[true, true, false, true, true]),
|
||||||
&'static [bool]
|
RocList<bool>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -353,8 +478,8 @@ mod gen_list {
|
||||||
List.map nonEmpty greaterThanOne
|
List.map nonEmpty greaterThanOne
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
&[true, true, false, true, true],
|
RocList::from_slice(&[true, true, false, true, true]),
|
||||||
&'static [bool]
|
RocList<bool>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -366,27 +491,31 @@ mod gen_list {
|
||||||
List.map [] (\x -> x > 0)
|
List.map [] (\x -> x > 0)
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
&[],
|
RocList::from_slice(&[]),
|
||||||
&'static [bool]
|
RocList<bool>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn list_join_empty_list() {
|
fn list_join_empty_list() {
|
||||||
assert_evals_to!("List.join []", &[], &'static [i64]);
|
assert_evals_to!("List.join []", RocList::from_slice(&[]), RocList<i64>);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn list_join_one_list() {
|
fn list_join_one_list() {
|
||||||
assert_evals_to!("List.join [ [1, 2, 3 ] ]", &[1, 2, 3], &'static [i64]);
|
assert_evals_to!(
|
||||||
|
"List.join [ [1, 2, 3 ] ]",
|
||||||
|
RocList::from_slice(&[1, 2, 3]),
|
||||||
|
RocList<i64>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn list_join_two_non_empty_lists() {
|
fn list_join_two_non_empty_lists() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
"List.join [ [1, 2, 3 ] , [4 ,5, 6] ]",
|
"List.join [ [1, 2, 3 ] , [4 ,5, 6] ]",
|
||||||
&[1, 2, 3, 4, 5, 6],
|
RocList::from_slice(&[1, 2, 3, 4, 5, 6]),
|
||||||
&'static [i64]
|
RocList<i64>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -394,8 +523,8 @@ mod gen_list {
|
||||||
fn list_join_two_non_empty_lists_of_float() {
|
fn list_join_two_non_empty_lists_of_float() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
"List.join [ [ 1.2, 1.1 ], [ 2.1, 2.2 ] ]",
|
"List.join [ [ 1.2, 1.1 ], [ 2.1, 2.2 ] ]",
|
||||||
&[1.2, 1.1, 2.1, 2.2],
|
RocList::from_slice(&[1.2, 1.1, 2.1, 2.2]),
|
||||||
&'static [f64]
|
RocList<f64>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -416,11 +545,11 @@ mod gen_list {
|
||||||
]
|
]
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
&[
|
RocList::from_slice(&[
|
||||||
1.2, 1.1, 2.1, 2.2, 3.0, 4.0, 5.0, 6.1, 9.0, 3.0, 4.0, 5.0, 6.1, 9.0, 3.0, 4.0,
|
1.2, 1.1, 2.1, 2.2, 3.0, 4.0, 5.0, 6.1, 9.0, 3.0, 4.0, 5.0, 6.1, 9.0, 3.0, 4.0,
|
||||||
5.0, 6.1, 9.0, 3.0, 4.0, 5.0, 6.1, 9.0, 3.0, 4.0, 5.0, 6.1, 9.0
|
5.0, 6.1, 9.0, 3.0, 4.0, 5.0, 6.1, 9.0, 3.0, 4.0, 5.0, 6.1, 9.0
|
||||||
],
|
]),
|
||||||
&'static [f64]
|
RocList<f64>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -436,35 +565,47 @@ mod gen_list {
|
||||||
List.join [ [ 0.2, 11.11 ], empty ]
|
List.join [ [ 0.2, 11.11 ], empty ]
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
&[0.2, 11.11],
|
RocList::from_slice(&[0.2, 11.11]),
|
||||||
&'static [f64]
|
RocList<f64>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn list_join_all_empty_lists() {
|
fn list_join_all_empty_lists() {
|
||||||
assert_evals_to!("List.join [ [], [], [] ]", &[], &'static [f64]);
|
assert_evals_to!(
|
||||||
|
"List.join [ [], [], [] ]",
|
||||||
|
RocList::from_slice(&[]),
|
||||||
|
RocList<f64>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn list_join_one_empty_list() {
|
fn list_join_one_empty_list() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
"List.join [ [ 1.2, 1.1 ], [] ]",
|
"List.join [ [ 1.2, 1.1 ], [] ]",
|
||||||
&[1.2, 1.1],
|
RocList::from_slice(&[1.2, 1.1]),
|
||||||
&'static [f64]
|
RocList<f64>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn list_single() {
|
fn list_single() {
|
||||||
assert_evals_to!("List.single 1", &[1], &'static [i64]);
|
assert_evals_to!("List.single 1", RocList::from_slice(&[1]), RocList<i64>);
|
||||||
assert_evals_to!("List.single 5.6", &[5.6], &'static [f64]);
|
assert_evals_to!("List.single 5.6", RocList::from_slice(&[5.6]), RocList<f64>);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn list_repeat() {
|
fn list_repeat() {
|
||||||
assert_evals_to!("List.repeat 5 1", &[1, 1, 1, 1, 1], &'static [i64]);
|
assert_evals_to!(
|
||||||
assert_evals_to!("List.repeat 4 2", &[2, 2, 2, 2], &'static [i64]);
|
"List.repeat 5 1",
|
||||||
|
RocList::from_slice(&[1, 1, 1, 1, 1]),
|
||||||
|
RocList<i64>
|
||||||
|
);
|
||||||
|
assert_evals_to!(
|
||||||
|
"List.repeat 4 2",
|
||||||
|
RocList::from_slice(&[2, 2, 2, 2]),
|
||||||
|
RocList<i64>
|
||||||
|
);
|
||||||
|
|
||||||
assert_evals_to!("List.repeat 2 []", &[&[], &[]], &'static [&'static [i64]]);
|
assert_evals_to!("List.repeat 2 []", &[&[], &[]], &'static [&'static [i64]]);
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
|
@ -483,8 +624,8 @@ mod gen_list {
|
||||||
|
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
"List.repeat 15 4",
|
"List.repeat 15 4",
|
||||||
&[4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4],
|
RocList::from_slice(&[4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]),
|
||||||
&'static [i64]
|
RocList<i64>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -492,11 +633,15 @@ mod gen_list {
|
||||||
fn list_reverse() {
|
fn list_reverse() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
"List.reverse [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ]",
|
"List.reverse [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ]",
|
||||||
&[12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1],
|
RocList::from_slice(&[12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]),
|
||||||
&'static [i64]
|
RocList<i64>
|
||||||
);
|
);
|
||||||
assert_evals_to!("List.reverse [1, 2, 3]", &[3, 2, 1], &'static [i64]);
|
assert_evals_to!(
|
||||||
assert_evals_to!("List.reverse [4]", &[4], &'static [i64]);
|
"List.reverse [1, 2, 3]",
|
||||||
|
RocList::from_slice(&[3, 2, 1]),
|
||||||
|
RocList<i64>
|
||||||
|
);
|
||||||
|
assert_evals_to!("List.reverse [4]", RocList::from_slice(&[4]), RocList<i64>);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -511,14 +656,14 @@ mod gen_list {
|
||||||
List.reverse emptyList
|
List.reverse emptyList
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
&[],
|
RocList::from_slice(&[]),
|
||||||
&'static [i64]
|
RocList<i64>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn list_reverse_empty_list() {
|
fn list_reverse_empty_list() {
|
||||||
assert_evals_to!("List.reverse []", &[], &'static [i64]);
|
assert_evals_to!("List.reverse []", RocList::from_slice(&[]), RocList<i64>);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -537,14 +682,14 @@ mod gen_list {
|
||||||
List.concat firstList secondList
|
List.concat firstList secondList
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
&[],
|
RocList::from_slice(&[]),
|
||||||
&'static [i64]
|
RocList<i64>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn list_concat_two_empty_lists() {
|
fn list_concat_two_empty_lists() {
|
||||||
assert_evals_to!("List.concat [] []", &[], &'static [i64]);
|
assert_evals_to!("List.concat [] []", RocList::from_slice(&[]), RocList<i64>);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -563,32 +708,40 @@ mod gen_list {
|
||||||
List.concat firstList secondList
|
List.concat firstList secondList
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
&[],
|
RocList::from_slice(&[]),
|
||||||
&'static [i64]
|
RocList<i64>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn list_concat_second_list_is_empty() {
|
fn list_concat_second_list_is_empty() {
|
||||||
assert_evals_to!("List.concat [ 12, 13 ] []", &[12, 13], &'static [i64]);
|
assert_evals_to!(
|
||||||
|
"List.concat [ 12, 13 ] []",
|
||||||
|
RocList::from_slice(&[12, 13]),
|
||||||
|
RocList<i64>
|
||||||
|
);
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
"List.concat [ 34, 43 ] [ 64, 55, 66 ]",
|
"List.concat [ 34, 43 ] [ 64, 55, 66 ]",
|
||||||
&[34, 43, 64, 55, 66],
|
RocList::from_slice(&[34, 43, 64, 55, 66]),
|
||||||
&'static [i64]
|
RocList<i64>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn list_concat_first_list_is_empty() {
|
fn list_concat_first_list_is_empty() {
|
||||||
assert_evals_to!("List.concat [] [ 23, 24 ]", &[23, 24], &'static [i64]);
|
assert_evals_to!(
|
||||||
|
"List.concat [] [ 23, 24 ]",
|
||||||
|
RocList::from_slice(&[23, 24]),
|
||||||
|
RocList<i64>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn list_concat_two_non_empty_lists() {
|
fn list_concat_two_non_empty_lists() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
"List.concat [1, 2 ] [ 3, 4 ]",
|
"List.concat [1, 2 ] [ 3, 4 ]",
|
||||||
&[1, 2, 3, 4],
|
RocList::from_slice(&[1, 2, 3, 4]),
|
||||||
&'static [i64]
|
RocList<i64>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -596,8 +749,8 @@ mod gen_list {
|
||||||
fn list_concat_two_bigger_non_empty_lists() {
|
fn list_concat_two_bigger_non_empty_lists() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
"List.concat [ 1.1, 2.2 ] [ 3.3, 4.4, 5.5 ]",
|
"List.concat [ 1.1, 2.2 ] [ 3.3, 4.4, 5.5 ]",
|
||||||
&[1.1, 2.2, 3.3, 4.4, 5.5],
|
RocList::from_slice(&[1.1, 2.2, 3.3, 4.4, 5.5]),
|
||||||
&'static [f64]
|
RocList<f64>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -614,12 +767,10 @@ mod gen_list {
|
||||||
|
|
||||||
expected.extend(vec2);
|
expected.extend(vec2);
|
||||||
|
|
||||||
let expected_slice: &[i64] = expected.as_ref();
|
|
||||||
|
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
&format!("List.concat {} {}", slice_str1, slice_str2),
|
&format!("List.concat {} {}", slice_str1, slice_str2),
|
||||||
expected_slice,
|
RocList::from_slice(&expected),
|
||||||
&'static [i64]
|
RocList<i64>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -849,8 +1000,8 @@ mod gen_list {
|
||||||
fn set_unique_int_list() {
|
fn set_unique_int_list() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
"List.set [ 12, 9, 7, 1, 5 ] 2 33",
|
"List.set [ 12, 9, 7, 1, 5 ] 2 33",
|
||||||
&[12, 9, 33, 1, 5],
|
RocList::from_slice(&[12, 9, 33, 1, 5]),
|
||||||
&'static [i64]
|
RocList<i64>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -858,8 +1009,8 @@ mod gen_list {
|
||||||
fn set_unique_list_oob() {
|
fn set_unique_list_oob() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
"List.set [ 3, 17, 4.1 ] 1337 9.25",
|
"List.set [ 3, 17, 4.1 ] 1337 9.25",
|
||||||
&[3.0, 17.0, 4.1],
|
RocList::from_slice(&[3.0, 17.0, 4.1]),
|
||||||
&'static [f64]
|
RocList<f64>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -948,8 +1099,8 @@ mod gen_list {
|
||||||
wrapLen [ 1, 7, 9 ]
|
wrapLen [ 1, 7, 9 ]
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
&[3],
|
RocList::from_slice(&[3]),
|
||||||
&'static [i64]
|
RocList<i64>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -964,6 +1115,8 @@ mod gen_list {
|
||||||
wrapFirst [ 1, 2 ]
|
wrapFirst [ 1, 2 ]
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
|
// RocList::from_slice(&[1]),
|
||||||
|
// RocList<i64>
|
||||||
&[1],
|
&[1],
|
||||||
&'static [i64]
|
&'static [i64]
|
||||||
);
|
);
|
||||||
|
@ -986,8 +1139,8 @@ mod gen_list {
|
||||||
dupe [ 1, 2 ]
|
dupe [ 1, 2 ]
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
&[1, 1],
|
RocList::from_slice(&[1, 1]),
|
||||||
&'static [i64]
|
RocList<i64>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1009,8 +1162,8 @@ mod gen_list {
|
||||||
swap 0 1 [ 1, 2 ]
|
swap 0 1 [ 1, 2 ]
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
&[2, 1],
|
RocList::from_slice(&[2, 1]),
|
||||||
&'static [i64]
|
RocList<i64>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1061,8 +1214,8 @@ mod gen_list {
|
||||||
// [ 1,3 ]
|
// [ 1,3 ]
|
||||||
// "#
|
// "#
|
||||||
// ),
|
// ),
|
||||||
// &[2, 1],
|
// RocList::from_slice(&[2, 1]),
|
||||||
// &'static [i64]
|
// RocList<i64>
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
@ -1100,8 +1253,8 @@ mod gen_list {
|
||||||
// [ 1,3 ]
|
// [ 1,3 ]
|
||||||
// "#
|
// "#
|
||||||
// ),
|
// ),
|
||||||
// &[2, 1],
|
// RocList::from_slice(&[2, 1]),
|
||||||
// &'static [i64]
|
// RocList<i64>
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
@ -1170,8 +1323,8 @@ mod gen_list {
|
||||||
quicksort [ 7, 4, 21, 19 ]
|
quicksort [ 7, 4, 21, 19 ]
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
&[4, 7, 19, 21],
|
RocList::from_slice(&[4, 7, 19, 21]),
|
||||||
&'static [i64]
|
RocList<i64>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1243,8 +1396,8 @@ mod gen_list {
|
||||||
quicksort [ 7, 4, 21, 19 ]
|
quicksort [ 7, 4, 21, 19 ]
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
&[19, 7, 4, 21],
|
RocList::from_slice(&[19, 7, 4, 21]),
|
||||||
&'static [i64]
|
RocList<i64>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1368,8 +1521,8 @@ mod gen_list {
|
||||||
id x
|
id x
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
&[1, 2, 3],
|
RocList::from_slice(&[1, 2, 3]),
|
||||||
&'static [i64]
|
RocList<i64>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1387,8 +1540,8 @@ mod gen_list {
|
||||||
id x
|
id x
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
&[0, 2, 3],
|
RocList::from_slice(&[0, 2, 3]),
|
||||||
&'static [i64]
|
RocList<i64>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1404,8 +1557,8 @@ mod gen_list {
|
||||||
Pair v _ -> v
|
Pair v _ -> v
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
&[1, 2, 3],
|
RocList::from_slice(&[1, 2, 3]),
|
||||||
&'static [i64]
|
RocList<i64>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -686,6 +686,11 @@ mod gen_num {
|
||||||
assert_evals_to!("Num.powInt 2 3", 8, i64);
|
assert_evals_to!("Num.powInt 2 3", 8, i64);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn atan() {
|
||||||
|
assert_evals_to!("Num.atan 10", 1.4711276743037347, f64);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = r#"Roc failed with message: "integer addition overflowed!"#)]
|
#[should_panic(expected = r#"Roc failed with message: "integer addition overflowed!"#)]
|
||||||
fn int_overflow() {
|
fn int_overflow() {
|
||||||
|
|
|
@ -396,7 +396,11 @@ macro_rules! assert_llvm_evals_to {
|
||||||
let (main_fn_name, errors, execution_engine) =
|
let (main_fn_name, errors, execution_engine) =
|
||||||
$crate::helpers::eval::helper_without_uniqueness(&arena, $src, $leak, &context);
|
$crate::helpers::eval::helper_without_uniqueness(&arena, $src, $leak, &context);
|
||||||
|
|
||||||
let transform = |success| assert_eq!($transform(success), $expected);
|
let transform = |success| {
|
||||||
|
let expected = $expected;
|
||||||
|
let given = $transform(success);
|
||||||
|
assert_eq!(&given, &expected);
|
||||||
|
};
|
||||||
run_jit_function!(execution_engine, main_fn_name, $ty, transform, errors)
|
run_jit_function!(execution_engine, main_fn_name, $ty, transform, errors)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
1660
compiler/load/src/docs.rs
Normal file
1660
compiler/load/src/docs.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -10,4 +10,5 @@
|
||||||
// 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 docs;
|
||||||
pub mod file;
|
pub mod file;
|
||||||
|
|
|
@ -44,6 +44,7 @@ pub enum LowLevel {
|
||||||
NumPowInt,
|
NumPowInt,
|
||||||
NumFloor,
|
NumFloor,
|
||||||
NumIsFinite,
|
NumIsFinite,
|
||||||
|
NumAtan,
|
||||||
Eq,
|
Eq,
|
||||||
NotEq,
|
NotEq,
|
||||||
And,
|
And,
|
||||||
|
|
|
@ -646,6 +646,7 @@ define_builtins! {
|
||||||
41 NUM_FLOOR: "floor"
|
41 NUM_FLOOR: "floor"
|
||||||
42 NUM_ADD_WRAP: "addWrap"
|
42 NUM_ADD_WRAP: "addWrap"
|
||||||
43 NUM_ADD_CHECKED: "addChecked"
|
43 NUM_ADD_CHECKED: "addChecked"
|
||||||
|
44 NUM_ATAN: "atan"
|
||||||
}
|
}
|
||||||
2 BOOL: "Bool" => {
|
2 BOOL: "Bool" => {
|
||||||
0 BOOL_BOOL: "Bool" imported // the Bool.Bool type alias
|
0 BOOL_BOOL: "Bool" imported // the Bool.Bool type alias
|
||||||
|
|
|
@ -526,6 +526,6 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
||||||
| NumPowInt => arena.alloc_slice_copy(&[irrelevant, irrelevant]),
|
| NumPowInt => arena.alloc_slice_copy(&[irrelevant, irrelevant]),
|
||||||
|
|
||||||
NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumRound | NumCeiling | NumFloor
|
NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumRound | NumCeiling | NumFloor
|
||||||
| NumToFloat | Not | NumIsFinite => arena.alloc_slice_copy(&[irrelevant]),
|
| NumToFloat | Not | NumIsFinite | NumAtan => arena.alloc_slice_copy(&[irrelevant]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -438,7 +438,9 @@ impl<'a> Procs<'a> {
|
||||||
|
|
||||||
fn get_args_ret_var(subs: &Subs, var: Variable) -> Option<(std::vec::Vec<Variable>, Variable)> {
|
fn get_args_ret_var(subs: &Subs, var: Variable) -> Option<(std::vec::Vec<Variable>, Variable)> {
|
||||||
match subs.get_without_compacting(var).content {
|
match subs.get_without_compacting(var).content {
|
||||||
Content::Structure(FlatType::Func(pattern_vars, ret_var)) => Some((pattern_vars, ret_var)),
|
Content::Structure(FlatType::Func(pattern_vars, _closure_var, ret_var)) => {
|
||||||
|
Some((pattern_vars, ret_var))
|
||||||
|
}
|
||||||
Content::Structure(FlatType::Apply(Symbol::ATTR_ATTR, args)) => {
|
Content::Structure(FlatType::Apply(Symbol::ATTR_ATTR, args)) => {
|
||||||
get_args_ret_var(subs, args[1])
|
get_args_ret_var(subs, args[1])
|
||||||
}
|
}
|
||||||
|
@ -1334,24 +1336,32 @@ pub fn with_hole<'a>(
|
||||||
},
|
},
|
||||||
LetNonRec(def, cont, _, _) => {
|
LetNonRec(def, cont, _, _) => {
|
||||||
if let roc_can::pattern::Pattern::Identifier(symbol) = &def.loc_pattern.value {
|
if let roc_can::pattern::Pattern::Identifier(symbol) = &def.loc_pattern.value {
|
||||||
if let Closure(ann, _, recursivity, loc_args, boxed_body) = def.loc_expr.value {
|
if let Closure {
|
||||||
|
function_type,
|
||||||
|
return_type,
|
||||||
|
recursive,
|
||||||
|
arguments,
|
||||||
|
loc_body: boxed_body,
|
||||||
|
..
|
||||||
|
} = def.loc_expr.value
|
||||||
|
{
|
||||||
// Extract Procs, but discard the resulting Expr::Load.
|
// Extract Procs, but discard the resulting Expr::Load.
|
||||||
// That Load looks up the pointer, which we won't use here!
|
// That Load looks up the pointer, which we won't use here!
|
||||||
|
|
||||||
let (loc_body, ret_var) = *boxed_body;
|
let loc_body = *boxed_body;
|
||||||
|
|
||||||
let is_self_recursive =
|
let is_self_recursive =
|
||||||
!matches!(recursivity, roc_can::expr::Recursive::NotRecursive);
|
!matches!(recursive, roc_can::expr::Recursive::NotRecursive);
|
||||||
|
|
||||||
procs.insert_named(
|
procs.insert_named(
|
||||||
env,
|
env,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
*symbol,
|
*symbol,
|
||||||
ann,
|
function_type,
|
||||||
loc_args,
|
arguments,
|
||||||
loc_body,
|
loc_body,
|
||||||
is_self_recursive,
|
is_self_recursive,
|
||||||
ret_var,
|
return_type,
|
||||||
);
|
);
|
||||||
|
|
||||||
return with_hole(env, cont.value, procs, layout_cache, assigned, hole);
|
return with_hole(env, cont.value, procs, layout_cache, assigned, hole);
|
||||||
|
@ -1418,24 +1428,32 @@ pub fn with_hole<'a>(
|
||||||
// because Roc is strict, only functions can be recursive!
|
// because Roc is strict, only functions can be recursive!
|
||||||
for def in defs.into_iter() {
|
for def in defs.into_iter() {
|
||||||
if let roc_can::pattern::Pattern::Identifier(symbol) = &def.loc_pattern.value {
|
if let roc_can::pattern::Pattern::Identifier(symbol) = &def.loc_pattern.value {
|
||||||
if let Closure(ann, _, recursivity, loc_args, boxed_body) = def.loc_expr.value {
|
if let Closure {
|
||||||
|
function_type,
|
||||||
|
return_type,
|
||||||
|
recursive,
|
||||||
|
arguments,
|
||||||
|
loc_body: boxed_body,
|
||||||
|
..
|
||||||
|
} = def.loc_expr.value
|
||||||
|
{
|
||||||
// Extract Procs, but discard the resulting Expr::Load.
|
// Extract Procs, but discard the resulting Expr::Load.
|
||||||
// That Load looks up the pointer, which we won't use here!
|
// That Load looks up the pointer, which we won't use here!
|
||||||
|
|
||||||
let (loc_body, ret_var) = *boxed_body;
|
let loc_body = *boxed_body;
|
||||||
|
|
||||||
let is_self_recursive =
|
let is_self_recursive =
|
||||||
!matches!(recursivity, roc_can::expr::Recursive::NotRecursive);
|
!matches!(recursive, roc_can::expr::Recursive::NotRecursive);
|
||||||
|
|
||||||
procs.insert_named(
|
procs.insert_named(
|
||||||
env,
|
env,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
*symbol,
|
*symbol,
|
||||||
ann,
|
function_type,
|
||||||
loc_args,
|
arguments,
|
||||||
loc_body,
|
loc_body,
|
||||||
is_self_recursive,
|
is_self_recursive,
|
||||||
ret_var,
|
return_type,
|
||||||
);
|
);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
|
@ -1943,11 +1961,25 @@ pub fn with_hole<'a>(
|
||||||
|
|
||||||
Accessor { .. } | Update { .. } => todo!("record access/accessor/update"),
|
Accessor { .. } | Update { .. } => todo!("record access/accessor/update"),
|
||||||
|
|
||||||
Closure(ann, name, _, loc_args, boxed_body) => {
|
Closure {
|
||||||
let (loc_body, ret_var) = *boxed_body;
|
function_type,
|
||||||
|
return_type,
|
||||||
|
name,
|
||||||
|
arguments,
|
||||||
|
loc_body: boxed_body,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let loc_body = *boxed_body;
|
||||||
|
|
||||||
match procs.insert_anonymous(env, name, ann, loc_args, loc_body, ret_var, layout_cache)
|
match procs.insert_anonymous(
|
||||||
{
|
env,
|
||||||
|
name,
|
||||||
|
function_type,
|
||||||
|
arguments,
|
||||||
|
loc_body,
|
||||||
|
return_type,
|
||||||
|
layout_cache,
|
||||||
|
) {
|
||||||
Ok(layout) => {
|
Ok(layout) => {
|
||||||
// TODO should the let have layout Pointer?
|
// TODO should the let have layout Pointer?
|
||||||
Stmt::Let(
|
Stmt::Let(
|
||||||
|
@ -1965,7 +1997,7 @@ pub fn with_hole<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
Call(boxed, loc_args, _) => {
|
Call(boxed, loc_args, _) => {
|
||||||
let (fn_var, loc_expr, ret_var) = *boxed;
|
let (fn_var, loc_expr, _closure_var, ret_var) = *boxed;
|
||||||
|
|
||||||
// even if a call looks like it's by name, it may in fact be by-pointer.
|
// even if a call looks like it's by name, it may in fact be by-pointer.
|
||||||
// E.g. in `(\f, x -> f x)` the call is in fact by pointer.
|
// E.g. in `(\f, x -> f x)` the call is in fact by pointer.
|
||||||
|
@ -2197,24 +2229,31 @@ pub fn from_can<'a>(
|
||||||
// Now that we know for sure it's a closure, get an owned
|
// Now that we know for sure it's a closure, get an owned
|
||||||
// version of these variant args so we can use them properly.
|
// version of these variant args so we can use them properly.
|
||||||
match def.loc_expr.value {
|
match def.loc_expr.value {
|
||||||
Closure(ann, _, recursivity, loc_args, boxed_body) => {
|
Closure {
|
||||||
|
function_type,
|
||||||
|
return_type,
|
||||||
|
recursive,
|
||||||
|
arguments,
|
||||||
|
loc_body: boxed_body,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
// Extract Procs, but discard the resulting Expr::Load.
|
// Extract Procs, but discard the resulting Expr::Load.
|
||||||
// That Load looks up the pointer, which we won't use here!
|
// That Load looks up the pointer, which we won't use here!
|
||||||
|
|
||||||
let (loc_body, ret_var) = *boxed_body;
|
let loc_body = *boxed_body;
|
||||||
|
|
||||||
let is_self_recursive =
|
let is_self_recursive =
|
||||||
!matches!(recursivity, roc_can::expr::Recursive::NotRecursive);
|
!matches!(recursive, roc_can::expr::Recursive::NotRecursive);
|
||||||
|
|
||||||
procs.insert_named(
|
procs.insert_named(
|
||||||
env,
|
env,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
*symbol,
|
*symbol,
|
||||||
ann,
|
function_type,
|
||||||
loc_args,
|
arguments,
|
||||||
loc_body,
|
loc_body,
|
||||||
is_self_recursive,
|
is_self_recursive,
|
||||||
ret_var,
|
return_type,
|
||||||
);
|
);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
|
@ -2229,28 +2268,35 @@ pub fn from_can<'a>(
|
||||||
}
|
}
|
||||||
LetNonRec(def, cont, _, _) => {
|
LetNonRec(def, cont, _, _) => {
|
||||||
if let roc_can::pattern::Pattern::Identifier(symbol) = &def.loc_pattern.value {
|
if let roc_can::pattern::Pattern::Identifier(symbol) = &def.loc_pattern.value {
|
||||||
if let Closure(_, _, _, _, _) = &def.loc_expr.value {
|
if let Closure { .. } = &def.loc_expr.value {
|
||||||
// Now that we know for sure it's a closure, get an owned
|
// Now that we know for sure it's a closure, get an owned
|
||||||
// version of these variant args so we can use them properly.
|
// version of these variant args so we can use them properly.
|
||||||
match def.loc_expr.value {
|
match def.loc_expr.value {
|
||||||
Closure(ann, _, recursivity, loc_args, boxed_body) => {
|
Closure {
|
||||||
|
function_type,
|
||||||
|
return_type,
|
||||||
|
recursive,
|
||||||
|
arguments,
|
||||||
|
loc_body: boxed_body,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
// Extract Procs, but discard the resulting Expr::Load.
|
// Extract Procs, but discard the resulting Expr::Load.
|
||||||
// That Load looks up the pointer, which we won't use here!
|
// That Load looks up the pointer, which we won't use here!
|
||||||
|
|
||||||
let (loc_body, ret_var) = *boxed_body;
|
let loc_body = *boxed_body;
|
||||||
|
|
||||||
let is_self_recursive =
|
let is_self_recursive =
|
||||||
!matches!(recursivity, roc_can::expr::Recursive::NotRecursive);
|
!matches!(recursive, roc_can::expr::Recursive::NotRecursive);
|
||||||
|
|
||||||
procs.insert_named(
|
procs.insert_named(
|
||||||
env,
|
env,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
*symbol,
|
*symbol,
|
||||||
ann,
|
function_type,
|
||||||
loc_args,
|
arguments,
|
||||||
loc_body,
|
loc_body,
|
||||||
is_self_recursive,
|
is_self_recursive,
|
||||||
ret_var,
|
return_type,
|
||||||
);
|
);
|
||||||
|
|
||||||
return from_can(env, cont.value, procs, layout_cache);
|
return from_can(env, cont.value, procs, layout_cache);
|
||||||
|
|
|
@ -391,7 +391,7 @@ fn layout_from_flat_type<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Func(args, ret_var) => {
|
Func(args, _, ret_var) => {
|
||||||
let mut fn_args = Vec::with_capacity_in(args.len(), arena);
|
let mut fn_args = Vec::with_capacity_in(args.len(), arena);
|
||||||
|
|
||||||
for arg_var in args {
|
for arg_var in args {
|
||||||
|
|
|
@ -796,7 +796,7 @@ fn count_arguments(tipe: &ErrorType) -> usize {
|
||||||
use ErrorType::*;
|
use ErrorType::*;
|
||||||
|
|
||||||
match tipe {
|
match tipe {
|
||||||
Function(args, _) => args.len(),
|
Function(args, _, _) => args.len(),
|
||||||
Type(Symbol::ATTR_ATTR, args) => count_arguments(&args[1]),
|
Type(Symbol::ATTR_ATTR, args) => count_arguments(&args[1]),
|
||||||
Alias(_, _, actual) => count_arguments(actual),
|
Alias(_, _, actual) => count_arguments(actual),
|
||||||
_ => 0,
|
_ => 0,
|
||||||
|
@ -1334,7 +1334,7 @@ pub fn to_doc<'b>(
|
||||||
use ErrorType::*;
|
use ErrorType::*;
|
||||||
|
|
||||||
match tipe {
|
match tipe {
|
||||||
Function(args, ret) => report_text::function(
|
Function(args, _, ret) => report_text::function(
|
||||||
alloc,
|
alloc,
|
||||||
parens,
|
parens,
|
||||||
args.into_iter()
|
args.into_iter()
|
||||||
|
@ -1474,7 +1474,7 @@ fn to_diff<'b>(
|
||||||
(FlexVar(x), FlexVar(y)) if x == y => same(alloc, parens, type1),
|
(FlexVar(x), FlexVar(y)) if x == y => same(alloc, parens, type1),
|
||||||
(RigidVar(x), RigidVar(y)) if x == y => same(alloc, parens, type1),
|
(RigidVar(x), RigidVar(y)) if x == y => same(alloc, parens, type1),
|
||||||
|
|
||||||
(Function(args1, ret1), Function(args2, ret2)) => {
|
(Function(args1, _, ret1), Function(args2, _, ret2)) => {
|
||||||
if args1.len() == args2.len() {
|
if args1.len() == args2.len() {
|
||||||
let mut status = Status::Similar;
|
let mut status = Status::Similar;
|
||||||
let arg_diff = traverse(alloc, Parens::InFn, args1, args2);
|
let arg_diff = traverse(alloc, Parens::InFn, args1, args2);
|
||||||
|
@ -2406,7 +2406,7 @@ fn type_problem_to_pretty<'b>(
|
||||||
match tipe {
|
match tipe {
|
||||||
Infinite | Error | FlexVar(_) => alloc.nil(),
|
Infinite | Error | FlexVar(_) => alloc.nil(),
|
||||||
RigidVar(y) => bad_double_rigid(x, y),
|
RigidVar(y) => bad_double_rigid(x, y),
|
||||||
Function(_, _) => bad_rigid_var(x, alloc.reflow("a function value")),
|
Function(_, _, _) => bad_rigid_var(x, alloc.reflow("a function value")),
|
||||||
Record(_, _) => bad_rigid_var(x, alloc.reflow("a record value")),
|
Record(_, _) => bad_rigid_var(x, alloc.reflow("a record value")),
|
||||||
TagUnion(_, _) | RecursiveTagUnion(_, _, _) => {
|
TagUnion(_, _) | RecursiveTagUnion(_, _, _) => {
|
||||||
bad_rigid_var(x, alloc.reflow("a tag value"))
|
bad_rigid_var(x, alloc.reflow("a tag value"))
|
||||||
|
|
|
@ -599,7 +599,7 @@ fn type_to_variable(
|
||||||
|
|
||||||
register(subs, rank, pools, content)
|
register(subs, rank, pools, content)
|
||||||
}
|
}
|
||||||
Function(args, ret_type) => {
|
Function(args, closure_type, ret_type) => {
|
||||||
let mut arg_vars = Vec::with_capacity(args.len());
|
let mut arg_vars = Vec::with_capacity(args.len());
|
||||||
|
|
||||||
for arg in args {
|
for arg in args {
|
||||||
|
@ -607,7 +607,8 @@ fn type_to_variable(
|
||||||
}
|
}
|
||||||
|
|
||||||
let ret_var = type_to_variable(subs, rank, pools, cached, ret_type);
|
let ret_var = type_to_variable(subs, rank, pools, cached, ret_type);
|
||||||
let content = Content::Structure(FlatType::Func(arg_vars, ret_var));
|
let closure_var = type_to_variable(subs, rank, pools, cached, closure_type);
|
||||||
|
let content = Content::Structure(FlatType::Func(arg_vars, closure_var, ret_var));
|
||||||
|
|
||||||
register(subs, rank, pools, content)
|
register(subs, rank, pools, content)
|
||||||
}
|
}
|
||||||
|
@ -1075,9 +1076,17 @@ fn adjust_rank_content(
|
||||||
rank
|
rank
|
||||||
}
|
}
|
||||||
|
|
||||||
Func(arg_vars, ret_var) => {
|
Func(arg_vars, closure_var, ret_var) => {
|
||||||
let mut rank = adjust_rank(subs, young_mark, visit_mark, group_rank, ret_var);
|
let mut rank = adjust_rank(subs, young_mark, visit_mark, group_rank, ret_var);
|
||||||
|
|
||||||
|
rank = rank.max(adjust_rank(
|
||||||
|
subs,
|
||||||
|
young_mark,
|
||||||
|
visit_mark,
|
||||||
|
group_rank,
|
||||||
|
closure_var,
|
||||||
|
));
|
||||||
|
|
||||||
for var in arg_vars {
|
for var in arg_vars {
|
||||||
rank = rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, var));
|
rank = rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, var));
|
||||||
}
|
}
|
||||||
|
@ -1239,14 +1248,15 @@ fn deep_copy_var_help(
|
||||||
Apply(symbol, args)
|
Apply(symbol, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
Func(arg_vars, ret_var) => {
|
Func(arg_vars, closure_var, ret_var) => {
|
||||||
let new_ret_var = deep_copy_var_help(subs, max_rank, pools, ret_var);
|
let new_ret_var = deep_copy_var_help(subs, max_rank, pools, ret_var);
|
||||||
|
let new_closure_var = deep_copy_var_help(subs, max_rank, pools, closure_var);
|
||||||
let arg_vars = arg_vars
|
let arg_vars = arg_vars
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|var| deep_copy_var_help(subs, max_rank, pools, var))
|
.map(|var| deep_copy_var_help(subs, max_rank, pools, var))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Func(arg_vars, new_ret_var)
|
Func(arg_vars, new_closure_var, new_ret_var)
|
||||||
}
|
}
|
||||||
|
|
||||||
same @ EmptyRecord | same @ EmptyTagUnion | same @ Erroneous(_) => same,
|
same @ EmptyRecord | same @ EmptyTagUnion | same @ Erroneous(_) => same,
|
||||||
|
|
|
@ -2430,6 +2430,18 @@ mod solve_expr {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn atan() {
|
||||||
|
infer_eq_without_problem(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
Num.atan
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"Float -> Float",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn reconstruct_path() {
|
fn reconstruct_path() {
|
||||||
infer_eq_without_problem(
|
infer_eq_without_problem(
|
||||||
|
|
|
@ -150,7 +150,7 @@ fn find_names_needed(
|
||||||
find_names_needed(var, subs, roots, root_appearances, names_taken);
|
find_names_needed(var, subs, roots, root_appearances, names_taken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Structure(Func(arg_vars, ret_var)) => {
|
Structure(Func(arg_vars, _closure_var, ret_var)) => {
|
||||||
for var in arg_vars {
|
for var in arg_vars {
|
||||||
find_names_needed(var, subs, roots, root_appearances, names_taken);
|
find_names_needed(var, subs, roots, root_appearances, names_taken);
|
||||||
}
|
}
|
||||||
|
@ -376,7 +376,7 @@ fn write_flat_type(env: &Env, flat_type: FlatType, subs: &Subs, buf: &mut String
|
||||||
Apply(symbol, args) => write_apply(env, symbol, args, subs, buf, parens),
|
Apply(symbol, args) => write_apply(env, symbol, args, subs, buf, parens),
|
||||||
EmptyRecord => buf.push_str(EMPTY_RECORD),
|
EmptyRecord => buf.push_str(EMPTY_RECORD),
|
||||||
EmptyTagUnion => buf.push_str(EMPTY_TAG_UNION),
|
EmptyTagUnion => buf.push_str(EMPTY_TAG_UNION),
|
||||||
Func(args, ret) => write_fn(env, args, ret, subs, buf, parens),
|
Func(args, _closure, ret) => write_fn(env, args, ret, subs, buf, parens),
|
||||||
Record(fields, ext_var) => {
|
Record(fields, ext_var) => {
|
||||||
use crate::types::{gather_fields, RecordStructure};
|
use crate::types::{gather_fields, RecordStructure};
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ impl<T> Solved<T> {
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum SolvedType {
|
pub enum SolvedType {
|
||||||
/// A function. The types of its arguments, then the type of its return value.
|
/// A function. The types of its arguments, then the type of its return value.
|
||||||
Func(Vec<SolvedType>, Box<SolvedType>),
|
Func(Vec<SolvedType>, Box<SolvedType>, Box<SolvedType>),
|
||||||
/// Applying a type to some arguments (e.g. Map.Map String Int)
|
/// Applying a type to some arguments (e.g. Map.Map String Int)
|
||||||
Apply(Symbol, Vec<SolvedType>),
|
Apply(Symbol, Vec<SolvedType>),
|
||||||
/// A bound type variable, e.g. `a` in `(a -> a)`
|
/// A bound type variable, e.g. `a` in `(a -> a)`
|
||||||
|
@ -107,8 +107,9 @@ impl SolvedType {
|
||||||
|
|
||||||
SolvedType::Apply(symbol, solved_types)
|
SolvedType::Apply(symbol, solved_types)
|
||||||
}
|
}
|
||||||
Function(args, box_ret) => {
|
Function(args, box_closure, box_ret) => {
|
||||||
let solved_ret = Self::from_type(solved_subs, *box_ret);
|
let solved_ret = Self::from_type(solved_subs, *box_ret);
|
||||||
|
let solved_closure = Self::from_type(solved_subs, *box_closure);
|
||||||
let mut solved_args = Vec::with_capacity(args.len());
|
let mut solved_args = Vec::with_capacity(args.len());
|
||||||
|
|
||||||
for arg in args.into_iter() {
|
for arg in args.into_iter() {
|
||||||
|
@ -117,7 +118,7 @@ impl SolvedType {
|
||||||
solved_args.push(solved_arg);
|
solved_args.push(solved_arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
SolvedType::Func(solved_args, Box::new(solved_ret))
|
SolvedType::Func(solved_args, Box::new(solved_closure), Box::new(solved_ret))
|
||||||
}
|
}
|
||||||
Record(fields, box_ext) => {
|
Record(fields, box_ext) => {
|
||||||
let solved_ext = Self::from_type(solved_subs, *box_ext);
|
let solved_ext = Self::from_type(solved_subs, *box_ext);
|
||||||
|
@ -227,7 +228,7 @@ impl SolvedType {
|
||||||
|
|
||||||
SolvedType::Apply(symbol, new_args)
|
SolvedType::Apply(symbol, new_args)
|
||||||
}
|
}
|
||||||
Func(args, ret) => {
|
Func(args, closure, ret) => {
|
||||||
let mut new_args = Vec::with_capacity(args.len());
|
let mut new_args = Vec::with_capacity(args.len());
|
||||||
|
|
||||||
for var in args {
|
for var in args {
|
||||||
|
@ -235,8 +236,9 @@ impl SolvedType {
|
||||||
}
|
}
|
||||||
|
|
||||||
let ret = Self::from_var(subs, ret);
|
let ret = Self::from_var(subs, ret);
|
||||||
|
let closure = Self::from_var(subs, closure);
|
||||||
|
|
||||||
SolvedType::Func(new_args, Box::new(ret))
|
SolvedType::Func(new_args, Box::new(closure), Box::new(ret))
|
||||||
}
|
}
|
||||||
Record(fields, ext_var) => {
|
Record(fields, ext_var) => {
|
||||||
let mut new_fields = Vec::with_capacity(fields.len());
|
let mut new_fields = Vec::with_capacity(fields.len());
|
||||||
|
|
|
@ -565,7 +565,7 @@ impl Content {
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum FlatType {
|
pub enum FlatType {
|
||||||
Apply(Symbol, Vec<Variable>),
|
Apply(Symbol, Vec<Variable>),
|
||||||
Func(Vec<Variable>, Variable),
|
Func(Vec<Variable>, Variable, Variable),
|
||||||
Record(MutMap<Lowercase, RecordField<Variable>>, Variable),
|
Record(MutMap<Lowercase, RecordField<Variable>>, Variable),
|
||||||
TagUnion(MutMap<TagName, Vec<Variable>>, Variable),
|
TagUnion(MutMap<TagName, Vec<Variable>>, Variable),
|
||||||
RecursiveTagUnion(Variable, MutMap<TagName, Vec<Variable>>, Variable),
|
RecursiveTagUnion(Variable, MutMap<TagName, Vec<Variable>>, Variable),
|
||||||
|
@ -606,8 +606,10 @@ fn occurs(
|
||||||
|
|
||||||
match flat_type {
|
match flat_type {
|
||||||
Apply(_, args) => short_circuit(subs, root_var, &new_seen, args.iter()),
|
Apply(_, args) => short_circuit(subs, root_var, &new_seen, args.iter()),
|
||||||
Func(arg_vars, ret_var) => {
|
Func(arg_vars, closure_var, ret_var) => {
|
||||||
let it = once(&ret_var).chain(arg_vars.iter());
|
let it = once(&ret_var)
|
||||||
|
.chain(once(&closure_var))
|
||||||
|
.chain(arg_vars.iter());
|
||||||
short_circuit(subs, root_var, &new_seen, it)
|
short_circuit(subs, root_var, &new_seen, it)
|
||||||
}
|
}
|
||||||
Record(vars_by_field, ext_var) => {
|
Record(vars_by_field, ext_var) => {
|
||||||
|
@ -699,14 +701,19 @@ fn explicit_substitute(
|
||||||
|
|
||||||
subs.set_content(in_var, Structure(Apply(symbol, new_args)));
|
subs.set_content(in_var, Structure(Apply(symbol, new_args)));
|
||||||
}
|
}
|
||||||
Func(arg_vars, ret_var) => {
|
Func(arg_vars, closure_var, ret_var) => {
|
||||||
let new_arg_vars = arg_vars
|
let new_arg_vars = arg_vars
|
||||||
.iter()
|
.iter()
|
||||||
.map(|var| explicit_substitute(subs, from, to, *var, seen))
|
.map(|var| explicit_substitute(subs, from, to, *var, seen))
|
||||||
.collect();
|
.collect();
|
||||||
let new_ret_var = explicit_substitute(subs, from, to, ret_var, seen);
|
let new_ret_var = explicit_substitute(subs, from, to, ret_var, seen);
|
||||||
|
let new_closure_var =
|
||||||
|
explicit_substitute(subs, from, to, closure_var, seen);
|
||||||
|
|
||||||
subs.set_content(in_var, Structure(Func(new_arg_vars, new_ret_var)));
|
subs.set_content(
|
||||||
|
in_var,
|
||||||
|
Structure(Func(new_arg_vars, new_closure_var, new_ret_var)),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
TagUnion(mut tags, ext_var) => {
|
TagUnion(mut tags, ext_var) => {
|
||||||
let new_ext_var = explicit_substitute(subs, from, to, ext_var, seen);
|
let new_ext_var = explicit_substitute(subs, from, to, ext_var, seen);
|
||||||
|
@ -804,8 +811,9 @@ fn get_var_names(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
FlatType::Func(arg_vars, ret_var) => {
|
FlatType::Func(arg_vars, closure_var, ret_var) => {
|
||||||
let taken_names = get_var_names(subs, ret_var, taken_names);
|
let taken_names = get_var_names(subs, ret_var, taken_names);
|
||||||
|
let taken_names = get_var_names(subs, closure_var, taken_names);
|
||||||
|
|
||||||
arg_vars.into_iter().fold(taken_names, |answer, arg_var| {
|
arg_vars.into_iter().fold(taken_names, |answer, arg_var| {
|
||||||
get_var_names(subs, arg_var, answer)
|
get_var_names(subs, arg_var, answer)
|
||||||
|
@ -998,14 +1006,15 @@ fn flat_type_to_err_type(
|
||||||
ErrorType::Type(symbol, arg_types)
|
ErrorType::Type(symbol, arg_types)
|
||||||
}
|
}
|
||||||
|
|
||||||
Func(arg_vars, ret_var) => {
|
Func(arg_vars, closure_var, ret_var) => {
|
||||||
let args = arg_vars
|
let args = arg_vars
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|arg_var| var_to_err_type(subs, state, arg_var))
|
.map(|arg_var| var_to_err_type(subs, state, arg_var))
|
||||||
.collect();
|
.collect();
|
||||||
let ret = var_to_err_type(subs, state, ret_var);
|
let ret = var_to_err_type(subs, state, ret_var);
|
||||||
|
let closure = var_to_err_type(subs, state, closure_var);
|
||||||
|
|
||||||
ErrorType::Function(args, Box::new(ret))
|
ErrorType::Function(args, Box::new(closure), Box::new(ret))
|
||||||
}
|
}
|
||||||
|
|
||||||
EmptyRecord => ErrorType::Record(SendMap::default(), TypeExt::Closed),
|
EmptyRecord => ErrorType::Record(SendMap::default(), TypeExt::Closed),
|
||||||
|
@ -1145,12 +1154,13 @@ fn restore_content(subs: &mut Subs, content: &Content) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Func(arg_vars, ret_var) => {
|
Func(arg_vars, closure_var, ret_var) => {
|
||||||
for &var in arg_vars {
|
for &var in arg_vars {
|
||||||
subs.restore(var);
|
subs.restore(var);
|
||||||
}
|
}
|
||||||
|
|
||||||
subs.restore(*ret_var);
|
subs.restore(*ret_var);
|
||||||
|
subs.restore(*closure_var);
|
||||||
}
|
}
|
||||||
|
|
||||||
EmptyRecord => (),
|
EmptyRecord => (),
|
||||||
|
|
|
@ -138,8 +138,8 @@ impl RecordField<Type> {
|
||||||
pub enum Type {
|
pub enum Type {
|
||||||
EmptyRec,
|
EmptyRec,
|
||||||
EmptyTagUnion,
|
EmptyTagUnion,
|
||||||
/// A function. The types of its arguments, then the type of its return value.
|
/// A function. The types of its arguments, size of its closure, then the type of its return value.
|
||||||
Function(Vec<Type>, Box<Type>),
|
Function(Vec<Type>, Box<Type>, Box<Type>),
|
||||||
Record(SendMap<Lowercase, RecordField<Type>>, Box<Type>),
|
Record(SendMap<Lowercase, RecordField<Type>>, Box<Type>),
|
||||||
TagUnion(Vec<(TagName, Vec<Type>)>, Box<Type>),
|
TagUnion(Vec<(TagName, Vec<Type>)>, Box<Type>),
|
||||||
Alias(Symbol, Vec<(Lowercase, Type)>, Box<Type>),
|
Alias(Symbol, Vec<(Lowercase, Type)>, Box<Type>),
|
||||||
|
@ -158,7 +158,7 @@ impl fmt::Debug for Type {
|
||||||
match self {
|
match self {
|
||||||
Type::EmptyRec => write!(f, "{{}}"),
|
Type::EmptyRec => write!(f, "{{}}"),
|
||||||
Type::EmptyTagUnion => write!(f, "[]"),
|
Type::EmptyTagUnion => write!(f, "[]"),
|
||||||
Type::Function(args, ret) => {
|
Type::Function(args, closure, ret) => {
|
||||||
write!(f, "Fn(")?;
|
write!(f, "Fn(")?;
|
||||||
|
|
||||||
for (index, arg) in args.iter().enumerate() {
|
for (index, arg) in args.iter().enumerate() {
|
||||||
|
@ -169,7 +169,9 @@ impl fmt::Debug for Type {
|
||||||
arg.fmt(f)?;
|
arg.fmt(f)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
write!(f, " -> ")?;
|
write!(f, " -")?;
|
||||||
|
closure.fmt(f)?;
|
||||||
|
write!(f, "-> ")?;
|
||||||
|
|
||||||
ret.fmt(f)?;
|
ret.fmt(f)?;
|
||||||
|
|
||||||
|
@ -339,7 +341,7 @@ impl fmt::Debug for Type {
|
||||||
|
|
||||||
impl Type {
|
impl Type {
|
||||||
pub fn arity(&self) -> usize {
|
pub fn arity(&self) -> usize {
|
||||||
if let Type::Function(args, _) = self {
|
if let Type::Function(args, _, _) = self {
|
||||||
args.len()
|
args.len()
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
|
@ -369,10 +371,11 @@ impl Type {
|
||||||
*self = replacement.clone();
|
*self = replacement.clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Function(args, ret) => {
|
Function(args, closure, ret) => {
|
||||||
for arg in args {
|
for arg in args {
|
||||||
arg.substitute(substitutions);
|
arg.substitute(substitutions);
|
||||||
}
|
}
|
||||||
|
closure.substitute(substitutions);
|
||||||
ret.substitute(substitutions);
|
ret.substitute(substitutions);
|
||||||
}
|
}
|
||||||
TagUnion(tags, ext) => {
|
TagUnion(tags, ext) => {
|
||||||
|
@ -427,10 +430,11 @@ impl Type {
|
||||||
use Type::*;
|
use Type::*;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Function(args, ret) => {
|
Function(args, closure, ret) => {
|
||||||
for arg in args {
|
for arg in args {
|
||||||
arg.substitute_alias(rep_symbol, actual);
|
arg.substitute_alias(rep_symbol, actual);
|
||||||
}
|
}
|
||||||
|
closure.substitute_alias(rep_symbol, actual);
|
||||||
ret.substitute_alias(rep_symbol, actual);
|
ret.substitute_alias(rep_symbol, actual);
|
||||||
}
|
}
|
||||||
RecursiveTagUnion(_, tags, ext) | TagUnion(tags, ext) => {
|
RecursiveTagUnion(_, tags, ext) | TagUnion(tags, ext) => {
|
||||||
|
@ -472,8 +476,9 @@ impl Type {
|
||||||
use Type::*;
|
use Type::*;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Function(args, ret) => {
|
Function(args, closure, ret) => {
|
||||||
ret.contains_symbol(rep_symbol)
|
ret.contains_symbol(rep_symbol)
|
||||||
|
|| closure.contains_symbol(rep_symbol)
|
||||||
|| args.iter().any(|arg| arg.contains_symbol(rep_symbol))
|
|| args.iter().any(|arg| arg.contains_symbol(rep_symbol))
|
||||||
}
|
}
|
||||||
RecursiveTagUnion(_, tags, ext) | TagUnion(tags, ext) => {
|
RecursiveTagUnion(_, tags, ext) | TagUnion(tags, ext) => {
|
||||||
|
@ -503,8 +508,9 @@ impl Type {
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Variable(v) => *v == rep_variable,
|
Variable(v) => *v == rep_variable,
|
||||||
Function(args, ret) => {
|
Function(args, closure, ret) => {
|
||||||
ret.contains_variable(rep_variable)
|
ret.contains_variable(rep_variable)
|
||||||
|
|| closure.contains_variable(rep_variable)
|
||||||
|| args.iter().any(|arg| arg.contains_variable(rep_variable))
|
|| args.iter().any(|arg| arg.contains_variable(rep_variable))
|
||||||
}
|
}
|
||||||
RecursiveTagUnion(_, tags, ext) | TagUnion(tags, ext) => {
|
RecursiveTagUnion(_, tags, ext) | TagUnion(tags, ext) => {
|
||||||
|
@ -553,10 +559,11 @@ impl Type {
|
||||||
use Type::*;
|
use Type::*;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Function(args, ret) => {
|
Function(args, closure, ret) => {
|
||||||
for arg in args {
|
for arg in args {
|
||||||
arg.instantiate_aliases(region, aliases, var_store, introduced);
|
arg.instantiate_aliases(region, aliases, var_store, introduced);
|
||||||
}
|
}
|
||||||
|
closure.instantiate_aliases(region, aliases, var_store, introduced);
|
||||||
ret.instantiate_aliases(region, aliases, var_store, introduced);
|
ret.instantiate_aliases(region, aliases, var_store, introduced);
|
||||||
}
|
}
|
||||||
RecursiveTagUnion(_, tags, ext) | TagUnion(tags, ext) => {
|
RecursiveTagUnion(_, tags, ext) | TagUnion(tags, ext) => {
|
||||||
|
@ -726,8 +733,9 @@ fn symbols_help(tipe: &Type, accum: &mut ImSet<Symbol>) {
|
||||||
use Type::*;
|
use Type::*;
|
||||||
|
|
||||||
match tipe {
|
match tipe {
|
||||||
Function(args, ret) => {
|
Function(args, closure, ret) => {
|
||||||
symbols_help(&ret, accum);
|
symbols_help(&ret, accum);
|
||||||
|
symbols_help(&closure, accum);
|
||||||
args.iter().for_each(|arg| symbols_help(arg, accum));
|
args.iter().for_each(|arg| symbols_help(arg, accum));
|
||||||
}
|
}
|
||||||
RecursiveTagUnion(_, tags, ext) | TagUnion(tags, ext) => {
|
RecursiveTagUnion(_, tags, ext) | TagUnion(tags, ext) => {
|
||||||
|
@ -776,10 +784,11 @@ fn variables_help(tipe: &Type, accum: &mut ImSet<Variable>) {
|
||||||
accum.insert(*v);
|
accum.insert(*v);
|
||||||
}
|
}
|
||||||
|
|
||||||
Function(args, ret) => {
|
Function(args, closure, ret) => {
|
||||||
for arg in args {
|
for arg in args {
|
||||||
variables_help(arg, accum);
|
variables_help(arg, accum);
|
||||||
}
|
}
|
||||||
|
variables_help(closure, accum);
|
||||||
variables_help(ret, accum);
|
variables_help(ret, accum);
|
||||||
}
|
}
|
||||||
Record(fields, ext) => {
|
Record(fields, ext) => {
|
||||||
|
@ -1014,7 +1023,7 @@ pub enum ErrorType {
|
||||||
Record(SendMap<Lowercase, RecordField<ErrorType>>, TypeExt),
|
Record(SendMap<Lowercase, RecordField<ErrorType>>, TypeExt),
|
||||||
TagUnion(SendMap<TagName, Vec<ErrorType>>, TypeExt),
|
TagUnion(SendMap<TagName, Vec<ErrorType>>, TypeExt),
|
||||||
RecursiveTagUnion(Box<ErrorType>, SendMap<TagName, Vec<ErrorType>>, TypeExt),
|
RecursiveTagUnion(Box<ErrorType>, SendMap<TagName, Vec<ErrorType>>, TypeExt),
|
||||||
Function(Vec<ErrorType>, Box<ErrorType>),
|
Function(Vec<ErrorType>, Box<ErrorType>, Box<ErrorType>),
|
||||||
Alias(Symbol, Vec<(Lowercase, ErrorType)>, Box<ErrorType>),
|
Alias(Symbol, Vec<(Lowercase, ErrorType)>, Box<ErrorType>),
|
||||||
Boolean(boolean_algebra::Bool),
|
Boolean(boolean_algebra::Bool),
|
||||||
Error,
|
Error,
|
||||||
|
@ -1102,7 +1111,7 @@ fn write_error_type_help(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Function(arguments, result) => {
|
Function(arguments, _closure, result) => {
|
||||||
let write_parens = parens != Parens::Unnecessary;
|
let write_parens = parens != Parens::Unnecessary;
|
||||||
|
|
||||||
if write_parens {
|
if write_parens {
|
||||||
|
@ -1246,7 +1255,7 @@ fn write_debug_error_type_help(error_type: ErrorType, buf: &mut String, parens:
|
||||||
buf.push(')');
|
buf.push(')');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Function(arguments, result) => {
|
Function(arguments, _closure, result) => {
|
||||||
let write_parens = parens != Parens::Unnecessary;
|
let write_parens = parens != Parens::Unnecessary;
|
||||||
|
|
||||||
if write_parens {
|
if write_parens {
|
||||||
|
|
|
@ -140,6 +140,13 @@ fn unify_context(subs: &mut Subs, pool: &mut Pool, ctx: Context) -> Outcome {
|
||||||
println!("\n --- \n");
|
println!("\n --- \n");
|
||||||
dbg!(ctx.second, type2);
|
dbg!(ctx.second, type2);
|
||||||
println!("\n --------------- \n");
|
println!("\n --------------- \n");
|
||||||
|
println!(
|
||||||
|
"{:?} {:?} ~ {:?} {:?}",
|
||||||
|
ctx.first,
|
||||||
|
subs.get(ctx.first).content,
|
||||||
|
ctx.second,
|
||||||
|
subs.get(ctx.second).content
|
||||||
|
);
|
||||||
}
|
}
|
||||||
match &ctx.first_desc.content {
|
match &ctx.first_desc.content {
|
||||||
FlexVar(opt_name) => unify_flex(subs, &ctx, opt_name, &ctx.second_desc.content),
|
FlexVar(opt_name) => unify_flex(subs, &ctx, opt_name, &ctx.second_desc.content),
|
||||||
|
@ -849,15 +856,23 @@ fn unify_flat_type(
|
||||||
problems
|
problems
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Func(l_args, l_ret), Func(r_args, r_ret)) if l_args.len() == r_args.len() => {
|
(Func(l_args, l_closure, l_ret), Func(r_args, r_closure, r_ret))
|
||||||
|
if l_args.len() == r_args.len() =>
|
||||||
|
{
|
||||||
let arg_problems = unify_zip(subs, pool, l_args.iter(), r_args.iter());
|
let arg_problems = unify_zip(subs, pool, l_args.iter(), r_args.iter());
|
||||||
let ret_problems = unify_pool(subs, pool, *l_ret, *r_ret);
|
let ret_problems = unify_pool(subs, pool, *l_ret, *r_ret);
|
||||||
|
let closure_problems = unify_pool(subs, pool, *l_closure, *r_closure);
|
||||||
|
|
||||||
if arg_problems.is_empty() && ret_problems.is_empty() {
|
if arg_problems.is_empty() && closure_problems.is_empty() && ret_problems.is_empty() {
|
||||||
merge(subs, ctx, Structure(Func((*r_args).clone(), *r_ret)))
|
merge(
|
||||||
|
subs,
|
||||||
|
ctx,
|
||||||
|
Structure(Func((*r_args).clone(), *r_closure, *r_ret)),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
let mut problems = ret_problems;
|
let mut problems = ret_problems;
|
||||||
|
|
||||||
|
problems.extend(closure_problems);
|
||||||
problems.extend(arg_problems);
|
problems.extend(arg_problems);
|
||||||
|
|
||||||
problems
|
problems
|
||||||
|
|
|
@ -923,13 +923,17 @@ pub fn annotate_usage(expr: &Expr, usage: &mut VarUsage) {
|
||||||
annotate_usage(&arg.value, usage);
|
annotate_usage(&arg.value, usage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Closure(_, _, _, args, body) => {
|
Closure {
|
||||||
|
arguments,
|
||||||
|
loc_body,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
// annotate defaults of optional record fields
|
// annotate defaults of optional record fields
|
||||||
for (_, loc_pattern) in args {
|
for (_, loc_pattern) in arguments {
|
||||||
annotate_usage_pattern(&loc_pattern.value, usage);
|
annotate_usage_pattern(&loc_pattern.value, usage);
|
||||||
}
|
}
|
||||||
|
|
||||||
annotate_usage(&body.0.value, usage);
|
annotate_usage(&loc_body.value, usage);
|
||||||
}
|
}
|
||||||
|
|
||||||
Tag { arguments, .. } => {
|
Tag { arguments, .. } => {
|
||||||
|
|
1
docs/.gitignore
vendored
Normal file
1
docs/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/build
|
|
@ -13,3 +13,6 @@ serde_json = "1.0.39"
|
||||||
serde_derive = "1.0.75"
|
serde_derive = "1.0.75"
|
||||||
fs_extra = "1.2.0"
|
fs_extra = "1.2.0"
|
||||||
pulldown-cmark = { version = "0.8", default-features = false }
|
pulldown-cmark = { version = "0.8", default-features = false }
|
||||||
|
roc_load = { path = "../compiler/load" }
|
||||||
|
roc_builtins = { path = "../compiler/builtins" }
|
||||||
|
roc_collections = { path = "../compiler/collections" }
|
||||||
|
|
182
docs/src/main.rs
182
docs/src/main.rs
|
@ -7,27 +7,9 @@ extern crate pulldown_cmark;
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
extern crate roc_load;
|
||||||
#[derive(Serialize)]
|
use roc_collections::all::MutMap;
|
||||||
pub struct Package {
|
use std::path::{Path, PathBuf};
|
||||||
name: String,
|
|
||||||
version: String,
|
|
||||||
docs: String,
|
|
||||||
modules: Vec<Module>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Clone)]
|
|
||||||
pub struct Module {
|
|
||||||
name: String,
|
|
||||||
docs: String,
|
|
||||||
entries: Vec<ModuleEntry>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Clone)]
|
|
||||||
pub struct ModuleEntry {
|
|
||||||
name: String,
|
|
||||||
docs: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
pub struct Template {
|
pub struct Template {
|
||||||
|
@ -39,6 +21,12 @@ pub struct Template {
|
||||||
module_links: Vec<TemplateLink>,
|
module_links: Vec<TemplateLink>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Clone)]
|
||||||
|
pub struct ModuleEntry {
|
||||||
|
name: String,
|
||||||
|
docs: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
pub struct TemplateLink {
|
pub struct TemplateLink {
|
||||||
name: String,
|
name: String,
|
||||||
|
@ -53,132 +41,49 @@ pub struct TemplateLinkEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
let package = Package {
|
let std_lib = roc_builtins::std::standard_stdlib();
|
||||||
|
let subs_by_module = MutMap::default();
|
||||||
|
let src_dir = Path::new("../compiler/builtins/docs");
|
||||||
|
let files = vec![
|
||||||
|
PathBuf::from(r"../compiler/builtins/docs/Bool.roc"),
|
||||||
|
PathBuf::from(r"../compiler/builtins/docs/Map.roc"),
|
||||||
|
// Not working
|
||||||
|
// PathBuf::from(r"../compiler/builtins/docs/List.roc"),
|
||||||
|
// Not working
|
||||||
|
// PathBuf::from(r"../compiler/builtins/docs/Num.roc"),
|
||||||
|
PathBuf::from(r"../compiler/builtins/docs/Set.roc"),
|
||||||
|
PathBuf::from(r"../compiler/builtins/docs/Str.roc"),
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut modules_docs = vec![];
|
||||||
|
|
||||||
|
// Load each file is files vector
|
||||||
|
for filename in files {
|
||||||
|
let loaded = roc_load::docs::load(filename, &std_lib, src_dir, subs_by_module.clone())
|
||||||
|
.expect("TODO gracefully handle load failing");
|
||||||
|
modules_docs.push(loaded.module_docs);
|
||||||
|
}
|
||||||
|
|
||||||
|
let package = roc_load::docs::Documentation {
|
||||||
name: "roc/builtins".to_string(),
|
name: "roc/builtins".to_string(),
|
||||||
version: "1.0.0".to_string(),
|
version: "1.0.0".to_string(),
|
||||||
docs: "Package introduction or README.".to_string(),
|
docs: "Package introduction or README.".to_string(),
|
||||||
modules: vec![
|
modules: modules_docs,
|
||||||
Module {
|
|
||||||
name: "Str".to_string(),
|
|
||||||
docs: "Module introduction".to_string(),
|
|
||||||
entries: vec![
|
|
||||||
ModuleEntry {
|
|
||||||
name: "Str".to_string(),
|
|
||||||
docs: "Hello world, this is a **complicated** *very simple* example."
|
|
||||||
.to_string(),
|
|
||||||
},
|
|
||||||
ModuleEntry {
|
|
||||||
name: "isEmpty".to_string(),
|
|
||||||
docs: "Hello world, this is a **complicated** *very simple* example."
|
|
||||||
.to_string(),
|
|
||||||
},
|
|
||||||
ModuleEntry {
|
|
||||||
name: "append".to_string(),
|
|
||||||
docs: "Hello world, this is a **complicated** *very simple* example."
|
|
||||||
.to_string(),
|
|
||||||
},
|
|
||||||
ModuleEntry {
|
|
||||||
name: "prepend".to_string(),
|
|
||||||
docs: "Hello world, this is a **complicated** *very simple* example."
|
|
||||||
.to_string(),
|
|
||||||
},
|
|
||||||
ModuleEntry {
|
|
||||||
name: "concat".to_string(),
|
|
||||||
docs: "Hello world, this is a **complicated** *very simple* example."
|
|
||||||
.to_string(),
|
|
||||||
},
|
|
||||||
ModuleEntry {
|
|
||||||
name: "join".to_string(),
|
|
||||||
docs: "Hello world, this is a **complicated** *very simple* example."
|
|
||||||
.to_string(),
|
|
||||||
},
|
|
||||||
ModuleEntry {
|
|
||||||
name: "split".to_string(),
|
|
||||||
docs: "Hello world, this is a **complicated** *very simple* example."
|
|
||||||
.to_string(),
|
|
||||||
},
|
|
||||||
ModuleEntry {
|
|
||||||
name: "countGraphemes".to_string(),
|
|
||||||
docs: "Hello world, this is a **complicated** *very simple* example."
|
|
||||||
.to_string(),
|
|
||||||
},
|
|
||||||
ModuleEntry {
|
|
||||||
name: "foldGraphemes".to_string(),
|
|
||||||
docs: "Hello world, this is a **complicated** *very simple* example."
|
|
||||||
.to_string(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
Module {
|
|
||||||
name: "Bool".to_string(),
|
|
||||||
docs: "Hello world, this is a **complicated** *very simple* example.".to_string(),
|
|
||||||
entries: vec![
|
|
||||||
ModuleEntry {
|
|
||||||
name: "isEq".to_string(),
|
|
||||||
docs: "Hello world, this is a **complicated** *very simple* example."
|
|
||||||
.to_string(),
|
|
||||||
},
|
|
||||||
ModuleEntry {
|
|
||||||
name: "isNeq".to_string(),
|
|
||||||
docs: "Hello world, this is a **complicated** *very simple* example."
|
|
||||||
.to_string(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
Module {
|
|
||||||
name: "Num".to_string(),
|
|
||||||
docs: "Hello world, this is a **complicated** *very simple* example.".to_string(),
|
|
||||||
entries: vec![
|
|
||||||
ModuleEntry {
|
|
||||||
name: "add".to_string(),
|
|
||||||
docs: "Hello world, this is a **complicated** *very simple* example."
|
|
||||||
.to_string(),
|
|
||||||
},
|
|
||||||
ModuleEntry {
|
|
||||||
name: "sub".to_string(),
|
|
||||||
docs: "Hello world, this is a **complicated** *very simple* example."
|
|
||||||
.to_string(),
|
|
||||||
},
|
|
||||||
ModuleEntry {
|
|
||||||
name: "mul".to_string(),
|
|
||||||
docs: "Hello world, this is a **complicated** *very simple* example."
|
|
||||||
.to_string(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
Module {
|
|
||||||
name: "List".to_string(),
|
|
||||||
docs: "Hello world, this is a **complicated** *very simple* example.".to_string(),
|
|
||||||
entries: vec![],
|
|
||||||
},
|
|
||||||
Module {
|
|
||||||
name: "Set".to_string(),
|
|
||||||
docs: "Hello world, this is a **complicated** *very simple* example.".to_string(),
|
|
||||||
entries: vec![],
|
|
||||||
},
|
|
||||||
Module {
|
|
||||||
name: "Map".to_string(),
|
|
||||||
docs: "Hello world, this is a **complicated** *very simple* example.".to_string(),
|
|
||||||
entries: vec![],
|
|
||||||
},
|
|
||||||
Module {
|
|
||||||
name: "Result".to_string(),
|
|
||||||
docs: "Hello world, this is a **complicated** *very simple* example.".to_string(),
|
|
||||||
entries: vec![],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Make sure the directories exists
|
// Remove old build folder
|
||||||
|
fs::remove_dir_all("./build")?;
|
||||||
|
|
||||||
|
// Make sure the output directories exists
|
||||||
fs::create_dir_all(format!("./build/{}/{}", package.name, package.version))?;
|
fs::create_dir_all(format!("./build/{}/{}", package.name, package.version))?;
|
||||||
|
|
||||||
// Register handlebar template
|
// Register handlebars template
|
||||||
let mut handlebars = handlebars::Handlebars::new();
|
let mut handlebars = handlebars::Handlebars::new();
|
||||||
assert!(handlebars
|
assert!(handlebars
|
||||||
.register_template_file("page", "./src/templates/page.hbs")
|
.register_template_file("page", "./src/templates/page.hbs")
|
||||||
.is_ok());
|
.is_ok());
|
||||||
|
|
||||||
let markdown_options = pulldown_cmark::Options::empty();
|
let markdown_options = pulldown_cmark::Options::all();
|
||||||
|
|
||||||
// Write each package's module docs
|
// Write each package's module docs
|
||||||
for module in &package.modules {
|
for module in &package.modules {
|
||||||
|
@ -198,11 +103,12 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|entry| {
|
.map(|entry| {
|
||||||
// Convert entry docs from markdown to html
|
// Convert entry docs from markdown to html
|
||||||
|
let mut entry_docs_html: String = String::new();
|
||||||
|
if let Some(docs) = entry.docs {
|
||||||
let entry_docs_parser =
|
let entry_docs_parser =
|
||||||
pulldown_cmark::Parser::new_ext(&entry.docs, markdown_options);
|
pulldown_cmark::Parser::new_ext(&docs, markdown_options);
|
||||||
let mut entry_docs_html: String =
|
|
||||||
String::with_capacity(entry.docs.len() * 3 / 2);
|
|
||||||
pulldown_cmark::html::push_html(&mut entry_docs_html, entry_docs_parser);
|
pulldown_cmark::html::push_html(&mut entry_docs_html, entry_docs_parser);
|
||||||
|
}
|
||||||
|
|
||||||
ModuleEntry {
|
ModuleEntry {
|
||||||
name: entry.name.clone(),
|
name: entry.name.clone(),
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
app Hello provides [ main ] imports []
|
app Hello provides [ main ] imports []
|
||||||
|
|
||||||
greeting =
|
greeting =
|
||||||
hi = "Hello, World!!!!!!!!!!!!!"
|
hi = "Hello"
|
||||||
|
name = "World"
|
||||||
|
|
||||||
hi
|
"\(hi), \(name)!!!!!!!!!!!!!"
|
||||||
|
|
||||||
main = greeting
|
main = greeting
|
||||||
|
|
|
@ -12,6 +12,10 @@ To run in release mode instead, do:
|
||||||
$ cargo run --release run Hello.roc
|
$ cargo run --release run Hello.roc
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
If you encounter `cannot find -lc++`, run the following for ubuntu `sudo apt install libc++-dev`.
|
||||||
|
|
||||||
## Design Notes
|
## Design Notes
|
||||||
|
|
||||||
This demonstrates the basic design of hosts: Roc code gets compiled into a pure
|
This demonstrates the basic design of hosts: Roc code gets compiled into a pure
|
||||||
|
|
|
@ -11,3 +11,7 @@ To run in release mode instead, do:
|
||||||
```bash
|
```bash
|
||||||
$ cargo run --release run Quicksort.roc
|
$ cargo run --release run Quicksort.roc
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
If you encounter `cannot find -lc++`, run the following for ubuntu `sudo apt install libc++-dev`.
|
12
roc_std/Cargo.toml
Normal file
12
roc_std/Cargo.toml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
[package]
|
||||||
|
name = "roc_std"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Richard Feldman <oss@rtfeldman.com>"]
|
||||||
|
repository = "https://github.com/rtfeldman/roc"
|
||||||
|
readme = "README.md"
|
||||||
|
edition = "2018"
|
||||||
|
description = "Rust representations of Roc data structures"
|
||||||
|
license = "Apache-2.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libc = "0.2"
|
216
roc_std/src/lib.rs
Normal file
216
roc_std/src/lib.rs
Normal file
|
@ -0,0 +1,216 @@
|
||||||
|
#![crate_type = "lib"]
|
||||||
|
#![no_std]
|
||||||
|
use core::fmt;
|
||||||
|
|
||||||
|
// A list of C functions that are being imported
|
||||||
|
extern "C" {
|
||||||
|
pub fn printf(format: *const u8, ...) -> i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
const REFCOUNT_1: usize = isize::MIN as usize;
|
||||||
|
|
||||||
|
//#[macro_export]
|
||||||
|
//macro_rules! roclist {
|
||||||
|
// () => (
|
||||||
|
// $crate::RocList::empty()
|
||||||
|
// );
|
||||||
|
// ($($x:expr),+ $(,)?) => (
|
||||||
|
// $crate::RocList::from_slice(&[$($x),+])
|
||||||
|
// );
|
||||||
|
//}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct RocList<T> {
|
||||||
|
elements: *mut T,
|
||||||
|
length: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum Storage {
|
||||||
|
ReadOnly,
|
||||||
|
Refcounted(usize),
|
||||||
|
Capacity(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> RocList<T> {
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.length
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.length == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn empty() -> Self {
|
||||||
|
RocList {
|
||||||
|
length: 0,
|
||||||
|
elements: core::ptr::null_mut(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, index: usize) -> Option<&T> {
|
||||||
|
if index < self.len() {
|
||||||
|
Some(unsafe {
|
||||||
|
let raw = self.elements.add(index);
|
||||||
|
|
||||||
|
&*raw
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn storage(&self) -> Option<Storage> {
|
||||||
|
use core::cmp::Ordering::*;
|
||||||
|
|
||||||
|
if self.length == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let value = *self.get_storage_ptr();
|
||||||
|
|
||||||
|
// NOTE doesn't work with elements of 16 or more bytes
|
||||||
|
match usize::cmp(&0, &value) {
|
||||||
|
Equal => Some(Storage::ReadOnly),
|
||||||
|
Less => Some(Storage::Refcounted(value)),
|
||||||
|
Greater => Some(Storage::Capacity(value)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_storage_ptr(&self) -> *const usize {
|
||||||
|
let ptr = self.elements as *const usize;
|
||||||
|
|
||||||
|
unsafe { ptr.offset(-1) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_storage_ptr_mut(&mut self) -> *mut usize {
|
||||||
|
self.get_storage_ptr() as *mut usize
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_element_ptr<Q>(elements: *const Q) -> *const usize {
|
||||||
|
let elem_alignment = core::mem::align_of::<T>();
|
||||||
|
let ptr = elements as *const usize;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
if elem_alignment <= core::mem::align_of::<usize>() {
|
||||||
|
ptr.offset(1)
|
||||||
|
} else {
|
||||||
|
// If elements have an alignment bigger than usize (e.g. an i128),
|
||||||
|
// we will have necessarily allocated two usize slots worth of
|
||||||
|
// space for the storage value (with the first usize slot being
|
||||||
|
// padding for alignment's sake), and we need to skip past both.
|
||||||
|
ptr.offset(2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_slice_with_capacity(slice: &[T], capacity: usize) -> RocList<T>
|
||||||
|
where
|
||||||
|
T: Copy,
|
||||||
|
{
|
||||||
|
assert!(slice.len() <= capacity);
|
||||||
|
|
||||||
|
let ptr = slice.as_ptr();
|
||||||
|
let element_bytes = capacity * core::mem::size_of::<T>();
|
||||||
|
|
||||||
|
let padding = {
|
||||||
|
if core::mem::align_of::<T>() <= core::mem::align_of::<usize>() {
|
||||||
|
// aligned on usize (8 bytes on 64-bit systems)
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
// aligned on 2*usize (16 bytes on 64-bit systems)
|
||||||
|
core::mem::size_of::<usize>()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let num_bytes = core::mem::size_of::<usize>() + padding + element_bytes;
|
||||||
|
|
||||||
|
let elements = unsafe {
|
||||||
|
let raw_ptr = libc::malloc(num_bytes);
|
||||||
|
|
||||||
|
// write the capacity
|
||||||
|
let capacity_ptr = raw_ptr as *mut usize;
|
||||||
|
*capacity_ptr = capacity;
|
||||||
|
|
||||||
|
let raw_ptr = Self::get_element_ptr(raw_ptr as *mut T);
|
||||||
|
|
||||||
|
{
|
||||||
|
// NOTE: using a memcpy here causes weird issues
|
||||||
|
let target_ptr = raw_ptr as *mut T;
|
||||||
|
let source_ptr = ptr as *const T;
|
||||||
|
let length = slice.len() as isize;
|
||||||
|
for index in 0..length {
|
||||||
|
*target_ptr.offset(index) = *source_ptr.offset(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
raw_ptr as *mut T
|
||||||
|
};
|
||||||
|
|
||||||
|
RocList {
|
||||||
|
length: slice.len(),
|
||||||
|
elements,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_slice(slice: &[T]) -> RocList<T>
|
||||||
|
where
|
||||||
|
T: Copy,
|
||||||
|
{
|
||||||
|
Self::from_slice_with_capacity(slice, slice.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_slice(&self) -> &[T] {
|
||||||
|
unsafe { core::slice::from_raw_parts(self.elements, self.length) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: fmt::Debug> fmt::Debug for RocList<T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
// RocList { storage: Refcounted(3), elements: [ 1,2,3,4] }
|
||||||
|
f.debug_struct("RocList")
|
||||||
|
.field("storage", &self.storage())
|
||||||
|
.field("elements", &self.as_slice())
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: PartialEq> PartialEq for RocList<T> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
if self.length != other.length {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..(self.length as isize) {
|
||||||
|
unsafe {
|
||||||
|
if *self.elements.offset(i) != *other.elements.offset(i) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Eq> Eq for RocList<T> {}
|
||||||
|
|
||||||
|
impl<T> Drop for RocList<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
use Storage::*;
|
||||||
|
match self.storage() {
|
||||||
|
None | Some(ReadOnly) => {}
|
||||||
|
Some(Capacity(_)) | Some(Refcounted(REFCOUNT_1)) => unsafe {
|
||||||
|
libc::free(self.get_storage_ptr() as *mut libc::c_void);
|
||||||
|
},
|
||||||
|
Some(Refcounted(rc)) => {
|
||||||
|
let sptr = self.get_storage_ptr_mut();
|
||||||
|
unsafe {
|
||||||
|
*sptr = rc - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,9 +11,9 @@ let
|
||||||
in { pkgs ? pinnedPkgs }:
|
in { pkgs ? pinnedPkgs }:
|
||||||
|
|
||||||
let
|
let
|
||||||
isOsX = builtins.currentSystem == "x86_64-darwin";
|
isMacOS = builtins.currentSystem == "x86_64-darwin";
|
||||||
darwin-frameworks =
|
darwin-frameworks =
|
||||||
if isOsX then
|
if isMacOS then
|
||||||
with pkgs.darwin.apple_sdk.frameworks; [
|
with pkgs.darwin.apple_sdk.frameworks; [
|
||||||
AppKit
|
AppKit
|
||||||
CoreFoundation
|
CoreFoundation
|
||||||
|
@ -26,6 +26,7 @@ let
|
||||||
else
|
else
|
||||||
[ ];
|
[ ];
|
||||||
llvm = pkgs.llvm_10;
|
llvm = pkgs.llvm_10;
|
||||||
|
lld = pkgs.lld_10; # this should match llvm's version
|
||||||
inputs =
|
inputs =
|
||||||
[
|
[
|
||||||
pkgs.rustup
|
pkgs.rustup
|
||||||
|
@ -35,6 +36,8 @@ let
|
||||||
pkgs.libffi
|
pkgs.libffi
|
||||||
pkgs.libxml2
|
pkgs.libxml2
|
||||||
pkgs.zlib
|
pkgs.zlib
|
||||||
|
# faster builds - see https://github.com/rtfeldman/roc/blob/trunk/BUILDING_FROM_SOURCE.md#use-lld-for-the-linker
|
||||||
|
lld
|
||||||
];
|
];
|
||||||
in pkgs.mkShell {
|
in pkgs.mkShell {
|
||||||
buildInputs = inputs ++ darwin-frameworks;
|
buildInputs = inputs ++ darwin-frameworks;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue