Merge branch 'trunk' of github.com:rtfeldman/roc into str-split

This commit is contained in:
Chad Stearns 2020-10-03 00:21:37 -04:00
commit d5dd03313a
62 changed files with 3436 additions and 678 deletions

View file

@ -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
View file

@ -1,3 +1,6 @@
target target
.direnv .direnv
*.rs.bk *.rs.bk
#valgrind
vgcore.*

View file

@ -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
View file

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

View file

@ -26,6 +26,7 @@ members = [
"vendor/pretty", "vendor/pretty",
"editor", "editor",
"cli", "cli",
"roc_std",
"docs" "docs"
] ]

View file

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

View file

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

View file

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

View 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

View file

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

View 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
}
}

View file

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

View file

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

View file

@ -1,5 +1,5 @@
interface Map interface Map2
exposes [ Map, isEmpty ] exposes [ isEmpty, map ]
imports [] imports []
isEmpty : Map * * -> Bool isEmpty : Map * * -> Bool

View file

@ -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 []

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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" }

View file

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

View file

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

View file

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

View file

@ -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,51 +441,202 @@ 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);
// TODO what would be most optimial for the branch predictor
//
// 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(then_block);
if !env.leak {
builder.build_free(refcount_ptr);
}
builder.build_unconditional_branch(cont_block);
} }
// build else block fn build_inc_str_help<'a, 'ctx, 'env>(
{ env: &Env<'a, 'ctx, 'env>,
builder.position_at_end(else_block); _layout_ids: &mut LayoutIds<'a>,
// our refcount 0 is actually usize::MAX, so decrementing the refcount means incrementing this value. layout: &Layout<'a>,
let decremented = env.builder.build_int_add( fn_val: FunctionValue<'ctx>,
ctx.i64_type().const_int(1 as u64, false), ) {
refcount, let builder = env.builder;
"decremented_refcount", 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),
); );
// 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);
builder.position_at_end(cont_block);
// this function returns void
builder.build_return(None);
} }
// emit merge block pub fn build_dec_str<'a, 'ctx, 'env>(
builder.position_at_end(cont_block); 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 // this function returns void
builder.build_return(None); builder.build_return(None);
@ -487,29 +644,40 @@ fn build_dec_list_help<'a, 'ctx, 'env>(
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",
) )
} }

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

@ -44,6 +44,7 @@ pub enum LowLevel {
NumPowInt, NumPowInt,
NumFloor, NumFloor,
NumIsFinite, NumIsFinite,
NumAtan,
Eq, Eq,
NotEq, NotEq,
And, And,

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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 => (),

View file

@ -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,6 +169,8 @@ impl fmt::Debug for Type {
arg.fmt(f)?; arg.fmt(f)?;
} }
write!(f, " -")?;
closure.fmt(f)?;
write!(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 {

View file

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

View file

@ -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
View file

@ -0,0 +1 @@
/build

View file

@ -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" }

View file

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

View file

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

View file

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

View file

@ -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
View 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
View 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;
}
}
}
}
}

View file

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