mirror of
https://github.com/roc-lang/roc.git
synced 2024-11-11 05:34:11 +03:00
Merge branch 'trunk' of github.com:rtfeldman/roc into dict
This commit is contained in:
commit
c7f6de2afe
@ -8,7 +8,7 @@ To build the compiler, you need these installed:
|
||||
* `libunwind` (macOS should already have this one installed)
|
||||
* `libc++-dev`
|
||||
* Python 2.7 (Windows only), `python-is-python3` (Ubuntu)
|
||||
* a particular version of Zig (see below)
|
||||
* [Zig](https://ziglang.org/) 0.7.1 or greater
|
||||
* a particular version of LLVM (see below)
|
||||
|
||||
To run the test suite (via `cargo test`), you additionally need to install:
|
||||
@ -24,32 +24,10 @@ MacOS systems should already have `libunwind`, but other systems will need to in
|
||||
Some systems may already have `libc++-dev` on them, but if not, you may need to install it. (On Ubuntu, this can be done with `sudo apt-get install libc++-dev`.)
|
||||
|
||||
### Zig
|
||||
We use a specific version of Zig, a build off the the commit `0088efc4b`. The latest tagged version of Zig, 0.6.0, doesn't include the feature to emit LLVM ir, which is a core feature of how we use Zig. To download this specific version, you can:
|
||||
* use the following commands on Debian/Ubuntu (on other distros, steps should be essentially the same):
|
||||
```
|
||||
cd /tmp
|
||||
# download the files
|
||||
wget https://ziglang.org/builds/zig-linux-x86_64-0.6.0+0088efc4b.tar.xz
|
||||
# uncompress:
|
||||
xz -d zig-linux-x86_64-0.6.0+0088efc4b.tar.xz
|
||||
# untar:
|
||||
tar xvf zig-linux-x86_64-0.6.0+0088efc4b.tar
|
||||
# move the files into /opt:
|
||||
sudo mkdir -p /opt/zig
|
||||
sudo mv zig-linux-x86_64-0.6.0+0088efc4b/* /opt/zig/
|
||||
```
|
||||
Then add `/opt/zig/` to your `PATH` (e.g. in `~/.bashrc`).
|
||||
|
||||
Reload your `.bashrc` file: `source ~/.bashrc` and test that `zig` is
|
||||
an available command.
|
||||
|
||||
* [macOS](https://ziglang.org/builds/zig-macos-x86_64-0.6.0+0088efc4b.tar.xz)
|
||||
|
||||
Alternatively, any recent master branch build should work. To install the latest master branch build you can use:
|
||||
* `brew install zig --HEAD` (on macos)
|
||||
* `snap install zig --classic --edge` (on ubunutu)
|
||||
|
||||
Once 0.7.0 is released, we'll switch back to installing the tagged releases and this process will get easier.
|
||||
If you're on MacOS, you can install with `brew install zig`
|
||||
If you're on Ubuntu and use Snap, you can install with `snap install zig --classic --beta`
|
||||
For any other OS, checkout the [Zig installation page](https://github.com/ziglang/zig/wiki/Install-Zig-from-a-Package-Manager)
|
||||
|
||||
### LLVM
|
||||
|
||||
|
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -2672,6 +2672,8 @@ dependencies = [
|
||||
"log",
|
||||
"maplit",
|
||||
"page_size",
|
||||
"pest",
|
||||
"pest_derive",
|
||||
"pretty_assertions",
|
||||
"quickcheck",
|
||||
"quickcheck_macros",
|
||||
|
@ -29,6 +29,7 @@ pub fn build_file(
|
||||
) -> Result<PathBuf, LoadingProblem> {
|
||||
let compilation_start = SystemTime::now();
|
||||
let arena = Bump::new();
|
||||
let ptr_bytes = target.pointer_width().unwrap().bytes() as u32;
|
||||
|
||||
// Step 1: compile the app and generate the .o file
|
||||
let subs_by_module = MutMap::default();
|
||||
@ -36,14 +37,15 @@ pub fn build_file(
|
||||
// Release builds use uniqueness optimizations
|
||||
let stdlib = match opt_level {
|
||||
OptLevel::Normal => roc_builtins::std::standard_stdlib(),
|
||||
OptLevel::Optimize => roc_builtins::unique::uniq_stdlib(),
|
||||
OptLevel::Optimize => roc_builtins::std::standard_stdlib(),
|
||||
};
|
||||
let loaded = roc_load::file::load_and_monomorphize(
|
||||
&arena,
|
||||
roc_file_path.clone(),
|
||||
stdlib,
|
||||
&stdlib,
|
||||
src_dir.as_path(),
|
||||
subs_by_module,
|
||||
ptr_bytes,
|
||||
)?;
|
||||
|
||||
let path_to_platform = loaded.platform_path.clone();
|
||||
|
@ -72,6 +72,9 @@ fn jit_to_ast_help<'a>(
|
||||
run_jit_function!(lib, main_fn_name, u8, |num| byte_to_ast(env, num, content)),
|
||||
)
|
||||
}
|
||||
Layout::Builtin(Builtin::Usize) => Ok(run_jit_function!(lib, main_fn_name, usize, |num| {
|
||||
num_to_ast(env, nat_to_ast(env.arena, num), content)
|
||||
})),
|
||||
Layout::Builtin(Builtin::Int64) => {
|
||||
Ok(run_jit_function!(lib, main_fn_name, i64, |num| num_to_ast(
|
||||
env,
|
||||
@ -161,7 +164,10 @@ fn jit_to_ast_help<'a>(
|
||||
|
||||
let size = layout.stack_size(env.ptr_bytes);
|
||||
match union_variant {
|
||||
UnionVariant::Wrapped(tags_and_layouts) => {
|
||||
UnionVariant::Wrapped {
|
||||
sorted_tag_layouts: tags_and_layouts,
|
||||
..
|
||||
} => {
|
||||
Ok(run_jit_function_dynamic_type!(
|
||||
lib,
|
||||
main_fn_name,
|
||||
@ -242,6 +248,11 @@ fn ptr_to_ast<'a>(
|
||||
|
||||
num_to_ast(env, i64_to_ast(env.arena, num), content)
|
||||
}
|
||||
Layout::Builtin(Builtin::Usize) => {
|
||||
let num = unsafe { *(ptr as *const usize) };
|
||||
|
||||
num_to_ast(env, nat_to_ast(env.arena, num), content)
|
||||
}
|
||||
Layout::Builtin(Builtin::Int1) => {
|
||||
// TODO: bits are not as expected here.
|
||||
// num is always false at the moment.
|
||||
@ -812,6 +823,12 @@ fn num_to_ast<'a>(env: &Env<'a, '_>, num_expr: Expr<'a>, content: &Content) -> E
|
||||
}
|
||||
}
|
||||
|
||||
/// This is centralized in case we want to format it differently later,
|
||||
/// e.g. adding underscores for large numbers
|
||||
fn nat_to_ast(arena: &Bump, num: usize) -> Expr<'_> {
|
||||
Expr::Num(arena.alloc(format!("{}", num)))
|
||||
}
|
||||
|
||||
/// This is centralized in case we want to format it differently later,
|
||||
/// e.g. adding underscores for large numbers
|
||||
fn i64_to_ast(arena: &Bump, num: i64) -> Expr<'_> {
|
||||
|
@ -35,14 +35,17 @@ pub fn gen_and_eval(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<R
|
||||
|
||||
let module_src = promote_expr_to_module(src_str);
|
||||
|
||||
let ptr_bytes = target.pointer_width().unwrap().bytes() as u32;
|
||||
|
||||
let exposed_types = MutMap::default();
|
||||
let loaded = roc_load::file::load_and_monomorphize_from_str(
|
||||
&arena,
|
||||
filename,
|
||||
&module_src,
|
||||
stdlib,
|
||||
&stdlib,
|
||||
src_dir,
|
||||
exposed_types,
|
||||
ptr_bytes,
|
||||
);
|
||||
|
||||
let mut loaded = loaded.expect("failed to load module");
|
||||
|
@ -54,22 +54,22 @@ mod repl_eval {
|
||||
|
||||
#[test]
|
||||
fn literal_0x0() {
|
||||
expect_success("0x0", "0 : I64");
|
||||
expect_success("0x0", "0 : Int *");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn literal_0x42() {
|
||||
expect_success("0x42", "66 : I64");
|
||||
expect_success("0x42", "66 : Int *");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn literal_0point0() {
|
||||
expect_success("0.0", "0 : F64");
|
||||
expect_success("0.0", "0 : Float *");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn literal_4point2() {
|
||||
expect_success("4.2", "4.2 : F64");
|
||||
expect_success("4.2", "4.2 : Float *");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -89,7 +89,7 @@ mod repl_eval {
|
||||
|
||||
#[test]
|
||||
fn num_rem() {
|
||||
expect_success("299 % 10", "Ok 9 : Result I64 [ DivByZero ]*");
|
||||
expect_success("299 % 10", "Ok 9 : Result (Int *) [ DivByZero ]*");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -153,7 +153,7 @@ mod repl_eval {
|
||||
#[test]
|
||||
fn single_element_tag_union() {
|
||||
expect_success("True 1", "True 1 : [ True (Num *) ]*");
|
||||
expect_success("Foo 1 3.14", "Foo 1 3.14 : [ Foo (Num *) F64 ]*");
|
||||
expect_success("Foo 1 3.14", "Foo 1 3.14 : [ Foo (Num *) (Float *) ]*");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -162,7 +162,7 @@ mod repl_eval {
|
||||
|
||||
expect_success(
|
||||
"if 1 == 1 then True 3 else False 3.14",
|
||||
"True 3 : [ False F64, True (Num *) ]*",
|
||||
"True 3 : [ False (Float *), True (Num *) ]*",
|
||||
)
|
||||
}
|
||||
|
||||
@ -191,7 +191,7 @@ mod repl_eval {
|
||||
|
||||
#[test]
|
||||
fn str_count_graphemes() {
|
||||
expect_success("Str.countGraphemes \"å🤔\"", "2 : I64");
|
||||
expect_success("Str.countGraphemes \"å🤔\"", "2 : Nat");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -206,12 +206,12 @@ mod repl_eval {
|
||||
|
||||
#[test]
|
||||
fn literal_int_list() {
|
||||
expect_success("[ 0x1, 0x2, 0x3 ]", "[ 1, 2, 3 ] : List I64");
|
||||
expect_success("[ 0x1, 0x2, 0x3 ]", "[ 1, 2, 3 ] : List (Int *)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn literal_float_list() {
|
||||
expect_success("[ 1.1, 2.2, 3.3 ]", "[ 1.1, 2.2, 3.3 ] : List F64");
|
||||
expect_success("[ 1.1, 2.2, 3.3 ]", "[ 1.1, 2.2, 3.3 ] : List (Float *)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -253,26 +253,37 @@ mod repl_eval {
|
||||
|
||||
#[test]
|
||||
fn num_bitwise_and() {
|
||||
expect_success("Num.bitwiseAnd 20 20", "20 : I64");
|
||||
expect_success("Num.bitwiseAnd 20 20", "20 : Int *");
|
||||
|
||||
expect_success("Num.bitwiseAnd 25 10", "8 : I64");
|
||||
expect_success("Num.bitwiseAnd 25 10", "8 : Int *");
|
||||
|
||||
expect_success("Num.bitwiseAnd 200 0", "0 : I64")
|
||||
expect_success("Num.bitwiseAnd 200 0", "0 : Int *")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn num_bitwise_xor() {
|
||||
expect_success("Num.bitwiseXor 20 20", "0 : Int *");
|
||||
|
||||
expect_success("Num.bitwiseXor 15 14", "1 : Int *");
|
||||
|
||||
expect_success("Num.bitwiseXor 7 15", "8 : Int *");
|
||||
|
||||
expect_success("Num.bitwiseXor 200 0", "200 : Int *")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn num_add_wrap() {
|
||||
expect_success("Num.addWrap Num.maxInt 1", "-9223372036854775808 : I64");
|
||||
expect_success("Num.addWrap Num.maxInt 1", "-9223372036854775808 : Int *");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn num_sub_wrap() {
|
||||
expect_success("Num.subWrap Num.minInt 1", "9223372036854775807 : I64");
|
||||
expect_success("Num.subWrap Num.minInt 1", "9223372036854775807 : Int *");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn num_mul_wrap() {
|
||||
expect_success("Num.mulWrap Num.maxInt 2", "-2 : I64");
|
||||
expect_success("Num.mulWrap Num.maxInt 2", "-2 : Int *");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -309,7 +320,7 @@ mod repl_eval {
|
||||
fn list_concat() {
|
||||
expect_success(
|
||||
"List.concat [ 1.1, 2.2 ] [ 3.3, 4.4, 5.5 ]",
|
||||
"[ 1.1, 2.2, 3.3, 4.4, 5.5 ] : List F64",
|
||||
"[ 1.1, 2.2, 3.3, 4.4, 5.5 ] : List (Float *)",
|
||||
);
|
||||
}
|
||||
|
||||
@ -368,7 +379,7 @@ mod repl_eval {
|
||||
fn basic_1_field_f64_record() {
|
||||
// Even though this gets unwrapped at runtime, the repl should still
|
||||
// report it as a record
|
||||
expect_success("{ foo: 4.2 }", "{ foo: 4.2 } : { foo : F64 }");
|
||||
expect_success("{ foo: 4.2 }", "{ foo: 4.2 } : { foo : Float * }");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -387,7 +398,7 @@ mod repl_eval {
|
||||
// report it as a record
|
||||
expect_success(
|
||||
"{ foo: { bar: { baz: 4.2 } } }",
|
||||
"{ foo: { bar: { baz: 4.2 } } } : { foo : { bar : { baz : F64 } } }",
|
||||
"{ foo: { bar: { baz: 4.2 } } } : { foo : { bar : { baz : Float * } } }",
|
||||
);
|
||||
}
|
||||
|
||||
@ -395,7 +406,7 @@ mod repl_eval {
|
||||
fn basic_2_field_i64_record() {
|
||||
expect_success(
|
||||
"{ foo: 0x4, bar: 0x2 }",
|
||||
"{ bar: 2, foo: 4 } : { bar : I64, foo : I64 }",
|
||||
"{ bar: 2, foo: 4 } : { bar : Int *, foo : Int * }",
|
||||
);
|
||||
}
|
||||
|
||||
@ -403,7 +414,7 @@ mod repl_eval {
|
||||
fn basic_2_field_f64_record() {
|
||||
expect_success(
|
||||
"{ foo: 4.1, bar: 2.3 }",
|
||||
"{ bar: 2.3, foo: 4.1 } : { bar : F64, foo : F64 }",
|
||||
"{ bar: 2.3, foo: 4.1 } : { bar : Float *, foo : Float * }",
|
||||
);
|
||||
}
|
||||
|
||||
@ -411,7 +422,7 @@ mod repl_eval {
|
||||
fn basic_2_field_mixed_record() {
|
||||
expect_success(
|
||||
"{ foo: 4.1, bar: 2 }",
|
||||
"{ bar: 2, foo: 4.1 } : { bar : Num *, foo : F64 }",
|
||||
"{ bar: 2, foo: 4.1 } : { bar : Num *, foo : Float * }",
|
||||
);
|
||||
}
|
||||
|
||||
@ -419,7 +430,7 @@ mod repl_eval {
|
||||
fn basic_3_field_record() {
|
||||
expect_success(
|
||||
"{ foo: 4.1, bar: 2, baz: 0x5 }",
|
||||
"{ bar: 2, baz: 5, foo: 4.1 } : { bar : Num *, baz : I64, foo : F64 }",
|
||||
"{ bar: 2, baz: 5, foo: 4.1 } : { bar : Num *, baz : Int *, foo : Float * }",
|
||||
);
|
||||
}
|
||||
|
||||
@ -434,7 +445,7 @@ mod repl_eval {
|
||||
fn list_of_2_field_records() {
|
||||
expect_success(
|
||||
"[ { foo: 4.1, bar: 2 } ]",
|
||||
"[ { bar: 2, foo: 4.1 } ] : List { bar : Num *, foo : F64 }",
|
||||
"[ { bar: 2, foo: 4.1 } ] : List { bar : Num *, foo : Float * }",
|
||||
);
|
||||
}
|
||||
|
||||
@ -466,7 +477,7 @@ mod repl_eval {
|
||||
fn list_of_3_field_records() {
|
||||
expect_success(
|
||||
"[ { foo: 4.1, bar: 2, baz: 0x3 } ]",
|
||||
"[ { bar: 2, baz: 3, foo: 4.1 } ] : List { bar : Num *, baz : I64, foo : F64 }",
|
||||
"[ { bar: 2, baz: 3, foo: 4.1 } ] : List { bar : Num *, baz : Int *, foo : Float * }",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,7 @@ comptime {
|
||||
exportStrFn(str.strConcatC, "concat");
|
||||
exportStrFn(str.strNumberOfBytes, "number_of_bytes");
|
||||
exportStrFn(str.strFromIntC, "from_int");
|
||||
exportStrFn(str.strEqual, "equal");
|
||||
}
|
||||
|
||||
// Export helpers - Must be run inside a comptime
|
||||
|
@ -260,6 +260,11 @@ pub const RocStr = extern struct {
|
||||
}
|
||||
};
|
||||
|
||||
// Str.equal
|
||||
pub fn strEqual(self: RocStr, other: RocStr) callconv(.C) bool {
|
||||
return self.eq(other);
|
||||
}
|
||||
|
||||
// Str.numberOfBytes
|
||||
pub fn strNumberOfBytes(string: RocStr) callconv(.C) usize {
|
||||
return string.len();
|
||||
|
@ -2,14 +2,15 @@ use std::fs::File;
|
||||
use std::io::prelude::Read;
|
||||
use std::vec::Vec;
|
||||
|
||||
const PATH: &str = env!(
|
||||
"BUILTINS_BC",
|
||||
"Env var BUILTINS_BC not found. Is there a problem with the build script?"
|
||||
);
|
||||
|
||||
pub fn get_bytes() -> Vec<u8> {
|
||||
// In the build script for the builtins module, we compile the builtins bitcode and set
|
||||
// BUILTINS_BC to the path to the compiled output.
|
||||
let path: &'static str = env!(
|
||||
"BUILTINS_BC",
|
||||
"Env var BUILTINS_BC not found. Is there a problem with the build script?"
|
||||
);
|
||||
let mut builtins_bitcode = File::open(path).expect("Unable to find builtins bitcode source");
|
||||
let mut builtins_bitcode = File::open(PATH).expect("Unable to find builtins bitcode source");
|
||||
let mut buffer = Vec::new();
|
||||
builtins_bitcode
|
||||
.read_to_end(&mut buffer)
|
||||
@ -31,3 +32,4 @@ pub const STR_STARTS_WITH: &str = "roc_builtins.str.starts_with";
|
||||
pub const STR_ENDS_WITH: &str = "roc_builtins.str.ends_with";
|
||||
pub const STR_NUMBER_OF_BYTES: &str = "roc_builtins.str.number_of_bytes";
|
||||
pub const STR_FROM_INT: &str = "roc_builtins.str.from_int";
|
||||
pub const STR_EQUAL: &str = "roc_builtins.str.equal";
|
||||
|
@ -3,8 +3,8 @@ use roc_module::ident::TagName;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_region::all::Region;
|
||||
use roc_types::builtin_aliases::{
|
||||
bool_type, dict_type, float_type, int_type, list_type, num_type, ordering_type, result_type,
|
||||
set_type, str_type,
|
||||
bool_type, dict_type, float_type, int_type, list_type, nat_type, num_type, ordering_type,
|
||||
result_type, set_type, str_type,
|
||||
};
|
||||
use roc_types::solved_types::SolvedType;
|
||||
use roc_types::subs::VarId;
|
||||
@ -91,10 +91,13 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||
),
|
||||
);
|
||||
|
||||
// addWrap : Int, Int -> Int
|
||||
// addWrap : Int range, Int range -> Int range
|
||||
add_type(
|
||||
Symbol::NUM_ADD_WRAP,
|
||||
top_level_function(vec![int_type(), int_type()], Box::new(int_type())),
|
||||
top_level_function(
|
||||
vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))],
|
||||
Box::new(int_type(flex(TVAR1))),
|
||||
),
|
||||
);
|
||||
|
||||
// sub or (-) : Num a, Num a -> Num a
|
||||
@ -106,10 +109,13 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||
),
|
||||
);
|
||||
|
||||
// subWrap : Int, Int -> Int
|
||||
// subWrap : Int range, Int range -> Int range
|
||||
add_type(
|
||||
Symbol::NUM_SUB_WRAP,
|
||||
top_level_function(vec![int_type(), int_type()], Box::new(int_type())),
|
||||
top_level_function(
|
||||
vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))],
|
||||
Box::new(int_type(flex(TVAR1))),
|
||||
),
|
||||
);
|
||||
|
||||
// subChecked : Num a, Num a -> Result (Num a) [ Overflow ]*
|
||||
@ -130,10 +136,13 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||
),
|
||||
);
|
||||
|
||||
// mulWrap : Int, Int -> Int
|
||||
// mulWrap : Int range, Int range -> Int range
|
||||
add_type(
|
||||
Symbol::NUM_MUL_WRAP,
|
||||
top_level_function(vec![int_type(), int_type()], Box::new(int_type())),
|
||||
top_level_function(
|
||||
vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))],
|
||||
Box::new(int_type(flex(TVAR1))),
|
||||
),
|
||||
);
|
||||
|
||||
// mulChecked : Num a, Num a -> Result (Num a) [ Overflow ]*
|
||||
@ -214,10 +223,13 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||
),
|
||||
);
|
||||
|
||||
// toFloat : Num a -> Float
|
||||
// toFloat : Num * -> Float *
|
||||
add_type(
|
||||
Symbol::NUM_TO_FLOAT,
|
||||
top_level_function(vec![num_type(flex(TVAR1))], Box::new(float_type())),
|
||||
top_level_function(
|
||||
vec![num_type(flex(TVAR1))],
|
||||
Box::new(float_type(flex(TVAR2))),
|
||||
),
|
||||
);
|
||||
|
||||
// isNegative : Num a -> Bool
|
||||
@ -250,11 +262,11 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||
top_level_function(vec![num_type(flex(TVAR1))], Box::new(bool_type())),
|
||||
);
|
||||
|
||||
// maxInt : Int
|
||||
add_type(Symbol::NUM_MAX_INT, int_type());
|
||||
// maxInt : Int range
|
||||
add_type(Symbol::NUM_MAX_INT, int_type(flex(TVAR1)));
|
||||
|
||||
// minInt : Int
|
||||
add_type(Symbol::NUM_MIN_INT, int_type());
|
||||
// minInt : Int range
|
||||
add_type(Symbol::NUM_MIN_INT, int_type(flex(TVAR1)));
|
||||
|
||||
// div : Int, Int -> Result Int [ DivByZero ]*
|
||||
let div_by_zero = SolvedType::TagUnion(
|
||||
@ -265,56 +277,68 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||
add_type(
|
||||
Symbol::NUM_DIV_INT,
|
||||
top_level_function(
|
||||
vec![int_type(), int_type()],
|
||||
Box::new(result_type(int_type(), div_by_zero.clone())),
|
||||
vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))],
|
||||
Box::new(result_type(int_type(flex(TVAR1)), div_by_zero.clone())),
|
||||
),
|
||||
);
|
||||
|
||||
// bitwiseAnd : Int, Int -> Int
|
||||
// bitwiseAnd : Int a, Int a -> Int a
|
||||
add_type(
|
||||
Symbol::NUM_BITWISE_AND,
|
||||
top_level_function(vec![int_type(), int_type()], Box::new(int_type())),
|
||||
top_level_function(
|
||||
vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))],
|
||||
Box::new(int_type(flex(TVAR1))),
|
||||
),
|
||||
);
|
||||
|
||||
// rem : Int, Int -> Result Int [ DivByZero ]*
|
||||
// bitwiseXor : Int a, Int a -> Int a
|
||||
add_type(
|
||||
Symbol::NUM_BITWISE_XOR,
|
||||
top_level_function(
|
||||
vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))],
|
||||
Box::new(int_type(flex(TVAR1))),
|
||||
),
|
||||
);
|
||||
|
||||
// rem : Int a, Int a -> Result (Int a) [ DivByZero ]*
|
||||
add_type(
|
||||
Symbol::NUM_REM,
|
||||
top_level_function(
|
||||
vec![int_type(), int_type()],
|
||||
Box::new(result_type(int_type(), div_by_zero.clone())),
|
||||
vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))],
|
||||
Box::new(result_type(int_type(flex(TVAR1)), div_by_zero.clone())),
|
||||
),
|
||||
);
|
||||
|
||||
// mod : Int, Int -> Result Int [ DivByZero ]*
|
||||
// mod : Int a, Int a -> Result (Int a) [ DivByZero ]*
|
||||
add_type(
|
||||
Symbol::NUM_MOD_INT,
|
||||
top_level_function(
|
||||
vec![int_type(), int_type()],
|
||||
Box::new(result_type(int_type(), div_by_zero.clone())),
|
||||
vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))],
|
||||
Box::new(result_type(int_type(flex(TVAR1)), div_by_zero.clone())),
|
||||
),
|
||||
);
|
||||
|
||||
// Float module
|
||||
|
||||
// div : Float, Float -> Float
|
||||
// div : Float a, Float a -> Float a
|
||||
add_type(
|
||||
Symbol::NUM_DIV_FLOAT,
|
||||
top_level_function(
|
||||
vec![float_type(), float_type()],
|
||||
Box::new(result_type(float_type(), div_by_zero.clone())),
|
||||
vec![float_type(flex(TVAR1)), float_type(flex(TVAR1))],
|
||||
Box::new(result_type(float_type(flex(TVAR1)), div_by_zero.clone())),
|
||||
),
|
||||
);
|
||||
|
||||
// mod : Float, Float -> Result Int [ DivByZero ]*
|
||||
// mod : Float a, Float a -> Result (Float a) [ DivByZero ]*
|
||||
add_type(
|
||||
Symbol::NUM_MOD_FLOAT,
|
||||
top_level_function(
|
||||
vec![float_type(), float_type()],
|
||||
Box::new(result_type(float_type(), div_by_zero)),
|
||||
vec![float_type(flex(TVAR1)), float_type(flex(TVAR1))],
|
||||
Box::new(result_type(float_type(flex(TVAR1)), div_by_zero)),
|
||||
),
|
||||
);
|
||||
|
||||
// sqrt : Float -> Float
|
||||
// sqrt : Float a -> Float a
|
||||
let sqrt_of_negative = SolvedType::TagUnion(
|
||||
vec![(TagName::Global("SqrtOfNegative".into()), vec![])],
|
||||
Box::new(SolvedType::Wildcard),
|
||||
@ -323,81 +347,114 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||
add_type(
|
||||
Symbol::NUM_SQRT,
|
||||
top_level_function(
|
||||
vec![float_type()],
|
||||
Box::new(result_type(float_type(), sqrt_of_negative)),
|
||||
vec![float_type(flex(TVAR1))],
|
||||
Box::new(result_type(float_type(flex(TVAR1)), sqrt_of_negative)),
|
||||
),
|
||||
);
|
||||
|
||||
// round : Float -> Int
|
||||
// round : Float a -> Int b
|
||||
add_type(
|
||||
Symbol::NUM_ROUND,
|
||||
top_level_function(vec![float_type()], Box::new(int_type())),
|
||||
top_level_function(
|
||||
vec![float_type(flex(TVAR1))],
|
||||
Box::new(int_type(flex(TVAR2))),
|
||||
),
|
||||
);
|
||||
|
||||
// sin : Float -> Float
|
||||
// sin : Float a -> Float a
|
||||
add_type(
|
||||
Symbol::NUM_SIN,
|
||||
top_level_function(vec![float_type()], Box::new(float_type())),
|
||||
top_level_function(
|
||||
vec![float_type(flex(TVAR1))],
|
||||
Box::new(float_type(flex(TVAR1))),
|
||||
),
|
||||
);
|
||||
|
||||
// cos : Float -> Float
|
||||
// cos : Float a -> Float a
|
||||
add_type(
|
||||
Symbol::NUM_COS,
|
||||
top_level_function(vec![float_type()], Box::new(float_type())),
|
||||
top_level_function(
|
||||
vec![float_type(flex(TVAR1))],
|
||||
Box::new(float_type(flex(TVAR1))),
|
||||
),
|
||||
);
|
||||
|
||||
// tan : Float -> Float
|
||||
// tan : Float a -> Float a
|
||||
add_type(
|
||||
Symbol::NUM_TAN,
|
||||
top_level_function(vec![float_type()], Box::new(float_type())),
|
||||
top_level_function(
|
||||
vec![float_type(flex(TVAR1))],
|
||||
Box::new(float_type(flex(TVAR1))),
|
||||
),
|
||||
);
|
||||
|
||||
// maxFloat : Float
|
||||
add_type(Symbol::NUM_MAX_FLOAT, float_type());
|
||||
// maxFloat : Float a
|
||||
add_type(Symbol::NUM_MAX_FLOAT, float_type(flex(TVAR1)));
|
||||
|
||||
// minFloat : Float
|
||||
add_type(Symbol::NUM_MIN_FLOAT, float_type());
|
||||
// minFloat : Float a
|
||||
add_type(Symbol::NUM_MIN_FLOAT, float_type(flex(TVAR1)));
|
||||
|
||||
// pow : Float, Float -> Float
|
||||
// pow : Float a, Float a -> Float a
|
||||
add_type(
|
||||
Symbol::NUM_POW,
|
||||
top_level_function(vec![float_type(), float_type()], Box::new(float_type())),
|
||||
top_level_function(
|
||||
vec![float_type(flex(TVAR1)), float_type(flex(TVAR1))],
|
||||
Box::new(float_type(flex(TVAR1))),
|
||||
),
|
||||
);
|
||||
|
||||
// ceiling : Float -> Int
|
||||
// ceiling : Float a -> Int b
|
||||
add_type(
|
||||
Symbol::NUM_CEILING,
|
||||
top_level_function(vec![float_type()], Box::new(int_type())),
|
||||
top_level_function(
|
||||
vec![float_type(flex(TVAR1))],
|
||||
Box::new(int_type(flex(TVAR2))),
|
||||
),
|
||||
);
|
||||
|
||||
// powInt : Int, Int -> Int
|
||||
// powInt : Int a, Int a -> Int a
|
||||
add_type(
|
||||
Symbol::NUM_POW_INT,
|
||||
top_level_function(vec![int_type(), int_type()], Box::new(int_type())),
|
||||
top_level_function(
|
||||
vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))],
|
||||
Box::new(int_type(flex(TVAR1))),
|
||||
),
|
||||
);
|
||||
|
||||
// floor : Float -> Int
|
||||
// floor : Float a -> Int b
|
||||
add_type(
|
||||
Symbol::NUM_FLOOR,
|
||||
top_level_function(vec![float_type()], Box::new(int_type())),
|
||||
top_level_function(
|
||||
vec![float_type(flex(TVAR1))],
|
||||
Box::new(int_type(flex(TVAR2))),
|
||||
),
|
||||
);
|
||||
|
||||
// atan : Float -> Float
|
||||
// atan : Float a -> Float a
|
||||
add_type(
|
||||
Symbol::NUM_ATAN,
|
||||
top_level_function(vec![float_type()], Box::new(float_type())),
|
||||
top_level_function(
|
||||
vec![float_type(flex(TVAR1))],
|
||||
Box::new(float_type(flex(TVAR1))),
|
||||
),
|
||||
);
|
||||
|
||||
// acos : Float -> Float
|
||||
// acos : Float a -> Float a
|
||||
add_type(
|
||||
Symbol::NUM_ACOS,
|
||||
top_level_function(vec![float_type()], Box::new(float_type())),
|
||||
top_level_function(
|
||||
vec![float_type(flex(TVAR1))],
|
||||
Box::new(float_type(flex(TVAR1))),
|
||||
),
|
||||
);
|
||||
|
||||
// asin : Float -> Float
|
||||
// asin : Float a -> Float a
|
||||
add_type(
|
||||
Symbol::NUM_ASIN,
|
||||
top_level_function(vec![float_type()], Box::new(float_type())),
|
||||
top_level_function(
|
||||
vec![float_type(flex(TVAR1))],
|
||||
Box::new(float_type(flex(TVAR1))),
|
||||
),
|
||||
);
|
||||
|
||||
// Bool module
|
||||
@ -461,21 +518,21 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||
top_level_function(vec![str_type(), str_type()], Box::new(bool_type())),
|
||||
);
|
||||
|
||||
// countGraphemes : Str -> Int
|
||||
// countGraphemes : Str -> Nat
|
||||
add_type(
|
||||
Symbol::STR_COUNT_GRAPHEMES,
|
||||
top_level_function(vec![str_type()], Box::new(int_type())),
|
||||
top_level_function(vec![str_type()], Box::new(nat_type())),
|
||||
);
|
||||
|
||||
// fromInt : Int -> Str
|
||||
// fromInt : Int a -> Str
|
||||
add_type(
|
||||
Symbol::STR_FROM_INT,
|
||||
top_level_function(vec![int_type()], Box::new(str_type())),
|
||||
top_level_function(vec![int_type(flex(TVAR1))], Box::new(str_type())),
|
||||
);
|
||||
|
||||
// List module
|
||||
|
||||
// get : List elem, Int -> Result elem [ OutOfBounds ]*
|
||||
// get : List elem, Nat -> Result elem [ OutOfBounds ]*
|
||||
let index_out_of_bounds = SolvedType::TagUnion(
|
||||
vec![(TagName::Global("OutOfBounds".into()), vec![])],
|
||||
Box::new(SolvedType::Wildcard),
|
||||
@ -484,7 +541,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||
add_type(
|
||||
Symbol::LIST_GET,
|
||||
top_level_function(
|
||||
vec![list_type(flex(TVAR1)), int_type()],
|
||||
vec![list_type(flex(TVAR1)), nat_type()],
|
||||
Box::new(result_type(flex(TVAR1), index_out_of_bounds)),
|
||||
),
|
||||
);
|
||||
@ -512,11 +569,11 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||
),
|
||||
);
|
||||
|
||||
// set : List elem, Int, elem -> List elem
|
||||
// set : List elem, Nat, elem -> List elem
|
||||
add_type(
|
||||
Symbol::LIST_SET,
|
||||
top_level_function(
|
||||
vec![list_type(flex(TVAR1)), int_type(), flex(TVAR1)],
|
||||
vec![list_type(flex(TVAR1)), nat_type(), flex(TVAR1)],
|
||||
Box::new(list_type(flex(TVAR1))),
|
||||
),
|
||||
);
|
||||
@ -631,11 +688,11 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||
top_level_function(vec![flex(TVAR1)], Box::new(list_type(flex(TVAR1)))),
|
||||
);
|
||||
|
||||
// repeat : Int, elem -> List elem
|
||||
// repeat : Nat, elem -> List elem
|
||||
add_type(
|
||||
Symbol::LIST_REPEAT,
|
||||
top_level_function(
|
||||
vec![int_type(), flex(TVAR1)],
|
||||
vec![nat_type(), flex(TVAR2)],
|
||||
Box::new(list_type(flex(TVAR1))),
|
||||
),
|
||||
);
|
||||
@ -649,10 +706,10 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||
),
|
||||
);
|
||||
|
||||
// len : List * -> Int
|
||||
// len : List * -> Nat
|
||||
add_type(
|
||||
Symbol::LIST_LEN,
|
||||
top_level_function(vec![list_type(flex(TVAR1))], Box::new(int_type())),
|
||||
top_level_function(vec![list_type(flex(TVAR1))], Box::new(nat_type())),
|
||||
);
|
||||
|
||||
// isEmpty : List * -> Bool
|
||||
|
@ -166,8 +166,8 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||
|
||||
// addWrap : Int, Int -> Int
|
||||
add_type(Symbol::NUM_ADD_WRAP, {
|
||||
let_tvars! { u, v, w };
|
||||
unique_function(vec![int_type(u), int_type(v)], int_type(w))
|
||||
let_tvars! { u, v, w, int };
|
||||
unique_function(vec![int_type(u, int), int_type(v, int)], int_type(w, int))
|
||||
});
|
||||
|
||||
// sub or (-) : Num a, Num a -> Num a
|
||||
@ -199,8 +199,8 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||
|
||||
// mulWrap : Int, Int -> Int
|
||||
add_type(Symbol::NUM_MUL_WRAP, {
|
||||
let_tvars! { u, v, w };
|
||||
unique_function(vec![int_type(u), int_type(v)], int_type(w))
|
||||
let_tvars! { u, v, w , int };
|
||||
unique_function(vec![int_type(u, int), int_type(v, int)], int_type(w, int))
|
||||
});
|
||||
|
||||
// mulChecked : Num a, Num a -> Result (Num a) [ Overflow ]*
|
||||
@ -257,38 +257,50 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||
|
||||
// rem : Attr * Int, Attr * Int -> Attr * (Result (Attr * Int) (Attr * [ DivByZero ]*))
|
||||
add_type(Symbol::NUM_REM, {
|
||||
let_tvars! { star1, star2, star3, star4, star5 };
|
||||
let_tvars! { star1, star2, star3, star4, star5, int };
|
||||
unique_function(
|
||||
vec![int_type(star1), int_type(star2)],
|
||||
result_type(star3, int_type(star4), lift(star5, div_by_zero())),
|
||||
vec![int_type(star1, int), int_type(star2, int)],
|
||||
result_type(star3, int_type(star4, int), lift(star5, div_by_zero())),
|
||||
)
|
||||
});
|
||||
|
||||
// maxInt : Int
|
||||
add_type(Symbol::NUM_MAX_INT, {
|
||||
let_tvars! { star };
|
||||
int_type(star)
|
||||
let_tvars! { star, int };
|
||||
int_type(star, int)
|
||||
});
|
||||
|
||||
// minInt : Int
|
||||
add_type(Symbol::NUM_MIN_INT, {
|
||||
let_tvars! { star };
|
||||
int_type(star)
|
||||
let_tvars! { star, int };
|
||||
int_type(star, int)
|
||||
});
|
||||
|
||||
// divFloor or (//) : Int, Int -> Result Int [ DivByZero ]*
|
||||
add_type(Symbol::NUM_DIV_INT, {
|
||||
let_tvars! { star1, star2, star3, star4, star5 };
|
||||
let_tvars! { star1, star2, star3, star4, star5, int };
|
||||
unique_function(
|
||||
vec![int_type(star1), int_type(star2)],
|
||||
result_type(star3, int_type(star4), lift(star5, div_by_zero())),
|
||||
vec![int_type(star1, int), int_type(star2, int)],
|
||||
result_type(star3, int_type(star4, int), lift(star5, div_by_zero())),
|
||||
)
|
||||
});
|
||||
|
||||
// bitwiseAnd : Attr * Int, Attr * Int -> Attr * Int
|
||||
add_type(Symbol::NUM_BITWISE_AND, {
|
||||
let_tvars! { star1, star2, star3 };
|
||||
unique_function(vec![int_type(star1), int_type(star2)], int_type(star3))
|
||||
let_tvars! { star1, star2, star3, int };
|
||||
unique_function(
|
||||
vec![int_type(star1, int), int_type(star2, int)],
|
||||
int_type(star3, int),
|
||||
)
|
||||
});
|
||||
|
||||
// bitwiseAnd : Attr * Int, Attr * Int -> Attr * Int
|
||||
add_type(Symbol::NUM_BITWISE_XOR, {
|
||||
let_tvars! { star1, star2, star3, int };
|
||||
unique_function(
|
||||
vec![int_type(star1, int), int_type(star2, int)],
|
||||
int_type(star3, int),
|
||||
)
|
||||
});
|
||||
|
||||
// divFloat : Float, Float -> Float
|
||||
@ -302,8 +314,8 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||
|
||||
// round : Float -> Int
|
||||
add_type(Symbol::NUM_ROUND, {
|
||||
let_tvars! { star1, star2 };
|
||||
unique_function(vec![float_type(star1)], int_type(star2))
|
||||
let_tvars! { star1, star2, int };
|
||||
unique_function(vec![float_type(star1)], int_type(star2, int))
|
||||
});
|
||||
|
||||
// sqrt : Float -> Float
|
||||
@ -391,20 +403,23 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||
|
||||
// ceiling : Float -> Int
|
||||
add_type(Symbol::NUM_CEILING, {
|
||||
let_tvars! { star1, star2 };
|
||||
unique_function(vec![float_type(star1)], int_type(star2))
|
||||
let_tvars! { star1, star2, int };
|
||||
unique_function(vec![float_type(star1)], int_type(star2, int))
|
||||
});
|
||||
|
||||
// powInt : Int, Int -> Int
|
||||
add_type(Symbol::NUM_POW_INT, {
|
||||
let_tvars! { star1, star2, star3 };
|
||||
unique_function(vec![int_type(star1), int_type(star2)], int_type(star3))
|
||||
let_tvars! { star1, star2, star3 , int };
|
||||
unique_function(
|
||||
vec![int_type(star1, int), int_type(star2, int)],
|
||||
int_type(star3, int),
|
||||
)
|
||||
});
|
||||
|
||||
// floor : Float -> Int
|
||||
add_type(Symbol::NUM_FLOOR, {
|
||||
let_tvars! { star1, star2 };
|
||||
unique_function(vec![float_type(star1)], int_type(star2))
|
||||
let_tvars! { star1, star2 , int};
|
||||
unique_function(vec![float_type(star1)], int_type(star2, int))
|
||||
});
|
||||
|
||||
// atan : Float -> Float
|
||||
@ -479,8 +494,8 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||
|
||||
// len : Attr * (List *) -> Attr * Int
|
||||
add_type(Symbol::LIST_LEN, {
|
||||
let_tvars! { star1, a, star2 };
|
||||
unique_function(vec![list_type(star1, a)], int_type(star2))
|
||||
let_tvars! { star1, a, star2 , int };
|
||||
unique_function(vec![list_type(star1, a)], int_type(star2, int))
|
||||
});
|
||||
|
||||
fn list_was_empty() -> SolvedType {
|
||||
@ -536,7 +551,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||
);
|
||||
|
||||
add_type(Symbol::LIST_GET, {
|
||||
let_tvars! { a, u, star1, star2, star3, star4 };
|
||||
let_tvars! { a, u, star1, star2, star3, star4, int};
|
||||
|
||||
unique_function(
|
||||
vec![
|
||||
@ -547,7 +562,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||
SolvedType::Apply(Symbol::LIST_LIST, vec![attr_type(u, a)]),
|
||||
],
|
||||
),
|
||||
int_type(star2),
|
||||
int_type(star2, int),
|
||||
],
|
||||
result_type(star3, attr_type(u, a), lift(star4, index_out_of_bounds)),
|
||||
)
|
||||
@ -559,7 +574,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||
// Attr (u | v) a
|
||||
// -> Attr * (List (Attr u a))
|
||||
add_type(Symbol::LIST_SET, {
|
||||
let_tvars! { u, v, w, star1, star2, a };
|
||||
let_tvars! { u, v, w, star1, star2, a, int};
|
||||
|
||||
unique_function(
|
||||
vec![
|
||||
@ -570,7 +585,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||
SolvedType::Apply(Symbol::LIST_LIST, vec![attr_type(u, a)]),
|
||||
],
|
||||
),
|
||||
int_type(star1),
|
||||
int_type(star1, int),
|
||||
SolvedType::Apply(Symbol::ATTR_ATTR, vec![container(u, vec![v]), flex(a)]),
|
||||
],
|
||||
SolvedType::Apply(
|
||||
@ -627,10 +642,10 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||
// , Attr Shared a
|
||||
// -> Attr * (List (Attr Shared a))
|
||||
add_type(Symbol::LIST_REPEAT, {
|
||||
let_tvars! { a, star1, star2 };
|
||||
let_tvars! { a, star1, star2, int };
|
||||
|
||||
unique_function(
|
||||
vec![int_type(star1), shared(flex(a))],
|
||||
vec![int_type(star1, int), shared(flex(a))],
|
||||
SolvedType::Apply(
|
||||
Symbol::ATTR_ATTR,
|
||||
vec![
|
||||
@ -1162,14 +1177,14 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||
|
||||
// Str.countGraphemes : Attr * Str, -> Attr * Int
|
||||
add_type(Symbol::STR_COUNT_GRAPHEMES, {
|
||||
let_tvars! { star1, star2 };
|
||||
unique_function(vec![str_type(star1)], int_type(star2))
|
||||
let_tvars! { star1, star2, int };
|
||||
unique_function(vec![str_type(star1)], int_type(star2, int))
|
||||
});
|
||||
|
||||
// fromInt : Attr * Int -> Attr * Str
|
||||
add_type(Symbol::STR_FROM_INT, {
|
||||
let_tvars! { star1, star2 };
|
||||
unique_function(vec![int_type(star1)], str_type(star2))
|
||||
let_tvars! { star1, star2, int };
|
||||
unique_function(vec![int_type(star1, int)], str_type(star2))
|
||||
});
|
||||
|
||||
// Result module
|
||||
@ -1242,9 +1257,8 @@ fn lift(u: VarId, a: SolvedType) -> SolvedType {
|
||||
|
||||
#[inline(always)]
|
||||
fn float_type(u: VarId) -> SolvedType {
|
||||
let b_64 = builtin_aliases::binary64_type();
|
||||
let attr_b_64 = lift(u, b_64);
|
||||
let fp = builtin_aliases::floatingpoint_type(attr_b_64);
|
||||
let inner_type = lift(u, flex(u));
|
||||
let fp = builtin_aliases::floatingpoint_type(inner_type.clone());
|
||||
let attr_fb = lift(u, fp);
|
||||
let num = builtin_aliases::num_type(attr_fb);
|
||||
|
||||
@ -1252,16 +1266,19 @@ fn float_type(u: VarId) -> SolvedType {
|
||||
Symbol::ATTR_ATTR,
|
||||
vec![
|
||||
flex(u),
|
||||
SolvedType::Alias(Symbol::NUM_F64, Vec::new(), Box::new(num)),
|
||||
SolvedType::Alias(
|
||||
Symbol::NUM_FLOAT,
|
||||
vec![("range".into(), inner_type)],
|
||||
Box::new(num),
|
||||
),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn int_type(u: VarId) -> SolvedType {
|
||||
let signed_64 = builtin_aliases::signed64_type();
|
||||
let attr_signed_64 = lift(u, signed_64);
|
||||
let integer = builtin_aliases::integer_type(attr_signed_64);
|
||||
fn int_type(u: VarId, range: VarId) -> SolvedType {
|
||||
let inner_type = lift(u, flex(range));
|
||||
let integer = builtin_aliases::integer_type(inner_type.clone());
|
||||
let attr_fb = lift(u, integer);
|
||||
let num = builtin_aliases::num_type(attr_fb);
|
||||
|
||||
@ -1269,7 +1286,11 @@ fn int_type(u: VarId) -> SolvedType {
|
||||
Symbol::ATTR_ATTR,
|
||||
vec![
|
||||
flex(u),
|
||||
SolvedType::Alias(Symbol::NUM_I64, Vec::new(), Box::new(num)),
|
||||
SolvedType::Alias(
|
||||
Symbol::NUM_INT,
|
||||
vec![("range".into(), inner_type)],
|
||||
Box::new(num),
|
||||
),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
@ -117,7 +117,8 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
|
||||
NUM_ASIN => num_asin,
|
||||
NUM_MAX_INT => num_max_int,
|
||||
NUM_MIN_INT => num_min_int,
|
||||
NUM_BITWISE_AND => num_bitwise_and
|
||||
NUM_BITWISE_AND => num_bitwise_and,
|
||||
NUM_BITWISE_XOR => num_bitwise_xor
|
||||
}
|
||||
}
|
||||
|
||||
@ -212,7 +213,8 @@ pub fn builtin_defs(var_store: &mut VarStore) -> MutMap<Symbol, Def> {
|
||||
/// Num.maxInt : Int
|
||||
fn num_max_int(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
let int_var = var_store.fresh();
|
||||
let body = Int(int_var, i64::MAX);
|
||||
let int_percision_var = var_store.fresh();
|
||||
let body = Int(int_var, int_percision_var, i64::MAX);
|
||||
|
||||
Def {
|
||||
annotation: None,
|
||||
@ -226,7 +228,8 @@ fn num_max_int(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
/// Num.minInt : Int
|
||||
fn num_min_int(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
let int_var = var_store.fresh();
|
||||
let body = Int(int_var, i64::MIN);
|
||||
let int_percision_var = var_store.fresh();
|
||||
let body = Int(int_var, int_percision_var, i64::MIN);
|
||||
|
||||
Def {
|
||||
annotation: None,
|
||||
@ -846,7 +849,7 @@ fn num_is_odd(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
let body = RunLowLevel {
|
||||
op: LowLevel::Eq,
|
||||
args: vec![
|
||||
(arg_var, Int(var_store.fresh(), 1)),
|
||||
(arg_var, Int(var_store.fresh(), var_store.fresh(), 1)),
|
||||
(
|
||||
arg_var,
|
||||
RunLowLevel {
|
||||
@ -930,6 +933,7 @@ fn num_sqrt(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
let bool_var = var_store.fresh();
|
||||
let float_var = var_store.fresh();
|
||||
let unbound_zero_var = var_store.fresh();
|
||||
let percision_var = var_store.fresh();
|
||||
let ret_var = var_store.fresh();
|
||||
|
||||
let body = If {
|
||||
@ -943,7 +947,7 @@ fn num_sqrt(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
op: LowLevel::NotEq,
|
||||
args: vec![
|
||||
(float_var, Var(Symbol::ARG_1)),
|
||||
(float_var, Float(unbound_zero_var, 0.0)),
|
||||
(float_var, Float(unbound_zero_var, percision_var, 0.0)),
|
||||
],
|
||||
ret_var: bool_var,
|
||||
},
|
||||
@ -1150,6 +1154,11 @@ fn num_bitwise_and(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
num_binop(symbol, var_store, LowLevel::NumBitwiseAnd)
|
||||
}
|
||||
|
||||
/// Num.bitwiseXor : Int, Int -> Int
|
||||
fn num_bitwise_xor(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
num_binop(symbol, var_store, LowLevel::NumBitwiseXor)
|
||||
}
|
||||
|
||||
/// List.isEmpty : List * -> Bool
|
||||
fn list_is_empty(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
let list_var = var_store.fresh();
|
||||
@ -1896,6 +1905,7 @@ fn num_div_float(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
let bool_var = var_store.fresh();
|
||||
let num_var = var_store.fresh();
|
||||
let unbound_zero_var = var_store.fresh();
|
||||
let percision_var = var_store.fresh();
|
||||
let ret_var = var_store.fresh();
|
||||
|
||||
let body = If {
|
||||
@ -1909,7 +1919,7 @@ fn num_div_float(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
op: LowLevel::NotEq,
|
||||
args: vec![
|
||||
(num_var, Var(Symbol::ARG_2)),
|
||||
(num_var, Float(unbound_zero_var, 0.0)),
|
||||
(num_var, Float(unbound_zero_var, percision_var, 0.0)),
|
||||
],
|
||||
ret_var: bool_var,
|
||||
},
|
||||
@ -1958,6 +1968,7 @@ fn num_div_int(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
let bool_var = var_store.fresh();
|
||||
let num_var = var_store.fresh();
|
||||
let unbound_zero_var = var_store.fresh();
|
||||
let unbound_zero_percision_var = var_store.fresh();
|
||||
let ret_var = var_store.fresh();
|
||||
|
||||
let body = If {
|
||||
@ -1971,7 +1982,10 @@ fn num_div_int(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
op: LowLevel::NotEq,
|
||||
args: vec![
|
||||
(num_var, Var(Symbol::ARG_2)),
|
||||
(num_var, Int(unbound_zero_var, 0)),
|
||||
(
|
||||
num_var,
|
||||
Int(unbound_zero_var, unbound_zero_percision_var, 0),
|
||||
),
|
||||
],
|
||||
ret_var: bool_var,
|
||||
},
|
||||
@ -2025,6 +2039,7 @@ fn list_first(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
let list_var = var_store.fresh();
|
||||
let len_var = var_store.fresh();
|
||||
let zero_var = var_store.fresh();
|
||||
let zero_percision_var = var_store.fresh();
|
||||
let list_elem_var = var_store.fresh();
|
||||
let ret_var = var_store.fresh();
|
||||
|
||||
@ -2039,7 +2054,7 @@ fn list_first(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
RunLowLevel {
|
||||
op: LowLevel::NotEq,
|
||||
args: vec![
|
||||
(len_var, Int(zero_var, 0)),
|
||||
(len_var, Int(zero_var, zero_percision_var, 0)),
|
||||
(
|
||||
len_var,
|
||||
RunLowLevel {
|
||||
@ -2061,7 +2076,10 @@ fn list_first(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
// List.#getUnsafe list 0
|
||||
RunLowLevel {
|
||||
op: LowLevel::ListGetUnsafe,
|
||||
args: vec![(list_var, Var(Symbol::ARG_1)), (len_var, Int(zero_var, 0))],
|
||||
args: vec![
|
||||
(list_var, Var(Symbol::ARG_1)),
|
||||
(len_var, Int(zero_var, zero_percision_var, 0)),
|
||||
],
|
||||
ret_var: list_elem_var,
|
||||
},
|
||||
],
|
||||
@ -2102,6 +2120,7 @@ fn list_last(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
let list_var = var_store.fresh();
|
||||
let len_var = var_store.fresh();
|
||||
let num_var = var_store.fresh();
|
||||
let num_percision_var = var_store.fresh();
|
||||
let list_elem_var = var_store.fresh();
|
||||
let ret_var = var_store.fresh();
|
||||
|
||||
@ -2116,7 +2135,7 @@ fn list_last(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
RunLowLevel {
|
||||
op: LowLevel::NotEq,
|
||||
args: vec![
|
||||
(len_var, Int(num_var, 0)),
|
||||
(len_var, Int(num_var, num_percision_var, 0)),
|
||||
(
|
||||
len_var,
|
||||
RunLowLevel {
|
||||
@ -2155,7 +2174,7 @@ fn list_last(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
ret_var: len_var,
|
||||
},
|
||||
),
|
||||
(arg_var, Int(num_var, 1)),
|
||||
(arg_var, Int(num_var, num_percision_var, 1)),
|
||||
],
|
||||
ret_var: len_var,
|
||||
},
|
||||
|
@ -735,8 +735,8 @@ fn pattern_to_vars_by_symbol(
|
||||
}
|
||||
|
||||
NumLiteral(_, _)
|
||||
| IntLiteral(_)
|
||||
| FloatLiteral(_)
|
||||
| IntLiteral(_, _)
|
||||
| FloatLiteral(_, _)
|
||||
| StrLiteral(_)
|
||||
| Underscore
|
||||
| MalformedPattern(_, _)
|
||||
@ -864,7 +864,9 @@ fn canonicalize_pending_def<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
Alias { name, ann, vars } => {
|
||||
Alias {
|
||||
name, ann, vars, ..
|
||||
} => {
|
||||
let symbol = name.value;
|
||||
let can_ann = canonicalize_annotation(env, scope, &ann.value, ann.region, var_store);
|
||||
|
||||
@ -1407,7 +1409,9 @@ fn to_pending_def<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
Alias { name, vars, ann } => {
|
||||
Alias {
|
||||
name, vars, ann, ..
|
||||
} => {
|
||||
let region = Region::span_across(&name.region, &ann.region);
|
||||
|
||||
match scope.introduce(
|
||||
|
@ -56,8 +56,8 @@ pub enum Expr {
|
||||
Num(Variable, i64),
|
||||
|
||||
// Int and Float store a variable to generate better error messages
|
||||
Int(Variable, i64),
|
||||
Float(Variable, f64),
|
||||
Int(Variable, Variable, i64),
|
||||
Float(Variable, Variable, f64),
|
||||
Str(InlinableString),
|
||||
List {
|
||||
list_var: Variable, // required for uniqueness of the list
|
||||
@ -1168,8 +1168,8 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
|
||||
// Num stores the `a` variable in `Num a`. Not the same as the variable
|
||||
// stored in Int and Float below, which is strictly for better error messages
|
||||
other @ Num(_, _)
|
||||
| other @ Int(_, _)
|
||||
| other @ Float(_, _)
|
||||
| other @ Int(_, _, _)
|
||||
| other @ Float(_, _, _)
|
||||
| other @ Str { .. }
|
||||
| other @ RuntimeError(_)
|
||||
| other @ EmptyRecord
|
||||
|
@ -363,8 +363,8 @@ fn fix_values_captured_in_closure_pattern(
|
||||
}
|
||||
Identifier(_)
|
||||
| NumLiteral(_, _)
|
||||
| IntLiteral(_)
|
||||
| FloatLiteral(_)
|
||||
| IntLiteral(_, _)
|
||||
| FloatLiteral(_, _)
|
||||
| StrLiteral(_)
|
||||
| Underscore
|
||||
| Shadowed(_, _)
|
||||
@ -414,8 +414,8 @@ fn fix_values_captured_in_closure_expr(
|
||||
}
|
||||
|
||||
Num(_, _)
|
||||
| Int(_, _)
|
||||
| Float(_, _)
|
||||
| Int(_, _, _)
|
||||
| Float(_, _, _)
|
||||
| Str(_)
|
||||
| Var(_)
|
||||
| EmptyRecord
|
||||
|
@ -45,7 +45,7 @@ pub fn int_expr_from_result(
|
||||
) -> Expr {
|
||||
// Int stores a variable to generate better error messages
|
||||
match result {
|
||||
Ok(int) => Expr::Int(var_store.fresh(), int),
|
||||
Ok(int) => Expr::Int(var_store.fresh(), var_store.fresh(), int),
|
||||
Err((raw, error)) => {
|
||||
let runtime_error = InvalidInt(error, base, region, raw.into());
|
||||
|
||||
@ -65,7 +65,7 @@ pub fn float_expr_from_result(
|
||||
) -> Expr {
|
||||
// Float stores a variable to generate better error messages
|
||||
match result {
|
||||
Ok(float) => Expr::Float(var_store.fresh(), float),
|
||||
Ok(float) => Expr::Float(var_store.fresh(), var_store.fresh(), float),
|
||||
Err((raw, error)) => {
|
||||
let runtime_error = InvalidFloat(error, region, raw.into());
|
||||
|
||||
|
@ -25,9 +25,9 @@ pub enum Pattern {
|
||||
ext_var: Variable,
|
||||
destructs: Vec<Located<RecordDestruct>>,
|
||||
},
|
||||
IntLiteral(i64),
|
||||
IntLiteral(Variable, i64),
|
||||
NumLiteral(Variable, i64),
|
||||
FloatLiteral(f64),
|
||||
FloatLiteral(Variable, f64),
|
||||
StrLiteral(Box<str>),
|
||||
Underscore,
|
||||
|
||||
@ -86,8 +86,8 @@ pub fn symbols_from_pattern_help(pattern: &Pattern, symbols: &mut Vec<Symbol>) {
|
||||
}
|
||||
|
||||
NumLiteral(_, _)
|
||||
| IntLiteral(_)
|
||||
| FloatLiteral(_)
|
||||
| IntLiteral(_, _)
|
||||
| FloatLiteral(_, _)
|
||||
| StrLiteral(_)
|
||||
| Underscore
|
||||
| MalformedPattern(_, _)
|
||||
@ -191,7 +191,7 @@ pub fn canonicalize_pattern<'a>(
|
||||
let problem = MalformedPatternProblem::MalformedFloat;
|
||||
malformed_pattern(env, problem, region)
|
||||
}
|
||||
Ok(float) => Pattern::FloatLiteral(float),
|
||||
Ok(float) => Pattern::FloatLiteral(var_store.fresh(), float),
|
||||
},
|
||||
ptype => unsupported_pattern(env, ptype, region),
|
||||
},
|
||||
@ -224,9 +224,9 @@ pub fn canonicalize_pattern<'a>(
|
||||
}
|
||||
Ok(int) => {
|
||||
if *is_negative {
|
||||
Pattern::IntLiteral(-int)
|
||||
Pattern::IntLiteral(var_store.fresh(), -int)
|
||||
} else {
|
||||
Pattern::IntLiteral(int)
|
||||
Pattern::IntLiteral(var_store.fresh(), int)
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -455,8 +455,8 @@ fn add_bindings_from_patterns(
|
||||
}
|
||||
}
|
||||
NumLiteral(_, _)
|
||||
| IntLiteral(_)
|
||||
| FloatLiteral(_)
|
||||
| IntLiteral(_, _)
|
||||
| FloatLiteral(_, _)
|
||||
| StrLiteral(_)
|
||||
| Underscore
|
||||
| Shadowed(_, _)
|
||||
|
@ -32,7 +32,7 @@ mod test_can {
|
||||
let actual_out = can_expr_with(&arena, test_home(), input);
|
||||
|
||||
match actual_out.loc_expr.value {
|
||||
Expr::Float(_, actual) => {
|
||||
Expr::Float(_, _, actual) => {
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
actual => {
|
||||
@ -46,11 +46,11 @@ mod test_can {
|
||||
let actual_out = can_expr_with(&arena, test_home(), input);
|
||||
|
||||
match actual_out.loc_expr.value {
|
||||
Expr::Int(_, actual) => {
|
||||
Expr::Int(_, _, actual) => {
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
actual => {
|
||||
panic!("Expected an I64, but got: {:?}", actual);
|
||||
panic!("Expected an Int *, but got: {:?}", actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -249,7 +249,7 @@ mod test_can {
|
||||
fn correct_annotated_body() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
f : I64 -> I64
|
||||
f : Int * -> Int *
|
||||
f = \ a -> a
|
||||
|
||||
f
|
||||
@ -265,7 +265,7 @@ mod test_can {
|
||||
fn correct_annotated_body_with_comments() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
f : I64 -> I64 # comment
|
||||
f : Int * -> Int * # comment
|
||||
f = \ a -> a
|
||||
|
||||
f
|
||||
@ -281,7 +281,7 @@ mod test_can {
|
||||
fn name_mismatch_annotated_body() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
f : I64 -> I64
|
||||
f : Int * -> Int *
|
||||
g = \ a -> a
|
||||
|
||||
g
|
||||
@ -307,7 +307,7 @@ mod test_can {
|
||||
fn name_mismatch_annotated_body_with_comment() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
f : I64 -> I64 # comment
|
||||
f : Int * -> Int * # comment
|
||||
g = \ a -> a
|
||||
|
||||
g
|
||||
@ -333,7 +333,7 @@ mod test_can {
|
||||
fn separated_annotated_body() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
f : I64 -> I64
|
||||
f : Int * -> Int *
|
||||
|
||||
f = \ a -> a
|
||||
|
||||
@ -354,7 +354,7 @@ mod test_can {
|
||||
fn separated_annotated_body_with_comment() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
f : I64 -> I64
|
||||
f : Int * -> Int *
|
||||
# comment
|
||||
f = \ a -> a
|
||||
|
||||
@ -375,9 +375,9 @@ mod test_can {
|
||||
fn shadowed_annotation() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
f : I64 -> I64
|
||||
f : Int * -> Int *
|
||||
|
||||
f : I64 -> I64
|
||||
f : Int * -> Int *
|
||||
|
||||
f
|
||||
"#
|
||||
@ -397,7 +397,7 @@ mod test_can {
|
||||
fn correct_nested_unannotated_body() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
f : I64
|
||||
f : Int *
|
||||
f =
|
||||
g = 42
|
||||
|
||||
@ -416,9 +416,9 @@ mod test_can {
|
||||
fn correct_nested_annotated_body() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
f : I64
|
||||
f : Int *
|
||||
f =
|
||||
g : I64
|
||||
g : Int *
|
||||
g = 42
|
||||
|
||||
g + 1
|
||||
@ -436,11 +436,11 @@ mod test_can {
|
||||
fn correct_nested_body_annotated_multiple_lines() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
f : I64
|
||||
f : Int *
|
||||
f =
|
||||
g : I64
|
||||
g : Int *
|
||||
g = 42
|
||||
h : I64
|
||||
h : Int *
|
||||
h = 5
|
||||
z = 4
|
||||
g + h + z
|
||||
@ -458,10 +458,10 @@ mod test_can {
|
||||
fn correct_nested_body_unannotated_multiple_lines() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
f : I64
|
||||
f : Int *
|
||||
f =
|
||||
g = 42
|
||||
h : I64
|
||||
h : Int *
|
||||
h = 5
|
||||
z = 4
|
||||
g + h + z
|
||||
@ -478,7 +478,7 @@ mod test_can {
|
||||
fn correct_double_nested_body() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
f : I64
|
||||
f : Int *
|
||||
f =
|
||||
g =
|
||||
h = 42
|
||||
@ -499,7 +499,7 @@ mod test_can {
|
||||
fn annotation_followed_with_unrelated_affectation() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
F : I64
|
||||
F : Int *
|
||||
|
||||
x = 1
|
||||
|
||||
@ -520,9 +520,9 @@ mod test_can {
|
||||
fn two_annotations_followed_with_unrelated_affectation() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
G : I64
|
||||
G : Int *
|
||||
|
||||
F : I64
|
||||
F : Int *
|
||||
|
||||
x = 1
|
||||
|
||||
|
@ -11,30 +11,48 @@ use roc_types::types::Reason;
|
||||
use roc_types::types::Type::{self, *};
|
||||
|
||||
#[inline(always)]
|
||||
pub fn int_literal(num_var: Variable, expected: Expected<Type>, region: Region) -> Constraint {
|
||||
pub fn int_literal(
|
||||
num_var: Variable,
|
||||
percision_var: Variable,
|
||||
expected: Expected<Type>,
|
||||
region: Region,
|
||||
) -> Constraint {
|
||||
let num_type = Variable(num_var);
|
||||
let reason = Reason::IntLiteral;
|
||||
let expected_literal = ForReason(reason, num_int(), region);
|
||||
|
||||
exists(
|
||||
vec![num_var],
|
||||
And(vec![
|
||||
Eq(num_type.clone(), expected_literal, Category::Int, region),
|
||||
Eq(
|
||||
num_type.clone(),
|
||||
ForReason(reason, num_int(Type::Variable(percision_var)), region),
|
||||
Category::Int,
|
||||
region,
|
||||
),
|
||||
Eq(num_type, expected, Category::Int, region),
|
||||
]),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn float_literal(num_var: Variable, expected: Expected<Type>, region: Region) -> Constraint {
|
||||
pub fn float_literal(
|
||||
num_var: Variable,
|
||||
percision_var: Variable,
|
||||
expected: Expected<Type>,
|
||||
region: Region,
|
||||
) -> Constraint {
|
||||
let num_type = Variable(num_var);
|
||||
let reason = Reason::FloatLiteral;
|
||||
let expected_literal = ForReason(reason, num_float(), region);
|
||||
|
||||
exists(
|
||||
vec![num_var],
|
||||
And(vec![
|
||||
Eq(num_type.clone(), expected_literal, Category::Float, region),
|
||||
Eq(
|
||||
num_type.clone(),
|
||||
ForReason(reason, num_float(Type::Variable(percision_var)), region),
|
||||
Category::Float,
|
||||
region,
|
||||
),
|
||||
Eq(num_type, expected, Category::Float, region),
|
||||
]),
|
||||
)
|
||||
@ -72,11 +90,11 @@ pub fn str_type() -> Type {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn num_float() -> Type {
|
||||
pub fn num_float(range: Type) -> Type {
|
||||
Type::Alias(
|
||||
Symbol::NUM_F64,
|
||||
vec![],
|
||||
Box::new(num_num(num_floatingpoint(num_binary64()))),
|
||||
Symbol::NUM_FLOAT,
|
||||
vec![("range".into(), range.clone())],
|
||||
Box::new(num_num(num_floatingpoint(range))),
|
||||
)
|
||||
}
|
||||
|
||||
@ -108,11 +126,11 @@ pub fn num_binary64() -> Type {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn num_int() -> Type {
|
||||
pub fn num_int(range: Type) -> Type {
|
||||
Type::Alias(
|
||||
Symbol::NUM_I64,
|
||||
vec![],
|
||||
Box::new(num_num(num_integer(num_signed64()))),
|
||||
Symbol::NUM_INT,
|
||||
vec![("range".into(), range.clone())],
|
||||
Box::new(num_num(num_integer(range))),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -96,7 +96,7 @@ pub fn constrain_expr(
|
||||
expected: Expected<Type>,
|
||||
) -> Constraint {
|
||||
match expr {
|
||||
Int(var, _) => int_literal(*var, expected, region),
|
||||
Int(var, percision, _) => int_literal(*var, *percision, expected, region),
|
||||
Num(var, _) => exists(
|
||||
vec![*var],
|
||||
Eq(
|
||||
@ -106,7 +106,7 @@ pub fn constrain_expr(
|
||||
region,
|
||||
),
|
||||
),
|
||||
Float(var, _) => float_literal(*var, expected, region),
|
||||
Float(var, percision, _) => float_literal(*var, *percision, expected, region),
|
||||
EmptyRecord => constrain_empty_record(region, expected),
|
||||
Expr::Record { record_var, fields } => {
|
||||
if fields.is_empty() {
|
||||
|
@ -57,8 +57,8 @@ fn headers_from_annotation_help(
|
||||
| MalformedPattern(_, _)
|
||||
| UnsupportedPattern(_)
|
||||
| NumLiteral(_, _)
|
||||
| IntLiteral(_)
|
||||
| FloatLiteral(_)
|
||||
| IntLiteral(_, _)
|
||||
| FloatLiteral(_, _)
|
||||
| StrLiteral(_) => true,
|
||||
|
||||
RecordDestructure { destructs, .. } => match annotation.value.shallow_dealias() {
|
||||
@ -154,20 +154,20 @@ pub fn constrain_pattern(
|
||||
));
|
||||
}
|
||||
|
||||
IntLiteral(_) => {
|
||||
IntLiteral(precision_var, _) => {
|
||||
state.constraints.push(Constraint::Pattern(
|
||||
region,
|
||||
PatternCategory::Int,
|
||||
builtins::num_int(),
|
||||
builtins::num_int(Type::Variable(*precision_var)),
|
||||
expected,
|
||||
));
|
||||
}
|
||||
|
||||
FloatLiteral(_) => {
|
||||
FloatLiteral(precision_var, _) => {
|
||||
state.constraints.push(Constraint::Pattern(
|
||||
region,
|
||||
PatternCategory::Float,
|
||||
builtins::num_float(),
|
||||
builtins::num_float(Type::Variable(*precision_var)),
|
||||
expected,
|
||||
));
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::builtins::{num_binary64, num_floatingpoint, num_integer, num_num, num_signed64};
|
||||
use crate::builtins::{num_floatingpoint, num_integer, num_num};
|
||||
use crate::expr::{exists, Info};
|
||||
use roc_can::annotation::IntroducedVariables;
|
||||
use roc_can::constraint::Constraint::{self, *};
|
||||
@ -173,18 +173,19 @@ fn constrain_pattern(
|
||||
));
|
||||
}
|
||||
|
||||
IntLiteral(_) => {
|
||||
IntLiteral(inner_var, _) => {
|
||||
let (a, b, c, num_type) = unique_int(var_store);
|
||||
|
||||
state.constraints.push(exists(
|
||||
vec![a, b, c],
|
||||
vec![*inner_var, a, b, c],
|
||||
Constraint::Pattern(pattern.region, PatternCategory::Int, num_type, expected),
|
||||
));
|
||||
}
|
||||
FloatLiteral(_) => {
|
||||
FloatLiteral(inner_var, _) => {
|
||||
let (a, b, c, num_type) = unique_float(var_store);
|
||||
|
||||
state.constraints.push(exists(
|
||||
vec![a, b, c],
|
||||
vec![*inner_var, a, b, c],
|
||||
Constraint::Pattern(pattern.region, PatternCategory::Float, num_type, expected),
|
||||
));
|
||||
}
|
||||
@ -423,10 +424,9 @@ fn unique_int(var_store: &mut VarStore) -> (Variable, Variable, Variable, Type)
|
||||
let num_uvar1 = var_store.fresh();
|
||||
let num_uvar2 = var_store.fresh();
|
||||
let num_uvar3 = var_store.fresh();
|
||||
let num_uvar4 = var_store.fresh();
|
||||
|
||||
let signed_64 = num_signed64();
|
||||
let attr_signed_64 = attr_type(Bool::variable(num_uvar1), signed_64);
|
||||
let integer = num_integer(attr_signed_64);
|
||||
let integer = num_integer(Type::Variable(num_uvar4));
|
||||
let attr_int = attr_type(Bool::variable(num_uvar2), integer);
|
||||
let num = num_num(attr_int);
|
||||
let attr_num = attr_type(Bool::variable(num_uvar3), num);
|
||||
@ -438,10 +438,9 @@ fn unique_float(var_store: &mut VarStore) -> (Variable, Variable, Variable, Type
|
||||
let num_uvar1 = var_store.fresh();
|
||||
let num_uvar2 = var_store.fresh();
|
||||
let num_uvar3 = var_store.fresh();
|
||||
let num_uvar4 = var_store.fresh();
|
||||
|
||||
let binary_64 = num_binary64();
|
||||
let attr_binary_64 = attr_type(Bool::variable(num_uvar1), binary_64);
|
||||
let fp = num_floatingpoint(attr_binary_64);
|
||||
let fp = num_floatingpoint(Type::Variable(num_uvar4));
|
||||
let attr_fp = attr_type(Bool::variable(num_uvar2), fp);
|
||||
let num = num_num(attr_fp);
|
||||
let attr_num = attr_type(Bool::variable(num_uvar3), num);
|
||||
@ -478,7 +477,7 @@ pub fn constrain_expr(
|
||||
]),
|
||||
)
|
||||
}
|
||||
Int(var, _) => {
|
||||
Int(var, _, _) => {
|
||||
let (a, b, c, num_type) = unique_int(var_store);
|
||||
|
||||
exists(
|
||||
@ -494,7 +493,7 @@ pub fn constrain_expr(
|
||||
]),
|
||||
)
|
||||
}
|
||||
Float(var, _) => {
|
||||
Float(var, _, _) => {
|
||||
let (a, b, c, num_type) = unique_float(var_store);
|
||||
|
||||
exists(
|
||||
@ -532,7 +531,9 @@ pub fn constrain_expr(
|
||||
),
|
||||
)
|
||||
}
|
||||
Record { record_var, fields } => {
|
||||
Record {
|
||||
record_var, fields, ..
|
||||
} => {
|
||||
// NOTE: canonicalization guarantees at least one field
|
||||
// zero fields generates an EmptyRecord
|
||||
let mut field_types = SendMap::default();
|
||||
|
@ -762,8 +762,8 @@ mod test_fmt {
|
||||
expr_formats_to(
|
||||
indoc!(
|
||||
r#"
|
||||
f: { y : I64,
|
||||
x : I64 ,
|
||||
f: { y : Int *,
|
||||
x : Int * ,
|
||||
}
|
||||
|
||||
f"#
|
||||
@ -772,8 +772,8 @@ mod test_fmt {
|
||||
r#"
|
||||
f :
|
||||
{
|
||||
y : I64,
|
||||
x : I64,
|
||||
y : Int *,
|
||||
x : Int *,
|
||||
}
|
||||
|
||||
f"#
|
||||
@ -787,8 +787,8 @@ mod test_fmt {
|
||||
r#"
|
||||
f :
|
||||
{
|
||||
y : I64,
|
||||
x : I64,
|
||||
y : Int *,
|
||||
x : Int *,
|
||||
}
|
||||
|
||||
f"#
|
||||
@ -800,7 +800,7 @@ mod test_fmt {
|
||||
expr_formats_same(indoc!(
|
||||
r#"
|
||||
f :
|
||||
I64
|
||||
Int *
|
||||
|
||||
f"#
|
||||
));
|
||||
@ -880,7 +880,7 @@ mod test_fmt {
|
||||
r#"
|
||||
f :
|
||||
{
|
||||
x: I64 # comment 1
|
||||
x: Int * # comment 1
|
||||
,
|
||||
# comment 2
|
||||
}
|
||||
@ -891,7 +891,7 @@ mod test_fmt {
|
||||
r#"
|
||||
f :
|
||||
{
|
||||
x : I64,
|
||||
x : Int *,
|
||||
# comment 1
|
||||
# comment 2
|
||||
}
|
||||
@ -2557,7 +2557,7 @@ mod test_fmt {
|
||||
fn record_type() {
|
||||
expr_formats_same(indoc!(
|
||||
r#"
|
||||
f : { foo : I64 }
|
||||
f : { foo : Int * }
|
||||
f = { foo: 1000 }
|
||||
|
||||
a
|
||||
@ -2608,11 +2608,11 @@ mod test_fmt {
|
||||
// r#"
|
||||
// f :
|
||||
// Result a
|
||||
// { x : I64
|
||||
// { x : Int *
|
||||
// , y : Float
|
||||
// }
|
||||
// c
|
||||
// -> I64
|
||||
// -> Int *
|
||||
// f =
|
||||
// \_ -> 4
|
||||
// "#
|
||||
|
@ -19,6 +19,7 @@ use crate::llvm::refcounting::{
|
||||
};
|
||||
use bumpalo::collections::Vec;
|
||||
use bumpalo::Bump;
|
||||
use either::Either;
|
||||
use inkwell::basic_block::BasicBlock;
|
||||
use inkwell::builder::Builder;
|
||||
use inkwell::context::Context;
|
||||
@ -41,7 +42,7 @@ use roc_collections::all::{ImMap, MutSet};
|
||||
use roc_module::ident::TagName;
|
||||
use roc_module::low_level::LowLevel;
|
||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||
use roc_mono::ir::{JoinPointId, Wrapped};
|
||||
use roc_mono::ir::{CallType, JoinPointId, Wrapped};
|
||||
use roc_mono::layout::{Builtin, ClosureLayout, Layout, LayoutIds, MemoryMode};
|
||||
use target_lexicon::CallingConvention;
|
||||
|
||||
@ -444,6 +445,14 @@ pub fn construct_optimization_passes<'a>(
|
||||
fpm.add_memcpy_optimize_pass(); // this one is very important
|
||||
|
||||
fpm.add_licm_pass();
|
||||
|
||||
// turn invoke into call
|
||||
mpm.add_prune_eh_pass();
|
||||
|
||||
// remove unused global values (often the `_wrapper` can be removed)
|
||||
mpm.add_global_dce_pass();
|
||||
|
||||
mpm.add_function_inlining_pass();
|
||||
}
|
||||
}
|
||||
|
||||
@ -490,15 +499,52 @@ fn get_inplace_from_layout(layout: &Layout<'_>) -> InPlace {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn int_with_precision<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
value: i128,
|
||||
precision: &Builtin,
|
||||
) -> IntValue<'ctx> {
|
||||
match precision {
|
||||
Builtin::Usize => ptr_int(env.context, env.ptr_bytes).const_int(value as u64, false),
|
||||
Builtin::Int128 => const_i128(env, value),
|
||||
Builtin::Int64 => env.context.i64_type().const_int(value as u64, false),
|
||||
Builtin::Int32 => env.context.i32_type().const_int(value as u64, false),
|
||||
Builtin::Int16 => env.context.i16_type().const_int(value as u64, false),
|
||||
Builtin::Int8 => env.context.i8_type().const_int(value as u64, false),
|
||||
_ => panic!("Invalid layout for int literal = {:?}", precision),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn float_with_precision<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
value: f64,
|
||||
precision: &Builtin,
|
||||
) -> FloatValue<'ctx> {
|
||||
match precision {
|
||||
Builtin::Float64 => env.context.f64_type().const_float(value),
|
||||
Builtin::Float32 => env.context.f32_type().const_float(value),
|
||||
_ => panic!("Invalid layout for float literal = {:?}", precision),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_exp_literal<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout: &Layout<'_>,
|
||||
literal: &roc_mono::ir::Literal<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
use roc_mono::ir::Literal::*;
|
||||
|
||||
match literal {
|
||||
Int(num) => env.context.i64_type().const_int(*num as u64, true).into(),
|
||||
Float(num) => env.context.f64_type().const_float(*num).into(),
|
||||
Int(int) => match layout {
|
||||
Layout::Builtin(builtin) => int_with_precision(env, *int as i128, builtin).into(),
|
||||
_ => panic!("Invalid layout for int literal = {:?}", layout),
|
||||
},
|
||||
|
||||
Float(float) => match layout {
|
||||
Layout::Builtin(builtin) => float_with_precision(env, *float, builtin).into(),
|
||||
_ => panic!("Invalid layout for float literal = {:?}", layout),
|
||||
},
|
||||
|
||||
Bool(b) => env.context.bool_type().const_int(*b as u64, false).into(),
|
||||
Byte(b) => env.context.i8_type().const_int(*b as u64, false).into(),
|
||||
Str(str_literal) => {
|
||||
@ -625,101 +671,27 @@ pub fn build_exp_literal<'a, 'ctx, 'env>(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||
pub fn build_exp_call<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
scope: &Scope<'a, 'ctx>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
layout: &Layout<'a>,
|
||||
expr: &roc_mono::ir::Expr<'a>,
|
||||
call: &roc_mono::ir::Call<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
use roc_mono::ir::CallType::*;
|
||||
use roc_mono::ir::Expr::*;
|
||||
let roc_mono::ir::Call {
|
||||
call_type,
|
||||
arguments,
|
||||
} = call;
|
||||
|
||||
match expr {
|
||||
Literal(literal) => build_exp_literal(env, literal),
|
||||
RunLowLevel(op, symbols) => {
|
||||
run_low_level(env, layout_ids, scope, parent, layout, *op, symbols)
|
||||
}
|
||||
|
||||
ForeignCall {
|
||||
foreign_symbol,
|
||||
arguments,
|
||||
ret_layout,
|
||||
match call_type {
|
||||
CallType::ByName {
|
||||
name, full_layout, ..
|
||||
} => {
|
||||
let mut arg_vals: Vec<BasicValueEnum> =
|
||||
let mut arg_tuples: Vec<BasicValueEnum> =
|
||||
Vec::with_capacity_in(arguments.len(), env.arena);
|
||||
|
||||
let mut arg_types = Vec::with_capacity_in(arguments.len() + 1, env.arena);
|
||||
|
||||
// crude approximation of the C calling convention
|
||||
let pass_result_by_pointer = ret_layout.stack_size(env.ptr_bytes) > 2 * env.ptr_bytes;
|
||||
|
||||
if pass_result_by_pointer {
|
||||
// the return value is too big to pass through a register, so the caller must
|
||||
// allocate space for it on its stack, and provide a pointer to write the result into
|
||||
let ret_type =
|
||||
basic_type_from_layout(env.arena, env.context, ret_layout, env.ptr_bytes);
|
||||
|
||||
let ret_ptr_type = get_ptr_type(&ret_type, AddressSpace::Generic);
|
||||
|
||||
let ret_ptr = env.builder.build_alloca(ret_type, "return_value");
|
||||
|
||||
arg_vals.push(ret_ptr.into());
|
||||
arg_types.push(ret_ptr_type.into());
|
||||
|
||||
for arg in arguments.iter() {
|
||||
let (value, layout) = load_symbol_and_layout(env, scope, arg);
|
||||
arg_vals.push(value);
|
||||
let arg_type =
|
||||
basic_type_from_layout(env.arena, env.context, layout, env.ptr_bytes);
|
||||
arg_types.push(arg_type);
|
||||
}
|
||||
|
||||
let function_type = env.context.void_type().fn_type(&arg_types, false);
|
||||
let function = get_foreign_symbol(env, foreign_symbol.clone(), function_type);
|
||||
|
||||
let call = env.builder.build_call(function, arg_vals.as_slice(), "tmp");
|
||||
|
||||
// this is a foreign function, use c calling convention
|
||||
call.set_call_convention(C_CALL_CONV);
|
||||
|
||||
call.try_as_basic_value();
|
||||
|
||||
env.builder.build_load(ret_ptr, "read_result")
|
||||
} else {
|
||||
for arg in arguments.iter() {
|
||||
let (value, layout) = load_symbol_and_layout(env, scope, arg);
|
||||
arg_vals.push(value);
|
||||
let arg_type =
|
||||
basic_type_from_layout(env.arena, env.context, layout, env.ptr_bytes);
|
||||
arg_types.push(arg_type);
|
||||
}
|
||||
|
||||
let ret_type =
|
||||
basic_type_from_layout(env.arena, env.context, ret_layout, env.ptr_bytes);
|
||||
let function_type = get_fn_type(&ret_type, &arg_types);
|
||||
let function = get_foreign_symbol(env, foreign_symbol.clone(), function_type);
|
||||
|
||||
let call = env.builder.build_call(function, arg_vals.as_slice(), "tmp");
|
||||
|
||||
// this is a foreign function, use c calling convention
|
||||
call.set_call_convention(C_CALL_CONV);
|
||||
|
||||
call.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap_or_else(|| panic!("LLVM error: Invalid call by pointer."))
|
||||
}
|
||||
}
|
||||
FunctionCall {
|
||||
call_type: ByName(name),
|
||||
full_layout,
|
||||
args,
|
||||
..
|
||||
} => {
|
||||
let mut arg_tuples: Vec<BasicValueEnum> = Vec::with_capacity_in(args.len(), env.arena);
|
||||
|
||||
for symbol in args.iter() {
|
||||
for symbol in arguments.iter() {
|
||||
arg_tuples.push(load_symbol(env, scope, symbol));
|
||||
}
|
||||
|
||||
@ -733,16 +705,13 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||
)
|
||||
}
|
||||
|
||||
FunctionCall {
|
||||
call_type: ByPointer(name),
|
||||
args,
|
||||
..
|
||||
} => {
|
||||
CallType::ByPointer { name, .. } => {
|
||||
let sub_expr = load_symbol(env, scope, name);
|
||||
|
||||
let mut arg_vals: Vec<BasicValueEnum> = Vec::with_capacity_in(args.len(), env.arena);
|
||||
let mut arg_vals: Vec<BasicValueEnum> =
|
||||
Vec::with_capacity_in(arguments.len(), env.arena);
|
||||
|
||||
for arg in args.iter() {
|
||||
for arg in arguments.iter() {
|
||||
arg_vals.push(load_symbol(env, scope, arg));
|
||||
}
|
||||
|
||||
@ -771,6 +740,33 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||
.unwrap_or_else(|| panic!("LLVM error: Invalid call by pointer."))
|
||||
}
|
||||
|
||||
CallType::LowLevel { op } => {
|
||||
run_low_level(env, layout_ids, scope, parent, layout, *op, arguments)
|
||||
}
|
||||
|
||||
CallType::Foreign {
|
||||
foreign_symbol: foreign,
|
||||
ret_layout,
|
||||
} => build_foreign_symbol(env, scope, foreign, arguments, ret_layout),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
scope: &Scope<'a, 'ctx>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
layout: &Layout<'a>,
|
||||
expr: &roc_mono::ir::Expr<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
use inkwell::types::BasicType;
|
||||
use roc_mono::ir::Expr::*;
|
||||
|
||||
match expr {
|
||||
Literal(literal) => build_exp_literal(env, layout, literal),
|
||||
|
||||
Call(call) => build_exp_call(env, layout_ids, scope, parent, layout, call),
|
||||
|
||||
Struct(sorted_fields) => {
|
||||
let ctx = env.context;
|
||||
let builder = env.builder;
|
||||
@ -916,15 +912,9 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||
field_types.push(field_type);
|
||||
|
||||
if let Layout::RecursivePointer = tag_field_layout {
|
||||
let ptr = allocate_with_refcount(env, &tag_layout, val);
|
||||
|
||||
let ptr = cast_basic_basic(
|
||||
builder,
|
||||
ptr.into(),
|
||||
ctx.i64_type().ptr_type(AddressSpace::Generic).into(),
|
||||
panic!(
|
||||
r"non-recursive tag unions cannot directly contain a recursive pointer"
|
||||
);
|
||||
|
||||
field_vals.push(ptr);
|
||||
} else {
|
||||
// this check fails for recursive tag unions, but can be helpful while debugging
|
||||
debug_assert_eq!(tag_field_layout, val_layout);
|
||||
@ -983,7 +973,88 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||
internal_type,
|
||||
)
|
||||
}
|
||||
Tag { .. } => unreachable!("tags should have a union layout"),
|
||||
Tag {
|
||||
arguments,
|
||||
tag_layout: Layout::RecursiveUnion(fields),
|
||||
union_size,
|
||||
tag_id,
|
||||
tag_name,
|
||||
..
|
||||
} => {
|
||||
let tag_layout = Layout::Union(fields);
|
||||
|
||||
debug_assert!(*union_size > 1);
|
||||
let ptr_size = env.ptr_bytes;
|
||||
|
||||
let ctx = env.context;
|
||||
let builder = env.builder;
|
||||
|
||||
// Determine types
|
||||
let num_fields = arguments.len() + 1;
|
||||
let mut field_types = Vec::with_capacity_in(num_fields, env.arena);
|
||||
let mut field_vals = Vec::with_capacity_in(num_fields, env.arena);
|
||||
|
||||
let tag_field_layouts = if let TagName::Closure(_) = tag_name {
|
||||
// closures ignore (and do not store) the discriminant
|
||||
&fields[*tag_id as usize][1..]
|
||||
} else {
|
||||
&fields[*tag_id as usize]
|
||||
};
|
||||
|
||||
for (field_symbol, tag_field_layout) in arguments.iter().zip(tag_field_layouts.iter()) {
|
||||
let (val, val_layout) = load_symbol_and_layout(env, scope, field_symbol);
|
||||
|
||||
// Zero-sized fields have no runtime representation.
|
||||
// The layout of the struct expects them to be dropped!
|
||||
if !tag_field_layout.is_dropped_because_empty() {
|
||||
let field_type =
|
||||
basic_type_from_layout(env.arena, env.context, tag_field_layout, ptr_size);
|
||||
|
||||
field_types.push(field_type);
|
||||
|
||||
if let Layout::RecursivePointer = tag_field_layout {
|
||||
debug_assert!(val.is_pointer_value());
|
||||
|
||||
// we store recursive pointers as `i64*`
|
||||
let ptr = cast_basic_basic(
|
||||
builder,
|
||||
val,
|
||||
ctx.i64_type().ptr_type(AddressSpace::Generic).into(),
|
||||
);
|
||||
|
||||
field_vals.push(ptr);
|
||||
} else {
|
||||
// this check fails for recursive tag unions, but can be helpful while debugging
|
||||
debug_assert_eq!(tag_field_layout, val_layout);
|
||||
|
||||
field_vals.push(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the struct_type
|
||||
let data_ptr = reserve_with_refcount(env, &tag_layout);
|
||||
let struct_type = ctx.struct_type(field_types.into_bump_slice(), false);
|
||||
let struct_ptr = cast_basic_basic(
|
||||
builder,
|
||||
data_ptr.into(),
|
||||
struct_type.ptr_type(AddressSpace::Generic).into(),
|
||||
)
|
||||
.into_pointer_value();
|
||||
|
||||
// Insert field exprs into struct_val
|
||||
for (index, field_val) in field_vals.into_iter().enumerate() {
|
||||
let field_ptr = builder
|
||||
.build_struct_gep(struct_ptr, index as u32, "struct_gep")
|
||||
.unwrap();
|
||||
|
||||
builder.build_store(field_ptr, field_val);
|
||||
}
|
||||
|
||||
data_ptr.into()
|
||||
}
|
||||
|
||||
Tag { .. } => unreachable!("tags should have a Union or RecursiveUnion layout"),
|
||||
|
||||
Reset(_) => todo!(),
|
||||
Reuse { .. } => todo!(),
|
||||
@ -1048,6 +1119,8 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||
field_layouts,
|
||||
..
|
||||
} => {
|
||||
use BasicValueEnum::*;
|
||||
|
||||
let builder = env.builder;
|
||||
|
||||
// Determine types, assumes the descriminant is in the field layouts
|
||||
@ -1067,28 +1140,61 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||
.struct_type(field_types.into_bump_slice(), false);
|
||||
|
||||
// cast the argument bytes into the desired shape for this tag
|
||||
let argument = load_symbol(env, scope, structure).into_struct_value();
|
||||
let argument = load_symbol(env, scope, structure);
|
||||
|
||||
let struct_value = cast_struct_struct(builder, argument, struct_type);
|
||||
let struct_layout = Layout::Struct(field_layouts);
|
||||
match argument {
|
||||
StructValue(value) => {
|
||||
let struct_value = cast_struct_struct(builder, value, struct_type);
|
||||
|
||||
let result = builder
|
||||
.build_extract_value(struct_value, *index as u32, "")
|
||||
.expect("desired field did not decode");
|
||||
let result = builder
|
||||
.build_extract_value(struct_value, *index as u32, "")
|
||||
.expect("desired field did not decode");
|
||||
|
||||
if let Some(Layout::RecursivePointer) = field_layouts.get(*index as usize) {
|
||||
let struct_layout = Layout::Struct(field_layouts);
|
||||
let desired_type = block_of_memory(env.context, &struct_layout, env.ptr_bytes);
|
||||
if let Some(Layout::RecursivePointer) = field_layouts.get(*index as usize) {
|
||||
let desired_type =
|
||||
block_of_memory(env.context, &struct_layout, env.ptr_bytes);
|
||||
|
||||
// the value is a pointer to the actual value; load that value!
|
||||
use inkwell::types::BasicType;
|
||||
let ptr = cast_basic_basic(
|
||||
builder,
|
||||
result,
|
||||
desired_type.ptr_type(AddressSpace::Generic).into(),
|
||||
);
|
||||
builder.build_load(ptr.into_pointer_value(), "load_recursive_field")
|
||||
} else {
|
||||
result
|
||||
// the value is a pointer to the actual value; load that value!
|
||||
let ptr = cast_basic_basic(
|
||||
builder,
|
||||
result,
|
||||
desired_type.ptr_type(AddressSpace::Generic).into(),
|
||||
);
|
||||
builder.build_load(ptr.into_pointer_value(), "load_recursive_field")
|
||||
} else {
|
||||
result
|
||||
}
|
||||
}
|
||||
PointerValue(value) => {
|
||||
let ptr = cast_basic_basic(
|
||||
builder,
|
||||
value.into(),
|
||||
struct_type.ptr_type(AddressSpace::Generic).into(),
|
||||
)
|
||||
.into_pointer_value();
|
||||
|
||||
let elem_ptr = builder
|
||||
.build_struct_gep(ptr, *index as u32, "at_index_struct_gep")
|
||||
.unwrap();
|
||||
|
||||
let result = builder.build_load(elem_ptr, "load_at_index_ptr");
|
||||
|
||||
if let Some(Layout::RecursivePointer) = field_layouts.get(*index as usize) {
|
||||
// a recursive field is stored as a `i64*`, to use it we must cast it to
|
||||
// a pointer to the block of memory representation
|
||||
cast_basic_basic(
|
||||
builder,
|
||||
result,
|
||||
block_of_memory(env.context, &struct_layout, env.ptr_bytes)
|
||||
.ptr_type(AddressSpace::Generic)
|
||||
.into(),
|
||||
)
|
||||
} else {
|
||||
result
|
||||
}
|
||||
}
|
||||
_ => panic!("cannot look up index in {:?}", argument),
|
||||
}
|
||||
}
|
||||
EmptyArray => empty_polymorphic_list(env),
|
||||
@ -1134,12 +1240,10 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn allocate_with_refcount<'a, 'ctx, 'env>(
|
||||
pub fn reserve_with_refcount<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout: &Layout<'a>,
|
||||
value: BasicValueEnum<'ctx>,
|
||||
) -> PointerValue<'ctx> {
|
||||
let builder = env.builder;
|
||||
let ctx = env.context;
|
||||
|
||||
let len_type = env.ptr_int();
|
||||
@ -1149,10 +1253,18 @@ pub fn allocate_with_refcount<'a, 'ctx, 'env>(
|
||||
|
||||
let rc1 = crate::llvm::refcounting::refcount_1(ctx, env.ptr_bytes);
|
||||
|
||||
let data_ptr = allocate_with_refcount_help(env, layout, value_bytes_intvalue, rc1);
|
||||
allocate_with_refcount_help(env, layout, value_bytes_intvalue, rc1)
|
||||
}
|
||||
|
||||
pub fn allocate_with_refcount<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout: &Layout<'a>,
|
||||
value: BasicValueEnum<'ctx>,
|
||||
) -> PointerValue<'ctx> {
|
||||
let data_ptr = reserve_with_refcount(env, layout);
|
||||
|
||||
// store the value in the pointer
|
||||
builder.build_store(data_ptr, value);
|
||||
env.builder.build_store(data_ptr, value);
|
||||
|
||||
data_ptr
|
||||
}
|
||||
@ -1299,6 +1411,92 @@ fn list_literal<'a, 'ctx, 'env>(
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn invoke_roc_function<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
scope: &mut Scope<'a, 'ctx>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
symbol: Symbol,
|
||||
layout: Layout<'a>,
|
||||
function_value: Either<FunctionValue<'ctx>, PointerValue<'ctx>>,
|
||||
arguments: &[Symbol],
|
||||
pass: &'a roc_mono::ir::Stmt<'a>,
|
||||
fail: &'a roc_mono::ir::Stmt<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let context = env.context;
|
||||
|
||||
let call_bt = basic_type_from_layout(env.arena, context, &layout, env.ptr_bytes);
|
||||
let alloca = create_entry_block_alloca(env, parent, call_bt, symbol.ident_string(&env.interns));
|
||||
|
||||
let mut arg_vals: Vec<BasicValueEnum> = Vec::with_capacity_in(arguments.len(), env.arena);
|
||||
|
||||
for arg in arguments.iter() {
|
||||
arg_vals.push(load_symbol(env, scope, arg));
|
||||
}
|
||||
|
||||
let pass_block = context.append_basic_block(parent, "invoke_pass");
|
||||
let fail_block = context.append_basic_block(parent, "invoke_fail");
|
||||
|
||||
let call_result = {
|
||||
let call = env.builder.build_invoke(
|
||||
function_value,
|
||||
arg_vals.as_slice(),
|
||||
pass_block,
|
||||
fail_block,
|
||||
"tmp",
|
||||
);
|
||||
|
||||
match function_value {
|
||||
Either::Left(function) => {
|
||||
call.set_call_convention(function.get_call_conventions());
|
||||
}
|
||||
Either::Right(_) => {
|
||||
call.set_call_convention(FAST_CALL_CONV);
|
||||
}
|
||||
}
|
||||
|
||||
call.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap_or_else(|| panic!("LLVM error: Invalid call by pointer."))
|
||||
};
|
||||
|
||||
{
|
||||
env.builder.position_at_end(pass_block);
|
||||
|
||||
env.builder.build_store(alloca, call_result);
|
||||
scope.insert(symbol, (layout, alloca));
|
||||
|
||||
build_exp_stmt(env, layout_ids, scope, parent, pass);
|
||||
|
||||
scope.remove(&symbol);
|
||||
}
|
||||
|
||||
{
|
||||
env.builder.position_at_end(fail_block);
|
||||
|
||||
let landing_pad_type = {
|
||||
let exception_ptr = context.i8_type().ptr_type(AddressSpace::Generic).into();
|
||||
let selector_value = context.i32_type().into();
|
||||
|
||||
context.struct_type(&[exception_ptr, selector_value], false)
|
||||
};
|
||||
|
||||
env.builder
|
||||
.build_catch_all_landing_pad(
|
||||
&landing_pad_type,
|
||||
&BasicValueEnum::IntValue(context.i8_type().const_zero()),
|
||||
context.i8_type().ptr_type(AddressSpace::Generic),
|
||||
"invoke_landing_pad",
|
||||
)
|
||||
.into_struct_value();
|
||||
|
||||
build_exp_stmt(env, layout_ids, scope, parent, fail);
|
||||
}
|
||||
|
||||
call_result
|
||||
}
|
||||
|
||||
pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
@ -1324,6 +1522,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||
let mut stack = Vec::with_capacity_in(queue.len(), env.arena);
|
||||
|
||||
for (symbol, expr, layout) in queue {
|
||||
debug_assert!(layout != &Layout::RecursivePointer);
|
||||
let context = &env.context;
|
||||
|
||||
let val = build_exp_expr(env, layout_ids, &scope, parent, layout, &expr);
|
||||
@ -1381,6 +1580,92 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||
value
|
||||
}
|
||||
|
||||
Invoke {
|
||||
symbol,
|
||||
call,
|
||||
layout,
|
||||
pass,
|
||||
fail: roc_mono::ir::Stmt::Rethrow,
|
||||
} => {
|
||||
// when the fail case is just Rethrow, there is no cleanup work to do
|
||||
// so we can just treat this invoke as a normal call
|
||||
let stmt =
|
||||
roc_mono::ir::Stmt::Let(*symbol, Expr::Call(call.clone()), layout.clone(), pass);
|
||||
build_exp_stmt(env, layout_ids, scope, parent, &stmt)
|
||||
}
|
||||
|
||||
Invoke {
|
||||
symbol,
|
||||
call,
|
||||
layout,
|
||||
pass,
|
||||
fail,
|
||||
} => match call.call_type {
|
||||
CallType::ByName {
|
||||
name,
|
||||
ref full_layout,
|
||||
..
|
||||
} => {
|
||||
let function_value = function_value_by_name(env, layout_ids, full_layout, name);
|
||||
|
||||
invoke_roc_function(
|
||||
env,
|
||||
layout_ids,
|
||||
scope,
|
||||
parent,
|
||||
*symbol,
|
||||
layout.clone(),
|
||||
function_value.into(),
|
||||
call.arguments,
|
||||
pass,
|
||||
fail,
|
||||
)
|
||||
}
|
||||
CallType::ByPointer { name, .. } => {
|
||||
let sub_expr = load_symbol(env, scope, &name);
|
||||
|
||||
let function_ptr = match sub_expr {
|
||||
BasicValueEnum::PointerValue(ptr) => ptr,
|
||||
non_ptr => {
|
||||
panic!(
|
||||
"Tried to call by pointer, but encountered a non-pointer: {:?}",
|
||||
non_ptr
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
invoke_roc_function(
|
||||
env,
|
||||
layout_ids,
|
||||
scope,
|
||||
parent,
|
||||
*symbol,
|
||||
layout.clone(),
|
||||
function_ptr.into(),
|
||||
call.arguments,
|
||||
pass,
|
||||
fail,
|
||||
)
|
||||
}
|
||||
CallType::Foreign {
|
||||
ref foreign_symbol,
|
||||
ref ret_layout,
|
||||
} => build_foreign_symbol(env, scope, foreign_symbol, call.arguments, ret_layout),
|
||||
|
||||
CallType::LowLevel { .. } => {
|
||||
unreachable!("lowlevel itself never throws exceptions")
|
||||
}
|
||||
},
|
||||
|
||||
Rethrow => {
|
||||
cxa_rethrow_exception(env);
|
||||
|
||||
// used in exception handling
|
||||
env.builder.build_unreachable();
|
||||
|
||||
env.context.i64_type().const_zero().into()
|
||||
}
|
||||
|
||||
Switch {
|
||||
branches,
|
||||
default_branch,
|
||||
@ -1594,6 +1879,22 @@ struct SwitchArgsIr<'a, 'ctx> {
|
||||
pub ret_type: BasicTypeEnum<'ctx>,
|
||||
}
|
||||
|
||||
fn const_i128<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>, value: i128) -> IntValue<'ctx> {
|
||||
// TODO verify the order [a, b] is correct for larger numbers when we can parse them
|
||||
debug_assert!(value <= i64::MAX as i128);
|
||||
|
||||
// truncate the lower 64 bits
|
||||
let value = value as u128;
|
||||
let a = value as u64;
|
||||
|
||||
// get the upper 64 bits
|
||||
let b = (value >> 64) as u64;
|
||||
|
||||
env.context
|
||||
.i128_type()
|
||||
.const_int_arbitrary_precision(&[a, b])
|
||||
}
|
||||
|
||||
fn build_switch_ir<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
@ -1631,6 +1932,15 @@ fn build_switch_ir<'a, 'ctx, 'env>(
|
||||
.build_bitcast(full_cond, env.context.i64_type(), "")
|
||||
.into_int_value()
|
||||
}
|
||||
Layout::Builtin(Builtin::Float32) => {
|
||||
// float matches are done on the bit pattern
|
||||
cond_layout = Layout::Builtin(Builtin::Int32);
|
||||
let full_cond = load_symbol(env, scope, cond_symbol);
|
||||
|
||||
builder
|
||||
.build_bitcast(full_cond, env.context.i32_type(), "")
|
||||
.into_int_value()
|
||||
}
|
||||
Layout::Union(_) => {
|
||||
// we match on the discriminant, not the whole Tag
|
||||
cond_layout = Layout::Builtin(Builtin::Int64);
|
||||
@ -1658,8 +1968,11 @@ fn build_switch_ir<'a, 'ctx, 'env>(
|
||||
//
|
||||
// they either need to all be i8, or i64
|
||||
let int_val = match cond_layout {
|
||||
Layout::Builtin(Builtin::Int128) => context.i128_type().const_int(*int as u64, false), /* TODO file an issue: you can't currently have an int literal bigger than 64 bits long, and also (as we see here), you can't currently have (at least in Inkwell) a when-branch with an i128 literal in its pattren */
|
||||
Layout::Builtin(Builtin::Usize) => {
|
||||
ptr_int(env.context, env.ptr_bytes).const_int(*int as u64, false)
|
||||
}
|
||||
Layout::Builtin(Builtin::Int64) => context.i64_type().const_int(*int as u64, false),
|
||||
Layout::Builtin(Builtin::Int128) => const_i128(env, *int as i128),
|
||||
Layout::Builtin(Builtin::Int32) => context.i32_type().const_int(*int as u64, false),
|
||||
Layout::Builtin(Builtin::Int16) => context.i16_type().const_int(*int as u64, false),
|
||||
Layout::Builtin(Builtin::Int8) => context.i8_type().const_int(*int as u64, false),
|
||||
@ -2027,7 +2340,11 @@ fn make_exception_catcher<'a, 'ctx, 'env>(
|
||||
) -> FunctionValue<'ctx> {
|
||||
let wrapper_function_name = format!("{}_catcher", roc_function.get_name().to_str().unwrap());
|
||||
|
||||
make_exception_catching_wrapper(env, roc_function, &wrapper_function_name)
|
||||
let function_value = make_exception_catching_wrapper(env, roc_function, &wrapper_function_name);
|
||||
|
||||
function_value.set_linkage(Linkage::Internal);
|
||||
|
||||
function_value
|
||||
}
|
||||
|
||||
fn make_exception_catching_wrapper<'a, 'ctx, 'env>(
|
||||
@ -2539,6 +2856,29 @@ pub fn verify_fn(fn_val: FunctionValue<'_>) {
|
||||
}
|
||||
}
|
||||
|
||||
fn function_value_by_name<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
layout: &Layout<'a>,
|
||||
symbol: Symbol,
|
||||
) -> FunctionValue<'ctx> {
|
||||
let fn_name = layout_ids
|
||||
.get(symbol, layout)
|
||||
.to_symbol_string(symbol, &env.interns);
|
||||
let fn_name = fn_name.as_str();
|
||||
|
||||
env.module.get_function(fn_name).unwrap_or_else(|| {
|
||||
if symbol.is_builtin() {
|
||||
panic!("Unrecognized builtin function: {:?}", fn_name)
|
||||
} else {
|
||||
panic!(
|
||||
"Unrecognized non-builtin function: {:?} (symbol: {:?}, layout: {:?})",
|
||||
fn_name, symbol, layout
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// #[allow(clippy::cognitive_complexity)]
|
||||
#[inline(always)]
|
||||
fn call_with_args<'a, 'ctx, 'env>(
|
||||
@ -2549,21 +2889,7 @@ fn call_with_args<'a, 'ctx, 'env>(
|
||||
_parent: FunctionValue<'ctx>,
|
||||
args: &[BasicValueEnum<'ctx>],
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let fn_name = layout_ids
|
||||
.get(symbol, layout)
|
||||
.to_symbol_string(symbol, &env.interns);
|
||||
let fn_name = fn_name.as_str();
|
||||
|
||||
let fn_val = env.module.get_function(fn_name).unwrap_or_else(|| {
|
||||
if symbol.is_builtin() {
|
||||
panic!("Unrecognized builtin function: {:?}", fn_name)
|
||||
} else {
|
||||
panic!(
|
||||
"Unrecognized non-builtin function: {:?} (symbol: {:?}, layout: {:?})",
|
||||
fn_name, symbol, layout
|
||||
)
|
||||
}
|
||||
});
|
||||
let fn_val = function_value_by_name(env, layout_ids, layout, symbol);
|
||||
|
||||
let call = env.builder.build_call(fn_val, args, "call");
|
||||
|
||||
@ -2746,7 +3072,16 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||
|
||||
let inplace = get_inplace_from_layout(layout);
|
||||
|
||||
list_keep_if(env, inplace, parent, func, func_layout, list, list_layout)
|
||||
list_keep_if(
|
||||
env,
|
||||
layout_ids,
|
||||
inplace,
|
||||
parent,
|
||||
func,
|
||||
func_layout,
|
||||
list,
|
||||
list_layout,
|
||||
)
|
||||
}
|
||||
ListContains => {
|
||||
// List.contains : List elem, elem -> Bool
|
||||
@ -2756,7 +3091,15 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||
|
||||
let (elem, elem_layout) = load_symbol_and_layout(env, scope, &args[1]);
|
||||
|
||||
list_contains(env, parent, elem, elem_layout, list, list_layout)
|
||||
list_contains(
|
||||
env,
|
||||
layout_ids,
|
||||
parent,
|
||||
elem,
|
||||
elem_layout,
|
||||
list,
|
||||
list_layout,
|
||||
)
|
||||
}
|
||||
ListWalk => {
|
||||
debug_assert_eq!(args.len(), 3);
|
||||
@ -2849,7 +3192,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||
use roc_mono::layout::Builtin::*;
|
||||
|
||||
match arg_builtin {
|
||||
Int128 | Int64 | Int32 | Int16 | Int8 => {
|
||||
Usize | Int128 | Int64 | Int32 | Int16 | Int8 => {
|
||||
build_int_unary_op(env, arg.into_int_value(), arg_builtin, op)
|
||||
}
|
||||
Float128 | Float64 | Float32 | Float16 => {
|
||||
@ -2887,7 +3230,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||
let tag_lt = env.context.i8_type().const_int(2_u64, false);
|
||||
|
||||
match lhs_builtin {
|
||||
Int128 | Int64 | Int32 | Int16 | Int8 => {
|
||||
Usize | Int128 | Int64 | Int32 | Int16 | Int8 => {
|
||||
let are_equal = env.builder.build_int_compare(
|
||||
IntPredicate::EQ,
|
||||
lhs_arg.into_int_value(),
|
||||
@ -2959,7 +3302,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||
|
||||
build_num_binop(env, parent, lhs_arg, lhs_layout, rhs_arg, rhs_layout, op)
|
||||
}
|
||||
NumBitwiseAnd => {
|
||||
NumBitwiseAnd | NumBitwiseXor => {
|
||||
debug_assert_eq!(args.len(), 2);
|
||||
|
||||
let (lhs_arg, lhs_layout) = load_symbol_and_layout(env, scope, &args[0]);
|
||||
@ -2981,7 +3324,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||
let (lhs_arg, lhs_layout) = load_symbol_and_layout(env, scope, &args[0]);
|
||||
let (rhs_arg, rhs_layout) = load_symbol_and_layout(env, scope, &args[1]);
|
||||
|
||||
build_eq(env, lhs_arg, rhs_arg, lhs_layout, rhs_layout)
|
||||
build_eq(env, layout_ids, lhs_arg, rhs_arg, lhs_layout, rhs_layout)
|
||||
}
|
||||
NotEq => {
|
||||
debug_assert_eq!(args.len(), 2);
|
||||
@ -2989,7 +3332,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||
let (lhs_arg, lhs_layout) = load_symbol_and_layout(env, scope, &args[0]);
|
||||
let (rhs_arg, rhs_layout) = load_symbol_and_layout(env, scope, &args[1]);
|
||||
|
||||
build_neq(env, lhs_arg, rhs_arg, lhs_layout, rhs_layout)
|
||||
build_neq(env, layout_ids, lhs_arg, rhs_arg, lhs_layout, rhs_layout)
|
||||
}
|
||||
And => {
|
||||
// The (&&) operator
|
||||
@ -3095,6 +3438,73 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||
}
|
||||
}
|
||||
|
||||
fn build_foreign_symbol<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
scope: &Scope<'a, 'ctx>,
|
||||
foreign: &roc_module::ident::ForeignSymbol,
|
||||
arguments: &[Symbol],
|
||||
ret_layout: &Layout<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let mut arg_vals: Vec<BasicValueEnum> = Vec::with_capacity_in(arguments.len(), env.arena);
|
||||
|
||||
let mut arg_types = Vec::with_capacity_in(arguments.len() + 1, env.arena);
|
||||
|
||||
// crude approximation of the C calling convention
|
||||
let pass_result_by_pointer = ret_layout.stack_size(env.ptr_bytes) > 2 * env.ptr_bytes;
|
||||
|
||||
if pass_result_by_pointer {
|
||||
// the return value is too big to pass through a register, so the caller must
|
||||
// allocate space for it on its stack, and provide a pointer to write the result into
|
||||
let ret_type = basic_type_from_layout(env.arena, env.context, ret_layout, env.ptr_bytes);
|
||||
|
||||
let ret_ptr_type = get_ptr_type(&ret_type, AddressSpace::Generic);
|
||||
|
||||
let ret_ptr = env.builder.build_alloca(ret_type, "return_value");
|
||||
|
||||
arg_vals.push(ret_ptr.into());
|
||||
arg_types.push(ret_ptr_type.into());
|
||||
|
||||
for arg in arguments.iter() {
|
||||
let (value, layout) = load_symbol_and_layout(env, scope, arg);
|
||||
arg_vals.push(value);
|
||||
let arg_type = basic_type_from_layout(env.arena, env.context, layout, env.ptr_bytes);
|
||||
arg_types.push(arg_type);
|
||||
}
|
||||
|
||||
let function_type = env.context.void_type().fn_type(&arg_types, false);
|
||||
let function = get_foreign_symbol(env, foreign.clone(), function_type);
|
||||
|
||||
let call = env.builder.build_call(function, arg_vals.as_slice(), "tmp");
|
||||
|
||||
// this is a foreign function, use c calling convention
|
||||
call.set_call_convention(C_CALL_CONV);
|
||||
|
||||
call.try_as_basic_value();
|
||||
|
||||
env.builder.build_load(ret_ptr, "read_result")
|
||||
} else {
|
||||
for arg in arguments.iter() {
|
||||
let (value, layout) = load_symbol_and_layout(env, scope, arg);
|
||||
arg_vals.push(value);
|
||||
let arg_type = basic_type_from_layout(env.arena, env.context, layout, env.ptr_bytes);
|
||||
arg_types.push(arg_type);
|
||||
}
|
||||
|
||||
let ret_type = basic_type_from_layout(env.arena, env.context, ret_layout, env.ptr_bytes);
|
||||
let function_type = get_fn_type(&ret_type, &arg_types);
|
||||
let function = get_foreign_symbol(env, foreign.clone(), function_type);
|
||||
|
||||
let call = env.builder.build_call(function, arg_vals.as_slice(), "tmp");
|
||||
|
||||
// this is a foreign function, use c calling convention
|
||||
call.set_call_convention(C_CALL_CONV);
|
||||
|
||||
call.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap_or_else(|| panic!("LLVM error: Invalid call by pointer."))
|
||||
}
|
||||
}
|
||||
|
||||
fn maybe_inplace_list<'a, 'ctx, 'env, InPlace, CloneFirst, Empty>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
@ -3251,6 +3661,7 @@ fn build_int_binop<'a, 'ctx, 'env>(
|
||||
NumDivUnchecked => bd.build_int_signed_div(lhs, rhs, "div_int").into(),
|
||||
NumPowInt => call_bitcode_fn(env, &[lhs.into(), rhs.into()], &bitcode::NUM_POW_INT),
|
||||
NumBitwiseAnd => bd.build_and(lhs, rhs, "int_bitwise_and").into(),
|
||||
NumBitwiseXor => bd.build_xor(lhs, rhs, "int_bitwise_xor").into(),
|
||||
_ => {
|
||||
unreachable!("Unrecognized int binary operation: {:?}", op);
|
||||
}
|
||||
@ -3316,7 +3727,7 @@ pub fn build_num_binop<'a, 'ctx, 'env>(
|
||||
use roc_mono::layout::Builtin::*;
|
||||
|
||||
match lhs_builtin {
|
||||
Int128 | Int64 | Int32 | Int16 | Int8 => build_int_binop(
|
||||
Usize | Int128 | Int64 | Int32 | Int16 | Int8 => build_int_binop(
|
||||
env,
|
||||
parent,
|
||||
lhs_arg.into_int_value(),
|
||||
@ -3567,6 +3978,7 @@ fn build_int_unary_op<'a, 'ctx, 'env>(
|
||||
int_abs_raise_on_overflow(env, arg, arg_layout)
|
||||
}
|
||||
NumToFloat => {
|
||||
// TODO: Handle differnt sized numbers
|
||||
// This is an Int, so we need to convert it.
|
||||
bd.build_cast(
|
||||
InstructionOpcode::SIToFP,
|
||||
@ -3693,6 +4105,7 @@ fn build_float_unary_op<'a, 'ctx, 'env>(
|
||||
|
||||
let bd = env.builder;
|
||||
|
||||
// TODO: Handle differnt sized floats
|
||||
match op {
|
||||
NumNeg => bd.build_float_neg(arg, "negate_float").into(),
|
||||
NumAbs => env.call_intrinsic(LLVM_FABS_F64, &[arg.into()]),
|
||||
@ -3871,8 +4284,7 @@ fn cxa_throw_exception<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>, info: BasicVal
|
||||
call.set_call_convention(C_CALL_CONV);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn cxa_rethrow_exception<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> BasicValueEnum<'ctx> {
|
||||
fn cxa_rethrow_exception(env: &Env<'_, '_, '_>) {
|
||||
let name = "__cxa_rethrow";
|
||||
|
||||
let module = env.module;
|
||||
@ -3891,10 +4303,10 @@ fn cxa_rethrow_exception<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> BasicValu
|
||||
cxa_rethrow
|
||||
}
|
||||
};
|
||||
let call = env.builder.build_call(function, &[], "never_used");
|
||||
let call = env.builder.build_call(function, &[], "rethrow");
|
||||
|
||||
call.set_call_convention(C_CALL_CONV);
|
||||
call.try_as_basic_value().left().unwrap()
|
||||
// call.try_as_basic_value().left().unwrap()
|
||||
}
|
||||
|
||||
fn get_foreign_symbol<'a, 'ctx, 'env>(
|
||||
|
@ -1003,6 +1003,7 @@ pub fn list_walk_backwards<'a, 'ctx, 'env>(
|
||||
/// List.contains : List elem, elem -> Bool
|
||||
pub fn list_contains<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
elem: BasicValueEnum<'ctx>,
|
||||
elem_layout: &Layout<'a>,
|
||||
@ -1034,6 +1035,7 @@ pub fn list_contains<'a, 'ctx, 'env>(
|
||||
|
||||
list_contains_help(
|
||||
env,
|
||||
layout_ids,
|
||||
parent,
|
||||
length,
|
||||
list_ptr,
|
||||
@ -1043,8 +1045,10 @@ pub fn list_contains<'a, 'ctx, 'env>(
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn list_contains_help<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
length: IntValue<'ctx>,
|
||||
source_ptr: PointerValue<'ctx>,
|
||||
@ -1082,7 +1086,14 @@ pub fn list_contains_help<'a, 'ctx, 'env>(
|
||||
|
||||
let current_elem = builder.build_load(current_elem_ptr, "load_elem");
|
||||
|
||||
let has_found = build_eq(env, current_elem, elem, list_elem_layout, elem_layout);
|
||||
let has_found = build_eq(
|
||||
env,
|
||||
layout_ids,
|
||||
current_elem,
|
||||
elem,
|
||||
list_elem_layout,
|
||||
elem_layout,
|
||||
);
|
||||
|
||||
builder.build_store(bool_alloca, has_found.into_int_value());
|
||||
|
||||
@ -1111,8 +1122,10 @@ pub fn list_contains_help<'a, 'ctx, 'env>(
|
||||
}
|
||||
|
||||
/// List.keepIf : List elem, (elem -> Bool) -> List elem
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn list_keep_if<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
output_inplace: InPlace,
|
||||
parent: FunctionValue<'ctx>,
|
||||
func: BasicValueEnum<'ctx>,
|
||||
@ -1212,6 +1225,9 @@ pub fn list_keep_if<'a, 'ctx, 'env>(
|
||||
|
||||
builder.position_at_end(cont_block);
|
||||
|
||||
// consume the input list
|
||||
decrement_refcount_layout(env, parent, layout_ids, list, list_layout);
|
||||
|
||||
builder.build_load(result, "load_result")
|
||||
}
|
||||
}
|
||||
|
@ -77,6 +77,28 @@ fn str_symbol_to_i128<'a, 'ctx, 'env>(
|
||||
.into_int_value()
|
||||
}
|
||||
|
||||
fn str_to_i128<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
value: BasicValueEnum<'ctx>,
|
||||
) -> IntValue<'ctx> {
|
||||
let cell = env.builder.build_alloca(value.get_type(), "cell");
|
||||
|
||||
env.builder.build_store(cell, value);
|
||||
|
||||
let i128_ptr = env
|
||||
.builder
|
||||
.build_bitcast(
|
||||
cell,
|
||||
env.context.i128_type().ptr_type(AddressSpace::Generic),
|
||||
"cast",
|
||||
)
|
||||
.into_pointer_value();
|
||||
|
||||
env.builder
|
||||
.build_load(i128_ptr, "load_as_i128")
|
||||
.into_int_value()
|
||||
}
|
||||
|
||||
fn zig_str_to_struct<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
zig_str: StructValue<'ctx>,
|
||||
@ -222,3 +244,19 @@ pub fn str_from_int<'a, 'ctx, 'env>(
|
||||
|
||||
zig_str_to_struct(env, zig_result).into()
|
||||
}
|
||||
|
||||
/// Str.equal : Str, Str -> Bool
|
||||
pub fn str_equal<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
value1: BasicValueEnum<'ctx>,
|
||||
value2: BasicValueEnum<'ctx>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let str1_i128 = str_to_i128(env, value1);
|
||||
let str2_i128 = str_to_i128(env, value2);
|
||||
|
||||
call_bitcode_fn(
|
||||
env,
|
||||
&[str1_i128.into(), str2_i128.into()],
|
||||
&bitcode::STR_EQUAL,
|
||||
)
|
||||
}
|
||||
|
@ -1,10 +1,16 @@
|
||||
use crate::llvm::build::Env;
|
||||
use inkwell::values::BasicValueEnum;
|
||||
use inkwell::{FloatPredicate, IntPredicate};
|
||||
use roc_mono::layout::{Builtin, Layout};
|
||||
use crate::llvm::build::{set_name, FAST_CALL_CONV};
|
||||
use crate::llvm::build_list::{list_len, load_list_ptr};
|
||||
use crate::llvm::build_str::str_equal;
|
||||
use crate::llvm::convert::{basic_type_from_layout, get_ptr_type};
|
||||
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, StructValue};
|
||||
use inkwell::{AddressSpace, FloatPredicate, IntPredicate};
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_mono::layout::{Builtin, Layout, LayoutIds};
|
||||
|
||||
pub fn build_eq<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
lhs_val: BasicValueEnum<'ctx>,
|
||||
rhs_val: BasicValueEnum<'ctx>,
|
||||
lhs_layout: &Layout<'a>,
|
||||
@ -43,6 +49,26 @@ pub fn build_eq<'a, 'ctx, 'env>(
|
||||
(Builtin::Int1, Builtin::Int1) => int_cmp(IntPredicate::EQ, "eq_i1"),
|
||||
(Builtin::Float64, Builtin::Float64) => float_cmp(FloatPredicate::OEQ, "eq_f64"),
|
||||
(Builtin::Float32, Builtin::Float32) => float_cmp(FloatPredicate::OEQ, "eq_f32"),
|
||||
(Builtin::Str, Builtin::Str) => str_equal(env, lhs_val, rhs_val),
|
||||
(Builtin::EmptyList, Builtin::EmptyList) => {
|
||||
env.context.bool_type().const_int(1, false).into()
|
||||
}
|
||||
(Builtin::List(_, _), Builtin::EmptyList)
|
||||
| (Builtin::EmptyList, Builtin::List(_, _)) => {
|
||||
unreachable!("the `==` operator makes sure its two arguments have the same type and thus layout")
|
||||
}
|
||||
(Builtin::List(_, elem1), Builtin::List(_, elem2)) => {
|
||||
debug_assert_eq!(elem1, elem2);
|
||||
|
||||
build_list_eq(
|
||||
env,
|
||||
layout_ids,
|
||||
lhs_layout,
|
||||
elem1,
|
||||
lhs_val.into_struct_value(),
|
||||
rhs_val.into_struct_value(),
|
||||
)
|
||||
}
|
||||
(b1, b2) => {
|
||||
todo!("Handle equals for builtin layouts {:?} == {:?}", b1, b2);
|
||||
}
|
||||
@ -57,6 +83,7 @@ pub fn build_eq<'a, 'ctx, 'env>(
|
||||
|
||||
pub fn build_neq<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
lhs_val: BasicValueEnum<'ctx>,
|
||||
rhs_val: BasicValueEnum<'ctx>,
|
||||
lhs_layout: &Layout<'a>,
|
||||
@ -95,6 +122,35 @@ pub fn build_neq<'a, 'ctx, 'env>(
|
||||
(Builtin::Int1, Builtin::Int1) => int_cmp(IntPredicate::NE, "neq_i1"),
|
||||
(Builtin::Float64, Builtin::Float64) => float_cmp(FloatPredicate::ONE, "neq_f64"),
|
||||
(Builtin::Float32, Builtin::Float32) => float_cmp(FloatPredicate::ONE, "neq_f32"),
|
||||
(Builtin::Str, Builtin::Str) => {
|
||||
let is_equal = str_equal(env, lhs_val, rhs_val).into_int_value();
|
||||
let result: IntValue = env.builder.build_not(is_equal, "negate");
|
||||
|
||||
result.into()
|
||||
}
|
||||
(Builtin::EmptyList, Builtin::EmptyList) => {
|
||||
env.context.bool_type().const_int(0, false).into()
|
||||
}
|
||||
(Builtin::List(_, _), Builtin::EmptyList)
|
||||
| (Builtin::EmptyList, Builtin::List(_, _)) => {
|
||||
unreachable!("the `==` operator makes sure its two arguments have the same type and thus layout")
|
||||
}
|
||||
(Builtin::List(_, elem1), Builtin::List(_, elem2)) => {
|
||||
debug_assert_eq!(elem1, elem2);
|
||||
|
||||
let equal = build_list_eq(
|
||||
env,
|
||||
layout_ids,
|
||||
lhs_layout,
|
||||
elem1,
|
||||
lhs_val.into_struct_value(),
|
||||
rhs_val.into_struct_value(),
|
||||
);
|
||||
|
||||
let not_equal: IntValue = env.builder.build_not(equal.into_int_value(), "not");
|
||||
|
||||
not_equal.into()
|
||||
}
|
||||
(b1, b2) => {
|
||||
todo!("Handle not equals for builtin layouts {:?} == {:?}", b1, b2);
|
||||
}
|
||||
@ -110,3 +166,203 @@ pub fn build_neq<'a, 'ctx, 'env>(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn build_list_eq<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
list_layout: &Layout<'a>,
|
||||
element_layout: &Layout<'a>,
|
||||
list1: StructValue<'ctx>,
|
||||
list2: StructValue<'ctx>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let block = env.builder.get_insert_block().expect("to be in a function");
|
||||
let di_location = env.builder.get_current_debug_location().unwrap();
|
||||
|
||||
let symbol = Symbol::LIST_EQ;
|
||||
let fn_name = layout_ids
|
||||
.get(symbol, &element_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 arena = env.arena;
|
||||
let arg_type = basic_type_from_layout(arena, env.context, &list_layout, env.ptr_bytes);
|
||||
|
||||
let function_value = crate::llvm::refcounting::build_header_help(
|
||||
env,
|
||||
&fn_name,
|
||||
env.context.bool_type().into(),
|
||||
&[arg_type, arg_type],
|
||||
);
|
||||
|
||||
build_list_eq_help(env, layout_ids, function_value, element_layout);
|
||||
|
||||
function_value
|
||||
}
|
||||
};
|
||||
|
||||
env.builder.position_at_end(block);
|
||||
env.builder
|
||||
.set_current_debug_location(env.context, di_location);
|
||||
let call = env
|
||||
.builder
|
||||
.build_call(function, &[list1.into(), list2.into()], "list_eq");
|
||||
|
||||
call.set_call_convention(FAST_CALL_CONV);
|
||||
|
||||
call.try_as_basic_value().left().unwrap()
|
||||
}
|
||||
|
||||
fn build_list_eq_help<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
element_layout: &Layout<'a>,
|
||||
) {
|
||||
let ctx = env.context;
|
||||
let builder = env.builder;
|
||||
|
||||
{
|
||||
use inkwell::debug_info::AsDIScope;
|
||||
|
||||
let func_scope = parent.get_subprogram().unwrap();
|
||||
let lexical_block = env.dibuilder.create_lexical_block(
|
||||
/* scope */ func_scope.as_debug_info_scope(),
|
||||
/* file */ env.compile_unit.get_file(),
|
||||
/* line_no */ 0,
|
||||
/* column_no */ 0,
|
||||
);
|
||||
|
||||
let loc = env.dibuilder.create_debug_location(
|
||||
ctx,
|
||||
/* line */ 0,
|
||||
/* column */ 0,
|
||||
/* current_scope */ lexical_block.as_debug_info_scope(),
|
||||
/* inlined_at */ None,
|
||||
);
|
||||
builder.set_current_debug_location(&ctx, loc);
|
||||
}
|
||||
|
||||
// Add args to scope
|
||||
let mut it = parent.get_param_iter();
|
||||
let list1 = it.next().unwrap().into_struct_value();
|
||||
let list2 = it.next().unwrap().into_struct_value();
|
||||
|
||||
set_name(list1.into(), Symbol::ARG_1.ident_string(&env.interns));
|
||||
set_name(list2.into(), Symbol::ARG_2.ident_string(&env.interns));
|
||||
|
||||
let entry = ctx.append_basic_block(parent, "entry");
|
||||
env.builder.position_at_end(entry);
|
||||
|
||||
let return_true = ctx.append_basic_block(parent, "return_true");
|
||||
let return_false = ctx.append_basic_block(parent, "return_false");
|
||||
|
||||
// first, check whether the length is equal
|
||||
|
||||
let len1 = list_len(env.builder, list1);
|
||||
let len2 = list_len(env.builder, list2);
|
||||
|
||||
let length_equal: IntValue =
|
||||
env.builder
|
||||
.build_int_compare(IntPredicate::EQ, len1, len2, "bounds_check");
|
||||
|
||||
let then_block = ctx.append_basic_block(parent, "then");
|
||||
|
||||
env.builder
|
||||
.build_conditional_branch(length_equal, then_block, return_false);
|
||||
|
||||
{
|
||||
// the length is equal; check elements pointwise
|
||||
env.builder.position_at_end(then_block);
|
||||
|
||||
let builder = env.builder;
|
||||
let element_type =
|
||||
basic_type_from_layout(env.arena, env.context, element_layout, env.ptr_bytes);
|
||||
let ptr_type = get_ptr_type(&element_type, AddressSpace::Generic);
|
||||
let ptr1 = load_list_ptr(env.builder, list1, ptr_type);
|
||||
let ptr2 = load_list_ptr(env.builder, list2, ptr_type);
|
||||
|
||||
// we know that len1 == len2
|
||||
let end = len1;
|
||||
|
||||
// allocate a stack slot for the current index
|
||||
let index_alloca = builder.build_alloca(ctx.i64_type(), "index");
|
||||
builder.build_store(index_alloca, ctx.i64_type().const_zero());
|
||||
|
||||
let loop_bb = ctx.append_basic_block(parent, "loop");
|
||||
let body_bb = ctx.append_basic_block(parent, "body");
|
||||
let increment_bb = ctx.append_basic_block(parent, "increment");
|
||||
|
||||
// the "top" of the loop
|
||||
builder.build_unconditional_branch(loop_bb);
|
||||
builder.position_at_end(loop_bb);
|
||||
|
||||
let curr_index = builder.build_load(index_alloca, "index").into_int_value();
|
||||
|
||||
// #index < end
|
||||
let loop_end_cond =
|
||||
builder.build_int_compare(IntPredicate::ULT, curr_index, end, "bounds_check");
|
||||
|
||||
// if we're at the end, and all elements were equal so far, return true
|
||||
// otherwise check the current elements for equality
|
||||
builder.build_conditional_branch(loop_end_cond, body_bb, return_true);
|
||||
|
||||
{
|
||||
// loop body
|
||||
builder.position_at_end(body_bb);
|
||||
|
||||
let elem1 = {
|
||||
let elem_ptr =
|
||||
unsafe { builder.build_in_bounds_gep(ptr1, &[curr_index], "load_index") };
|
||||
builder.build_load(elem_ptr, "get_elem")
|
||||
};
|
||||
|
||||
let elem2 = {
|
||||
let elem_ptr =
|
||||
unsafe { builder.build_in_bounds_gep(ptr2, &[curr_index], "load_index") };
|
||||
builder.build_load(elem_ptr, "get_elem")
|
||||
};
|
||||
|
||||
let are_equal = build_eq(
|
||||
env,
|
||||
layout_ids,
|
||||
elem1,
|
||||
elem2,
|
||||
element_layout,
|
||||
element_layout,
|
||||
)
|
||||
.into_int_value();
|
||||
|
||||
// if the elements are equal, increment the index and check the next element
|
||||
// otherwise, return false
|
||||
builder.build_conditional_branch(are_equal, increment_bb, return_false);
|
||||
}
|
||||
|
||||
{
|
||||
env.builder.position_at_end(increment_bb);
|
||||
|
||||
// constant 1i64
|
||||
let one = ctx.i64_type().const_int(1, false);
|
||||
|
||||
let next_index = builder.build_int_add(curr_index, one, "nextindex");
|
||||
|
||||
builder.build_store(index_alloca, next_index);
|
||||
|
||||
// jump back to the top of the loop
|
||||
builder.build_unconditional_branch(loop_bb);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
env.builder.position_at_end(return_true);
|
||||
env.builder
|
||||
.build_return(Some(&env.context.bool_type().const_int(1, false)));
|
||||
}
|
||||
|
||||
{
|
||||
env.builder.position_at_end(return_false);
|
||||
env.builder
|
||||
.build_return(Some(&env.context.bool_type().const_int(0, false)));
|
||||
}
|
||||
}
|
||||
|
@ -137,7 +137,10 @@ pub fn basic_type_from_layout<'ctx>(
|
||||
|
||||
basic_type_from_record(arena, context, sorted_fields, ptr_bytes)
|
||||
}
|
||||
RecursiveUnion(_) | Union(_) => block_of_memory(context, layout, ptr_bytes),
|
||||
RecursiveUnion(_) => block_of_memory(context, layout, ptr_bytes)
|
||||
.ptr_type(AddressSpace::Generic)
|
||||
.into(),
|
||||
Union(_) => block_of_memory(context, layout, ptr_bytes),
|
||||
RecursivePointer => {
|
||||
// TODO make this dynamic
|
||||
context
|
||||
@ -165,6 +168,7 @@ pub fn basic_type_from_builtin<'ctx>(
|
||||
Int16 => context.i16_type().as_basic_type_enum(),
|
||||
Int8 => context.i8_type().as_basic_type_enum(),
|
||||
Int1 => context.bool_type().as_basic_type_enum(),
|
||||
Usize => ptr_int(context, ptr_bytes).as_basic_type_enum(),
|
||||
Float128 => context.f128_type().as_basic_type_enum(),
|
||||
Float64 => context.f64_type().as_basic_type_enum(),
|
||||
Float32 => context.f32_type().as_basic_type_enum(),
|
||||
|
@ -8,6 +8,7 @@ use bumpalo::collections::Vec;
|
||||
use inkwell::context::Context;
|
||||
use inkwell::debug_info::AsDIScope;
|
||||
use inkwell::module::Linkage;
|
||||
use inkwell::types::{AnyTypeEnum, BasicTypeEnum};
|
||||
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue};
|
||||
use inkwell::{AddressSpace, IntPredicate};
|
||||
use roc_module::symbol::Symbol;
|
||||
@ -342,7 +343,8 @@ pub fn decrement_refcount_layout<'a, 'ctx, 'env>(
|
||||
}
|
||||
|
||||
RecursiveUnion(tags) => {
|
||||
build_dec_union(env, layout_ids, tags, value);
|
||||
debug_assert!(value.is_pointer_value());
|
||||
build_dec_rec_union(env, layout_ids, tags, value.into_pointer_value());
|
||||
}
|
||||
|
||||
FunctionPointer(_, _) | Pointer(_) => {}
|
||||
@ -426,7 +428,8 @@ pub fn increment_refcount_layout<'a, 'ctx, 'env>(
|
||||
}
|
||||
|
||||
RecursiveUnion(tags) => {
|
||||
build_inc_union(env, layout_ids, tags, value);
|
||||
debug_assert!(value.is_pointer_value());
|
||||
build_inc_rec_union(env, layout_ids, tags, value.into_pointer_value());
|
||||
}
|
||||
Closure(_, closure_layout, _) => {
|
||||
if closure_layout.contains_refcounted() {
|
||||
@ -993,12 +996,28 @@ pub fn build_header<'a, 'ctx, 'env>(
|
||||
fn_name: &str,
|
||||
) -> FunctionValue<'ctx> {
|
||||
let arena = env.arena;
|
||||
let context = &env.context;
|
||||
|
||||
let arg_type = basic_type_from_layout(arena, env.context, &layout, env.ptr_bytes);
|
||||
build_header_help(env, fn_name, env.context.void_type().into(), &[arg_type])
|
||||
}
|
||||
|
||||
// inc and dec return void
|
||||
let fn_type = context.void_type().fn_type(&[arg_type], false);
|
||||
/// Build an increment or decrement function for a specific layout
|
||||
pub fn build_header_help<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
fn_name: &str,
|
||||
return_type: AnyTypeEnum<'ctx>,
|
||||
arguments: &[BasicTypeEnum<'ctx>],
|
||||
) -> FunctionValue<'ctx> {
|
||||
use inkwell::types::AnyTypeEnum::*;
|
||||
let fn_type = match return_type {
|
||||
ArrayType(t) => t.fn_type(arguments, false),
|
||||
FloatType(t) => t.fn_type(arguments, false),
|
||||
FunctionType(_) => unreachable!("functions cannot return functions"),
|
||||
IntType(t) => t.fn_type(arguments, false),
|
||||
PointerType(t) => t.fn_type(arguments, false),
|
||||
StructType(t) => t.fn_type(arguments, false),
|
||||
VectorType(t) => t.fn_type(arguments, false),
|
||||
VoidType(t) => t.fn_type(arguments, false),
|
||||
};
|
||||
|
||||
let fn_val = env
|
||||
.module
|
||||
@ -1015,6 +1034,202 @@ pub fn build_header<'a, 'ctx, 'env>(
|
||||
fn_val
|
||||
}
|
||||
|
||||
pub fn build_dec_rec_union<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
fields: &'a [&'a [Layout<'a>]],
|
||||
value: PointerValue<'ctx>,
|
||||
) {
|
||||
let layout = Layout::RecursiveUnion(fields);
|
||||
|
||||
let block = env.builder.get_insert_block().expect("to be in a function");
|
||||
let di_location = env.builder.get_current_debug_location().unwrap();
|
||||
|
||||
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_rec_union_help(env, layout_ids, fields, function_value);
|
||||
|
||||
function_value
|
||||
}
|
||||
};
|
||||
|
||||
env.builder.position_at_end(block);
|
||||
env.builder
|
||||
.set_current_debug_location(env.context, di_location);
|
||||
|
||||
let call = env
|
||||
.builder
|
||||
.build_call(function, &[value.into()], "decrement_union");
|
||||
|
||||
call.set_call_convention(FAST_CALL_CONV);
|
||||
}
|
||||
|
||||
pub fn build_dec_rec_union_help<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
tags: &[&[Layout<'a>]],
|
||||
fn_val: FunctionValue<'ctx>,
|
||||
) {
|
||||
debug_assert!(!tags.is_empty());
|
||||
|
||||
use inkwell::types::BasicType;
|
||||
|
||||
let context = &env.context;
|
||||
let builder = env.builder;
|
||||
|
||||
// Add a basic block for the entry point
|
||||
let entry = context.append_basic_block(fn_val, "entry");
|
||||
|
||||
builder.position_at_end(entry);
|
||||
|
||||
let func_scope = fn_val.get_subprogram().unwrap();
|
||||
let lexical_block = env.dibuilder.create_lexical_block(
|
||||
/* scope */ func_scope.as_debug_info_scope(),
|
||||
/* file */ env.compile_unit.get_file(),
|
||||
/* line_no */ 0,
|
||||
/* column_no */ 0,
|
||||
);
|
||||
|
||||
let loc = env.dibuilder.create_debug_location(
|
||||
context,
|
||||
/* line */ 0,
|
||||
/* column */ 0,
|
||||
/* current_scope */ lexical_block.as_debug_info_scope(),
|
||||
/* inlined_at */ None,
|
||||
);
|
||||
builder.set_current_debug_location(&context, loc);
|
||||
|
||||
// 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 parent = fn_val;
|
||||
|
||||
let layout = Layout::RecursiveUnion(tags);
|
||||
let before_block = env.builder.get_insert_block().expect("to be in a function");
|
||||
|
||||
debug_assert!(arg_val.is_pointer_value());
|
||||
let value_ptr = arg_val.into_pointer_value();
|
||||
|
||||
// next, make a jump table for all possible values of the tag_id
|
||||
let mut cases = Vec::with_capacity_in(tags.len(), env.arena);
|
||||
|
||||
let merge_block = env.context.append_basic_block(parent, "decrement_merge");
|
||||
|
||||
builder.set_current_debug_location(&context, loc);
|
||||
|
||||
for (tag_id, field_layouts) in tags.iter().enumerate() {
|
||||
// if none of the fields are or contain anything refcounted, just move on
|
||||
if !field_layouts
|
||||
.iter()
|
||||
.any(|x| x.is_refcounted() || x.contains_refcounted())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let block = env.context.append_basic_block(parent, "tag_id_decrement");
|
||||
env.builder.position_at_end(block);
|
||||
|
||||
let wrapper_type = basic_type_from_layout(
|
||||
env.arena,
|
||||
env.context,
|
||||
&Layout::Struct(field_layouts),
|
||||
env.ptr_bytes,
|
||||
);
|
||||
|
||||
// cast the opaque pointer to a pointer of the correct shape
|
||||
let struct_ptr = cast_basic_basic(
|
||||
env.builder,
|
||||
value_ptr.into(),
|
||||
wrapper_type.ptr_type(AddressSpace::Generic).into(),
|
||||
)
|
||||
.into_pointer_value();
|
||||
|
||||
for (i, field_layout) in field_layouts.iter().enumerate() {
|
||||
if let Layout::RecursivePointer = field_layout {
|
||||
// this field has type `*i64`, but is really a pointer to the data we want
|
||||
let elem_pointer = env
|
||||
.builder
|
||||
.build_struct_gep(struct_ptr, i as u32, "gep_recursive_pointer")
|
||||
.unwrap();
|
||||
|
||||
let ptr_as_i64_ptr = env
|
||||
.builder
|
||||
.build_load(elem_pointer, "load_recursive_pointer");
|
||||
|
||||
debug_assert!(ptr_as_i64_ptr.is_pointer_value());
|
||||
|
||||
// therefore we must cast it to our desired type
|
||||
let union_type =
|
||||
basic_type_from_layout(env.arena, env.context, &layout, env.ptr_bytes);
|
||||
let recursive_field_ptr = cast_basic_basic(env.builder, ptr_as_i64_ptr, union_type);
|
||||
|
||||
// recursively decrement the field
|
||||
let call = env.builder.build_call(
|
||||
fn_val,
|
||||
&[recursive_field_ptr],
|
||||
"recursive_tag_decrement",
|
||||
);
|
||||
|
||||
// Because it's an internal-only function, use the fast calling convention.
|
||||
call.set_call_convention(FAST_CALL_CONV);
|
||||
} else if field_layout.contains_refcounted() {
|
||||
// TODO this loads the whole field onto the stack;
|
||||
// that's wasteful if e.g. the field is a big record, where only
|
||||
// some fields are actually refcounted.
|
||||
let elem_pointer = env
|
||||
.builder
|
||||
.build_struct_gep(struct_ptr, i as u32, "gep_recursive_pointer")
|
||||
.unwrap();
|
||||
|
||||
let field = env
|
||||
.builder
|
||||
.build_load(elem_pointer, "decrement_struct_field");
|
||||
|
||||
decrement_refcount_layout(env, parent, layout_ids, field, field_layout);
|
||||
}
|
||||
}
|
||||
|
||||
env.builder.build_unconditional_branch(merge_block);
|
||||
|
||||
cases.push((
|
||||
env.context.i64_type().const_int(tag_id as u64, false),
|
||||
block,
|
||||
));
|
||||
}
|
||||
|
||||
cases.reverse();
|
||||
|
||||
env.builder.position_at_end(before_block);
|
||||
|
||||
// read the tag_id
|
||||
let current_tag_id = rec_union_read_tag(env, value_ptr);
|
||||
|
||||
// switch on it
|
||||
env.builder
|
||||
.build_switch(current_tag_id, merge_block, &cases);
|
||||
|
||||
env.builder.position_at_end(merge_block);
|
||||
|
||||
// decrement this cons-cell itself
|
||||
let refcount_ptr = PointerToRefcount::from_ptr_to_data(env, value_ptr);
|
||||
refcount_ptr.decrement(env, &layout);
|
||||
|
||||
// this function returns void
|
||||
builder.build_return(None);
|
||||
}
|
||||
|
||||
pub fn build_dec_union<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
@ -1061,8 +1276,6 @@ pub fn build_dec_union_help<'a, 'ctx, 'env>(
|
||||
) {
|
||||
debug_assert!(!tags.is_empty());
|
||||
|
||||
use inkwell::types::BasicType;
|
||||
|
||||
let context = &env.context;
|
||||
let builder = env.builder;
|
||||
|
||||
@ -1088,32 +1301,18 @@ pub fn build_dec_union_help<'a, 'ctx, 'env>(
|
||||
);
|
||||
builder.set_current_debug_location(&context, loc);
|
||||
|
||||
let mut scope = Scope::default();
|
||||
|
||||
// Add args to scope
|
||||
let arg_symbol = Symbol::ARG_1;
|
||||
let layout = Layout::Union(tags);
|
||||
|
||||
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 layout = Layout::RecursiveUnion(tags);
|
||||
let before_block = env.builder.get_insert_block().expect("to be in a function");
|
||||
|
||||
debug_assert!(arg_val.is_struct_value());
|
||||
let wrapper_struct = arg_val.into_struct_value();
|
||||
|
||||
// next, make a jump table for all possible values of the tag_id
|
||||
@ -1147,39 +1346,7 @@ pub fn build_dec_union_help<'a, 'ctx, 'env>(
|
||||
|
||||
for (i, field_layout) in field_layouts.iter().enumerate() {
|
||||
if let Layout::RecursivePointer = field_layout {
|
||||
// this field has type `*i64`, but is really a pointer to the data we want
|
||||
let ptr_as_i64_ptr = env
|
||||
.builder
|
||||
.build_extract_value(wrapper_struct, i as u32, "decrement_struct_field")
|
||||
.unwrap();
|
||||
|
||||
debug_assert!(ptr_as_i64_ptr.is_pointer_value());
|
||||
|
||||
// therefore we must cast it to our desired type
|
||||
let union_type = block_of_memory(env.context, &layout, env.ptr_bytes);
|
||||
let recursive_field_ptr = cast_basic_basic(
|
||||
env.builder,
|
||||
ptr_as_i64_ptr,
|
||||
union_type.ptr_type(AddressSpace::Generic).into(),
|
||||
)
|
||||
.into_pointer_value();
|
||||
|
||||
let recursive_field = env
|
||||
.builder
|
||||
.build_load(recursive_field_ptr, "load_recursive_field");
|
||||
|
||||
// recursively decrement the field
|
||||
let call =
|
||||
env.builder
|
||||
.build_call(fn_val, &[recursive_field], "recursive_tag_decrement");
|
||||
|
||||
// Because it's an internal-only function, use the fast calling convention.
|
||||
call.set_call_convention(FAST_CALL_CONV);
|
||||
|
||||
// TODO do this decrement before the recursive call?
|
||||
// Then the recursive call is potentially TCE'd
|
||||
let refcount_ptr = PointerToRefcount::from_ptr_to_data(env, recursive_field_ptr);
|
||||
refcount_ptr.decrement(env, &layout);
|
||||
panic!("a non-recursive tag union cannot contain RecursivePointer");
|
||||
} else if field_layout.contains_refcounted() {
|
||||
let field_ptr = env
|
||||
.builder
|
||||
@ -1227,6 +1394,213 @@ pub fn build_dec_union_help<'a, 'ctx, 'env>(
|
||||
builder.build_return(None);
|
||||
}
|
||||
|
||||
pub fn build_inc_rec_union<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
fields: &'a [&'a [Layout<'a>]],
|
||||
value: PointerValue<'ctx>,
|
||||
) {
|
||||
let layout = Layout::RecursiveUnion(fields);
|
||||
|
||||
let block = env.builder.get_insert_block().expect("to be in a function");
|
||||
let di_location = env.builder.get_current_debug_location().unwrap();
|
||||
|
||||
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_rec_union_help(env, layout_ids, fields, function_value);
|
||||
|
||||
function_value
|
||||
}
|
||||
};
|
||||
|
||||
env.builder.position_at_end(block);
|
||||
env.builder
|
||||
.set_current_debug_location(env.context, di_location);
|
||||
let call = env
|
||||
.builder
|
||||
.build_call(function, &[value.into()], "increment_union");
|
||||
|
||||
call.set_call_convention(FAST_CALL_CONV);
|
||||
}
|
||||
|
||||
fn rec_union_read_tag<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
value_ptr: PointerValue<'ctx>,
|
||||
) -> IntValue<'ctx> {
|
||||
// Assumption: the tag is the first thing stored
|
||||
// so cast the pointer to the data to a `i64*`
|
||||
let tag_ptr = cast_basic_basic(
|
||||
env.builder,
|
||||
value_ptr.into(),
|
||||
env.context
|
||||
.i64_type()
|
||||
.ptr_type(AddressSpace::Generic)
|
||||
.into(),
|
||||
)
|
||||
.into_pointer_value();
|
||||
|
||||
env.builder
|
||||
.build_load(tag_ptr, "load_tag_id")
|
||||
.into_int_value()
|
||||
}
|
||||
|
||||
pub fn build_inc_rec_union_help<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
tags: &[&[Layout<'a>]],
|
||||
fn_val: FunctionValue<'ctx>,
|
||||
) {
|
||||
debug_assert!(!tags.is_empty());
|
||||
|
||||
use inkwell::types::BasicType;
|
||||
|
||||
let context = &env.context;
|
||||
let builder = env.builder;
|
||||
|
||||
// Add a basic block for the entry point
|
||||
let entry = context.append_basic_block(fn_val, "entry");
|
||||
|
||||
builder.position_at_end(entry);
|
||||
|
||||
let func_scope = fn_val.get_subprogram().unwrap();
|
||||
let lexical_block = env.dibuilder.create_lexical_block(
|
||||
/* scope */ func_scope.as_debug_info_scope(),
|
||||
/* file */ env.compile_unit.get_file(),
|
||||
/* line_no */ 0,
|
||||
/* column_no */ 0,
|
||||
);
|
||||
|
||||
let loc = env.dibuilder.create_debug_location(
|
||||
context,
|
||||
/* line */ 0,
|
||||
/* column */ 0,
|
||||
/* current_scope */ lexical_block.as_debug_info_scope(),
|
||||
/* inlined_at */ None,
|
||||
);
|
||||
builder.set_current_debug_location(&context, loc);
|
||||
|
||||
// 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 parent = fn_val;
|
||||
|
||||
let layout = Layout::RecursiveUnion(tags);
|
||||
let before_block = env.builder.get_insert_block().expect("to be in a function");
|
||||
|
||||
debug_assert!(arg_val.is_pointer_value());
|
||||
let value_ptr = arg_val.into_pointer_value();
|
||||
|
||||
// read the tag_id
|
||||
let tag_id = rec_union_read_tag(env, value_ptr);
|
||||
|
||||
let tag_id_u8 = cast_basic_basic(env.builder, tag_id.into(), env.context.i8_type().into());
|
||||
|
||||
// next, make a jump table for all possible values of the tag_id
|
||||
let mut cases = Vec::with_capacity_in(tags.len(), env.arena);
|
||||
|
||||
let merge_block = env.context.append_basic_block(parent, "increment_merge");
|
||||
|
||||
for (tag_id, field_layouts) in tags.iter().enumerate() {
|
||||
// if none of the fields are or contain anything refcounted, just move on
|
||||
if !field_layouts
|
||||
.iter()
|
||||
.any(|x| x.is_refcounted() || x.contains_refcounted())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let block = env.context.append_basic_block(parent, "tag_id_increment");
|
||||
env.builder.position_at_end(block);
|
||||
|
||||
let wrapper_type = basic_type_from_layout(
|
||||
env.arena,
|
||||
env.context,
|
||||
&Layout::Struct(field_layouts),
|
||||
env.ptr_bytes,
|
||||
);
|
||||
|
||||
// cast the opaque pointer to a pointer of the correct shape
|
||||
let struct_ptr = cast_basic_basic(
|
||||
env.builder,
|
||||
value_ptr.into(),
|
||||
wrapper_type.ptr_type(AddressSpace::Generic).into(),
|
||||
)
|
||||
.into_pointer_value();
|
||||
|
||||
for (i, field_layout) in field_layouts.iter().enumerate() {
|
||||
if let Layout::RecursivePointer = field_layout {
|
||||
// this field has type `*i64`, but is really a pointer to the data we want
|
||||
let elem_pointer = env
|
||||
.builder
|
||||
.build_struct_gep(struct_ptr, i as u32, "gep_recursive_pointer")
|
||||
.unwrap();
|
||||
|
||||
let ptr_as_i64_ptr = env
|
||||
.builder
|
||||
.build_load(elem_pointer, "load_recursive_pointer");
|
||||
|
||||
debug_assert!(ptr_as_i64_ptr.is_pointer_value());
|
||||
|
||||
// therefore we must cast it to our desired type
|
||||
let union_type = block_of_memory(env.context, &layout, env.ptr_bytes);
|
||||
let recursive_field_ptr = cast_basic_basic(
|
||||
env.builder,
|
||||
ptr_as_i64_ptr,
|
||||
union_type.ptr_type(AddressSpace::Generic).into(),
|
||||
);
|
||||
|
||||
// recursively increment the field
|
||||
let call = env.builder.build_call(
|
||||
fn_val,
|
||||
&[recursive_field_ptr],
|
||||
"recursive_tag_increment",
|
||||
);
|
||||
|
||||
// Because it's an internal-only function, use the fast calling convention.
|
||||
call.set_call_convention(FAST_CALL_CONV);
|
||||
} else if field_layout.contains_refcounted() {
|
||||
let elem_pointer = env
|
||||
.builder
|
||||
.build_struct_gep(struct_ptr, i as u32, "gep_field")
|
||||
.unwrap();
|
||||
|
||||
let field = env.builder.build_load(elem_pointer, "load_field");
|
||||
|
||||
increment_refcount_layout(env, parent, layout_ids, field, field_layout);
|
||||
}
|
||||
}
|
||||
|
||||
env.builder.build_unconditional_branch(merge_block);
|
||||
|
||||
cases.push((env.context.i8_type().const_int(tag_id as u64, false), block));
|
||||
}
|
||||
|
||||
env.builder.position_at_end(before_block);
|
||||
|
||||
env.builder
|
||||
.build_switch(tag_id_u8.into_int_value(), merge_block, &cases);
|
||||
|
||||
env.builder.position_at_end(merge_block);
|
||||
|
||||
// increment this cons cell
|
||||
let refcount_ptr = PointerToRefcount::from_ptr_to_data(env, value_ptr);
|
||||
refcount_ptr.increment(env);
|
||||
|
||||
// this function returns void
|
||||
builder.build_return(None);
|
||||
}
|
||||
|
||||
pub fn build_inc_union<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
@ -1299,26 +1673,12 @@ pub fn build_inc_union_help<'a, 'ctx, 'env>(
|
||||
);
|
||||
builder.set_current_debug_location(&context, loc);
|
||||
|
||||
let mut scope = Scope::default();
|
||||
|
||||
// Add args to scope
|
||||
let arg_symbol = Symbol::ARG_1;
|
||||
let layout = Layout::Union(tags);
|
||||
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 layout = Layout::RecursiveUnion(tags);
|
||||
|
@ -415,25 +415,21 @@ mod gen_list {
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
// "panicked at 'not yet implemented: Handle equals for builtin layouts Str == Str'"
|
||||
//
|
||||
// #[test]
|
||||
// fn list_keep_if_str_is_hello() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// strIsHello : Str -> Bool
|
||||
// strIsHello = \str ->
|
||||
// str == "Hello"
|
||||
//
|
||||
// List.keepIf ["Hello", "Hello", "Goodbye"] strIsHello
|
||||
// "#
|
||||
// ),
|
||||
// RocList::from_slice(&["Hello", "Hello"]),
|
||||
// RocList<&'static str>
|
||||
// );
|
||||
// }
|
||||
#[test]
|
||||
fn list_keep_if_str_is_hello() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
List.keepIf ["x", "y", "x"] (\x -> x == "x")
|
||||
"#
|
||||
),
|
||||
RocList::from_slice(&[
|
||||
RocStr::from_slice("x".as_bytes()),
|
||||
RocStr::from_slice("x".as_bytes())
|
||||
]),
|
||||
RocList<RocStr>
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_map_on_empty_list_with_int_layout() {
|
||||
@ -1277,7 +1273,7 @@ mod gen_list {
|
||||
app "quicksort" provides [ main ] to "./platform"
|
||||
|
||||
|
||||
swap : I64, I64, List a -> List a
|
||||
swap : Nat, Nat, List a -> List a
|
||||
swap = \i, j, list ->
|
||||
when Pair (List.get list i) (List.get list j) is
|
||||
Pair (Ok atI) (Ok atJ) ->
|
||||
@ -1400,7 +1396,7 @@ mod gen_list {
|
||||
quicksortHelp list 0 (n - 1)
|
||||
|
||||
|
||||
quicksortHelp : List (Num a), I64, I64 -> List (Num a)
|
||||
quicksortHelp : List (Num a), Nat, Nat -> List (Num a)
|
||||
quicksortHelp = \list, low, high ->
|
||||
if low < high then
|
||||
when partition low high list is
|
||||
@ -1412,7 +1408,7 @@ mod gen_list {
|
||||
list
|
||||
|
||||
|
||||
swap : I64, I64, List a -> List a
|
||||
swap : Nat, Nat, List a -> List a
|
||||
swap = \i, j, list ->
|
||||
when Pair (List.get list i) (List.get list j) is
|
||||
Pair (Ok atI) (Ok atJ) ->
|
||||
@ -1423,7 +1419,7 @@ mod gen_list {
|
||||
_ ->
|
||||
[]
|
||||
|
||||
partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partition : Nat, Nat, List (Num a) -> [ Pair Nat (List (Num a)) ]
|
||||
partition = \low, high, initialList ->
|
||||
when List.get initialList high is
|
||||
Ok pivot ->
|
||||
@ -1435,7 +1431,7 @@ mod gen_list {
|
||||
Pair (low - 1) initialList
|
||||
|
||||
|
||||
partitionHelp : I64, I64, List (Num a), I64, (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partitionHelp : Nat, Nat, List (Num a), Nat, (Num a) -> [ Pair Nat (List (Num a)) ]
|
||||
partitionHelp = \i, j, list, high, pivot ->
|
||||
if j < high then
|
||||
when List.get list j is
|
||||
@ -1470,7 +1466,7 @@ mod gen_list {
|
||||
quicksortHelp list 0 (List.len list - 1)
|
||||
|
||||
|
||||
quicksortHelp : List (Num a), I64, I64 -> List (Num a)
|
||||
quicksortHelp : List (Num a), Nat, Nat -> List (Num a)
|
||||
quicksortHelp = \list, low, high ->
|
||||
if low < high then
|
||||
when partition low high list is
|
||||
@ -1482,7 +1478,7 @@ mod gen_list {
|
||||
list
|
||||
|
||||
|
||||
swap : I64, I64, List a -> List a
|
||||
swap : Nat, Nat, List a -> List a
|
||||
swap = \i, j, list ->
|
||||
when Pair (List.get list i) (List.get list j) is
|
||||
Pair (Ok atI) (Ok atJ) ->
|
||||
@ -1493,7 +1489,7 @@ mod gen_list {
|
||||
_ ->
|
||||
[]
|
||||
|
||||
partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partition : Nat, Nat, List (Num a) -> [ Pair Nat (List (Num a)) ]
|
||||
partition = \low, high, initialList ->
|
||||
when List.get initialList high is
|
||||
Ok pivot ->
|
||||
@ -1505,7 +1501,7 @@ mod gen_list {
|
||||
Pair (low - 1) initialList
|
||||
|
||||
|
||||
partitionHelp : I64, I64, List (Num a), I64, Num a -> [ Pair I64 (List (Num a)) ]
|
||||
partitionHelp : Nat, Nat, List (Num a), Nat, Num a -> [ Pair Nat (List (Num a)) ]
|
||||
partitionHelp = \i, j, list, high, pivot ->
|
||||
# if j < high then
|
||||
if False then
|
||||
@ -1543,7 +1539,7 @@ mod gen_list {
|
||||
quicksortHelp list 0 (List.len list - 1)
|
||||
|
||||
|
||||
quicksortHelp : List (Num a), I64, I64 -> List (Num a)
|
||||
quicksortHelp : List (Num a), Nat, Nat -> List (Num a)
|
||||
quicksortHelp = \list, low, high ->
|
||||
if low < high then
|
||||
when partition low high list is
|
||||
@ -1555,7 +1551,7 @@ mod gen_list {
|
||||
list
|
||||
|
||||
|
||||
swap : I64, I64, List a -> List a
|
||||
swap : Nat, Nat, List a -> List a
|
||||
swap = \i, j, list ->
|
||||
when Pair (List.get list i) (List.get list j) is
|
||||
Pair (Ok atI) (Ok atJ) ->
|
||||
@ -1566,7 +1562,7 @@ mod gen_list {
|
||||
_ ->
|
||||
[]
|
||||
|
||||
partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partition : Nat, Nat, List (Num a) -> [ Pair Nat (List (Num a)) ]
|
||||
partition = \low, high, initialList ->
|
||||
when List.get initialList high is
|
||||
Ok pivot ->
|
||||
@ -1578,7 +1574,7 @@ mod gen_list {
|
||||
Pair (low - 1) initialList
|
||||
|
||||
|
||||
partitionHelp : I64, I64, List (Num a), I64, Num a -> [ Pair I64 (List (Num a)) ]
|
||||
partitionHelp : Nat, Nat, List (Num a), Nat, Num a -> [ Pair Nat (List (Num a)) ]
|
||||
partitionHelp = \i, j, list, high, pivot ->
|
||||
if j < high then
|
||||
when List.get list j is
|
||||
@ -1707,4 +1703,55 @@ mod gen_list {
|
||||
assert_evals_to!("List.sum [ 1, 2, 3 ]", 6, i64);
|
||||
assert_evals_to!("List.sum [ 1.1, 2.2, 3.3 ]", 6.6, f64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_eq_empty() {
|
||||
assert_evals_to!("[] == []", true, bool);
|
||||
assert_evals_to!("[] != []", false, bool);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_eq_by_length() {
|
||||
assert_evals_to!("[1] == []", false, bool);
|
||||
assert_evals_to!("[] == [1]", false, bool);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_eq_compare_pointwise() {
|
||||
assert_evals_to!("[1] == [1]", true, bool);
|
||||
assert_evals_to!("[2] == [1]", false, bool);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_eq_nested() {
|
||||
assert_evals_to!("[[1]] == [[1]]", true, bool);
|
||||
assert_evals_to!("[[2]] == [[1]]", false, bool);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_neq_compare_pointwise() {
|
||||
assert_evals_to!("[1] != [1]", false, bool);
|
||||
assert_evals_to!("[2] != [1]", true, bool);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_neq_nested() {
|
||||
assert_evals_to!("[[1]] != [[1]]", false, bool);
|
||||
assert_evals_to!("[[2]] != [[1]]", true, bool);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = r#"Roc failed with message: "integer addition overflowed!"#)]
|
||||
fn cleanup_because_exception() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
x = [ 1,2 ]
|
||||
5 + Num.maxInt + 3 + List.len x
|
||||
"#
|
||||
),
|
||||
RocList::from_slice(&[false; 1]),
|
||||
RocList<bool>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,357 @@ mod helpers;
|
||||
mod gen_num {
|
||||
use roc_std::RocOrder;
|
||||
|
||||
#[test]
|
||||
fn nat_alias() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
i : Nat
|
||||
i = 1
|
||||
|
||||
i
|
||||
"#
|
||||
),
|
||||
1,
|
||||
usize
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn i128_signed_int_alias() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
i : I128
|
||||
i = 128
|
||||
|
||||
i
|
||||
"#
|
||||
),
|
||||
128,
|
||||
i128
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn i64_signed_int_alias() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
i : I64
|
||||
i = 64
|
||||
|
||||
i
|
||||
"#
|
||||
),
|
||||
64,
|
||||
i64
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn i32_signed_int_alias() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
i : I32
|
||||
i = 32
|
||||
|
||||
i
|
||||
"#
|
||||
),
|
||||
32,
|
||||
i32
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn i16_signed_int_alias() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
i : I16
|
||||
i = 16
|
||||
|
||||
i
|
||||
"#
|
||||
),
|
||||
16,
|
||||
i16
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn i8_signed_int_alias() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
i : I8
|
||||
i = 8
|
||||
|
||||
i
|
||||
"#
|
||||
),
|
||||
8,
|
||||
i8
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn i128_hex_int_alias() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
f : I128
|
||||
f = 0x123
|
||||
|
||||
f
|
||||
"#
|
||||
),
|
||||
0x123,
|
||||
i128
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn i64_hex_int_alias() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
f : I64
|
||||
f = 0x123
|
||||
|
||||
f
|
||||
"#
|
||||
),
|
||||
0x123,
|
||||
i64
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn i32_hex_int_alias() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
f : I32
|
||||
f = 0x123
|
||||
|
||||
f
|
||||
"#
|
||||
),
|
||||
0x123,
|
||||
i32
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn i16_hex_int_alias() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
f : I16
|
||||
f = 0x123
|
||||
|
||||
f
|
||||
"#
|
||||
),
|
||||
0x123,
|
||||
i16
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn i8_hex_int_alias() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
f : I8
|
||||
f = 0xA
|
||||
|
||||
f
|
||||
"#
|
||||
),
|
||||
0xA,
|
||||
i8
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn u128_signed_int_alias() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
i : U128
|
||||
i = 128
|
||||
|
||||
i
|
||||
"#
|
||||
),
|
||||
128,
|
||||
u128
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn u64_signed_int_alias() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
i : U64
|
||||
i = 64
|
||||
|
||||
i
|
||||
"#
|
||||
),
|
||||
64,
|
||||
u64
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn u32_signed_int_alias() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
i : U32
|
||||
i = 32
|
||||
|
||||
i
|
||||
"#
|
||||
),
|
||||
32,
|
||||
u32
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn u16_signed_int_alias() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
i : U16
|
||||
i = 16
|
||||
|
||||
i
|
||||
"#
|
||||
),
|
||||
16,
|
||||
u16
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn u8_signed_int_alias() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
i : U8
|
||||
i = 8
|
||||
|
||||
i
|
||||
"#
|
||||
),
|
||||
8,
|
||||
u8
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn u128_hex_int_alias() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
f : U128
|
||||
f = 0x123
|
||||
|
||||
f
|
||||
"#
|
||||
),
|
||||
0x123,
|
||||
i128
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn u64_hex_int_alias() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
f : U64
|
||||
f = 0x123
|
||||
|
||||
f
|
||||
"#
|
||||
),
|
||||
0x123,
|
||||
u64
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn u32_hex_int_alias() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
f : U32
|
||||
f = 0x123
|
||||
|
||||
f
|
||||
"#
|
||||
),
|
||||
0x123,
|
||||
u32
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn u16_hex_int_alias() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
f : U16
|
||||
f = 0x123
|
||||
|
||||
f
|
||||
"#
|
||||
),
|
||||
0x123,
|
||||
u16
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn u8_hex_int_alias() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
f : U8
|
||||
f = 0xA
|
||||
|
||||
f
|
||||
"#
|
||||
),
|
||||
0xA,
|
||||
u8
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f64_float_alias() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
f : F64
|
||||
f = 3.6
|
||||
|
||||
f
|
||||
"#
|
||||
),
|
||||
3.6,
|
||||
f64
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn f32_float_alias() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
f : F32
|
||||
f = 3.6
|
||||
|
||||
f
|
||||
"#
|
||||
),
|
||||
3.6,
|
||||
f32
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f64_sqrt() {
|
||||
// FIXME this works with normal types, but fails when checking uniqueness types
|
||||
@ -406,6 +757,14 @@ mod gen_num {
|
||||
assert_evals_to!("Num.bitwiseAnd 200 0", 0, i64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bitwise_xor() {
|
||||
assert_evals_to!("Num.bitwiseXor 20 20", 0, i64);
|
||||
assert_evals_to!("Num.bitwiseXor 15 14", 1, i64);
|
||||
assert_evals_to!("Num.bitwiseXor 7 15", 8, i64);
|
||||
assert_evals_to!("Num.bitwiseXor 200 0", 200, i64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lt_i64() {
|
||||
assert_evals_to!("1 < 2", true, bool);
|
||||
|
@ -135,7 +135,7 @@ mod gen_primitives {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
x : [ Pair I64 I64 ]
|
||||
x : [ Pair (Int *) (Int *) ]
|
||||
x = Pair 0x2 0x3
|
||||
|
||||
when x is
|
||||
@ -152,7 +152,7 @@ mod gen_primitives {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
x : [A I64, B I64]
|
||||
x : [A (Int *), B (Int *)]
|
||||
x = A 0x2
|
||||
|
||||
when x is
|
||||
@ -170,7 +170,7 @@ mod gen_primitives {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
x : [A I64, B I64]
|
||||
x : [A (Int *), B (Int *)]
|
||||
x = B 0x3
|
||||
|
||||
when x is
|
||||
@ -293,7 +293,7 @@ mod gen_primitives {
|
||||
indoc!(
|
||||
r#"
|
||||
wrapper = \{} ->
|
||||
alwaysFloatIdentity : I64 -> (F64 -> F64)
|
||||
alwaysFloatIdentity : Int * -> (Float * -> Float *)
|
||||
alwaysFloatIdentity = \_ ->
|
||||
(\a -> a)
|
||||
|
||||
@ -557,14 +557,14 @@ mod gen_primitives {
|
||||
|
||||
LinkedList a : [ Nil, Cons a (LinkedList a) ]
|
||||
|
||||
len : LinkedList a -> I64
|
||||
len : LinkedList a -> Int *
|
||||
len = \list ->
|
||||
when list is
|
||||
Nil -> 0
|
||||
Cons _ rest -> 1 + len rest
|
||||
|
||||
main =
|
||||
nil : LinkedList I64
|
||||
nil : LinkedList {}
|
||||
nil = Nil
|
||||
|
||||
len nil
|
||||
@ -584,10 +584,10 @@ mod gen_primitives {
|
||||
|
||||
LinkedList a : [ Nil, Cons a (LinkedList a) ]
|
||||
|
||||
nil : LinkedList I64
|
||||
nil : LinkedList (Int *)
|
||||
nil = Nil
|
||||
|
||||
length : LinkedList a -> I64
|
||||
length : LinkedList a -> Int *
|
||||
length = \list ->
|
||||
when list is
|
||||
Nil -> 0
|
||||
@ -611,10 +611,10 @@ mod gen_primitives {
|
||||
|
||||
LinkedList a : [ Nil, Cons a (LinkedList a) ]
|
||||
|
||||
one : LinkedList I64
|
||||
one : LinkedList (Int *)
|
||||
one = Cons 1 Nil
|
||||
|
||||
length : LinkedList a -> I64
|
||||
length : LinkedList a -> Int *
|
||||
length = \list ->
|
||||
when list is
|
||||
Nil -> 0
|
||||
@ -638,10 +638,10 @@ mod gen_primitives {
|
||||
|
||||
LinkedList a : [ Nil, Cons a (LinkedList a) ]
|
||||
|
||||
one : LinkedList I64
|
||||
one : LinkedList (Int *)
|
||||
one = Cons 1 Nil
|
||||
|
||||
length : LinkedList a -> I64
|
||||
length : LinkedList a -> Int *
|
||||
length = \list ->
|
||||
when list is
|
||||
Nil -> 0
|
||||
@ -665,10 +665,10 @@ mod gen_primitives {
|
||||
|
||||
LinkedList a : [ Nil, Cons a (LinkedList a) ]
|
||||
|
||||
three : LinkedList I64
|
||||
three : LinkedList (Int *)
|
||||
three = Cons 3 (Cons 2 (Cons 1 Nil))
|
||||
|
||||
length : LinkedList a -> I64
|
||||
length : LinkedList a -> Int *
|
||||
length = \list ->
|
||||
when list is
|
||||
Nil -> 0
|
||||
@ -693,7 +693,7 @@ mod gen_primitives {
|
||||
|
||||
LinkedList a : [ Nil, Cons a (LinkedList a) ]
|
||||
|
||||
three : LinkedList I64
|
||||
three : LinkedList (Int *)
|
||||
three = Cons 3 (Cons 2 (Cons 1 Nil))
|
||||
|
||||
|
||||
@ -721,10 +721,10 @@ mod gen_primitives {
|
||||
|
||||
LinkedList a : [ Nil, Cons a (LinkedList a) ]
|
||||
|
||||
zero : LinkedList I64
|
||||
zero : LinkedList (Int *)
|
||||
zero = Nil
|
||||
|
||||
sum : LinkedList I64 -> I64
|
||||
sum : LinkedList (Int *) -> Int *
|
||||
sum = \list ->
|
||||
when list is
|
||||
Nil -> 0
|
||||
@ -748,7 +748,7 @@ mod gen_primitives {
|
||||
|
||||
LinkedList a : [ Nil, Cons a (LinkedList a) ]
|
||||
|
||||
three : LinkedList I64
|
||||
three : LinkedList (Int *)
|
||||
three = Cons 3 (Cons 2 (Cons 1 Nil))
|
||||
|
||||
sum : LinkedList (Num a) -> Num a
|
||||
@ -779,7 +779,7 @@ mod gen_primitives {
|
||||
r#"
|
||||
Maybe a : [ Nothing, Just a ]
|
||||
|
||||
x : Maybe (Maybe I64)
|
||||
x : Maybe (Maybe (Int *))
|
||||
x = Just (Just 41)
|
||||
|
||||
when x is
|
||||
@ -796,7 +796,7 @@ mod gen_primitives {
|
||||
r#"
|
||||
Maybe a : [ Nothing, Just a ]
|
||||
|
||||
x : Maybe (Maybe I64)
|
||||
x : Maybe (Maybe (Int *))
|
||||
x = Just Nothing
|
||||
|
||||
when x is
|
||||
@ -814,7 +814,7 @@ mod gen_primitives {
|
||||
r#"
|
||||
Maybe a : [ Nothing, Just a ]
|
||||
|
||||
x : Maybe (Maybe I64)
|
||||
x : Maybe (Maybe (Int *))
|
||||
x = Nothing
|
||||
|
||||
when x is
|
||||
@ -908,7 +908,7 @@ mod gen_primitives {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
foo : I64
|
||||
foo : Int *
|
||||
|
||||
|
||||
foo
|
||||
@ -1033,11 +1033,11 @@ mod gen_primitives {
|
||||
runEffect : Effect a -> a
|
||||
runEffect = \@Effect thunk -> thunk {}
|
||||
|
||||
foo : Effect F64
|
||||
foo : Effect (Float *)
|
||||
foo =
|
||||
succeed 3.14
|
||||
|
||||
main : F64
|
||||
main : Float *
|
||||
main =
|
||||
runEffect foo
|
||||
|
||||
@ -1058,14 +1058,14 @@ mod gen_primitives {
|
||||
# succeed : a -> ({} -> a)
|
||||
succeed = \x -> \{} -> x
|
||||
|
||||
foo : {} -> F64
|
||||
foo : {} -> Float *
|
||||
foo =
|
||||
succeed 3.14
|
||||
|
||||
# runEffect : ({} -> a) -> a
|
||||
runEffect = \thunk -> thunk {}
|
||||
|
||||
main : F64
|
||||
main : Float *
|
||||
main =
|
||||
runEffect foo
|
||||
"#
|
||||
@ -1122,6 +1122,40 @@ mod gen_primitives {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn linked_list_is_singleton() {
|
||||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
ConsList a : [ Cons a (ConsList a), Nil ]
|
||||
|
||||
empty : ConsList a
|
||||
empty = Nil
|
||||
|
||||
isSingleton : ConsList a -> Bool
|
||||
isSingleton = \list ->
|
||||
when list is
|
||||
Cons _ Nil ->
|
||||
True
|
||||
|
||||
_ ->
|
||||
False
|
||||
|
||||
main : Bool
|
||||
main =
|
||||
myList : ConsList I64
|
||||
myList = empty
|
||||
|
||||
isSingleton myList
|
||||
"#
|
||||
),
|
||||
false,
|
||||
bool
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn linked_list_is_empty_1() {
|
||||
assert_non_opt_evals_to!(
|
||||
@ -1145,7 +1179,7 @@ mod gen_primitives {
|
||||
|
||||
main : Bool
|
||||
main =
|
||||
myList : ConsList I64
|
||||
myList : ConsList (Int *)
|
||||
myList = empty
|
||||
|
||||
isEmpty myList
|
||||
@ -1176,7 +1210,7 @@ mod gen_primitives {
|
||||
|
||||
main : Bool
|
||||
main =
|
||||
myList : ConsList I64
|
||||
myList : ConsList I64
|
||||
myList = Cons 0x1 Nil
|
||||
|
||||
isEmpty myList
|
||||
@ -1187,6 +1221,26 @@ mod gen_primitives {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn linked_list_singleton() {
|
||||
// verifies only that valid llvm is produced
|
||||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
ConsList a : [ Cons a (ConsList a), Nil ]
|
||||
|
||||
main : ConsList I64
|
||||
main = Cons 0x1 Nil
|
||||
"#
|
||||
),
|
||||
0,
|
||||
i64,
|
||||
|_| 0
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recursive_functon_with_rigid() {
|
||||
assert_non_opt_evals_to!(
|
||||
@ -1194,16 +1248,16 @@ mod gen_primitives {
|
||||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
State a : { count : I64, x : a }
|
||||
State a : { count : Int *, x : a }
|
||||
|
||||
foo : State a -> I64
|
||||
foo : State a -> Int *
|
||||
foo = \state ->
|
||||
if state.count == 0 then
|
||||
0
|
||||
else
|
||||
1 + foo { count: state.count - 1, x: state.x }
|
||||
|
||||
main : I64
|
||||
main : Int *
|
||||
main =
|
||||
foo { count: 3, x: {} }
|
||||
"#
|
||||
@ -1284,7 +1338,7 @@ mod gen_primitives {
|
||||
_ ->
|
||||
Node color key value left right
|
||||
|
||||
main : RedBlackTree I64 {}
|
||||
main : RedBlackTree (Int *) {}
|
||||
main =
|
||||
insert 0 {} Empty
|
||||
"#
|
||||
@ -1325,7 +1379,7 @@ mod gen_primitives {
|
||||
_ ->
|
||||
Empty
|
||||
|
||||
main : RedBlackTree I64
|
||||
main : RedBlackTree (Int *)
|
||||
main =
|
||||
balance Red 0 Empty Empty
|
||||
"#
|
||||
@ -1348,13 +1402,14 @@ mod gen_primitives {
|
||||
balance = \key, left ->
|
||||
Node key left Empty
|
||||
|
||||
main : RedBlackTree I64
|
||||
main : RedBlackTree (Int *)
|
||||
main =
|
||||
balance 0 Empty
|
||||
"#
|
||||
),
|
||||
1,
|
||||
i64
|
||||
&i64,
|
||||
|x: &i64| *x
|
||||
);
|
||||
}
|
||||
|
||||
@ -1395,7 +1450,7 @@ mod gen_primitives {
|
||||
_ ->
|
||||
Empty
|
||||
|
||||
main : RedBlackTree I64 I64
|
||||
main : RedBlackTree (Int *) (Int *)
|
||||
main =
|
||||
balance Red 0 0 Empty Empty
|
||||
"#
|
||||
@ -1445,13 +1500,14 @@ mod gen_primitives {
|
||||
_ ->
|
||||
Node color key value left right
|
||||
|
||||
main : RedBlackTree I64 I64
|
||||
main : RedBlackTree (Int *) (Int *)
|
||||
main =
|
||||
balance Red 0 0 Empty Empty
|
||||
"#
|
||||
),
|
||||
1,
|
||||
i64
|
||||
&i64,
|
||||
|x: &i64| *x
|
||||
);
|
||||
}
|
||||
|
||||
@ -1465,7 +1521,7 @@ mod gen_primitives {
|
||||
|
||||
ConsList a : [ Cons a (ConsList a), Nil ]
|
||||
|
||||
balance : ConsList I64 -> I64
|
||||
balance : ConsList (Int *) -> Int *
|
||||
balance = \right ->
|
||||
when right is
|
||||
Cons 1 foo ->
|
||||
@ -1474,7 +1530,7 @@ mod gen_primitives {
|
||||
_ -> 3
|
||||
_ -> 3
|
||||
|
||||
main : I64
|
||||
main : Int *
|
||||
main =
|
||||
when balance Nil is
|
||||
_ -> 3
|
||||
@ -1491,13 +1547,13 @@ mod gen_primitives {
|
||||
|
||||
ConsList a : [ Cons a (ConsList a), Nil ]
|
||||
|
||||
balance : ConsList I64 -> I64
|
||||
balance : ConsList (Int *) -> Int *
|
||||
balance = \right ->
|
||||
when right is
|
||||
Cons 1 (Cons 1 _) -> 3
|
||||
_ -> 3
|
||||
|
||||
main : I64
|
||||
main : Int *
|
||||
main =
|
||||
when balance Nil is
|
||||
_ -> 3
|
||||
@ -1519,7 +1575,7 @@ mod gen_primitives {
|
||||
|
||||
ConsList a : [ Cons a (ConsList a), Nil ]
|
||||
|
||||
balance : ConsList I64 -> I64
|
||||
balance : ConsList (Int *) -> Int *
|
||||
balance = \right ->
|
||||
when right is
|
||||
Cons 1 foo ->
|
||||
@ -1528,7 +1584,7 @@ mod gen_primitives {
|
||||
_ -> 3
|
||||
_ -> 3
|
||||
|
||||
main : I64
|
||||
main : Int *
|
||||
main =
|
||||
when balance Nil is
|
||||
_ -> 3
|
||||
@ -1548,13 +1604,13 @@ mod gen_primitives {
|
||||
|
||||
ConsList a : [ Cons a (ConsList a), Nil ]
|
||||
|
||||
foo : ConsList I64 -> I64
|
||||
foo : ConsList (Int *) -> Int *
|
||||
foo = \list ->
|
||||
when list is
|
||||
Cons _ (Cons x _) -> x
|
||||
_ -> 0
|
||||
|
||||
main : I64
|
||||
main : Int *
|
||||
main =
|
||||
foo (Cons 1 (Cons 32 Nil))
|
||||
"#
|
||||
@ -1571,15 +1627,15 @@ mod gen_primitives {
|
||||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
BTree : [ Node BTree BTree, Leaf I64 ]
|
||||
BTree : [ Node BTree BTree, Leaf (Int *) ]
|
||||
|
||||
foo : BTree -> I64
|
||||
foo : BTree -> Int *
|
||||
foo = \btree ->
|
||||
when btree is
|
||||
Node (Node (Leaf x) _) _ -> x
|
||||
_ -> 0
|
||||
|
||||
main : I64
|
||||
main : Int *
|
||||
main =
|
||||
foo (Node (Node (Leaf 32) (Leaf 0)) (Leaf 0))
|
||||
"#
|
||||
@ -1603,7 +1659,7 @@ mod gen_primitives {
|
||||
A -> (\_ -> 3.14)
|
||||
B -> (\_ -> 3.14)
|
||||
|
||||
main : F64
|
||||
main : Float *
|
||||
main =
|
||||
(foo {}) 0
|
||||
"#
|
||||
@ -1646,7 +1702,7 @@ mod gen_primitives {
|
||||
Ok x -> transform x
|
||||
Err e -> fail e
|
||||
|
||||
main : Task {} F64
|
||||
main : Task {} (Float *)
|
||||
main = after (always "foo") (\_ -> always {})
|
||||
|
||||
"#
|
||||
@ -1676,7 +1732,7 @@ mod gen_primitives {
|
||||
@Effect inner
|
||||
|
||||
|
||||
main : Task {} F64
|
||||
main : Task {} (Float *)
|
||||
main = always {}
|
||||
"#
|
||||
),
|
||||
@ -1707,7 +1763,7 @@ mod gen_primitives {
|
||||
|
||||
Task a err : Effect (Result a err)
|
||||
|
||||
always : a -> Task a F64
|
||||
always : a -> Task a (Float *)
|
||||
always = \x -> effectAlways (Ok x)
|
||||
|
||||
# the problem is that this restricts to `Task {} *`
|
||||
@ -1722,7 +1778,7 @@ mod gen_primitives {
|
||||
# but here it must be `forall b. Task b {}`
|
||||
Err e -> fail e
|
||||
|
||||
main : Task {} F64
|
||||
main : Task {} (Float *)
|
||||
main =
|
||||
after (always "foo") (\_ -> always {})
|
||||
"#
|
||||
@ -1774,7 +1830,7 @@ mod gen_primitives {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
x : Result I64 F64
|
||||
x : Result (Int *) (Float *)
|
||||
x = Ok 4
|
||||
|
||||
(Ok y) = x
|
||||
@ -1803,4 +1859,49 @@ mod gen_primitives {
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn case_or_pattern() {
|
||||
// the `0` branch body should only be generated once in the future
|
||||
// it is currently duplicated
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
x : [ Red, Green, Blue ]
|
||||
x = Red
|
||||
|
||||
when x is
|
||||
Red | Green -> 0
|
||||
Blue -> 1
|
||||
"#
|
||||
),
|
||||
0,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn case_jump() {
|
||||
// the decision tree will generate a jump to the `1` branch here
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
ConsList a : [ Cons a (ConsList a), Nil ]
|
||||
|
||||
x : ConsList I64
|
||||
x = Nil
|
||||
|
||||
main =
|
||||
when Pair x x is
|
||||
Pair Nil _ -> 1
|
||||
Pair _ Nil -> 2
|
||||
Pair (Cons a _) (Cons b _) -> a + b + 3
|
||||
"#
|
||||
),
|
||||
1,
|
||||
i64
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -514,4 +514,28 @@ mod gen_str {
|
||||
let min = format!("{}", i64::MIN);
|
||||
assert_evals_to!(r#"Str.fromInt Num.minInt"#, &min, &'static str);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn str_equality() {
|
||||
assert_evals_to!(r#""a" == "a""#, true, bool);
|
||||
assert_evals_to!(
|
||||
r#""loremipsumdolarsitamet" == "loremipsumdolarsitamet""#,
|
||||
true,
|
||||
bool
|
||||
);
|
||||
assert_evals_to!(r#""a" != "b""#, true, bool);
|
||||
assert_evals_to!(r#""a" == "b""#, false, bool);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn str_clone() {
|
||||
use roc_std::RocStr;
|
||||
let long = RocStr::from_slice("loremipsumdolarsitamet".as_bytes());
|
||||
let short = RocStr::from_slice("x".as_bytes());
|
||||
let empty = RocStr::from_slice("".as_bytes());
|
||||
|
||||
debug_assert_eq!(long.clone(), long);
|
||||
debug_assert_eq!(short.clone(), short);
|
||||
debug_assert_eq!(empty.clone(), empty);
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ fn promote_expr_to_module(src: &str) -> String {
|
||||
pub fn helper<'a>(
|
||||
arena: &'a bumpalo::Bump,
|
||||
src: &str,
|
||||
stdlib: roc_builtins::std::StdLib,
|
||||
stdlib: &'a roc_builtins::std::StdLib,
|
||||
leak: bool,
|
||||
context: &'a inkwell::context::Context,
|
||||
) -> (&'static str, String, Library) {
|
||||
@ -41,6 +41,9 @@ pub fn helper<'a>(
|
||||
module_src = &temp;
|
||||
}
|
||||
|
||||
let target = target_lexicon::Triple::host();
|
||||
let ptr_bytes = target.pointer_width().unwrap().bytes() as u32;
|
||||
|
||||
let exposed_types = MutMap::default();
|
||||
let loaded = roc_load::file::load_and_monomorphize_from_str(
|
||||
arena,
|
||||
@ -49,6 +52,7 @@ pub fn helper<'a>(
|
||||
stdlib,
|
||||
src_dir,
|
||||
exposed_types,
|
||||
ptr_bytes,
|
||||
);
|
||||
|
||||
let mut loaded = loaded.expect("failed to load module");
|
||||
@ -73,9 +77,6 @@ pub fn helper<'a>(
|
||||
),
|
||||
};
|
||||
|
||||
let target = target_lexicon::Triple::host();
|
||||
let ptr_bytes = target.pointer_width().unwrap().bytes() as u32;
|
||||
|
||||
let mut lines = Vec::new();
|
||||
// errors whose reporting we delay (so we can see that code gen generates runtime errors)
|
||||
let mut delayed_errors = Vec::new();
|
||||
@ -294,38 +295,6 @@ pub fn helper<'a>(
|
||||
(main_fn_name, delayed_errors.join("\n"), lib)
|
||||
}
|
||||
|
||||
// TODO this is almost all code duplication with assert_llvm_evals_to
|
||||
// the only difference is that this calls uniq_expr instead of can_expr.
|
||||
// Should extract the common logic into test helpers.
|
||||
#[macro_export]
|
||||
macro_rules! assert_opt_evals_to {
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr, $leak:expr) => {
|
||||
use bumpalo::Bump;
|
||||
use inkwell::context::Context;
|
||||
use roc_gen::run_jit_function;
|
||||
|
||||
let arena = Bump::new();
|
||||
|
||||
let context = Context::create();
|
||||
|
||||
let stdlib = roc_builtins::unique::uniq_stdlib();
|
||||
|
||||
let (main_fn_name, errors, lib) =
|
||||
$crate::helpers::eval::helper(&arena, $src, stdlib, $leak, &context);
|
||||
|
||||
let transform = |success| {
|
||||
let expected = $expected;
|
||||
let given = $transform(success);
|
||||
assert_eq!(&given, &expected);
|
||||
};
|
||||
run_jit_function!(lib, main_fn_name, $ty, transform, errors)
|
||||
};
|
||||
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||
assert_opt_evals_to!($src, $expected, $ty, $transform, true)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! assert_llvm_evals_to {
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr, $leak:expr) => {
|
||||
@ -334,9 +303,10 @@ macro_rules! assert_llvm_evals_to {
|
||||
use roc_gen::run_jit_function;
|
||||
|
||||
let arena = Bump::new();
|
||||
|
||||
let context = Context::create();
|
||||
let stdlib = roc_builtins::std::standard_stdlib();
|
||||
|
||||
// NOTE the stdlib must be in the arena; just taking a reference will segfault
|
||||
let stdlib = arena.alloc(roc_builtins::std::standard_stdlib());
|
||||
|
||||
let (main_fn_name, errors, lib) =
|
||||
$crate::helpers::eval::helper(&arena, $src, stdlib, $leak, &context);
|
||||
@ -374,7 +344,8 @@ macro_rules! assert_evals_to {
|
||||
assert_llvm_evals_to!($src, $expected, $ty, $transform, $leak);
|
||||
}
|
||||
{
|
||||
assert_opt_evals_to!($src, $expected, $ty, $transform, $leak);
|
||||
// NOTE at the moment, the optimized tests do the same thing
|
||||
// assert_opt_evals_to!($src, $expected, $ty, $transform, $leak);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -307,6 +307,16 @@ impl Assembler<AArch64GPReg> for AArch64Assembler {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn sub_reg64_reg64_reg64(
|
||||
_buf: &mut Vec<'_, u8>,
|
||||
_dst: AArch64GPReg,
|
||||
_src1: AArch64GPReg,
|
||||
_src2: AArch64GPReg,
|
||||
) {
|
||||
unimplemented!("registers subtractions not implemented yet for AArch64");
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn ret(buf: &mut Vec<'_, u8>) {
|
||||
ret_reg64(buf, AArch64GPReg::LR)
|
||||
|
@ -51,6 +51,7 @@ pub trait Assembler<GPReg: GPRegTrait> {
|
||||
fn mov_reg64_stack32(buf: &mut Vec<'_, u8>, dst: GPReg, offset: i32);
|
||||
fn mov_stack32_reg64(buf: &mut Vec<'_, u8>, offset: i32, src: GPReg);
|
||||
fn sub_reg64_reg64_imm32(buf: &mut Vec<'_, u8>, dst: GPReg, src1: GPReg, imm32: i32);
|
||||
fn sub_reg64_reg64_reg64(buf: &mut Vec<'_, u8>, dst: GPReg, src1: GPReg, src2: GPReg);
|
||||
fn ret(buf: &mut Vec<'_, u8>);
|
||||
}
|
||||
|
||||
@ -194,6 +195,19 @@ impl<'a, GPReg: GPRegTrait, ASM: Assembler<GPReg>, CC: CallConv<GPReg>> Backend<
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn build_num_sub_i64(
|
||||
&mut self,
|
||||
dst: &Symbol,
|
||||
src1: &Symbol,
|
||||
src2: &Symbol,
|
||||
) -> Result<(), String> {
|
||||
let dst_reg = self.claim_gp_reg(dst)?;
|
||||
let src1_reg = self.load_to_reg(src1)?;
|
||||
let src2_reg = self.load_to_reg(src2)?;
|
||||
ASM::sub_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn load_literal(&mut self, sym: &Symbol, lit: &Literal<'a>) -> Result<(), String> {
|
||||
match lit {
|
||||
Literal::Int(x) => {
|
||||
|
@ -310,6 +310,20 @@ impl Assembler<X86_64GPReg> for X86_64Assembler {
|
||||
}
|
||||
}
|
||||
#[inline(always)]
|
||||
fn sub_reg64_reg64_reg64(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
dst: X86_64GPReg,
|
||||
src1: X86_64GPReg,
|
||||
src2: X86_64GPReg,
|
||||
) {
|
||||
if dst == src1 {
|
||||
sub_reg64_reg64(buf, dst, src2);
|
||||
} else {
|
||||
mov_reg64_reg64(buf, dst, src1);
|
||||
sub_reg64_reg64(buf, dst, src2);
|
||||
}
|
||||
}
|
||||
#[inline(always)]
|
||||
fn ret(buf: &mut Vec<'_, u8>) {
|
||||
ret(buf);
|
||||
}
|
||||
@ -379,6 +393,16 @@ fn add_reg64_reg64(buf: &mut Vec<'_, u8>, dst: X86_64GPReg, src: X86_64GPReg) {
|
||||
buf.extend(&[rex, 0x01, 0xC0 + dst_mod + src_mod]);
|
||||
}
|
||||
|
||||
/// `SUB r/m64,r64` -> Sub r64 to r/m64.
|
||||
#[inline(always)]
|
||||
fn sub_reg64_reg64(buf: &mut Vec<'_, u8>, dst: X86_64GPReg, src: X86_64GPReg) {
|
||||
let rex = add_rm_extension(dst, REX_W);
|
||||
let rex = add_reg_extension(src, rex);
|
||||
let dst_mod = dst as u8 % 8;
|
||||
let src_mod = (src as u8 % 8) << 3;
|
||||
buf.extend(&[rex, 0x29, 0xC0 + dst_mod + src_mod]);
|
||||
}
|
||||
|
||||
/// `CMOVL r64,r/m64` -> Move if less (SF≠ OF).
|
||||
#[inline(always)]
|
||||
fn cmovl_reg64_reg64(buf: &mut Vec<'_, u8>, dst: X86_64GPReg, src: X86_64GPReg) {
|
||||
|
@ -24,7 +24,7 @@ pub struct Env<'a> {
|
||||
}
|
||||
|
||||
// INLINED_SYMBOLS is a set of all of the functions we automatically inline if seen.
|
||||
const INLINED_SYMBOLS: [Symbol; 2] = [Symbol::NUM_ABS, Symbol::NUM_ADD];
|
||||
const INLINED_SYMBOLS: [Symbol; 3] = [Symbol::NUM_ABS, Symbol::NUM_ADD, Symbol::NUM_SUB];
|
||||
|
||||
// These relocations likely will need a length.
|
||||
// They may even need more definition, but this should be at least good enough for how we will use elf.
|
||||
@ -82,6 +82,18 @@ where
|
||||
self.free_symbols(stmt);
|
||||
Ok(())
|
||||
}
|
||||
Stmt::Invoke {
|
||||
symbol,
|
||||
layout,
|
||||
call,
|
||||
pass,
|
||||
fail: _,
|
||||
} => {
|
||||
// for now, treat invoke as a normal call
|
||||
|
||||
let stmt = Stmt::Let(*symbol, Expr::Call(call.clone()), layout.clone(), pass);
|
||||
self.build_stmt(&stmt)
|
||||
}
|
||||
x => Err(format!("the statement, {:?}, is not yet implemented", x)),
|
||||
}
|
||||
}
|
||||
@ -103,26 +115,35 @@ where
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Expr::FunctionCall {
|
||||
call_type: CallType::ByName(func_sym),
|
||||
args,
|
||||
..
|
||||
} => {
|
||||
match *func_sym {
|
||||
Symbol::NUM_ABS => {
|
||||
// Instead of calling the function, just inline it.
|
||||
self.build_expr(sym, &Expr::RunLowLevel(LowLevel::NumAbs, args), layout)
|
||||
Expr::Call(roc_mono::ir::Call {
|
||||
call_type,
|
||||
arguments,
|
||||
}) => {
|
||||
match call_type {
|
||||
CallType::ByName { name: func_sym, .. } => {
|
||||
match *func_sym {
|
||||
Symbol::NUM_ABS => {
|
||||
// Instead of calling the function, just inline it.
|
||||
self.build_run_low_level(sym, &LowLevel::NumAbs, arguments, layout)
|
||||
}
|
||||
Symbol::NUM_ADD => {
|
||||
// Instead of calling the function, just inline it.
|
||||
self.build_run_low_level(sym, &LowLevel::NumAdd, arguments, layout)
|
||||
}
|
||||
Symbol::NUM_SUB => {
|
||||
// Instead of calling the function, just inline it.
|
||||
self.build_run_low_level(sym, &LowLevel::NumSub, arguments, layout)
|
||||
}
|
||||
x => Err(format!("the function, {:?}, is not yet implemented", x)),
|
||||
}
|
||||
}
|
||||
Symbol::NUM_ADD => {
|
||||
// Instead of calling the function, just inline it.
|
||||
self.build_expr(sym, &Expr::RunLowLevel(LowLevel::NumAdd, args), layout)
|
||||
|
||||
CallType::LowLevel { op: lowlevel } => {
|
||||
self.build_run_low_level(sym, lowlevel, arguments, layout)
|
||||
}
|
||||
x => Err(format!("the function, {:?}, is not yet implemented", x)),
|
||||
x => Err(format!("the call type, {:?}, is not yet implemented", x)),
|
||||
}
|
||||
}
|
||||
Expr::RunLowLevel(lowlevel, args) => {
|
||||
self.build_run_low_level(sym, lowlevel, args, layout)
|
||||
}
|
||||
x => Err(format!("the expression, {:?}, is not yet implemented", x)),
|
||||
}
|
||||
}
|
||||
@ -155,6 +176,15 @@ where
|
||||
x => Err(format!("layout, {:?}, not implemented yet", x)),
|
||||
}
|
||||
}
|
||||
LowLevel::NumSub => {
|
||||
// TODO: when this is expanded to floats. deal with typecasting here, and then call correct low level method.
|
||||
match layout {
|
||||
Layout::Builtin(Builtin::Int64) => {
|
||||
self.build_num_sub_i64(sym, &args[0], &args[1])
|
||||
}
|
||||
x => Err(format!("layout, {:?}, not implemented yet", x)),
|
||||
}
|
||||
}
|
||||
x => Err(format!("low level, {:?}. is not yet implemented", x)),
|
||||
}
|
||||
}
|
||||
@ -163,7 +193,7 @@ where
|
||||
/// It only deals with inputs and outputs of i64 type.
|
||||
fn build_num_abs_i64(&mut self, dst: &Symbol, src: &Symbol) -> Result<(), String>;
|
||||
|
||||
/// build_num_add_i64 stores the absolute value of src into dst.
|
||||
/// build_num_add_i64 stores the sum of src1 and src2 into dst.
|
||||
/// It only deals with inputs and outputs of i64 type.
|
||||
fn build_num_add_i64(
|
||||
&mut self,
|
||||
@ -172,6 +202,15 @@ where
|
||||
src2: &Symbol,
|
||||
) -> Result<(), String>;
|
||||
|
||||
/// build_num_sub_i64 stores the `src1 - src2` difference into dst.
|
||||
/// It only deals with inputs and outputs of i64 type.
|
||||
fn build_num_sub_i64(
|
||||
&mut self,
|
||||
dst: &Symbol,
|
||||
src1: &Symbol,
|
||||
src2: &Symbol,
|
||||
) -> Result<(), String>;
|
||||
|
||||
/// literal_map gets the map from symbol to literal, used for lazy loading and literal folding.
|
||||
fn literal_map(&mut self) -> &mut MutMap<Symbol, Literal<'a>>;
|
||||
|
||||
@ -244,36 +283,9 @@ where
|
||||
match expr {
|
||||
Expr::Literal(_) => {}
|
||||
Expr::FunctionPointer(sym, _) => self.set_last_seen(*sym, stmt),
|
||||
Expr::FunctionCall {
|
||||
call_type, args, ..
|
||||
} => {
|
||||
for sym in *args {
|
||||
self.set_last_seen(*sym, stmt);
|
||||
}
|
||||
match call_type {
|
||||
CallType::ByName(sym) => {
|
||||
// For functions that we won't inline, we should not be a leaf function.
|
||||
if !INLINED_SYMBOLS.contains(sym) {
|
||||
self.set_not_leaf_function();
|
||||
}
|
||||
}
|
||||
CallType::ByPointer(sym) => {
|
||||
self.set_not_leaf_function();
|
||||
self.set_last_seen(*sym, stmt);
|
||||
}
|
||||
}
|
||||
}
|
||||
Expr::RunLowLevel(_, args) => {
|
||||
for sym in *args {
|
||||
self.set_last_seen(*sym, stmt);
|
||||
}
|
||||
}
|
||||
Expr::ForeignCall { arguments, .. } => {
|
||||
for sym in *arguments {
|
||||
self.set_last_seen(*sym, stmt);
|
||||
}
|
||||
self.set_not_leaf_function();
|
||||
}
|
||||
|
||||
Expr::Call(call) => self.scan_ast_call(call, stmt),
|
||||
|
||||
Expr::Tag { arguments, .. } => {
|
||||
for sym in *arguments {
|
||||
self.set_last_seen(*sym, stmt);
|
||||
@ -320,6 +332,20 @@ where
|
||||
}
|
||||
self.scan_ast(following);
|
||||
}
|
||||
|
||||
Stmt::Invoke {
|
||||
symbol,
|
||||
layout,
|
||||
call,
|
||||
pass,
|
||||
fail: _,
|
||||
} => {
|
||||
// for now, treat invoke as a normal call
|
||||
|
||||
let stmt = Stmt::Let(*symbol, Expr::Call(call.clone()), layout.clone(), pass);
|
||||
self.scan_ast(&stmt);
|
||||
}
|
||||
|
||||
Stmt::Switch {
|
||||
cond_symbol,
|
||||
branches,
|
||||
@ -335,6 +361,7 @@ where
|
||||
Stmt::Ret(sym) => {
|
||||
self.set_last_seen(*sym, stmt);
|
||||
}
|
||||
Stmt::Rethrow => {}
|
||||
Stmt::Inc(sym, following) => {
|
||||
self.set_last_seen(*sym, stmt);
|
||||
self.scan_ast(following);
|
||||
@ -364,4 +391,30 @@ where
|
||||
Stmt::RuntimeError(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn scan_ast_call(&mut self, call: &roc_mono::ir::Call, stmt: &roc_mono::ir::Stmt<'a>) {
|
||||
let roc_mono::ir::Call {
|
||||
call_type,
|
||||
arguments,
|
||||
} = call;
|
||||
|
||||
for sym in *arguments {
|
||||
self.set_last_seen(*sym, stmt);
|
||||
}
|
||||
|
||||
match call_type {
|
||||
CallType::ByName { name: sym, .. } => {
|
||||
// For functions that we won't inline, we should not be a leaf function.
|
||||
if !INLINED_SYMBOLS.contains(sym) {
|
||||
self.set_not_leaf_function();
|
||||
}
|
||||
}
|
||||
CallType::ByPointer { name: sym, .. } => {
|
||||
self.set_not_leaf_function();
|
||||
self.set_last_seen(*sym, stmt);
|
||||
}
|
||||
CallType::LowLevel { .. } => {}
|
||||
CallType::Foreign { .. } => self.set_not_leaf_function(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +40,19 @@ mod gen_num {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gen_sub_i64() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
1 - 2 - 3
|
||||
"#
|
||||
),
|
||||
-4,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn i64_force_stack() {
|
||||
// This claims 33 registers. One more than Arm and RISC-V, and many more than x86-64.
|
||||
|
@ -47,9 +47,10 @@ pub fn helper<'a>(
|
||||
arena,
|
||||
filename,
|
||||
&module_src,
|
||||
stdlib,
|
||||
&stdlib,
|
||||
src_dir,
|
||||
exposed_types,
|
||||
8,
|
||||
);
|
||||
|
||||
let mut loaded = loaded.expect("failed to load module");
|
||||
|
@ -699,7 +699,7 @@ struct State<'a> {
|
||||
pub root_id: ModuleId,
|
||||
pub platform_id: Option<ModuleId>,
|
||||
pub goal_phase: Phase,
|
||||
pub stdlib: StdLib,
|
||||
pub stdlib: &'a StdLib,
|
||||
pub exposed_types: SubsByModule,
|
||||
pub output_path: Option<&'a str>,
|
||||
pub platform_path: Option<To<'a>>,
|
||||
@ -944,9 +944,10 @@ fn enqueue_task<'a>(
|
||||
pub fn load_and_typecheck(
|
||||
arena: &Bump,
|
||||
filename: PathBuf,
|
||||
stdlib: StdLib,
|
||||
stdlib: &StdLib,
|
||||
src_dir: &Path,
|
||||
exposed_types: SubsByModule,
|
||||
ptr_bytes: u32,
|
||||
) -> Result<LoadedModule, LoadingProblem> {
|
||||
use LoadResult::*;
|
||||
|
||||
@ -959,6 +960,7 @@ pub fn load_and_typecheck(
|
||||
src_dir,
|
||||
exposed_types,
|
||||
Phase::SolveTypes,
|
||||
ptr_bytes,
|
||||
)? {
|
||||
Monomorphized(_) => unreachable!(""),
|
||||
TypeChecked(module) => Ok(module),
|
||||
@ -968,9 +970,10 @@ pub fn load_and_typecheck(
|
||||
pub fn load_and_monomorphize<'a>(
|
||||
arena: &'a Bump,
|
||||
filename: PathBuf,
|
||||
stdlib: StdLib,
|
||||
stdlib: &'a StdLib,
|
||||
src_dir: &Path,
|
||||
exposed_types: SubsByModule,
|
||||
ptr_bytes: u32,
|
||||
) -> Result<MonomorphizedModule<'a>, LoadingProblem> {
|
||||
use LoadResult::*;
|
||||
|
||||
@ -983,6 +986,7 @@ pub fn load_and_monomorphize<'a>(
|
||||
src_dir,
|
||||
exposed_types,
|
||||
Phase::MakeSpecializations,
|
||||
ptr_bytes,
|
||||
)? {
|
||||
Monomorphized(module) => Ok(module),
|
||||
TypeChecked(_) => unreachable!(""),
|
||||
@ -993,9 +997,10 @@ pub fn load_and_monomorphize_from_str<'a>(
|
||||
arena: &'a Bump,
|
||||
filename: PathBuf,
|
||||
src: &'a str,
|
||||
stdlib: StdLib,
|
||||
stdlib: &'a StdLib,
|
||||
src_dir: &Path,
|
||||
exposed_types: SubsByModule,
|
||||
ptr_bytes: u32,
|
||||
) -> Result<MonomorphizedModule<'a>, LoadingProblem> {
|
||||
use LoadResult::*;
|
||||
|
||||
@ -1008,6 +1013,7 @@ pub fn load_and_monomorphize_from_str<'a>(
|
||||
src_dir,
|
||||
exposed_types,
|
||||
Phase::MakeSpecializations,
|
||||
ptr_bytes,
|
||||
)? {
|
||||
Monomorphized(module) => Ok(module),
|
||||
TypeChecked(_) => unreachable!(""),
|
||||
@ -1140,10 +1146,11 @@ fn load<'a>(
|
||||
arena: &'a Bump,
|
||||
//filename: PathBuf,
|
||||
load_start: LoadStart<'a>,
|
||||
stdlib: StdLib,
|
||||
stdlib: &'a StdLib,
|
||||
src_dir: &Path,
|
||||
exposed_types: SubsByModule,
|
||||
goal_phase: Phase,
|
||||
ptr_bytes: u32,
|
||||
) -> Result<LoadResult<'a>, LoadingProblem>
|
||||
where
|
||||
{
|
||||
@ -1259,8 +1266,14 @@ where
|
||||
// added. In that case, do nothing, and keep waiting
|
||||
// until we receive a Shutdown message.
|
||||
if let Some(task) = find_task(&worker, injector, stealers) {
|
||||
run_task(task, worker_arena, src_dir, msg_tx.clone())
|
||||
.expect("Msg channel closed unexpectedly.");
|
||||
run_task(
|
||||
task,
|
||||
worker_arena,
|
||||
src_dir,
|
||||
msg_tx.clone(),
|
||||
ptr_bytes,
|
||||
)
|
||||
.expect("Msg channel closed unexpectedly.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1790,8 +1803,6 @@ fn update<'a>(
|
||||
if state.dependencies.solved_all() && state.goal_phase == Phase::MakeSpecializations {
|
||||
debug_assert!(work.is_empty(), "still work remaining {:?}", &work);
|
||||
|
||||
Proc::insert_refcount_operations(arena, &mut state.procedures);
|
||||
|
||||
// display the mono IR of the module, for debug purposes
|
||||
if roc_mono::ir::PRETTY_PRINT_IR_SYMBOLS {
|
||||
let procs_string = state
|
||||
@ -1805,6 +1816,8 @@ fn update<'a>(
|
||||
println!("{}", result);
|
||||
}
|
||||
|
||||
Proc::insert_refcount_operations(arena, &mut state.procedures);
|
||||
|
||||
msg_tx
|
||||
.send(Msg::FinishedAllSpecialization {
|
||||
subs,
|
||||
@ -3341,6 +3354,7 @@ fn make_specializations<'a>(
|
||||
mut layout_cache: LayoutCache<'a>,
|
||||
specializations_we_must_make: ExternalSpecializations,
|
||||
mut module_timing: ModuleTiming,
|
||||
ptr_bytes: u32,
|
||||
) -> Msg<'a> {
|
||||
let make_specializations_start = SystemTime::now();
|
||||
let mut mono_problems = Vec::new();
|
||||
@ -3351,6 +3365,7 @@ fn make_specializations<'a>(
|
||||
subs: &mut subs,
|
||||
home,
|
||||
ident_ids: &mut ident_ids,
|
||||
ptr_bytes,
|
||||
};
|
||||
|
||||
procs
|
||||
@ -3396,6 +3411,7 @@ fn build_pending_specializations<'a>(
|
||||
decls: Vec<Declaration>,
|
||||
mut module_timing: ModuleTiming,
|
||||
mut layout_cache: LayoutCache<'a>,
|
||||
ptr_bytes: u32,
|
||||
// TODO remove
|
||||
exposed_to_host: MutMap<Symbol, Variable>,
|
||||
) -> Msg<'a> {
|
||||
@ -3410,6 +3426,7 @@ fn build_pending_specializations<'a>(
|
||||
subs: &mut subs,
|
||||
home,
|
||||
ident_ids: &mut ident_ids,
|
||||
ptr_bytes,
|
||||
};
|
||||
|
||||
// Add modules' decls to Procs
|
||||
@ -3613,6 +3630,7 @@ fn run_task<'a>(
|
||||
arena: &'a Bump,
|
||||
src_dir: &Path,
|
||||
msg_tx: MsgSender<'a>,
|
||||
ptr_bytes: u32,
|
||||
) -> Result<(), LoadingProblem> {
|
||||
use BuildTask::*;
|
||||
|
||||
@ -3685,6 +3703,7 @@ fn run_task<'a>(
|
||||
decls,
|
||||
module_timing,
|
||||
layout_cache,
|
||||
ptr_bytes,
|
||||
exposed_to_host,
|
||||
)),
|
||||
MakeSpecializations {
|
||||
@ -3704,6 +3723,7 @@ fn run_task<'a>(
|
||||
layout_cache,
|
||||
specializations_we_must_make,
|
||||
module_timing,
|
||||
ptr_bytes,
|
||||
)),
|
||||
}?;
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
app "quicksort" provides [ swap, partition, partitionHelp, quicksort ] to "./platform"
|
||||
|
||||
quicksort : List (Num a), I64, I64 -> List (Num a)
|
||||
quicksort : List (Num a), Nat, Nat -> List (Num a)
|
||||
quicksort = \list, low, high ->
|
||||
when partition low high list is
|
||||
Pair partitionIndex partitioned ->
|
||||
@ -9,7 +9,7 @@ quicksort = \list, low, high ->
|
||||
|> quicksort (partitionIndex + 1) high
|
||||
|
||||
|
||||
swap : I64, I64, List a -> List a
|
||||
swap : Nat, Nat, List a -> List a
|
||||
swap = \i, j, list ->
|
||||
when Pair (List.get list i) (List.get list j) is
|
||||
Pair (Ok atI) (Ok atJ) ->
|
||||
@ -21,7 +21,7 @@ swap = \i, j, list ->
|
||||
[]
|
||||
|
||||
|
||||
partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partition : Nat, Nat, List (Num a) -> [ Pair Nat (List (Num a)) ]
|
||||
partition = \low, high, initialList ->
|
||||
when List.get initialList high is
|
||||
Ok pivot ->
|
||||
@ -33,7 +33,7 @@ partition = \low, high, initialList ->
|
||||
Pair (low - 1) initialList
|
||||
|
||||
|
||||
partitionHelp : I64, I64, List (Num a), I64, (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partitionHelp : Nat, Nat, List (Num a), Nat, (Num a) -> [ Pair Nat (List (Num a)) ]
|
||||
partitionHelp = \i, j, list, high, pivot ->
|
||||
if j < high then
|
||||
when List.get list j is
|
||||
|
@ -1,7 +1,7 @@
|
||||
app "quicksort" provides [ quicksort ] to "./platform"
|
||||
|
||||
quicksort = \originalList ->
|
||||
quicksortHelp : List (Num a), I64, I64 -> List (Num a)
|
||||
quicksortHelp : List (Num a), Nat, Nat -> List (Num a)
|
||||
quicksortHelp = \list, low, high ->
|
||||
if low < high then
|
||||
when partition low high list is
|
||||
@ -13,7 +13,7 @@ quicksort = \originalList ->
|
||||
list
|
||||
|
||||
|
||||
swap : I64, I64, List a -> List a
|
||||
swap : Nat, Nat, List a -> List a
|
||||
swap = \i, j, list ->
|
||||
when Pair (List.get list i) (List.get list j) is
|
||||
Pair (Ok atI) (Ok atJ) ->
|
||||
@ -24,7 +24,7 @@ quicksort = \originalList ->
|
||||
_ ->
|
||||
[]
|
||||
|
||||
partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partition : Nat, Nat, List (Num a) -> [ Pair Nat (List (Num a)) ]
|
||||
partition = \low, high, initialList ->
|
||||
when List.get initialList high is
|
||||
Ok pivot ->
|
||||
@ -36,7 +36,7 @@ quicksort = \originalList ->
|
||||
Pair (low - 1) initialList
|
||||
|
||||
|
||||
partitionHelp : I64, I64, List (Num a), I64, (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partitionHelp : Nat, Nat, List (Num a), Nat, (Num a) -> [ Pair Nat (List (Num a)) ]
|
||||
partitionHelp = \i, j, list, high, pivot ->
|
||||
if j < high then
|
||||
when List.get list j is
|
||||
|
@ -2,7 +2,7 @@ interface Quicksort
|
||||
exposes [ swap, partition, quicksort ]
|
||||
imports []
|
||||
|
||||
quicksort : List (Num a), I64, I64 -> List (Num a)
|
||||
quicksort : List (Num a), Nat, Nat -> List (Num a)
|
||||
quicksort = \list, low, high ->
|
||||
when partition low high list is
|
||||
Pair partitionIndex partitioned ->
|
||||
@ -11,7 +11,7 @@ quicksort = \list, low, high ->
|
||||
|> quicksort (partitionIndex + 1) high
|
||||
|
||||
|
||||
swap : I64, I64, List a -> List a
|
||||
swap : Nat, Nat, List a -> List a
|
||||
swap = \i, j, list ->
|
||||
when Pair (List.get list i) (List.get list j) is
|
||||
Pair (Ok atI) (Ok atJ) ->
|
||||
@ -23,7 +23,7 @@ swap = \i, j, list ->
|
||||
[]
|
||||
|
||||
|
||||
partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partition : Nat, Nat, List (Num a) -> [ Pair Nat (List (Num a)) ]
|
||||
partition = \low, high, initialList ->
|
||||
when List.get initialList high is
|
||||
Ok pivot ->
|
||||
@ -35,7 +35,7 @@ partition = \low, high, initialList ->
|
||||
Pair (low - 1) initialList
|
||||
|
||||
|
||||
partitionHelp : I64, I64, List (Num a), I64, (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partitionHelp : Nat, Nat, List (Num a), Nat, (Num a) -> [ Pair Nat (List (Num a)) ]
|
||||
partitionHelp = \i, j, list, high, pivot ->
|
||||
if j < high then
|
||||
when List.get list j is
|
||||
|
@ -82,9 +82,10 @@ mod test_load {
|
||||
roc_load::file::load_and_typecheck(
|
||||
arena,
|
||||
full_file_path,
|
||||
stdlib,
|
||||
&stdlib,
|
||||
dir.path(),
|
||||
exposed_types,
|
||||
8,
|
||||
)
|
||||
};
|
||||
|
||||
@ -123,9 +124,10 @@ mod test_load {
|
||||
let loaded = roc_load::file::load_and_typecheck(
|
||||
&arena,
|
||||
filename,
|
||||
roc_builtins::std::standard_stdlib(),
|
||||
&roc_builtins::std::standard_stdlib(),
|
||||
src_dir.as_path(),
|
||||
subs_by_module,
|
||||
8,
|
||||
);
|
||||
let mut loaded_module = loaded.expect("Test module failed to load");
|
||||
|
||||
@ -285,9 +287,10 @@ mod test_load {
|
||||
let loaded = roc_load::file::load_and_typecheck(
|
||||
&arena,
|
||||
filename,
|
||||
roc_builtins::std::standard_stdlib(),
|
||||
&roc_builtins::std::standard_stdlib(),
|
||||
src_dir.as_path(),
|
||||
subs_by_module,
|
||||
8,
|
||||
);
|
||||
|
||||
let mut loaded_module = loaded.expect("Test module failed to load");
|
||||
@ -357,14 +360,14 @@ mod test_load {
|
||||
expect_types(
|
||||
loaded_module,
|
||||
hashmap! {
|
||||
"floatTest" => "F64",
|
||||
"divisionFn" => "F64, F64 -> Result F64 [ DivByZero ]*",
|
||||
"divisionTest" => "Result F64 [ DivByZero ]*",
|
||||
"intTest" => "I64",
|
||||
"x" => "F64",
|
||||
"floatTest" => "Float *",
|
||||
"divisionFn" => "Float a, Float a -> Result (Float a) [ DivByZero ]*",
|
||||
"divisionTest" => "Result (Float *) [ DivByZero ]*",
|
||||
"intTest" => "Int *",
|
||||
"x" => "Float *",
|
||||
"constantNum" => "Num *",
|
||||
"divDep1ByDep2" => "Result F64 [ DivByZero ]*",
|
||||
"fromDep2" => "F64",
|
||||
"divDep1ByDep2" => "Result (Float *) [ DivByZero ]*",
|
||||
"fromDep2" => "Float *",
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -377,10 +380,10 @@ mod test_load {
|
||||
expect_types(
|
||||
loaded_module,
|
||||
hashmap! {
|
||||
"swap" => "I64, I64, List a -> List a",
|
||||
"partition" => "I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]",
|
||||
"partitionHelp" => "I64, I64, List (Num a), I64, Num a -> [ Pair I64 (List (Num a)) ]",
|
||||
"quicksort" => "List (Num a), I64, I64 -> List (Num a)",
|
||||
"swap" => "Nat, Nat, List a -> List a",
|
||||
"partition" => "Nat, Nat, List (Num a) -> [ Pair Nat (List (Num a)) ]",
|
||||
"partitionHelp" => "Nat, Nat, List (Num a), Nat, Num a -> [ Pair Nat (List (Num a)) ]",
|
||||
"quicksort" => "List (Num a), Nat, Nat -> List (Num a)",
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -406,10 +409,10 @@ mod test_load {
|
||||
expect_types(
|
||||
loaded_module,
|
||||
hashmap! {
|
||||
"swap" => "I64, I64, List a -> List a",
|
||||
"partition" => "I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]",
|
||||
"partitionHelp" => "I64, I64, List (Num a), I64, Num a -> [ Pair I64 (List (Num a)) ]",
|
||||
"quicksort" => "List (Num a), I64, I64 -> List (Num a)",
|
||||
"swap" => "Nat, Nat, List a -> List a",
|
||||
"partition" => "Nat, Nat, List (Num a) -> [ Pair Nat (List (Num a)) ]",
|
||||
"partitionHelp" => "Nat, Nat, List (Num a), Nat, Num a -> [ Pair Nat (List (Num a)) ]",
|
||||
"quicksort" => "List (Num a), Nat, Nat -> List (Num a)",
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -454,7 +457,7 @@ mod test_load {
|
||||
expect_types(
|
||||
loaded_module,
|
||||
hashmap! {
|
||||
"blah2" => "F64",
|
||||
"blah2" => "Float *",
|
||||
"blah3" => "Str",
|
||||
"str" => "Str",
|
||||
"alwaysThree" => "* -> Str",
|
||||
@ -476,7 +479,7 @@ mod test_load {
|
||||
expect_types(
|
||||
loaded_module,
|
||||
hashmap! {
|
||||
"blah2" => "F64",
|
||||
"blah2" => "Float *",
|
||||
"blah3" => "Str",
|
||||
"str" => "Str",
|
||||
"alwaysThree" => "* -> Str",
|
||||
|
@ -1,423 +0,0 @@
|
||||
#[macro_use]
|
||||
extern crate pretty_assertions;
|
||||
#[macro_use]
|
||||
extern crate maplit;
|
||||
|
||||
extern crate bumpalo;
|
||||
extern crate inlinable_string;
|
||||
extern crate roc_collections;
|
||||
extern crate roc_load;
|
||||
extern crate roc_module;
|
||||
|
||||
mod helpers;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_uniq_load {
|
||||
use crate::helpers::fixtures_dir;
|
||||
use bumpalo::Bump;
|
||||
use inlinable_string::InlinableString;
|
||||
use roc_builtins::unique;
|
||||
use roc_can::def::Declaration::*;
|
||||
use roc_can::def::Def;
|
||||
use roc_collections::all::MutMap;
|
||||
use roc_constrain::module::SubsByModule;
|
||||
use roc_load::file::LoadedModule;
|
||||
use roc_module::ident::ModuleName;
|
||||
use roc_module::symbol::{Interns, ModuleId};
|
||||
use roc_types::pretty_print::{content_to_string, name_all_type_vars};
|
||||
use roc_types::subs::Subs;
|
||||
use std::collections::HashMap;
|
||||
|
||||
// HELPERS
|
||||
|
||||
fn load_fixture(
|
||||
dir_name: &str,
|
||||
module_name: &str,
|
||||
subs_by_module: SubsByModule,
|
||||
) -> LoadedModule {
|
||||
let arena = Bump::new();
|
||||
let src_dir = fixtures_dir().join(dir_name);
|
||||
let filename = src_dir.join(format!("{}.roc", module_name));
|
||||
let loaded = roc_load::file::load_and_typecheck(
|
||||
&arena,
|
||||
filename,
|
||||
unique::uniq_stdlib(),
|
||||
src_dir.as_path(),
|
||||
subs_by_module,
|
||||
);
|
||||
let mut loaded_module = loaded.expect("Test module failed to load");
|
||||
|
||||
let home = loaded_module.module_id;
|
||||
|
||||
assert_eq!(
|
||||
loaded_module.can_problems.remove(&home).unwrap_or_default(),
|
||||
Vec::new()
|
||||
);
|
||||
assert_eq!(
|
||||
loaded_module
|
||||
.type_problems
|
||||
.remove(&home)
|
||||
.unwrap_or_default(),
|
||||
Vec::new()
|
||||
);
|
||||
|
||||
let expected_name = loaded_module
|
||||
.interns
|
||||
.module_ids
|
||||
.get_name(loaded_module.module_id)
|
||||
.expect("Test ModuleID not found in module_ids");
|
||||
|
||||
// App module names are hardcoded and not based on anything user-specified
|
||||
if expected_name != ModuleName::APP {
|
||||
assert_eq!(expected_name, &InlinableString::from(module_name));
|
||||
}
|
||||
|
||||
loaded_module
|
||||
}
|
||||
|
||||
fn expect_def(
|
||||
interns: &Interns,
|
||||
subs: &mut Subs,
|
||||
home: ModuleId,
|
||||
def: &Def,
|
||||
expected_types: &mut HashMap<&str, &str>,
|
||||
) {
|
||||
for (symbol, expr_var) in &def.pattern_vars {
|
||||
let content = subs.get(*expr_var).content;
|
||||
|
||||
name_all_type_vars(*expr_var, subs);
|
||||
|
||||
let actual_str = content_to_string(content, subs, home, &interns);
|
||||
let fully_qualified = symbol.fully_qualified(&interns, home).to_string();
|
||||
let expected_type = expected_types
|
||||
.remove(fully_qualified.as_str())
|
||||
.unwrap_or_else(|| {
|
||||
panic!("Defs included an unexpected symbol: {:?}", fully_qualified)
|
||||
});
|
||||
|
||||
assert_eq!((&symbol, expected_type), (&symbol, actual_str.as_str()));
|
||||
}
|
||||
}
|
||||
|
||||
fn expect_types(mut loaded_module: LoadedModule, mut expected_types: HashMap<&str, &str>) {
|
||||
let home = loaded_module.module_id;
|
||||
let mut subs = loaded_module.solved.into_inner();
|
||||
|
||||
assert_eq!(
|
||||
loaded_module.can_problems.remove(&home).unwrap_or_default(),
|
||||
Vec::new()
|
||||
);
|
||||
assert_eq!(
|
||||
loaded_module
|
||||
.type_problems
|
||||
.remove(&home)
|
||||
.unwrap_or_default(),
|
||||
Vec::new()
|
||||
);
|
||||
|
||||
for decl in loaded_module.declarations_by_id.remove(&home).unwrap() {
|
||||
match decl {
|
||||
Declare(def) => expect_def(
|
||||
&loaded_module.interns,
|
||||
&mut subs,
|
||||
home,
|
||||
&def,
|
||||
&mut expected_types,
|
||||
),
|
||||
DeclareRec(defs) => {
|
||||
for def in defs {
|
||||
expect_def(
|
||||
&loaded_module.interns,
|
||||
&mut subs,
|
||||
home,
|
||||
&def,
|
||||
&mut expected_types,
|
||||
);
|
||||
}
|
||||
}
|
||||
Builtin(_) => {}
|
||||
cycle @ InvalidCycle(_, _) => {
|
||||
panic!("Unexpected cyclic def in module declarations: {:?}", cycle);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
expected_types,
|
||||
HashMap::default(),
|
||||
"Some expected types were not found in the defs"
|
||||
);
|
||||
}
|
||||
|
||||
// TESTS
|
||||
|
||||
#[test]
|
||||
fn interface_with_deps() {
|
||||
let arena = Bump::new();
|
||||
let subs_by_module = MutMap::default();
|
||||
let src_dir = fixtures_dir().join("interface_with_deps");
|
||||
let filename = src_dir.join("Primary.roc");
|
||||
let loaded = roc_load::file::load_and_typecheck(
|
||||
&arena,
|
||||
filename,
|
||||
roc_builtins::std::standard_stdlib(),
|
||||
src_dir.as_path(),
|
||||
subs_by_module,
|
||||
);
|
||||
|
||||
let mut loaded_module = loaded.expect("Test module failed to load");
|
||||
let home = loaded_module.module_id;
|
||||
|
||||
assert_eq!(
|
||||
loaded_module.can_problems.remove(&home).unwrap_or_default(),
|
||||
Vec::new()
|
||||
);
|
||||
assert_eq!(
|
||||
loaded_module
|
||||
.type_problems
|
||||
.remove(&home)
|
||||
.unwrap_or_default(),
|
||||
Vec::new()
|
||||
);
|
||||
|
||||
let def_count: usize = loaded_module
|
||||
.declarations_by_id
|
||||
.remove(&loaded_module.module_id)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(|decl| decl.def_count())
|
||||
.sum();
|
||||
|
||||
let expected_name = loaded_module
|
||||
.interns
|
||||
.module_ids
|
||||
.get_name(loaded_module.module_id)
|
||||
.expect("Test ModuleID not found in module_ids");
|
||||
|
||||
assert_eq!(expected_name, &InlinableString::from("Primary"));
|
||||
assert_eq!(def_count, 10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_unit() {
|
||||
let subs_by_module = MutMap::default();
|
||||
let loaded_module = load_fixture("no_deps", "Unit", subs_by_module);
|
||||
|
||||
expect_types(
|
||||
loaded_module,
|
||||
hashmap! {
|
||||
"unit" => "Attr * Unit",
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn import_alias() {
|
||||
let subs_by_module = MutMap::default();
|
||||
let loaded_module = load_fixture("interface_with_deps", "ImportAlias", subs_by_module);
|
||||
|
||||
expect_types(
|
||||
loaded_module,
|
||||
hashmap! {
|
||||
"unit" => "Attr * Dep1.Unit",
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_and_typecheck() {
|
||||
let subs_by_module = MutMap::default();
|
||||
let loaded_module = load_fixture("interface_with_deps", "WithBuiltins", subs_by_module);
|
||||
|
||||
expect_types(
|
||||
loaded_module,
|
||||
hashmap! {
|
||||
"floatTest" => "Attr Shared F64",
|
||||
"divisionFn" => "Attr Shared (Attr * F64, Attr * F64 -> Attr * (Result (Attr * F64) (Attr * [ DivByZero ]*)))",
|
||||
"divisionTest" => "Attr * (Result (Attr * F64) (Attr * [ DivByZero ]*))",
|
||||
"intTest" => "Attr * I64",
|
||||
"x" => "Attr * F64",
|
||||
"constantNum" => "Attr * (Num (Attr * *))",
|
||||
"divDep1ByDep2" => "Attr * (Result (Attr * F64) (Attr * [ DivByZero ]*))",
|
||||
"fromDep2" => "Attr * F64",
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn load_astar() {
|
||||
let subs_by_module = MutMap::default();
|
||||
let loaded_module = load_fixture("interface_with_deps", "AStar", subs_by_module);
|
||||
|
||||
expect_types(
|
||||
loaded_module,
|
||||
hashmap! {
|
||||
"findPath" => "Attr * (Attr * { costFunction : Attr Shared (Attr Shared position, Attr Shared position -> Attr * F64), end : Attr Shared position, moveFunction : Attr Shared (Attr Shared position -> Attr * (Set (Attr * position))), start : Attr Shared position } -> Attr * (Result (Attr * (List (Attr Shared position))) (Attr * [ KeyNotFound ]*)))",
|
||||
"initialModel" => "Attr * (Attr Shared position -> Attr * (Model (Attr Shared position)))",
|
||||
"reconstructPath" => "Attr Shared (Attr Shared (Map (Attr * position) (Attr Shared position)), Attr Shared position -> Attr * (List (Attr Shared position)))",
|
||||
"updateCost" => "Attr * (Attr Shared position, Attr Shared position, Attr Shared (Model (Attr Shared position)) -> Attr Shared (Model (Attr Shared position)))",
|
||||
"cheapestOpen" => "Attr * (Attr * (Attr Shared position -> Attr * F64), Attr (* | a | b | c) (Model (Attr Shared position)) -> Attr * (Result (Attr Shared position) (Attr * [ KeyNotFound ]*)))",
|
||||
"astar" => "Attr Shared (Attr Shared (Attr Shared position, Attr Shared position -> Attr * F64), Attr Shared (Attr Shared position -> Attr * (Set (Attr * position))), Attr Shared position, Attr Shared (Model (Attr Shared position)) -> Attr * [ Err (Attr * [ KeyNotFound ]*), Ok (Attr * (List (Attr Shared position))) ]*)",
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_and_typecheck_quicksort() {
|
||||
let subs_by_module = MutMap::default();
|
||||
let loaded_module = load_fixture("interface_with_deps", "Quicksort", subs_by_module);
|
||||
|
||||
expect_types(
|
||||
loaded_module,
|
||||
hashmap! {
|
||||
"swap" => "Attr * (Attr * I64, Attr * I64, Attr * (List (Attr Shared a)) -> Attr * (List (Attr Shared a)))",
|
||||
"partition" => "Attr * (Attr Shared I64, Attr Shared I64, Attr b (List (Attr Shared (Num (Attr Shared a)))) -> Attr * [ Pair (Attr * I64) (Attr b (List (Attr Shared (Num (Attr Shared a))))) ])",
|
||||
|
||||
"partitionHelp" => "Attr Shared (Attr b I64, Attr Shared I64, Attr c (List (Attr Shared (Num (Attr Shared a)))), Attr Shared I64, Attr Shared (Num (Attr Shared a)) -> Attr * [ Pair (Attr b I64) (Attr c (List (Attr Shared (Num (Attr Shared a))))) ])",
|
||||
"quicksort" => "Attr Shared (Attr b (List (Attr Shared (Num (Attr Shared a)))), Attr Shared I64, Attr Shared I64 -> Attr b (List (Attr Shared (Num (Attr Shared a)))))",
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn quickcheck_nested_let() {
|
||||
let subs_by_module = MutMap::default();
|
||||
let loaded_module = load_fixture("app_with_deps", "QuicksortOneDef", subs_by_module);
|
||||
|
||||
expect_types(
|
||||
loaded_module,
|
||||
hashmap! {
|
||||
"quicksort" => "Attr * (Attr b (List (Attr Shared (Num (Attr Shared a)))) -> Attr b (List (Attr Shared (Num (Attr Shared a)))))",
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_principal_types() {
|
||||
let subs_by_module = MutMap::default();
|
||||
let loaded_module = load_fixture("no_deps", "Principal", subs_by_module);
|
||||
|
||||
expect_types(
|
||||
loaded_module,
|
||||
hashmap! {
|
||||
"intVal" => "Attr * Str",
|
||||
"identity" => "Attr * (a -> a)",
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn load_dep_types() {
|
||||
let subs_by_module = MutMap::default();
|
||||
let loaded_module = load_fixture("interface_with_deps", "Primary", subs_by_module);
|
||||
|
||||
// the inferred signature for withDefault is wrong, part of the alias in alias issue.
|
||||
// "withDefault" => "Attr * (Attr * (Res.Res (Attr a b) (Attr * *)), Attr a b -> Attr a b)",
|
||||
expect_types(
|
||||
loaded_module,
|
||||
hashmap! {
|
||||
"blah2" => "Attr * F64",
|
||||
"blah3" => "Attr * Str",
|
||||
"str" => "Attr * Str",
|
||||
"alwaysThree" => "Attr * (* -> Attr * Str)",
|
||||
"identity" => "Attr * (a -> a)",
|
||||
"z" => "Attr * Str",
|
||||
"w" => "Attr * (Dep1.Identity (Attr * {}))",
|
||||
"succeed" => "Attr * (Attr b a -> Attr * (Dep1.Identity (Attr b a)))",
|
||||
"yay" => "Attr * (Res.Res (Attr * {}) (Attr * err))",
|
||||
"withDefault" => "Attr * (Attr (* | b | c) (Res.Res (Attr b a) (Attr c *)), Attr b a -> Attr b a)",
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn load_custom_res() {
|
||||
let subs_by_module = MutMap::default();
|
||||
let loaded_module = load_fixture("interface_with_deps", "Res", subs_by_module);
|
||||
|
||||
expect_types(
|
||||
loaded_module,
|
||||
hashmap! {
|
||||
"withDefault" =>"Attr * (Attr (* | b | c) (Res (Attr b a) (Attr c err)), Attr b a -> Attr b a)",
|
||||
"map" => "Attr * (Attr (* | c | d) (Res (Attr c a) (Attr d err)), Attr * (Attr c a -> Attr e b) -> Attr * (Res (Attr e b) (Attr d err)))",
|
||||
"andThen" => "Attr * (Attr (* | c | d) (Res (Attr c a) (Attr d err)), Attr * (Attr c a -> Attr f (Res (Attr e b) (Attr d err))) -> Attr f (Res (Attr e b) (Attr d err)))",
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn imported_dep_regression() {
|
||||
let subs_by_module = MutMap::default();
|
||||
let loaded_module = load_fixture("interface_with_deps", "OneDep", subs_by_module);
|
||||
|
||||
expect_types(
|
||||
loaded_module,
|
||||
hashmap! {
|
||||
"str" => "Attr * Str",
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn load_records() {
|
||||
// test_async(async {
|
||||
// use roc::types::{ErrorType, Mismatch, Problem, TypeExt};
|
||||
|
||||
// let subs_by_module = MutMap::default();
|
||||
// let loaded_module =
|
||||
// load_fixture("interface_with_deps", "Records", subs_by_module);
|
||||
|
||||
// // NOTE: `a` here is unconstrained, so unifies with <type error>
|
||||
// let expected_types = hashmap! {
|
||||
// "Records.intVal" => "a",
|
||||
// };
|
||||
|
||||
// let a = ErrorType::FlexVar("a".into());
|
||||
|
||||
// let mut record = SendMap::default();
|
||||
// record.insert("x".into(), a);
|
||||
|
||||
// let problem = Problem::Mismatch(
|
||||
// Mismatch::TypeMismatch,
|
||||
// ErrorType::Record(SendMap::default(), TypeExt::Closed),
|
||||
// ErrorType::Record(record, TypeExt::FlexOpen("b".into())),
|
||||
// );
|
||||
|
||||
// assert_eq!(loaded_module.problems, vec![problem]);
|
||||
// assert_eq!(expected_types.len(), loaded_module.declarations.len());
|
||||
|
||||
// let mut subs = loaded_module.solved.into_inner();
|
||||
|
||||
// for decl in loaded_module.declarations {
|
||||
// let def = match decl {
|
||||
// Declare(def) => def,
|
||||
// rec_decl @ DeclareRec(_) => {
|
||||
// panic!(
|
||||
// "Unexpected recursive def in module declarations: {:?}",
|
||||
// rec_decl
|
||||
// );
|
||||
// }
|
||||
// cycle @ InvalidCycle(_, _) => {
|
||||
// panic!("Unexpected cyclic def in module declarations: {:?}", cycle);
|
||||
// }
|
||||
// };
|
||||
|
||||
// for (symbol, expr_var) in def.pattern_vars {
|
||||
// let content = subs.get(expr_var).content;
|
||||
|
||||
// name_all_type_vars(expr_var, &mut subs);
|
||||
|
||||
// let actual_str = content_to_string(content, &mut subs);
|
||||
// let expected_type = expected_types.get(symbol.as_str()).unwrap_or_else(|| {
|
||||
// panic!("Defs included an unexpected symbol: {:?}", symbol)
|
||||
// });
|
||||
|
||||
// assert_eq!((&symbol, expected_type), (&symbol, &actual_str.as_str()));
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
}
|
@ -59,6 +59,7 @@ pub enum LowLevel {
|
||||
NumAcos,
|
||||
NumAsin,
|
||||
NumBitwiseAnd,
|
||||
NumBitwiseXor,
|
||||
Eq,
|
||||
NotEq,
|
||||
And,
|
||||
|
@ -738,6 +738,7 @@ define_builtins! {
|
||||
10 INC: "#inc" // internal function that increments the refcount
|
||||
11 DEC: "#dec" // internal function that increments the refcount
|
||||
12 ARG_CLOSURE: "#arg_closure" // symbol used to store the closure record
|
||||
13 LIST_EQ: "#list_eq" // internal function that checks list equality
|
||||
}
|
||||
1 NUM: "Num" => {
|
||||
0 NUM_NUM: "Num" imported // the Num.Num type alias
|
||||
@ -822,10 +823,16 @@ define_builtins! {
|
||||
79 NUM_AT_BINARY32: "@Binary32"
|
||||
80 NUM_BINARY32: "Binary32" imported
|
||||
81 NUM_BITWISE_AND: "bitwiseAnd"
|
||||
82 NUM_SUB_WRAP: "subWrap"
|
||||
83 NUM_SUB_CHECKED: "subChecked"
|
||||
84 NUM_MUL_WRAP: "mulWrap"
|
||||
85 NUM_MUL_CHECKED: "mulChecked"
|
||||
82 NUM_BITWISE_XOR: "bitwiseXor"
|
||||
83 NUM_SUB_WRAP: "subWrap"
|
||||
84 NUM_SUB_CHECKED: "subChecked"
|
||||
85 NUM_MUL_WRAP: "mulWrap"
|
||||
86 NUM_MUL_CHECKED: "mulChecked"
|
||||
87 NUM_INT: "Int" imported
|
||||
88 NUM_FLOAT: "Float" imported
|
||||
89 NUM_AT_NATURAL: "@Natural"
|
||||
90 NUM_NATURAL: "Natural" imported
|
||||
91 NUM_NAT: "Nat" imported
|
||||
}
|
||||
2 BOOL: "Bool" => {
|
||||
0 BOOL_BOOL: "Bool" imported // the Bool.Bool type alias
|
||||
|
@ -156,6 +156,10 @@ impl<'a> ParamMap<'a> {
|
||||
Let(_, _, _, cont) => {
|
||||
stack.push(cont);
|
||||
}
|
||||
Invoke { pass, fail, .. } => {
|
||||
stack.push(pass);
|
||||
stack.push(fail);
|
||||
}
|
||||
Switch {
|
||||
branches,
|
||||
default_branch,
|
||||
@ -166,7 +170,7 @@ impl<'a> ParamMap<'a> {
|
||||
}
|
||||
Inc(_, _) | Dec(_, _) => unreachable!("these have not been introduced yet"),
|
||||
|
||||
Ret(_) | Jump(_, _) | RuntimeError(_) => {
|
||||
Ret(_) | Rethrow | Jump(_, _) | RuntimeError(_) => {
|
||||
// these are terminal, do nothing
|
||||
}
|
||||
}
|
||||
@ -295,6 +299,62 @@ impl<'a> BorrowInfState<'a> {
|
||||
///
|
||||
/// and determines whether z and which of the symbols used in e
|
||||
/// must be taken as owned paramters
|
||||
fn collect_call(&mut self, z: Symbol, e: &crate::ir::Call<'a>) {
|
||||
use crate::ir::CallType::*;
|
||||
|
||||
let crate::ir::Call {
|
||||
call_type,
|
||||
arguments,
|
||||
} = e;
|
||||
|
||||
match call_type {
|
||||
ByName {
|
||||
name, arg_layouts, ..
|
||||
}
|
||||
| ByPointer {
|
||||
name, arg_layouts, ..
|
||||
} => {
|
||||
// get the borrow signature of the applied function
|
||||
let ps = match self.param_map.get_symbol(*name) {
|
||||
Some(slice) => slice,
|
||||
None => Vec::from_iter_in(
|
||||
arg_layouts.iter().cloned().map(|layout| Param {
|
||||
symbol: Symbol::UNDERSCORE,
|
||||
borrow: false,
|
||||
layout,
|
||||
}),
|
||||
self.arena,
|
||||
)
|
||||
.into_bump_slice(),
|
||||
};
|
||||
|
||||
// the return value will be owned
|
||||
self.own_var(z);
|
||||
|
||||
// if the function exects an owned argument (ps), the argument must be owned (args)
|
||||
self.own_args_using_params(arguments, ps);
|
||||
}
|
||||
|
||||
LowLevel { op } => {
|
||||
// very unsure what demand RunLowLevel should place upon its arguments
|
||||
self.own_var(z);
|
||||
|
||||
let ps = lowlevel_borrow_signature(self.arena, *op);
|
||||
|
||||
self.own_args_using_bools(arguments, ps);
|
||||
}
|
||||
|
||||
Foreign { .. } => {
|
||||
// very unsure what demand ForeignCall should place upon its arguments
|
||||
self.own_var(z);
|
||||
|
||||
let ps = foreign_borrow_signature(self.arena, arguments.len());
|
||||
|
||||
self.own_args_using_bools(arguments, ps);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_expr(&mut self, z: Symbol, e: &Expr<'a>) {
|
||||
use Expr::*;
|
||||
|
||||
@ -334,73 +394,40 @@ impl<'a> BorrowInfState<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
FunctionCall {
|
||||
call_type,
|
||||
args,
|
||||
arg_layouts,
|
||||
..
|
||||
} => {
|
||||
// get the borrow signature of the applied function
|
||||
let ps = match self.param_map.get_symbol(call_type.get_inner()) {
|
||||
Some(slice) => slice,
|
||||
None => Vec::from_iter_in(
|
||||
arg_layouts.iter().cloned().map(|layout| Param {
|
||||
symbol: Symbol::UNDERSCORE,
|
||||
borrow: false,
|
||||
layout,
|
||||
}),
|
||||
self.arena,
|
||||
)
|
||||
.into_bump_slice(),
|
||||
};
|
||||
|
||||
// the return value will be owned
|
||||
self.own_var(z);
|
||||
|
||||
// if the function exects an owned argument (ps), the argument must be owned (args)
|
||||
self.own_args_using_params(args, ps);
|
||||
}
|
||||
|
||||
RunLowLevel(op, args) => {
|
||||
// very unsure what demand RunLowLevel should place upon its arguments
|
||||
self.own_var(z);
|
||||
|
||||
let ps = lowlevel_borrow_signature(self.arena, *op);
|
||||
|
||||
self.own_args_using_bools(args, ps);
|
||||
}
|
||||
|
||||
ForeignCall { arguments, .. } => {
|
||||
// very unsure what demand ForeignCall should place upon its arguments
|
||||
self.own_var(z);
|
||||
|
||||
let ps = foreign_borrow_signature(self.arena, arguments.len());
|
||||
|
||||
self.own_args_using_bools(arguments, ps);
|
||||
}
|
||||
Call(call) => self.collect_call(z, call),
|
||||
|
||||
Literal(_) | FunctionPointer(_, _) | RuntimeErrorFunction(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
fn preserve_tail_call(&mut self, x: Symbol, v: &Expr<'a>, b: &Stmt<'a>) {
|
||||
if let (
|
||||
Expr::FunctionCall {
|
||||
call_type,
|
||||
args: ys,
|
||||
..
|
||||
},
|
||||
Stmt::Ret(z),
|
||||
) = (v, b)
|
||||
{
|
||||
let g = call_type.get_inner();
|
||||
if self.current_proc == g && x == *z {
|
||||
// anonymous functions (for which the ps may not be known)
|
||||
// can never be tail-recursive, so this is fine
|
||||
if let Some(ps) = self.param_map.get_symbol(g) {
|
||||
self.own_params_using_args(ys, ps)
|
||||
match (v, b) {
|
||||
(
|
||||
Expr::Call(crate::ir::Call {
|
||||
call_type: crate::ir::CallType::ByName { name: g, .. },
|
||||
arguments: ys,
|
||||
..
|
||||
}),
|
||||
Stmt::Ret(z),
|
||||
)
|
||||
| (
|
||||
Expr::Call(crate::ir::Call {
|
||||
call_type: crate::ir::CallType::ByPointer { name: g, .. },
|
||||
arguments: ys,
|
||||
..
|
||||
}),
|
||||
Stmt::Ret(z),
|
||||
) => {
|
||||
if self.current_proc == *g && x == *z {
|
||||
// anonymous functions (for which the ps may not be known)
|
||||
// can never be tail-recursive, so this is fine
|
||||
if let Some(ps) = self.param_map.get_symbol(*g) {
|
||||
self.own_params_using_args(ys, ps)
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -444,11 +471,29 @@ impl<'a> BorrowInfState<'a> {
|
||||
self.collect_stmt(b);
|
||||
self.preserve_tail_call(*x, &Expr::FunctionPointer(*fsymbol, layout.clone()), b);
|
||||
}
|
||||
|
||||
Let(x, v, _, b) => {
|
||||
self.collect_stmt(b);
|
||||
self.collect_expr(*x, v);
|
||||
self.preserve_tail_call(*x, v, b);
|
||||
}
|
||||
|
||||
Invoke {
|
||||
symbol,
|
||||
call,
|
||||
layout: _,
|
||||
pass,
|
||||
fail,
|
||||
} => {
|
||||
self.collect_stmt(pass);
|
||||
self.collect_stmt(fail);
|
||||
|
||||
self.collect_call(*symbol, call);
|
||||
|
||||
// TODO how to preserve the tail call of an invoke?
|
||||
// self.preserve_tail_call(*x, v, b);
|
||||
}
|
||||
|
||||
Jump(j, ys) => {
|
||||
let ps = self.param_map.get_join_point(*j);
|
||||
|
||||
@ -470,7 +515,7 @@ impl<'a> BorrowInfState<'a> {
|
||||
}
|
||||
Inc(_, _) | Dec(_, _) => unreachable!("these have not been introduced yet"),
|
||||
|
||||
Ret(_) | RuntimeError(_) => {
|
||||
Ret(_) | RuntimeError(_) | Rethrow => {
|
||||
// these are terminal, do nothing
|
||||
}
|
||||
}
|
||||
@ -533,9 +578,8 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
||||
|
||||
Eq | NotEq | And | Or | NumAdd | NumAddWrap | NumAddChecked | NumSub | NumSubWrap
|
||||
| NumSubChecked | NumMul | NumMulWrap | NumMulChecked | NumGt | NumGte | NumLt | NumLte
|
||||
| NumCompare | NumDivUnchecked | NumRemUnchecked | NumPow | NumPowInt | NumBitwiseAnd => {
|
||||
arena.alloc_slice_copy(&[irrelevant, irrelevant])
|
||||
}
|
||||
| NumCompare | NumDivUnchecked | NumRemUnchecked | NumPow | NumPowInt | NumBitwiseAnd
|
||||
| NumBitwiseXor => arena.alloc_slice_copy(&[irrelevant, irrelevant]),
|
||||
|
||||
NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumRound | NumCeiling | NumFloor
|
||||
| NumToFloat | Not | NumIsFinite | NumAtan | NumAcos | NumAsin => {
|
||||
|
@ -66,7 +66,7 @@ pub enum Test<'a> {
|
||||
union: crate::exhaustive::Union,
|
||||
arguments: Vec<(Pattern<'a>, Layout<'a>)>,
|
||||
},
|
||||
IsInt(i64),
|
||||
IsInt(i128),
|
||||
// float patterns are stored as u64 so they are comparable/hashable
|
||||
IsFloat(u64),
|
||||
IsStr(Box<str>),
|
||||
@ -902,7 +902,10 @@ pub fn optimize_when<'a>(
|
||||
|
||||
let decision_tree = compile(patterns);
|
||||
let decider = tree_to_decider(decision_tree);
|
||||
let target_counts = count_targets(&decider);
|
||||
|
||||
// for each target (branch body), count in how many ways it can be reached
|
||||
let mut target_counts = bumpalo::vec![in env.arena; 0; indexed_branches.len()];
|
||||
count_targets(&mut target_counts, &decider);
|
||||
|
||||
let mut choices = MutMap::default();
|
||||
let mut jumps = Vec::new();
|
||||
@ -910,8 +913,9 @@ pub fn optimize_when<'a>(
|
||||
for (index, branch) in indexed_branches.into_iter() {
|
||||
let ((branch_index, choice), opt_jump) = create_choices(&target_counts, index, branch);
|
||||
|
||||
if let Some(jump) = opt_jump {
|
||||
jumps.push(jump);
|
||||
if let Some((index, body)) = opt_jump {
|
||||
let id = JoinPointId(env.unique_symbol());
|
||||
jumps.push((index, id, body));
|
||||
}
|
||||
|
||||
choices.insert(branch_index, choice);
|
||||
@ -919,7 +923,7 @@ pub fn optimize_when<'a>(
|
||||
|
||||
let choice_decider = insert_choices(&choices, decider);
|
||||
|
||||
decide_to_branching(
|
||||
let mut stmt = decide_to_branching(
|
||||
env,
|
||||
procs,
|
||||
layout_cache,
|
||||
@ -928,7 +932,18 @@ pub fn optimize_when<'a>(
|
||||
ret_layout,
|
||||
choice_decider,
|
||||
&jumps,
|
||||
)
|
||||
);
|
||||
|
||||
for (_, id, body) in jumps.into_iter() {
|
||||
stmt = Stmt::Join {
|
||||
id,
|
||||
parameters: &[],
|
||||
continuation: env.arena.alloc(body),
|
||||
remainder: env.arena.alloc(stmt),
|
||||
};
|
||||
}
|
||||
|
||||
stmt
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -1092,7 +1107,9 @@ fn test_to_equality<'a>(
|
||||
)
|
||||
}
|
||||
Test::IsInt(test_int) => {
|
||||
let lhs = Expr::Literal(Literal::Int(test_int));
|
||||
// TODO don't downcast i128 here
|
||||
debug_assert!(test_int <= i64::MAX as i128);
|
||||
let lhs = Expr::Literal(Literal::Int(test_int as i64));
|
||||
let lhs_symbol = env.unique_symbol();
|
||||
stores.push((lhs_symbol, Layout::Builtin(Builtin::Int64), lhs));
|
||||
|
||||
@ -1277,7 +1294,10 @@ fn compile_test<'a>(
|
||||
ret_layout,
|
||||
);
|
||||
|
||||
let test = Expr::RunLowLevel(LowLevel::Eq, arena.alloc([lhs, rhs]));
|
||||
let test = Expr::Call(crate::ir::Call {
|
||||
call_type: crate::ir::CallType::LowLevel { op: LowLevel::Eq },
|
||||
arguments: arena.alloc([lhs, rhs]),
|
||||
});
|
||||
|
||||
// write to the test symbol
|
||||
cond = Stmt::Let(
|
||||
@ -1328,7 +1348,7 @@ fn decide_to_branching<'a>(
|
||||
cond_layout: Layout<'a>,
|
||||
ret_layout: Layout<'a>,
|
||||
decider: Decider<'a, Choice<'a>>,
|
||||
jumps: &Vec<(u64, Stmt<'a>)>,
|
||||
jumps: &Vec<(u64, JoinPointId, Stmt<'a>)>,
|
||||
) -> Stmt<'a> {
|
||||
use Choice::*;
|
||||
use Decider::*;
|
||||
@ -1337,12 +1357,11 @@ fn decide_to_branching<'a>(
|
||||
|
||||
match decider {
|
||||
Leaf(Jump(label)) => {
|
||||
// we currently inline the jumps: does fewer jumps but produces a larger artifact
|
||||
let (_, expr) = jumps
|
||||
.iter()
|
||||
.find(|(l, _)| l == &label)
|
||||
let index = jumps
|
||||
.binary_search_by_key(&label, |ref r| r.0)
|
||||
.expect("jump not in list of jumps");
|
||||
expr.clone()
|
||||
|
||||
Stmt::Jump(jumps[index].1, &[])
|
||||
}
|
||||
Leaf(Inline(expr)) => expr,
|
||||
Chain {
|
||||
@ -1619,39 +1638,32 @@ fn to_chain<'a>(
|
||||
/// If a target appears exactly once in a Decider, the corresponding expression
|
||||
/// can be inlined. Whether things are inlined or jumps is called a "choice".
|
||||
|
||||
fn count_targets(decision_tree: &Decider<u64>) -> MutMap<u64, u64> {
|
||||
let mut result = MutMap::default();
|
||||
count_targets_help(decision_tree, &mut result);
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn count_targets_help(decision_tree: &Decider<u64>, targets: &mut MutMap<u64, u64>) {
|
||||
fn count_targets(targets: &mut bumpalo::collections::Vec<u64>, initial: &Decider<u64>) {
|
||||
use Decider::*;
|
||||
match decision_tree {
|
||||
Leaf(target) => match targets.get_mut(target) {
|
||||
None => {
|
||||
targets.insert(*target, 1);
|
||||
|
||||
let mut stack = vec![initial];
|
||||
|
||||
while let Some(decision_tree) = stack.pop() {
|
||||
match decision_tree {
|
||||
Leaf(target) => {
|
||||
targets[*target as usize] += 1;
|
||||
}
|
||||
Some(current) => {
|
||||
*current += 1;
|
||||
|
||||
Chain {
|
||||
success, failure, ..
|
||||
} => {
|
||||
stack.push(success);
|
||||
stack.push(failure);
|
||||
}
|
||||
},
|
||||
|
||||
Chain {
|
||||
success, failure, ..
|
||||
} => {
|
||||
count_targets_help(success, targets);
|
||||
count_targets_help(failure, targets);
|
||||
}
|
||||
FanOut {
|
||||
tests, fallback, ..
|
||||
} => {
|
||||
stack.push(fallback);
|
||||
|
||||
FanOut {
|
||||
tests, fallback, ..
|
||||
} => {
|
||||
count_targets_help(fallback, targets);
|
||||
|
||||
for (_, decider) in tests {
|
||||
count_targets_help(decider, targets);
|
||||
for (_, decider) in tests {
|
||||
stack.push(decider);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1659,11 +1671,11 @@ fn count_targets_help(decision_tree: &Decider<u64>, targets: &mut MutMap<u64, u6
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn create_choices<'a>(
|
||||
target_counts: &MutMap<u64, u64>,
|
||||
target_counts: &bumpalo::collections::Vec<'a, u64>,
|
||||
target: u64,
|
||||
branch: Stmt<'a>,
|
||||
) -> ((u64, Choice<'a>), Option<(u64, Stmt<'a>)>) {
|
||||
match target_counts.get(&target) {
|
||||
match target_counts.get(target as usize) {
|
||||
None => unreachable!(
|
||||
"this should never happen: {:?} not in {:?}",
|
||||
target, target_counts
|
||||
|
@ -37,7 +37,7 @@ pub enum Pattern {
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Literal {
|
||||
Int(i64),
|
||||
Int(i128),
|
||||
Bit(bool),
|
||||
Byte(u8),
|
||||
Float(u64),
|
||||
|
@ -31,10 +31,27 @@ pub fn occuring_variables(stmt: &Stmt<'_>) -> (MutSet<Symbol>, MutSet<Symbol>) {
|
||||
bound_variables.insert(*symbol);
|
||||
stack.push(cont);
|
||||
}
|
||||
|
||||
Invoke {
|
||||
symbol,
|
||||
call,
|
||||
pass,
|
||||
fail,
|
||||
..
|
||||
} => {
|
||||
occuring_variables_call(call, &mut result);
|
||||
result.insert(*symbol);
|
||||
bound_variables.insert(*symbol);
|
||||
stack.push(pass);
|
||||
stack.push(fail);
|
||||
}
|
||||
|
||||
Ret(symbol) => {
|
||||
result.insert(*symbol);
|
||||
}
|
||||
|
||||
Rethrow => {}
|
||||
|
||||
Inc(symbol, cont) | Dec(symbol, cont) => {
|
||||
result.insert(*symbol);
|
||||
stack.push(cont);
|
||||
@ -75,6 +92,12 @@ pub fn occuring_variables(stmt: &Stmt<'_>) -> (MutSet<Symbol>, MutSet<Symbol>) {
|
||||
(result, bound_variables)
|
||||
}
|
||||
|
||||
fn occuring_variables_call(call: &crate::ir::Call<'_>, result: &mut MutSet<Symbol>) {
|
||||
// NOTE though the function name does occur, it is a static constant in the program
|
||||
// for liveness, it should not be included here.
|
||||
result.extend(call.arguments.iter().copied());
|
||||
}
|
||||
|
||||
pub fn occuring_variables_expr(expr: &Expr<'_>, result: &mut MutSet<Symbol>) {
|
||||
use Expr::*;
|
||||
|
||||
@ -86,11 +109,7 @@ pub fn occuring_variables_expr(expr: &Expr<'_>, result: &mut MutSet<Symbol>) {
|
||||
result.insert(*symbol);
|
||||
}
|
||||
|
||||
FunctionCall { args, .. } => {
|
||||
// NOTE thouth the function name does occur, it is a static constant in the program
|
||||
// for liveness, it should not be included here.
|
||||
result.extend(args.iter().copied());
|
||||
}
|
||||
Call(call) => occuring_variables_call(call, result),
|
||||
|
||||
Tag { arguments, .. }
|
||||
| Struct(arguments)
|
||||
@ -108,12 +127,6 @@ pub fn occuring_variables_expr(expr: &Expr<'_>, result: &mut MutSet<Symbol>) {
|
||||
Reset(x) => {
|
||||
result.insert(*x);
|
||||
}
|
||||
RunLowLevel(_, args) => {
|
||||
result.extend(args.iter());
|
||||
}
|
||||
ForeignCall { arguments, .. } => {
|
||||
result.extend(arguments.iter());
|
||||
}
|
||||
|
||||
EmptyArray | RuntimeErrorFunction(_) | Literal(_) => {}
|
||||
}
|
||||
@ -208,6 +221,11 @@ fn consume_expr(m: &VarMap, e: &Expr<'_>) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
fn consume_call(_: &VarMap, _: &crate::ir::Call<'_>) -> bool {
|
||||
// variables bound by a call (or invoke) must always be consumed
|
||||
true
|
||||
}
|
||||
|
||||
impl<'a> Context<'a> {
|
||||
pub fn new(arena: &'a Bump, param_map: &'a ParamMap<'a>) -> Self {
|
||||
let mut vars = MutMap::default();
|
||||
@ -410,6 +428,75 @@ impl<'a> Context<'a> {
|
||||
b
|
||||
}
|
||||
|
||||
fn visit_call(
|
||||
&self,
|
||||
z: Symbol,
|
||||
call_type: crate::ir::CallType<'a>,
|
||||
arguments: &'a [Symbol],
|
||||
l: Layout<'a>,
|
||||
b: &'a Stmt<'a>,
|
||||
b_live_vars: &LiveVarSet,
|
||||
) -> &'a Stmt<'a> {
|
||||
use crate::ir::CallType::*;
|
||||
|
||||
match &call_type {
|
||||
LowLevel { op } => {
|
||||
let ps = crate::borrow::lowlevel_borrow_signature(self.arena, *op);
|
||||
let b = self.add_dec_after_lowlevel(arguments, ps, b, b_live_vars);
|
||||
|
||||
let v = Expr::Call(crate::ir::Call {
|
||||
call_type,
|
||||
arguments,
|
||||
});
|
||||
|
||||
&*self.arena.alloc(Stmt::Let(z, v, l, b))
|
||||
}
|
||||
|
||||
Foreign { .. } => {
|
||||
let ps = crate::borrow::foreign_borrow_signature(self.arena, arguments.len());
|
||||
let b = self.add_dec_after_lowlevel(arguments, ps, b, b_live_vars);
|
||||
|
||||
let v = Expr::Call(crate::ir::Call {
|
||||
call_type,
|
||||
arguments,
|
||||
});
|
||||
|
||||
&*self.arena.alloc(Stmt::Let(z, v, l, b))
|
||||
}
|
||||
|
||||
ByName {
|
||||
name, arg_layouts, ..
|
||||
}
|
||||
| ByPointer {
|
||||
name, arg_layouts, ..
|
||||
} => {
|
||||
// get the borrow signature
|
||||
let ps = match self.param_map.get_symbol(*name) {
|
||||
Some(slice) => slice,
|
||||
None => Vec::from_iter_in(
|
||||
arg_layouts.iter().cloned().map(|layout| Param {
|
||||
symbol: Symbol::UNDERSCORE,
|
||||
borrow: false,
|
||||
layout,
|
||||
}),
|
||||
self.arena,
|
||||
)
|
||||
.into_bump_slice(),
|
||||
};
|
||||
|
||||
let v = Expr::Call(crate::ir::Call {
|
||||
call_type,
|
||||
arguments,
|
||||
});
|
||||
|
||||
let b = self.add_dec_after_application(arguments, ps, b, b_live_vars);
|
||||
let b = self.arena.alloc(Stmt::Let(z, v, l, b));
|
||||
|
||||
self.add_inc_before(arguments, ps, b, b_live_vars)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
fn visit_variable_declaration(
|
||||
&self,
|
||||
@ -445,45 +532,10 @@ impl<'a> Context<'a> {
|
||||
self.arena.alloc(Stmt::Let(z, v, l, b))
|
||||
}
|
||||
|
||||
RunLowLevel(op, args) => {
|
||||
let ps = crate::borrow::lowlevel_borrow_signature(self.arena, op);
|
||||
let b = self.add_dec_after_lowlevel(args, ps, b, b_live_vars);
|
||||
|
||||
self.arena.alloc(Stmt::Let(z, v, l, b))
|
||||
}
|
||||
|
||||
ForeignCall { arguments, .. } => {
|
||||
let ps = crate::borrow::foreign_borrow_signature(self.arena, arguments.len());
|
||||
let b = self.add_dec_after_lowlevel(arguments, ps, b, b_live_vars);
|
||||
|
||||
self.arena.alloc(Stmt::Let(z, v, l, b))
|
||||
}
|
||||
|
||||
FunctionCall {
|
||||
args: ys,
|
||||
arg_layouts,
|
||||
Call(crate::ir::Call {
|
||||
call_type,
|
||||
..
|
||||
} => {
|
||||
// get the borrow signature
|
||||
let ps = match self.param_map.get_symbol(call_type.get_inner()) {
|
||||
Some(slice) => slice,
|
||||
None => Vec::from_iter_in(
|
||||
arg_layouts.iter().cloned().map(|layout| Param {
|
||||
symbol: Symbol::UNDERSCORE,
|
||||
borrow: false,
|
||||
layout,
|
||||
}),
|
||||
self.arena,
|
||||
)
|
||||
.into_bump_slice(),
|
||||
};
|
||||
|
||||
let b = self.add_dec_after_application(ys, ps, b, b_live_vars);
|
||||
let b = self.arena.alloc(Stmt::Let(z, v, l, b));
|
||||
|
||||
self.add_inc_before(ys, ps, b, b_live_vars)
|
||||
}
|
||||
arguments,
|
||||
}) => self.visit_call(z, call_type, arguments, l, b, b_live_vars),
|
||||
|
||||
EmptyArray
|
||||
| FunctionPointer(_, _)
|
||||
@ -499,21 +551,45 @@ impl<'a> Context<'a> {
|
||||
(new_b, live_vars)
|
||||
}
|
||||
|
||||
fn update_var_info_invoke(
|
||||
&self,
|
||||
symbol: Symbol,
|
||||
layout: &Layout<'a>,
|
||||
call: &crate::ir::Call<'a>,
|
||||
) -> Self {
|
||||
// is this value a constant?
|
||||
// TODO do function pointers also fall into this category?
|
||||
let persistent = call.arguments.is_empty();
|
||||
|
||||
// must this value be consumed?
|
||||
let consume = consume_call(&self.vars, call);
|
||||
|
||||
self.update_var_info_help(symbol, layout, persistent, consume)
|
||||
}
|
||||
|
||||
fn update_var_info(&self, symbol: Symbol, layout: &Layout<'a>, expr: &Expr<'a>) -> Self {
|
||||
let mut ctx = self.clone();
|
||||
|
||||
// can this type be reference-counted at runtime?
|
||||
let reference = layout.contains_refcounted();
|
||||
|
||||
// is this value a constant?
|
||||
// TODO do function pointers also fall into this category?
|
||||
let persistent = match expr {
|
||||
Expr::FunctionCall { args, .. } => args.is_empty(),
|
||||
Expr::Call(crate::ir::Call { arguments, .. }) => arguments.is_empty(),
|
||||
_ => false,
|
||||
};
|
||||
|
||||
// must this value be consumed?
|
||||
let consume = consume_expr(&ctx.vars, expr);
|
||||
let consume = consume_expr(&self.vars, expr);
|
||||
|
||||
self.update_var_info_help(symbol, layout, persistent, consume)
|
||||
}
|
||||
|
||||
fn update_var_info_help(
|
||||
&self,
|
||||
symbol: Symbol,
|
||||
layout: &Layout<'a>,
|
||||
persistent: bool,
|
||||
consume: bool,
|
||||
) -> Self {
|
||||
// can this type be reference-counted at runtime?
|
||||
let reference = layout.contains_refcounted();
|
||||
|
||||
let info = VarInfo {
|
||||
reference,
|
||||
@ -521,6 +597,8 @@ impl<'a> Context<'a> {
|
||||
consume,
|
||||
};
|
||||
|
||||
let mut ctx = self.clone();
|
||||
|
||||
ctx.vars.insert(symbol, info);
|
||||
|
||||
ctx
|
||||
@ -628,6 +706,47 @@ impl<'a> Context<'a> {
|
||||
)
|
||||
}
|
||||
|
||||
Invoke {
|
||||
symbol,
|
||||
call,
|
||||
pass,
|
||||
fail,
|
||||
layout,
|
||||
} => {
|
||||
// TODO this combines parts of Let and Switch. Did this happen correctly?
|
||||
let mut case_live_vars = collect_stmt(stmt, &self.jp_live_vars, MutSet::default());
|
||||
|
||||
case_live_vars.remove(symbol);
|
||||
|
||||
let fail = {
|
||||
// TODO should we use ctor info like Lean?
|
||||
let ctx = self.clone();
|
||||
let (b, alt_live_vars) = ctx.visit_stmt(fail);
|
||||
ctx.add_dec_for_alt(&case_live_vars, &alt_live_vars, b)
|
||||
};
|
||||
|
||||
case_live_vars.insert(*symbol);
|
||||
|
||||
let pass = {
|
||||
// TODO should we use ctor info like Lean?
|
||||
let ctx = self.clone();
|
||||
let ctx = ctx.update_var_info_invoke(*symbol, layout, call);
|
||||
let (b, alt_live_vars) = ctx.visit_stmt(pass);
|
||||
ctx.add_dec_for_alt(&case_live_vars, &alt_live_vars, b)
|
||||
};
|
||||
|
||||
let invoke = Invoke {
|
||||
symbol: *symbol,
|
||||
call: call.clone(),
|
||||
pass,
|
||||
fail,
|
||||
layout: layout.clone(),
|
||||
};
|
||||
|
||||
let stmt = self.arena.alloc(invoke);
|
||||
|
||||
(stmt, case_live_vars)
|
||||
}
|
||||
Join {
|
||||
id: j,
|
||||
parameters: _,
|
||||
@ -673,6 +792,8 @@ impl<'a> Context<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
Rethrow => (stmt, MutSet::default()),
|
||||
|
||||
Jump(j, xs) => {
|
||||
let empty = MutSet::default();
|
||||
let j_live_vars = match self.jp_live_vars.get(j) {
|
||||
@ -757,6 +878,25 @@ pub fn collect_stmt(
|
||||
|
||||
vars
|
||||
}
|
||||
Invoke {
|
||||
symbol,
|
||||
call,
|
||||
pass,
|
||||
fail,
|
||||
..
|
||||
} => {
|
||||
vars = collect_stmt(pass, jp_live_vars, vars);
|
||||
vars = collect_stmt(fail, jp_live_vars, vars);
|
||||
|
||||
vars.remove(symbol);
|
||||
|
||||
let mut result = MutSet::default();
|
||||
occuring_variables_call(call, &mut result);
|
||||
|
||||
vars.extend(result);
|
||||
|
||||
vars
|
||||
}
|
||||
Ret(symbol) => {
|
||||
vars.insert(*symbol);
|
||||
vars
|
||||
@ -813,6 +953,8 @@ pub fn collect_stmt(
|
||||
vars
|
||||
}
|
||||
|
||||
Rethrow => vars,
|
||||
|
||||
RuntimeError(_) => vars,
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -11,6 +11,7 @@ pub const MAX_ENUM_SIZE: usize = (std::mem::size_of::<u8>() * 8) as usize;
|
||||
|
||||
/// If a (Num *) gets translated to a Layout, this is the numeric type it defaults to.
|
||||
const DEFAULT_NUM_BUILTIN: Builtin<'_> = Builtin::Int64;
|
||||
pub const TAG_SIZE: Builtin<'_> = Builtin::Int64;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum LayoutProblem {
|
||||
@ -157,7 +158,13 @@ impl<'a> ClosureLayout<'a> {
|
||||
|
||||
Ok(Some(closure_layout))
|
||||
}
|
||||
Wrapped(tags) => {
|
||||
Wrapped {
|
||||
sorted_tag_layouts: tags,
|
||||
is_recursive,
|
||||
} => {
|
||||
// TODO handle recursive closures
|
||||
debug_assert!(!is_recursive);
|
||||
|
||||
let closure_layout =
|
||||
ClosureLayout::from_tag_union(arena, tags.into_bump_slice());
|
||||
|
||||
@ -312,6 +319,7 @@ pub enum Builtin<'a> {
|
||||
Int16,
|
||||
Int8,
|
||||
Int1,
|
||||
Usize,
|
||||
Float128,
|
||||
Float64,
|
||||
Float32,
|
||||
@ -362,14 +370,60 @@ impl<'a> Layout<'a> {
|
||||
}
|
||||
Structure(flat_type) => layout_from_flat_type(env, flat_type),
|
||||
|
||||
// Ints
|
||||
Alias(Symbol::NUM_I128, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Layout::Builtin(Builtin::Int128))
|
||||
}
|
||||
Alias(Symbol::NUM_I64, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Layout::Builtin(Builtin::Int64))
|
||||
}
|
||||
Alias(Symbol::NUM_I32, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Layout::Builtin(Builtin::Int32))
|
||||
}
|
||||
Alias(Symbol::NUM_I16, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Layout::Builtin(Builtin::Int16))
|
||||
}
|
||||
Alias(Symbol::NUM_I8, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Layout::Builtin(Builtin::Int8))
|
||||
}
|
||||
|
||||
// I think unsigned and signed use the same layout
|
||||
Alias(Symbol::NUM_U128, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Layout::Builtin(Builtin::Int128))
|
||||
}
|
||||
Alias(Symbol::NUM_U64, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Layout::Builtin(Builtin::Int64))
|
||||
}
|
||||
Alias(Symbol::NUM_U32, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Layout::Builtin(Builtin::Int32))
|
||||
}
|
||||
Alias(Symbol::NUM_U16, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Layout::Builtin(Builtin::Int16))
|
||||
}
|
||||
Alias(Symbol::NUM_U8, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Layout::Builtin(Builtin::Int8))
|
||||
}
|
||||
|
||||
// Floats
|
||||
Alias(Symbol::NUM_F64, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Layout::Builtin(Builtin::Float64))
|
||||
}
|
||||
Alias(Symbol::NUM_F32, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Layout::Builtin(Builtin::Float32))
|
||||
}
|
||||
|
||||
Alias(_, _, var) => Self::from_var(env, var),
|
||||
Error => Err(LayoutProblem::Erroneous),
|
||||
}
|
||||
@ -654,6 +708,7 @@ impl<'a> Builtin<'a> {
|
||||
const I16_SIZE: u32 = std::mem::size_of::<i16>() as u32;
|
||||
const I8_SIZE: u32 = std::mem::size_of::<i8>() as u32;
|
||||
const I1_SIZE: u32 = std::mem::size_of::<bool>() as u32;
|
||||
const USIZE_SIZE: u32 = std::mem::size_of::<usize>() as u32;
|
||||
const F128_SIZE: u32 = 16;
|
||||
const F64_SIZE: u32 = std::mem::size_of::<f64>() as u32;
|
||||
const F32_SIZE: u32 = std::mem::size_of::<f32>() as u32;
|
||||
@ -682,6 +737,7 @@ impl<'a> Builtin<'a> {
|
||||
Int16 => Builtin::I16_SIZE,
|
||||
Int8 => Builtin::I8_SIZE,
|
||||
Int1 => Builtin::I1_SIZE,
|
||||
Usize => Builtin::USIZE_SIZE,
|
||||
Float128 => Builtin::F128_SIZE,
|
||||
Float64 => Builtin::F64_SIZE,
|
||||
Float32 => Builtin::F32_SIZE,
|
||||
@ -707,6 +763,7 @@ impl<'a> Builtin<'a> {
|
||||
Int16 => align_of::<i16>() as u32,
|
||||
Int8 => align_of::<i8>() as u32,
|
||||
Int1 => align_of::<bool>() as u32,
|
||||
Usize => align_of::<usize>() as u32,
|
||||
Float128 => align_of::<i128>() as u32,
|
||||
Float64 => align_of::<f64>() as u32,
|
||||
Float32 => align_of::<f32>() as u32,
|
||||
@ -722,7 +779,7 @@ impl<'a> Builtin<'a> {
|
||||
use Builtin::*;
|
||||
|
||||
match self {
|
||||
Int128 | Int64 | Int32 | Int16 | Int8 | Int1 | Float128 | Float64 | Float32
|
||||
Int128 | Int64 | Int32 | Int16 | Int8 | Int1 | Usize | Float128 | Float64 | Float32
|
||||
| Float16 | EmptyStr | EmptyDict | EmptyList | EmptySet => true,
|
||||
Str | Dict(_, _) | Set(_) | List(_, _) => false,
|
||||
}
|
||||
@ -733,7 +790,7 @@ impl<'a> Builtin<'a> {
|
||||
use Builtin::*;
|
||||
|
||||
match self {
|
||||
Int128 | Int64 | Int32 | Int16 | Int8 | Int1 | Float128 | Float64 | Float32
|
||||
Int128 | Int64 | Int32 | Int16 | Int8 | Int1 | Usize | Float128 | Float64 | Float32
|
||||
| Float16 | EmptyStr | EmptyDict | EmptyList | EmptySet => false,
|
||||
List(mode, element_layout) => match mode {
|
||||
MemoryMode::Refcounted => true,
|
||||
@ -757,14 +814,64 @@ fn layout_from_flat_type<'a>(
|
||||
match flat_type {
|
||||
Apply(symbol, args) => {
|
||||
match symbol {
|
||||
// Ints
|
||||
Symbol::NUM_NAT => {
|
||||
debug_assert_eq!(args.len(), 0);
|
||||
Ok(Layout::Builtin(Builtin::Usize))
|
||||
}
|
||||
|
||||
Symbol::NUM_I128 => {
|
||||
debug_assert_eq!(args.len(), 0);
|
||||
Ok(Layout::Builtin(Builtin::Int128))
|
||||
}
|
||||
Symbol::NUM_I64 => {
|
||||
debug_assert_eq!(args.len(), 0);
|
||||
Ok(Layout::Builtin(Builtin::Int64))
|
||||
}
|
||||
Symbol::NUM_I32 => {
|
||||
debug_assert_eq!(args.len(), 0);
|
||||
Ok(Layout::Builtin(Builtin::Int32))
|
||||
}
|
||||
Symbol::NUM_I16 => {
|
||||
debug_assert_eq!(args.len(), 0);
|
||||
Ok(Layout::Builtin(Builtin::Int16))
|
||||
}
|
||||
Symbol::NUM_I8 => {
|
||||
debug_assert_eq!(args.len(), 0);
|
||||
Ok(Layout::Builtin(Builtin::Int8))
|
||||
}
|
||||
|
||||
Symbol::NUM_U128 => {
|
||||
debug_assert_eq!(args.len(), 0);
|
||||
Ok(Layout::Builtin(Builtin::Int128))
|
||||
}
|
||||
Symbol::NUM_U64 => {
|
||||
debug_assert_eq!(args.len(), 0);
|
||||
Ok(Layout::Builtin(Builtin::Int64))
|
||||
}
|
||||
Symbol::NUM_U32 => {
|
||||
debug_assert_eq!(args.len(), 0);
|
||||
Ok(Layout::Builtin(Builtin::Int32))
|
||||
}
|
||||
Symbol::NUM_U16 => {
|
||||
debug_assert_eq!(args.len(), 0);
|
||||
Ok(Layout::Builtin(Builtin::Int16))
|
||||
}
|
||||
Symbol::NUM_U8 => {
|
||||
debug_assert_eq!(args.len(), 0);
|
||||
Ok(Layout::Builtin(Builtin::Int8))
|
||||
}
|
||||
|
||||
// Floats
|
||||
Symbol::NUM_F64 => {
|
||||
debug_assert_eq!(args.len(), 0);
|
||||
Ok(Layout::Builtin(Builtin::Float64))
|
||||
}
|
||||
Symbol::NUM_F32 => {
|
||||
debug_assert_eq!(args.len(), 0);
|
||||
Ok(Layout::Builtin(Builtin::Float32))
|
||||
}
|
||||
|
||||
Symbol::NUM_NUM | Symbol::NUM_AT_NUM => {
|
||||
// Num.Num should only ever have 1 argument, e.g. Num.Num Int.Integer
|
||||
debug_assert_eq!(args.len(), 1);
|
||||
@ -774,6 +881,7 @@ fn layout_from_flat_type<'a>(
|
||||
|
||||
layout_from_num_content(content)
|
||||
}
|
||||
|
||||
Symbol::STR_STR => Ok(Layout::Builtin(Builtin::Str)),
|
||||
Symbol::LIST_LIST => list_layout_from_elem(env, args[0]),
|
||||
Symbol::ATTR_ATTR => {
|
||||
@ -888,7 +996,7 @@ fn layout_from_flat_type<'a>(
|
||||
let mut tag_layout = Vec::with_capacity_in(variables.len() + 1, arena);
|
||||
|
||||
// store the discriminant
|
||||
tag_layout.push(Layout::Builtin(Builtin::Int64));
|
||||
tag_layout.push(Layout::Builtin(TAG_SIZE));
|
||||
|
||||
for var in variables {
|
||||
// TODO does this cause problems with mutually recursive unions?
|
||||
@ -993,10 +1101,16 @@ pub enum UnionVariant<'a> {
|
||||
Never,
|
||||
Unit,
|
||||
UnitWithArguments,
|
||||
BoolUnion { ttrue: TagName, ffalse: TagName },
|
||||
BoolUnion {
|
||||
ttrue: TagName,
|
||||
ffalse: TagName,
|
||||
},
|
||||
ByteUnion(Vec<'a, TagName>),
|
||||
Unwrapped(Vec<'a, Layout<'a>>),
|
||||
Wrapped(Vec<'a, (TagName, &'a [Layout<'a>])>),
|
||||
Wrapped {
|
||||
sorted_tag_layouts: Vec<'a, (TagName, &'a [Layout<'a>])>,
|
||||
is_recursive: bool,
|
||||
},
|
||||
}
|
||||
|
||||
pub fn union_sorted_tags<'a>(arena: &'a Bump, var: Variable, subs: &Subs) -> UnionVariant<'a> {
|
||||
@ -1119,7 +1233,7 @@ pub fn union_sorted_tags_help<'a>(
|
||||
let mut arg_layouts = Vec::with_capacity_in(arguments.len() + 1, arena);
|
||||
|
||||
// add the tag discriminant (size currently always hardcoded to i64)
|
||||
arg_layouts.push(Layout::Builtin(Builtin::Int64));
|
||||
arg_layouts.push(Layout::Builtin(TAG_SIZE));
|
||||
|
||||
for var in arguments {
|
||||
match Layout::from_var(&mut env, var) {
|
||||
@ -1175,7 +1289,10 @@ pub fn union_sorted_tags_help<'a>(
|
||||
|
||||
UnionVariant::ByteUnion(tag_names)
|
||||
}
|
||||
_ => UnionVariant::Wrapped(answer),
|
||||
_ => UnionVariant::Wrapped {
|
||||
sorted_tag_layouts: answer,
|
||||
is_recursive: opt_rec_var.is_some(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1214,13 +1331,21 @@ pub fn layout_from_tag_union<'a>(
|
||||
Layout::Struct(field_layouts.into_bump_slice())
|
||||
}
|
||||
}
|
||||
Wrapped(tags) => {
|
||||
Wrapped {
|
||||
sorted_tag_layouts: tags,
|
||||
is_recursive,
|
||||
} => {
|
||||
let mut tag_layouts = Vec::with_capacity_in(tags.len(), arena);
|
||||
|
||||
for (_, tag_layout) in tags {
|
||||
tag_layouts.push(tag_layout);
|
||||
}
|
||||
Layout::Union(tag_layouts.into_bump_slice())
|
||||
|
||||
if is_recursive {
|
||||
Layout::RecursiveUnion(tag_layouts.into_bump_slice())
|
||||
} else {
|
||||
Layout::Union(tag_layouts.into_bump_slice())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1257,8 +1382,27 @@ fn layout_from_num_content<'a>(content: Content) -> Result<Layout<'a>, LayoutPro
|
||||
Ok(Layout::Builtin(DEFAULT_NUM_BUILTIN))
|
||||
}
|
||||
Structure(Apply(symbol, args)) => match symbol {
|
||||
// Ints
|
||||
Symbol::NUM_NAT => Ok(Layout::Builtin(Builtin::Usize)),
|
||||
|
||||
Symbol::NUM_INTEGER => Ok(Layout::Builtin(Builtin::Int64)),
|
||||
Symbol::NUM_I128 => Ok(Layout::Builtin(Builtin::Int128)),
|
||||
Symbol::NUM_I64 => Ok(Layout::Builtin(Builtin::Int64)),
|
||||
Symbol::NUM_I32 => Ok(Layout::Builtin(Builtin::Int32)),
|
||||
Symbol::NUM_I16 => Ok(Layout::Builtin(Builtin::Int16)),
|
||||
Symbol::NUM_I8 => Ok(Layout::Builtin(Builtin::Int8)),
|
||||
|
||||
Symbol::NUM_U128 => Ok(Layout::Builtin(Builtin::Int128)),
|
||||
Symbol::NUM_U64 => Ok(Layout::Builtin(Builtin::Int64)),
|
||||
Symbol::NUM_U32 => Ok(Layout::Builtin(Builtin::Int32)),
|
||||
Symbol::NUM_U16 => Ok(Layout::Builtin(Builtin::Int16)),
|
||||
Symbol::NUM_U8 => Ok(Layout::Builtin(Builtin::Int8)),
|
||||
|
||||
// Floats
|
||||
Symbol::NUM_FLOATINGPOINT => Ok(Layout::Builtin(Builtin::Float64)),
|
||||
Symbol::NUM_F64 => Ok(Layout::Builtin(Builtin::Float64)),
|
||||
Symbol::NUM_F32 => Ok(Layout::Builtin(Builtin::Float32)),
|
||||
|
||||
_ => {
|
||||
panic!(
|
||||
"Invalid Num.Num type application: {:?}",
|
||||
@ -1293,16 +1437,62 @@ fn unwrap_num_tag<'a>(subs: &Subs, var: Variable) -> Result<Layout<'a>, LayoutPr
|
||||
Content::Alias(Symbol::NUM_INTEGER, args, _) => {
|
||||
debug_assert!(args.len() == 1);
|
||||
|
||||
// TODO: we probably need to match on the type of the arg
|
||||
// and return the correct builtin ex: Builtin::{Int32, Int16}
|
||||
Ok(Layout::Builtin(Builtin::Int64))
|
||||
let (_, precision_var) = args[0];
|
||||
|
||||
let precision = subs.get_without_compacting(precision_var).content;
|
||||
|
||||
match precision {
|
||||
Content::Alias(symbol, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
|
||||
let builtin = match symbol {
|
||||
Symbol::NUM_SIGNED128 => Builtin::Int128,
|
||||
Symbol::NUM_SIGNED64 => Builtin::Int64,
|
||||
Symbol::NUM_SIGNED32 => Builtin::Int32,
|
||||
Symbol::NUM_SIGNED16 => Builtin::Int16,
|
||||
Symbol::NUM_SIGNED8 => Builtin::Int8,
|
||||
Symbol::NUM_UNSIGNED128 => Builtin::Int128,
|
||||
Symbol::NUM_UNSIGNED64 => Builtin::Int64,
|
||||
Symbol::NUM_UNSIGNED32 => Builtin::Int32,
|
||||
Symbol::NUM_UNSIGNED16 => Builtin::Int16,
|
||||
Symbol::NUM_UNSIGNED8 => Builtin::Int8,
|
||||
Symbol::NUM_NATURAL => Builtin::Usize,
|
||||
_ => unreachable!("not a valid int variant: {:?} {:?}", symbol, args),
|
||||
};
|
||||
|
||||
Ok(Layout::Builtin(builtin))
|
||||
}
|
||||
Content::FlexVar(_) | Content::RigidVar(_) => {
|
||||
// default to i64
|
||||
Ok(Layout::Builtin(Builtin::Int64))
|
||||
}
|
||||
_ => unreachable!("not a valid int variant: {:?}", precision),
|
||||
}
|
||||
}
|
||||
Content::Alias(Symbol::NUM_FLOATINGPOINT, args, _) => {
|
||||
debug_assert!(args.len() == 1);
|
||||
|
||||
// TODO: we probably need to match on the type of the arg
|
||||
// and return the correct builtin ex: Builtin::Float32
|
||||
Ok(Layout::Builtin(Builtin::Float64))
|
||||
let (_, precision_var) = args[0];
|
||||
|
||||
let precision = subs.get_without_compacting(precision_var).content;
|
||||
|
||||
match precision {
|
||||
Content::Alias(Symbol::NUM_BINARY32, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
|
||||
Ok(Layout::Builtin(Builtin::Float32))
|
||||
}
|
||||
Content::Alias(Symbol::NUM_BINARY64, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
|
||||
Ok(Layout::Builtin(Builtin::Float64))
|
||||
}
|
||||
Content::FlexVar(_) | Content::RigidVar(_) => {
|
||||
// default to f64
|
||||
Ok(Layout::Builtin(Builtin::Float64))
|
||||
}
|
||||
_ => unreachable!("not a valid float variant: {:?}", precision),
|
||||
}
|
||||
}
|
||||
Content::FlexVar(_) | Content::RigidVar(_) => {
|
||||
// If this was still a (Num *) then default to compiling it to i64
|
||||
|
@ -75,17 +75,38 @@ fn insert_jumps<'a>(
|
||||
match stmt {
|
||||
Let(
|
||||
symbol,
|
||||
Expr::FunctionCall {
|
||||
call_type: CallType::ByName(fsym),
|
||||
args,
|
||||
Expr::Call(crate::ir::Call {
|
||||
call_type: CallType::ByName { name: fsym, .. },
|
||||
arguments,
|
||||
..
|
||||
},
|
||||
}),
|
||||
_,
|
||||
Stmt::Ret(rsym),
|
||||
) if needle == *fsym && symbol == rsym => {
|
||||
// replace the call and return with a jump
|
||||
|
||||
let jump = Stmt::Jump(goal_id, args);
|
||||
let jump = Stmt::Jump(goal_id, arguments);
|
||||
|
||||
Some(arena.alloc(jump))
|
||||
}
|
||||
|
||||
Invoke {
|
||||
symbol,
|
||||
call:
|
||||
crate::ir::Call {
|
||||
call_type: CallType::ByName { name: fsym, .. },
|
||||
arguments,
|
||||
..
|
||||
},
|
||||
fail,
|
||||
pass: Stmt::Ret(rsym),
|
||||
..
|
||||
} if needle == *fsym && symbol == rsym => {
|
||||
debug_assert_eq!(fail, &&Stmt::Rethrow);
|
||||
|
||||
// replace the call and return with a jump
|
||||
|
||||
let jump = Stmt::Jump(goal_id, arguments);
|
||||
|
||||
Some(arena.alloc(jump))
|
||||
}
|
||||
@ -101,6 +122,35 @@ fn insert_jumps<'a>(
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
Invoke {
|
||||
symbol,
|
||||
call,
|
||||
fail,
|
||||
pass,
|
||||
layout,
|
||||
} => {
|
||||
let opt_pass = insert_jumps(arena, pass, goal_id, needle);
|
||||
let opt_fail = insert_jumps(arena, fail, goal_id, needle);
|
||||
|
||||
if opt_pass.is_some() || opt_fail.is_some() {
|
||||
let pass = opt_pass.unwrap_or(pass);
|
||||
let fail = opt_fail.unwrap_or(fail);
|
||||
|
||||
let stmt = Invoke {
|
||||
symbol: *symbol,
|
||||
call: call.clone(),
|
||||
layout: layout.clone(),
|
||||
pass,
|
||||
fail,
|
||||
};
|
||||
|
||||
Some(arena.alloc(stmt))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
Join {
|
||||
id,
|
||||
parameters,
|
||||
@ -187,6 +237,7 @@ fn insert_jumps<'a>(
|
||||
None => None,
|
||||
},
|
||||
|
||||
Rethrow => None,
|
||||
Ret(_) => None,
|
||||
Jump(_, _) => None,
|
||||
RuntimeError(_) => None,
|
||||
|
@ -58,9 +58,10 @@ mod test_mono {
|
||||
arena,
|
||||
filename,
|
||||
&module_src,
|
||||
stdlib,
|
||||
&stdlib,
|
||||
src_dir,
|
||||
exposed_types,
|
||||
8,
|
||||
);
|
||||
|
||||
let mut loaded = loaded.expect("failed to load module");
|
||||
@ -161,6 +162,45 @@ mod test_mono {
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ir_int_add() {
|
||||
compiles_to_ir(
|
||||
r#"
|
||||
x = [ 1,2 ]
|
||||
5 + 4 + 3 + List.len x
|
||||
"#,
|
||||
indoc!(
|
||||
r#"
|
||||
procedure List.7 (#Attr.2):
|
||||
let Test.6 = lowlevel ListLen #Attr.2;
|
||||
ret Test.6;
|
||||
|
||||
procedure Num.24 (#Attr.2, #Attr.3):
|
||||
let Test.5 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Test.5;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.11 = 1i64;
|
||||
let Test.12 = 2i64;
|
||||
let Test.1 = Array [Test.11, Test.12];
|
||||
let Test.9 = 5i64;
|
||||
let Test.10 = 4i64;
|
||||
invoke Test.7 = CallByName Num.24 Test.9 Test.10 catch
|
||||
dec Test.1;
|
||||
unreachable;
|
||||
let Test.8 = 3i64;
|
||||
invoke Test.3 = CallByName Num.24 Test.7 Test.8 catch
|
||||
dec Test.1;
|
||||
unreachable;
|
||||
let Test.4 = CallByName List.7 Test.1;
|
||||
dec Test.1;
|
||||
let Test.2 = CallByName Num.24 Test.3 Test.4;
|
||||
ret Test.2;
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ir_assignment() {
|
||||
compiles_to_ir(
|
||||
@ -1853,6 +1893,7 @@ mod test_mono {
|
||||
let Test.2 = S Test.9 Test.8;
|
||||
let Test.5 = 1i64;
|
||||
let Test.6 = Index 0 Test.2;
|
||||
dec Test.2;
|
||||
let Test.7 = lowlevel Eq Test.5 Test.6;
|
||||
if Test.7 then
|
||||
let Test.3 = 0i64;
|
||||
@ -1904,12 +1945,16 @@ mod test_mono {
|
||||
let Test.10 = lowlevel Eq Test.8 Test.9;
|
||||
if Test.10 then
|
||||
let Test.4 = Index 1 Test.2;
|
||||
inc Test.4;
|
||||
dec Test.2;
|
||||
let Test.3 = 1i64;
|
||||
ret Test.3;
|
||||
else
|
||||
dec Test.2;
|
||||
let Test.5 = 0i64;
|
||||
ret Test.5;
|
||||
else
|
||||
dec Test.2;
|
||||
let Test.6 = 0i64;
|
||||
ret Test.6;
|
||||
"#
|
||||
@ -2104,7 +2149,7 @@ mod test_mono {
|
||||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
swap : I64, I64, List a -> List a
|
||||
swap : Nat, Nat, List a -> List a
|
||||
swap = \i, j, list ->
|
||||
when Pair (List.get list i) (List.get list j) is
|
||||
Pair (Ok atI) (Ok atJ) ->
|
||||
|
@ -1624,8 +1624,8 @@ fn to_diff<'b>(
|
||||
let right = to_doc(alloc, Parens::Unnecessary, type2);
|
||||
|
||||
let is_int = |t: &ErrorType| match t {
|
||||
ErrorType::Type(Symbol::NUM_I64, _) => true,
|
||||
ErrorType::Alias(Symbol::NUM_I64, _, _) => true,
|
||||
ErrorType::Type(Symbol::NUM_INT, _) => true,
|
||||
ErrorType::Alias(Symbol::NUM_INT, _, _) => true,
|
||||
|
||||
ErrorType::Type(Symbol::NUM_NUM, args) => {
|
||||
matches!( &args.get(0) ,Some(ErrorType::Type(Symbol::NUM_INTEGER, _)) | Some(ErrorType::Alias(Symbol::NUM_INTEGER, _, _)))
|
||||
@ -1636,8 +1636,8 @@ fn to_diff<'b>(
|
||||
_ => false,
|
||||
};
|
||||
let is_float = |t: &ErrorType| match t {
|
||||
ErrorType::Type(Symbol::NUM_F64, _) => true,
|
||||
ErrorType::Alias(Symbol::NUM_F64, _, _) => true,
|
||||
ErrorType::Type(Symbol::NUM_FLOAT, _) => true,
|
||||
ErrorType::Alias(Symbol::NUM_FLOAT, _, _) => true,
|
||||
|
||||
ErrorType::Type(Symbol::NUM_NUM, args) => {
|
||||
matches!(&args.get(0), Some(ErrorType::Type(Symbol::NUM_FLOATINGPOINT, _)) | Some(ErrorType::Alias(Symbol::NUM_FLOATINGPOINT, _, _)))
|
||||
@ -1891,7 +1891,7 @@ fn diff_tag_union<'b>(
|
||||
alloc.tag_name(field.clone()),
|
||||
// TODO add spaces between args
|
||||
args.iter()
|
||||
.map(|arg| to_doc(alloc, Parens::Unnecessary, arg.clone()))
|
||||
.map(|arg| to_doc(alloc, Parens::InTypeParam, arg.clone()))
|
||||
.collect(),
|
||||
)
|
||||
};
|
||||
|
@ -94,6 +94,7 @@ mod test_reporting {
|
||||
problems: &mut mono_problems,
|
||||
home,
|
||||
ident_ids: &mut ident_ids,
|
||||
ptr_bytes: 8,
|
||||
};
|
||||
let _mono_expr = Stmt::new(
|
||||
&mut mono_env,
|
||||
@ -446,9 +447,9 @@ mod test_reporting {
|
||||
these names seem close though:
|
||||
|
||||
baz
|
||||
Nat
|
||||
Str
|
||||
U8
|
||||
F64
|
||||
"#
|
||||
),
|
||||
)
|
||||
@ -960,7 +961,7 @@ mod test_reporting {
|
||||
r#"
|
||||
bar = { bar : 0x3 }
|
||||
|
||||
f : { foo : I64 } -> Bool
|
||||
f : { foo : Int * } -> Bool
|
||||
f = \_ -> True
|
||||
|
||||
f bar
|
||||
@ -977,11 +978,11 @@ mod test_reporting {
|
||||
|
||||
This `bar` value is a:
|
||||
|
||||
{ bar : I64 }
|
||||
{ bar : Int a }
|
||||
|
||||
But `f` needs the 1st argument to be:
|
||||
|
||||
{ foo : I64 }
|
||||
{ foo : Int a }
|
||||
|
||||
Tip: Seems like a record field typo. Maybe `bar` should be `foo`?
|
||||
|
||||
@ -1036,7 +1037,7 @@ mod test_reporting {
|
||||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
f : [ Red I64, Green Bool ] -> Bool
|
||||
f : [ Red (Int *), Green Bool ] -> Bool
|
||||
f = \_ -> True
|
||||
|
||||
f (Blue 3.14)
|
||||
@ -1053,11 +1054,11 @@ mod test_reporting {
|
||||
|
||||
This `Blue` global tag application has the type:
|
||||
|
||||
[ Blue F64 ]a
|
||||
[ Blue (Float a) ]b
|
||||
|
||||
But `f` needs the 1st argument to be:
|
||||
|
||||
[ Green Bool, Red I64 ]
|
||||
[ Green Bool, Red (Int a) ]
|
||||
|
||||
Tip: Seems like a tag typo. Maybe `Blue` should be `Red`?
|
||||
|
||||
@ -1074,7 +1075,7 @@ mod test_reporting {
|
||||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
x : I64
|
||||
x : Int *
|
||||
x = if True then 3.14 else 4
|
||||
|
||||
x
|
||||
@ -1091,11 +1092,11 @@ mod test_reporting {
|
||||
|
||||
The 1st branch is a float of type:
|
||||
|
||||
F64
|
||||
Float a
|
||||
|
||||
But the type annotation on `x` says it should be:
|
||||
|
||||
I64
|
||||
Int b
|
||||
|
||||
Tip: You can convert between Int and Float using functions like
|
||||
`Num.toFloat` and `Num.round`.
|
||||
@ -1109,7 +1110,7 @@ mod test_reporting {
|
||||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
x : I64
|
||||
x : Int *
|
||||
x =
|
||||
when True is
|
||||
_ -> 3.14
|
||||
@ -1123,18 +1124,18 @@ mod test_reporting {
|
||||
|
||||
Something is off with the body of the `x` definition:
|
||||
|
||||
1│ x : I64
|
||||
1│ x : Int *
|
||||
2│ x =
|
||||
3│> when True is
|
||||
4│> _ -> 3.14
|
||||
|
||||
This `when`expression produces:
|
||||
|
||||
F64
|
||||
Float a
|
||||
|
||||
But the type annotation on `x` says it should be:
|
||||
|
||||
I64
|
||||
Int b
|
||||
|
||||
Tip: You can convert between Int and Float using functions like
|
||||
`Num.toFloat` and `Num.round`.
|
||||
@ -1148,7 +1149,7 @@ mod test_reporting {
|
||||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
x : I64 -> I64
|
||||
x : Int * -> Int *
|
||||
x = \_ -> 3.14
|
||||
|
||||
x
|
||||
@ -1160,17 +1161,17 @@ mod test_reporting {
|
||||
|
||||
Something is off with the body of the `x` definition:
|
||||
|
||||
1│ x : I64 -> I64
|
||||
1│ x : Int * -> Int *
|
||||
2│ x = \_ -> 3.14
|
||||
^^^^
|
||||
|
||||
The body is a float of type:
|
||||
|
||||
F64
|
||||
Float a
|
||||
|
||||
But the type annotation on `x` says it should be:
|
||||
|
||||
I64
|
||||
Int b
|
||||
|
||||
Tip: You can convert between Int and Float using functions like
|
||||
`Num.toFloat` and `Num.round`.
|
||||
@ -1373,7 +1374,7 @@ mod test_reporting {
|
||||
Bool
|
||||
U8
|
||||
F64
|
||||
Str
|
||||
Nat
|
||||
"#
|
||||
),
|
||||
)
|
||||
@ -1482,7 +1483,7 @@ mod test_reporting {
|
||||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
{ x } : { x : I64 }
|
||||
{ x } : { x : Int * }
|
||||
{ x } = { x: 4.0 }
|
||||
|
||||
x
|
||||
@ -1494,17 +1495,17 @@ mod test_reporting {
|
||||
|
||||
Something is off with the body of this definition:
|
||||
|
||||
1│ { x } : { x : I64 }
|
||||
1│ { x } : { x : Int * }
|
||||
2│ { x } = { x: 4.0 }
|
||||
^^^^^^^^^^
|
||||
|
||||
The body is a record of type:
|
||||
|
||||
{ x : F64 }
|
||||
{ x : Float a }
|
||||
|
||||
But the type annotation says it should be:
|
||||
|
||||
{ x : I64 }
|
||||
{ x : Int b }
|
||||
|
||||
Tip: You can convert between Int and Float using functions like
|
||||
`Num.toFloat` and `Num.round`.
|
||||
@ -1643,7 +1644,7 @@ mod test_reporting {
|
||||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
x : { a : I64, b : F64, c : Bool }
|
||||
x : { a : Int *, b : Float *, c : Bool }
|
||||
x = { b: 4.0 }
|
||||
|
||||
x
|
||||
@ -1655,17 +1656,17 @@ mod test_reporting {
|
||||
|
||||
Something is off with the body of the `x` definition:
|
||||
|
||||
1│ x : { a : I64, b : F64, c : Bool }
|
||||
1│ x : { a : Int *, b : Float *, c : Bool }
|
||||
2│ x = { b: 4.0 }
|
||||
^^^^^^^^^^
|
||||
|
||||
The body is a record of type:
|
||||
|
||||
{ b : F64 }
|
||||
{ b : Float a }
|
||||
|
||||
But the type annotation on `x` says it should be:
|
||||
|
||||
{ a : I64, b : F64, c : Bool }
|
||||
{ a : Int a, b : Float b, c : Bool }
|
||||
|
||||
Tip: Looks like the c and a fields are missing.
|
||||
"#
|
||||
@ -1787,7 +1788,7 @@ mod test_reporting {
|
||||
|
||||
The body is an integer of type:
|
||||
|
||||
I64
|
||||
Int a
|
||||
|
||||
But the type annotation on `f` says it should be:
|
||||
|
||||
@ -1795,7 +1796,7 @@ mod test_reporting {
|
||||
|
||||
Tip: The type annotation uses the type variable `msg` to say that this
|
||||
definition can produce any type of value. But in the body I see that
|
||||
it will only produce a `I64` value of a single specific type. Maybe
|
||||
it will only produce a `Int` value of a single specific type. Maybe
|
||||
change the type annotation to be more specific? Maybe change the code
|
||||
to be more general?
|
||||
"#
|
||||
@ -2093,7 +2094,7 @@ mod test_reporting {
|
||||
|
||||
But `add` needs the 2nd argument to be:
|
||||
|
||||
Num (Integer Signed64)
|
||||
Num (Integer a)
|
||||
"#
|
||||
),
|
||||
)
|
||||
@ -2118,11 +2119,11 @@ mod test_reporting {
|
||||
|
||||
This argument is a float of type:
|
||||
|
||||
F64
|
||||
Float a
|
||||
|
||||
But `add` needs the 2nd argument to be:
|
||||
|
||||
Num (Integer Signed64)
|
||||
Num (Integer a)
|
||||
|
||||
Tip: You can convert between Int and Float using functions like
|
||||
`Num.toFloat` and `Num.round`.
|
||||
@ -2580,9 +2581,9 @@ mod test_reporting {
|
||||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
Foo : { x : I64 }
|
||||
Foo : { x : Int * }
|
||||
|
||||
f : Foo -> I64
|
||||
f : Foo -> Int *
|
||||
f = \r -> r.x
|
||||
|
||||
f { y: 3.14 }
|
||||
@ -2600,11 +2601,11 @@ mod test_reporting {
|
||||
|
||||
This argument is a record of type:
|
||||
|
||||
{ y : F64 }
|
||||
{ y : Float a }
|
||||
|
||||
But `f` needs the 1st argument to be:
|
||||
|
||||
{ x : I64 }
|
||||
{ x : Int a }
|
||||
|
||||
Tip: Seems like a record field typo. Maybe `y` should be `x`?
|
||||
|
||||
|
@ -59,9 +59,10 @@ mod solve_expr {
|
||||
let result = roc_load::file::load_and_typecheck(
|
||||
arena,
|
||||
full_file_path,
|
||||
stdlib,
|
||||
&stdlib,
|
||||
dir.path(),
|
||||
exposed_types,
|
||||
8,
|
||||
);
|
||||
|
||||
dir.close()?;
|
||||
@ -169,7 +170,7 @@ mod solve_expr {
|
||||
|
||||
#[test]
|
||||
fn float_literal() {
|
||||
infer_eq("0.5", "F64");
|
||||
infer_eq("0.5", "Float *");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -762,7 +763,7 @@ mod solve_expr {
|
||||
(\a -> a) 3.14
|
||||
"#
|
||||
),
|
||||
"F64",
|
||||
"Float *",
|
||||
);
|
||||
}
|
||||
|
||||
@ -1026,7 +1027,7 @@ mod solve_expr {
|
||||
|
||||
#[test]
|
||||
fn two_field_record() {
|
||||
infer_eq("{ x: 5, y : 3.14 }", "{ x : Num *, y : F64 }");
|
||||
infer_eq("{ x: 5, y : 3.14 }", "{ x : Num *, y : Float * }");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -2385,7 +2386,7 @@ mod solve_expr {
|
||||
threePointZero
|
||||
"#
|
||||
),
|
||||
"F64",
|
||||
"Float *",
|
||||
);
|
||||
}
|
||||
|
||||
@ -2982,7 +2983,7 @@ mod solve_expr {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
partition : I64, I64, List I64 -> [ Pair I64 (List I64) ]
|
||||
partition : Nat, Nat, List (Int a) -> [ Pair Nat (List (Int a)) ]
|
||||
partition = \low, high, initialList ->
|
||||
when List.get initialList high is
|
||||
Ok _ ->
|
||||
@ -2994,7 +2995,7 @@ mod solve_expr {
|
||||
partition
|
||||
"#
|
||||
),
|
||||
"I64, I64, List I64 -> [ Pair I64 (List I64) ]",
|
||||
"Nat, Nat, List (Int a) -> [ Pair Nat (List (Int a)) ]",
|
||||
);
|
||||
}
|
||||
|
||||
@ -3004,7 +3005,7 @@ mod solve_expr {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
swap : I64, I64, List a -> List a
|
||||
swap : Nat, Nat, List a -> List a
|
||||
swap = \i, j, list ->
|
||||
when Pair (List.get list i) (List.get list j) is
|
||||
Pair (Ok atI) (Ok atJ) ->
|
||||
@ -3015,7 +3016,7 @@ mod solve_expr {
|
||||
_ ->
|
||||
list
|
||||
|
||||
partition : I64, I64, List I64 -> [ Pair I64 (List I64) ]
|
||||
partition : Nat, Nat, List (Int a) -> [ Pair Nat (List (Int a)) ]
|
||||
partition = \low, high, initialList ->
|
||||
when List.get initialList high is
|
||||
Ok pivot ->
|
||||
@ -3043,7 +3044,7 @@ mod solve_expr {
|
||||
partition
|
||||
"#
|
||||
),
|
||||
"I64, I64, List I64 -> [ Pair I64 (List I64) ]",
|
||||
"Nat, Nat, List (Int a) -> [ Pair Nat (List (Int a)) ]",
|
||||
);
|
||||
});
|
||||
}
|
||||
@ -3077,6 +3078,15 @@ mod solve_expr {
|
||||
),
|
||||
"Result (Num *) [ OutOfBounds ]*",
|
||||
);
|
||||
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
List.get
|
||||
"#
|
||||
),
|
||||
"List a, Nat -> Result a [ OutOfBounds ]*",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -3117,7 +3127,7 @@ mod solve_expr {
|
||||
Num.toFloat
|
||||
"#
|
||||
),
|
||||
"Num * -> F64",
|
||||
"Num * -> Float *",
|
||||
);
|
||||
}
|
||||
|
||||
@ -3129,7 +3139,7 @@ mod solve_expr {
|
||||
Num.pow
|
||||
"#
|
||||
),
|
||||
"F64, F64 -> F64",
|
||||
"Float a, Float a -> Float a",
|
||||
);
|
||||
}
|
||||
|
||||
@ -3141,7 +3151,7 @@ mod solve_expr {
|
||||
Num.ceiling
|
||||
"#
|
||||
),
|
||||
"F64 -> I64",
|
||||
"Float * -> Int *",
|
||||
);
|
||||
}
|
||||
|
||||
@ -3153,7 +3163,7 @@ mod solve_expr {
|
||||
Num.floor
|
||||
"#
|
||||
),
|
||||
"F64 -> I64",
|
||||
"Float * -> Int *",
|
||||
);
|
||||
}
|
||||
|
||||
@ -3165,7 +3175,7 @@ mod solve_expr {
|
||||
Num.powInt
|
||||
"#
|
||||
),
|
||||
"I64, I64 -> I64",
|
||||
"Int a, Int a -> Int a",
|
||||
);
|
||||
}
|
||||
|
||||
@ -3177,7 +3187,7 @@ mod solve_expr {
|
||||
Num.atan
|
||||
"#
|
||||
),
|
||||
"F64 -> F64",
|
||||
"Float a -> Float a",
|
||||
);
|
||||
}
|
||||
|
||||
@ -3444,7 +3454,7 @@ mod solve_expr {
|
||||
negatePoint { x: 1, y: 2.1, z: 0x3 }
|
||||
"#
|
||||
),
|
||||
"{ x : Num a, y : F64, z : I64 }",
|
||||
"{ x : Num a, y : F64, z : Int * }",
|
||||
);
|
||||
}
|
||||
|
||||
@ -4096,7 +4106,7 @@ mod solve_expr {
|
||||
r#"
|
||||
app "test" provides [ partitionHelp ] to "./platform"
|
||||
|
||||
swap : I64, I64, List a -> List a
|
||||
swap : Nat, Nat, List a -> List a
|
||||
swap = \i, j, list ->
|
||||
when Pair (List.get list i) (List.get list j) is
|
||||
Pair (Ok atI) (Ok atJ) ->
|
||||
@ -4107,7 +4117,7 @@ mod solve_expr {
|
||||
_ ->
|
||||
[]
|
||||
|
||||
partitionHelp : I64, I64, List (Num a), I64, (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partitionHelp : Nat, Nat, List (Num a), Nat, (Num a) -> [ Pair Nat (List (Num a)) ]
|
||||
partitionHelp = \i, j, list, high, pivot ->
|
||||
if j < high then
|
||||
when List.get list j is
|
||||
@ -4123,7 +4133,7 @@ mod solve_expr {
|
||||
Pair i list
|
||||
"#
|
||||
),
|
||||
"I64, I64, List (Num a), I64, Num a -> [ Pair I64 (List (Num a)) ]",
|
||||
"Nat, Nat, List (Num a), Nat, Num a -> [ Pair Nat (List (Num a)) ]",
|
||||
);
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -28,6 +28,26 @@ pub fn aliases() -> MutMap<Symbol, BuiltinAlias> {
|
||||
aliases.insert(symbol, alias);
|
||||
};
|
||||
|
||||
// Int range : [ @Int range ]
|
||||
add_alias(
|
||||
Symbol::NUM_INT,
|
||||
BuiltinAlias {
|
||||
region: Region::zero(),
|
||||
vars: vec![Located::at(Region::zero(), "range".into())],
|
||||
typ: int_alias_content(flex(TVAR1)),
|
||||
},
|
||||
);
|
||||
|
||||
// Float range : [ @Float range ]
|
||||
add_alias(
|
||||
Symbol::NUM_FLOAT,
|
||||
BuiltinAlias {
|
||||
region: Region::zero(),
|
||||
vars: vec![Located::at(Region::zero(), "range".into())],
|
||||
typ: float_alias_content(flex(TVAR1)),
|
||||
},
|
||||
);
|
||||
|
||||
// Num range : [ @Num range ]
|
||||
add_alias(
|
||||
Symbol::NUM_NUM,
|
||||
@ -48,6 +68,26 @@ pub fn aliases() -> MutMap<Symbol, BuiltinAlias> {
|
||||
},
|
||||
);
|
||||
|
||||
// Natural : [ @Natural ]
|
||||
add_alias(
|
||||
Symbol::NUM_NATURAL,
|
||||
BuiltinAlias {
|
||||
region: Region::zero(),
|
||||
vars: vec![],
|
||||
typ: natural_alias_content(),
|
||||
},
|
||||
);
|
||||
|
||||
// Nat : Int Natural
|
||||
add_alias(
|
||||
Symbol::NUM_NAT,
|
||||
BuiltinAlias {
|
||||
region: Region::zero(),
|
||||
vars: Vec::new(),
|
||||
typ: int_alias_content(natural_type()),
|
||||
},
|
||||
);
|
||||
|
||||
// Signed128 : [ @Signed128 ]
|
||||
add_alias(
|
||||
Symbol::NUM_SIGNED128,
|
||||
@ -58,7 +98,7 @@ pub fn aliases() -> MutMap<Symbol, BuiltinAlias> {
|
||||
},
|
||||
);
|
||||
|
||||
// I128 : Num (Integer Signed128)
|
||||
// I128 : Int Signed128
|
||||
add_alias(
|
||||
Symbol::NUM_I128,
|
||||
BuiltinAlias {
|
||||
@ -68,7 +108,7 @@ pub fn aliases() -> MutMap<Symbol, BuiltinAlias> {
|
||||
},
|
||||
);
|
||||
|
||||
// U128 : Num (Integer Unsigned128)
|
||||
// U128 : Int Unsigned128
|
||||
add_alias(
|
||||
Symbol::NUM_U128,
|
||||
BuiltinAlias {
|
||||
@ -88,7 +128,7 @@ pub fn aliases() -> MutMap<Symbol, BuiltinAlias> {
|
||||
},
|
||||
);
|
||||
|
||||
// I64 : Num (Integer Signed64)
|
||||
// I64 : Int Signed64
|
||||
add_alias(
|
||||
Symbol::NUM_I64,
|
||||
BuiltinAlias {
|
||||
@ -98,7 +138,7 @@ pub fn aliases() -> MutMap<Symbol, BuiltinAlias> {
|
||||
},
|
||||
);
|
||||
|
||||
// U64 : Num (Integer Unsigned64)
|
||||
// U64 : Int Unsigned64
|
||||
add_alias(
|
||||
Symbol::NUM_U64,
|
||||
BuiltinAlias {
|
||||
@ -118,7 +158,7 @@ pub fn aliases() -> MutMap<Symbol, BuiltinAlias> {
|
||||
},
|
||||
);
|
||||
|
||||
// I32 : Num (Integer Signed32)
|
||||
// I32 : Int Signed32
|
||||
add_alias(
|
||||
Symbol::NUM_I32,
|
||||
BuiltinAlias {
|
||||
@ -128,7 +168,7 @@ pub fn aliases() -> MutMap<Symbol, BuiltinAlias> {
|
||||
},
|
||||
);
|
||||
|
||||
// U32 : Num (Integer Unsigned32)
|
||||
// U32 : Int Unsigned32
|
||||
add_alias(
|
||||
Symbol::NUM_U32,
|
||||
BuiltinAlias {
|
||||
@ -148,7 +188,7 @@ pub fn aliases() -> MutMap<Symbol, BuiltinAlias> {
|
||||
},
|
||||
);
|
||||
|
||||
// I16 : Num (Integer Signed16)
|
||||
// I16 : Int Signed16
|
||||
add_alias(
|
||||
Symbol::NUM_I16,
|
||||
BuiltinAlias {
|
||||
@ -158,7 +198,7 @@ pub fn aliases() -> MutMap<Symbol, BuiltinAlias> {
|
||||
},
|
||||
);
|
||||
|
||||
// U16 : Num (Integer Unsigned16)
|
||||
// U16 : Int Unsigned16
|
||||
add_alias(
|
||||
Symbol::NUM_U16,
|
||||
BuiltinAlias {
|
||||
@ -178,7 +218,7 @@ pub fn aliases() -> MutMap<Symbol, BuiltinAlias> {
|
||||
},
|
||||
);
|
||||
|
||||
// I8 : Num (Integer Signed8)
|
||||
// I8 : Int Signed8
|
||||
add_alias(
|
||||
Symbol::NUM_I8,
|
||||
BuiltinAlias {
|
||||
@ -188,7 +228,7 @@ pub fn aliases() -> MutMap<Symbol, BuiltinAlias> {
|
||||
},
|
||||
);
|
||||
|
||||
// U8 : Num (Integer Unsigned8)
|
||||
// U8 : Int Unsigned8
|
||||
add_alias(
|
||||
Symbol::NUM_U8,
|
||||
BuiltinAlias {
|
||||
@ -228,7 +268,7 @@ pub fn aliases() -> MutMap<Symbol, BuiltinAlias> {
|
||||
},
|
||||
);
|
||||
|
||||
// F64 : Num (FloatingPoint Binary64)
|
||||
// F64 : Float Binary64
|
||||
add_alias(
|
||||
Symbol::NUM_F64,
|
||||
BuiltinAlias {
|
||||
@ -238,7 +278,7 @@ pub fn aliases() -> MutMap<Symbol, BuiltinAlias> {
|
||||
},
|
||||
);
|
||||
|
||||
// F32 : Num (FloatingPoint Binary32)
|
||||
// F32 : Float Binary32
|
||||
add_alias(
|
||||
Symbol::NUM_F32,
|
||||
BuiltinAlias {
|
||||
@ -312,27 +352,39 @@ fn floatingpoint_alias_content(range: SolvedType) -> SolvedType {
|
||||
// FLOAT
|
||||
|
||||
#[inline(always)]
|
||||
pub fn float_type() -> SolvedType {
|
||||
pub fn float_type(range: SolvedType) -> SolvedType {
|
||||
SolvedType::Alias(
|
||||
Symbol::NUM_F64,
|
||||
Vec::new(),
|
||||
Box::new(float_alias_content(binary64_type())),
|
||||
Symbol::NUM_FLOAT,
|
||||
vec![("range".into(), range.clone())],
|
||||
Box::new(float_alias_content(range)),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn float_alias_content(typ: SolvedType) -> SolvedType {
|
||||
num_type(floatingpoint_type(typ))
|
||||
fn float_alias_content(range: SolvedType) -> SolvedType {
|
||||
num_type(floatingpoint_type(range))
|
||||
}
|
||||
|
||||
// Nat
|
||||
|
||||
#[inline(always)]
|
||||
pub fn nat_type() -> SolvedType {
|
||||
SolvedType::Alias(Symbol::NUM_NAT, vec![], Box::new(nat_alias_content()))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn nat_alias_content() -> SolvedType {
|
||||
int_alias_content(natural_type())
|
||||
}
|
||||
|
||||
// INT
|
||||
|
||||
#[inline(always)]
|
||||
pub fn int_type() -> SolvedType {
|
||||
pub fn int_type(range: SolvedType) -> SolvedType {
|
||||
SolvedType::Alias(
|
||||
Symbol::NUM_I64,
|
||||
Vec::new(),
|
||||
Box::new(int_alias_content(signed64_type())),
|
||||
Symbol::NUM_INT,
|
||||
vec![("range".into(), range.clone())],
|
||||
Box::new(int_alias_content(range)),
|
||||
)
|
||||
}
|
||||
|
||||
@ -385,6 +437,20 @@ fn binary32_alias_content() -> SolvedType {
|
||||
single_private_tag(Symbol::NUM_AT_BINARY32, vec![])
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn natural_type() -> SolvedType {
|
||||
SolvedType::Alias(
|
||||
Symbol::NUM_NATURAL,
|
||||
vec![],
|
||||
Box::new(natural_alias_content()),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn natural_alias_content() -> SolvedType {
|
||||
single_private_tag(Symbol::NUM_AT_NATURAL, vec![])
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn signed128_type() -> SolvedType {
|
||||
SolvedType::Alias(
|
||||
|
@ -14,7 +14,7 @@ use roc_types::types::Type::{self, *};
|
||||
pub fn int_literal(num_var: Variable, expected: Expected<Type>, region: Region) -> Constraint {
|
||||
let num_type = Variable(num_var);
|
||||
let reason = Reason::IntLiteral;
|
||||
let int_type = builtin_type(Symbol::NUM_I64, vec![]);
|
||||
let int_type = builtin_type(Symbol::NUM_INT, vec![]);
|
||||
let expected_literal = ForReason(reason, int_type, region);
|
||||
|
||||
exists(
|
||||
@ -30,7 +30,7 @@ pub fn int_literal(num_var: Variable, expected: Expected<Type>, region: Region)
|
||||
pub fn float_literal(num_var: Variable, expected: Expected<Type>, region: Region) -> Constraint {
|
||||
let num_type = Variable(num_var);
|
||||
let reason = Reason::FloatLiteral;
|
||||
let float_type = builtin_type(Symbol::NUM_F64, vec![]);
|
||||
let float_type = builtin_type(Symbol::NUM_FLOAT, vec![]);
|
||||
let expected_literal = ForReason(reason, float_type, region);
|
||||
|
||||
exists(
|
||||
|
@ -752,9 +752,9 @@ fn annotate_usage_pattern(pattern: &Pattern, usage: &mut VarUsage) {
|
||||
|
||||
match pattern {
|
||||
Identifier(_)
|
||||
| IntLiteral(_)
|
||||
| IntLiteral(_, _)
|
||||
| NumLiteral(_, _)
|
||||
| FloatLiteral(_)
|
||||
| FloatLiteral(_, _)
|
||||
| StrLiteral(_)
|
||||
| Underscore
|
||||
| Shadowed(_, _)
|
||||
@ -785,8 +785,8 @@ pub fn annotate_usage(expr: &Expr, usage: &mut VarUsage) {
|
||||
match expr {
|
||||
RuntimeError(_)
|
||||
| Num(_, _)
|
||||
| Int(_, _)
|
||||
| Float(_, _)
|
||||
| Int(_, _, _)
|
||||
| Float(_, _, _)
|
||||
| Str { .. }
|
||||
| EmptyRecord
|
||||
| Accessor { .. }
|
||||
|
@ -129,9 +129,10 @@ fn files_to_documentations(
|
||||
let mut loaded = roc_load::file::load_and_typecheck(
|
||||
&arena,
|
||||
filename,
|
||||
std_lib.clone(),
|
||||
&std_lib,
|
||||
src_dir,
|
||||
MutMap::default(),
|
||||
8, // TODO: Is it okay to hardcode ptr_bytes here? I think it should be fine since we'er only type checking (also, 8 => 32bit system)
|
||||
)
|
||||
.expect("TODO gracefully handle load failing");
|
||||
files_docs.extend(loaded.documentation.drain().map(|x| x.1));
|
||||
|
@ -66,6 +66,8 @@ cgmath = "0.17.0"
|
||||
itertools = "0.9.0"
|
||||
snafu = { version = "0.6", features = ["backtraces"] }
|
||||
colored = "2"
|
||||
pest = "2.1"
|
||||
pest_derive = "2.1"
|
||||
|
||||
|
||||
[dependencies.bytemuck]
|
||||
|
@ -55,10 +55,11 @@ These are potentially inspirational resources for the editor's design.
|
||||
### Productivity features
|
||||
|
||||
* When refactoring;
|
||||
- cutting and pasting code to a new file should automatically add imports to the new file and delete them from the old file.
|
||||
- When renaming a function for example, references in comments should be brought to the user's attention.
|
||||
- Cutting and pasting code to a new file should automatically add imports to the new file and delete them from the old file.
|
||||
- Ability to link e.g. variable name in comments to actual variable name. Comment is automatically updated when variable name is changed.
|
||||
* Automatically create all "arms" when pattern matching after entering `when var is` based on the type.
|
||||
- All `when ... is` should be updated if the type is changed, e.g. adding Indigo to the Color type should add an arm everywhere where `when color is` is used.
|
||||
* When a function is called like `foo(false)`, the name of the boolean argument should be shown automatically; `foo(`*is_active:*`false)`. This should be done for booleans and numbers.
|
||||
|
||||
|
||||
### Non-Code Related Inspiration
|
||||
|
@ -24,6 +24,8 @@ pub enum EdError {
|
||||
err_msg: String,
|
||||
backtrace: Backtrace,
|
||||
},
|
||||
#[snafu(display("MissingGlyphDims: glyph_dim_rect_opt was None for model. It needs to be set using the example_code_glyph_rect function."))]
|
||||
MissingGlyphDims {},
|
||||
}
|
||||
|
||||
pub type EdResult<T, E = EdError> = std::result::Result<T, E>;
|
||||
|
@ -1 +1,5 @@
|
||||
pub const WHITE: [f32; 3] = [1.0, 1.0, 1.0];
|
||||
pub const TXT_COLOR: [f32; 4] = [0.4666, 0.2, 1.0, 1.0];
|
||||
pub const CODE_COLOR: [f32; 4] = [0.0, 0.05, 0.46, 1.0];
|
||||
pub const CARET_COLOR: [f32; 3] = WHITE;
|
||||
pub const SELECT_COLOR: [f32; 3] = [0.45, 0.61, 1.0];
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Adapted from https://github.com/sotrh/learn-wgpu
|
||||
// by Benjamin Hansen, licensed under the MIT license
|
||||
use crate::graphics::lowlevel::vertex::Vertex;
|
||||
use super::vertex::Vertex;
|
||||
use crate::graphics::primitives::rect::Rect;
|
||||
use bumpalo::collections::Vec as BumpVec;
|
||||
use wgpu::util::{BufferInitDescriptor, DeviceExt};
|
||||
|
@ -1,3 +1,4 @@
|
||||
pub mod buffer;
|
||||
pub mod ortho;
|
||||
pub mod pipelines;
|
||||
pub mod vertex;
|
||||
|
@ -66,6 +66,7 @@ pub fn update_ortho_buffer(
|
||||
cmd_queue.submit(Some(encoder.finish()));
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct OrthoResources {
|
||||
pub buffer: Buffer,
|
||||
pub bind_group_layout: BindGroupLayout,
|
||||
|
71
editor/src/graphics/lowlevel/pipelines.rs
Normal file
71
editor/src/graphics/lowlevel/pipelines.rs
Normal file
@ -0,0 +1,71 @@
|
||||
use super::ortho::{init_ortho, OrthoResources};
|
||||
use super::vertex::Vertex;
|
||||
|
||||
pub struct RectResources {
|
||||
pub pipeline: wgpu::RenderPipeline,
|
||||
pub ortho: OrthoResources,
|
||||
}
|
||||
|
||||
pub fn make_rect_pipeline(
|
||||
gpu_device: &wgpu::Device,
|
||||
swap_chain_descr: &wgpu::SwapChainDescriptor,
|
||||
) -> RectResources {
|
||||
let ortho = init_ortho(swap_chain_descr.width, swap_chain_descr.height, gpu_device);
|
||||
|
||||
let pipeline_layout = gpu_device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
bind_group_layouts: &[&ortho.bind_group_layout],
|
||||
push_constant_ranges: &[],
|
||||
label: Some("Rectangle pipeline layout"),
|
||||
});
|
||||
let pipeline = create_render_pipeline(
|
||||
&gpu_device,
|
||||
&pipeline_layout,
|
||||
swap_chain_descr.format,
|
||||
&[Vertex::DESC],
|
||||
wgpu::include_spirv!("../shaders/rect.vert.spv"),
|
||||
wgpu::include_spirv!("../shaders/rect.frag.spv"),
|
||||
);
|
||||
|
||||
RectResources { pipeline, ortho }
|
||||
}
|
||||
|
||||
pub fn create_render_pipeline(
|
||||
device: &wgpu::Device,
|
||||
layout: &wgpu::PipelineLayout,
|
||||
color_format: wgpu::TextureFormat,
|
||||
vertex_descs: &[wgpu::VertexBufferDescriptor],
|
||||
vs_src: wgpu::ShaderModuleSource,
|
||||
fs_src: wgpu::ShaderModuleSource,
|
||||
) -> wgpu::RenderPipeline {
|
||||
let vs_module = device.create_shader_module(vs_src);
|
||||
let fs_module = device.create_shader_module(fs_src);
|
||||
|
||||
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: Some("Render pipeline"),
|
||||
layout: Some(&layout),
|
||||
vertex_stage: wgpu::ProgrammableStageDescriptor {
|
||||
module: &vs_module,
|
||||
entry_point: "main",
|
||||
},
|
||||
fragment_stage: Some(wgpu::ProgrammableStageDescriptor {
|
||||
module: &fs_module,
|
||||
entry_point: "main",
|
||||
}),
|
||||
rasterization_state: None,
|
||||
primitive_topology: wgpu::PrimitiveTopology::TriangleList,
|
||||
color_states: &[wgpu::ColorStateDescriptor {
|
||||
format: color_format,
|
||||
color_blend: wgpu::BlendDescriptor::REPLACE,
|
||||
alpha_blend: wgpu::BlendDescriptor::REPLACE,
|
||||
write_mask: wgpu::ColorWrite::ALL,
|
||||
}],
|
||||
depth_stencil_state: None,
|
||||
sample_count: 1,
|
||||
sample_mask: !0,
|
||||
alpha_to_coverage_enabled: false,
|
||||
vertex_state: wgpu::VertexStateDescriptor {
|
||||
index_format: wgpu::IndexFormat::Uint32,
|
||||
vertex_buffers: vertex_descs,
|
||||
},
|
||||
})
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
pub mod colors;
|
||||
pub mod lowlevel;
|
||||
pub mod primitives;
|
||||
pub mod style;
|
||||
|
@ -1,10 +1,11 @@
|
||||
// Adapted from https://github.com/sotrh/learn-wgpu
|
||||
// by Benjamin Hansen, licensed under the MIT license
|
||||
|
||||
use crate::graphics::primitives::rect::Rect;
|
||||
use super::rect::Rect;
|
||||
use crate::graphics::colors::CODE_COLOR;
|
||||
use crate::graphics::style::{CODE_FONT_SIZE, CODE_TXT_XY};
|
||||
use ab_glyph::{FontArc, Glyph, InvalidFont};
|
||||
use cgmath::{Vector2, Vector4};
|
||||
use itertools::Itertools;
|
||||
use wgpu_glyph::{ab_glyph, GlyphBrush, GlyphBrushBuilder, GlyphCruncher, Section};
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -25,22 +26,50 @@ impl Default for Text {
|
||||
area_bounds: (std::f32::INFINITY, std::f32::INFINITY).into(),
|
||||
color: (1.0, 1.0, 1.0, 1.0).into(),
|
||||
text: String::new(),
|
||||
size: 16.0,
|
||||
size: CODE_FONT_SIZE,
|
||||
visible: true,
|
||||
centered: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// returns bounding boxes for every glyph
|
||||
pub fn queue_text_draw(text: &Text, glyph_brush: &mut GlyphBrush<()>) -> Vec<Vec<Rect>> {
|
||||
let layout = wgpu_glyph::Layout::default().h_align(if text.centered {
|
||||
// necessary to get dimensions for caret
|
||||
pub fn example_code_glyph_rect(glyph_brush: &mut GlyphBrush<()>) -> Rect {
|
||||
let code_text = Text {
|
||||
position: CODE_TXT_XY.into(),
|
||||
area_bounds: (std::f32::INFINITY, std::f32::INFINITY).into(),
|
||||
color: CODE_COLOR.into(),
|
||||
text: "a".to_owned(),
|
||||
size: CODE_FONT_SIZE,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let layout = layout_from_text(&code_text);
|
||||
|
||||
let section = section_from_text(&code_text, layout);
|
||||
|
||||
let mut glyph_section_iter = glyph_brush.glyphs_custom_layout(section, &layout);
|
||||
|
||||
if let Some(glyph) = glyph_section_iter.next() {
|
||||
glyph_to_rect(glyph)
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
fn layout_from_text(text: &Text) -> wgpu_glyph::Layout<wgpu_glyph::BuiltInLineBreaker> {
|
||||
wgpu_glyph::Layout::default().h_align(if text.centered {
|
||||
wgpu_glyph::HorizontalAlign::Center
|
||||
} else {
|
||||
wgpu_glyph::HorizontalAlign::Left
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
let section = Section {
|
||||
fn section_from_text(
|
||||
text: &Text,
|
||||
layout: wgpu_glyph::Layout<wgpu_glyph::BuiltInLineBreaker>,
|
||||
) -> wgpu_glyph::Section {
|
||||
Section {
|
||||
screen_position: text.position.into(),
|
||||
bounds: text.area_bounds.into(),
|
||||
layout,
|
||||
@ -50,48 +79,31 @@ pub fn queue_text_draw(text: &Text, glyph_brush: &mut GlyphBrush<()>) -> Vec<Vec
|
||||
wgpu_glyph::Text::new(&text.text)
|
||||
.with_color(text.color)
|
||||
.with_scale(text.size),
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
// returns glyphs per line
|
||||
pub fn queue_text_draw(text: &Text, glyph_brush: &mut GlyphBrush<()>) {
|
||||
let layout = layout_from_text(text);
|
||||
|
||||
let section = section_from_text(text, layout);
|
||||
|
||||
glyph_brush.queue(section.clone());
|
||||
}
|
||||
|
||||
let glyph_section_iter = glyph_brush.glyphs_custom_layout(section, &layout);
|
||||
fn glyph_to_rect(glyph: &wgpu_glyph::SectionGlyph) -> Rect {
|
||||
let position = glyph.glyph.position;
|
||||
let px_scale = glyph.glyph.scale;
|
||||
let width = glyph_width(&glyph.glyph);
|
||||
let height = px_scale.y;
|
||||
let top_y = glyph_top_y(&glyph.glyph);
|
||||
|
||||
glyph_section_iter
|
||||
.map(|section_glyph| {
|
||||
let position = section_glyph.glyph.position;
|
||||
let px_scale = section_glyph.glyph.scale;
|
||||
let width = glyph_width(§ion_glyph.glyph);
|
||||
let height = px_scale.y;
|
||||
let top_y = glyph_top_y(§ion_glyph.glyph);
|
||||
|
||||
Rect {
|
||||
top_left_coords: [position.x, top_y].into(),
|
||||
width,
|
||||
height,
|
||||
color: [1.0, 1.0, 1.0],
|
||||
}
|
||||
})
|
||||
.group_by(|rect| rect.top_left_coords.y)
|
||||
.into_iter()
|
||||
.map(|(_y_coord, rect_group)| {
|
||||
let mut rects_vec = rect_group.collect::<Vec<Rect>>();
|
||||
let last_rect_opt = rects_vec.last().cloned();
|
||||
// add extra rect to make it easy to highlight the newline character
|
||||
if let Some(last_rect) = last_rect_opt {
|
||||
rects_vec.push(Rect {
|
||||
top_left_coords: [
|
||||
last_rect.top_left_coords.x + last_rect.width,
|
||||
last_rect.top_left_coords.y,
|
||||
]
|
||||
.into(),
|
||||
width: last_rect.width,
|
||||
height: last_rect.height,
|
||||
color: last_rect.color,
|
||||
});
|
||||
}
|
||||
rects_vec
|
||||
})
|
||||
.collect()
|
||||
Rect {
|
||||
top_left_coords: [position.x, top_y].into(),
|
||||
width,
|
||||
height,
|
||||
color: [1.0, 1.0, 1.0],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn glyph_top_y(glyph: &Glyph) -> f32 {
|
||||
@ -101,7 +113,7 @@ pub fn glyph_top_y(glyph: &Glyph) -> f32 {
|
||||
}
|
||||
|
||||
pub fn glyph_width(glyph: &Glyph) -> f32 {
|
||||
glyph.scale.x * 0.5
|
||||
glyph.scale.x * 0.4765
|
||||
}
|
||||
|
||||
pub fn build_glyph_brush(
|
||||
|
2
editor/src/graphics/style.rs
Normal file
2
editor/src/graphics/style.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub const CODE_FONT_SIZE: f32 = 40.0;
|
||||
pub const CODE_TXT_XY: (f32, f32) = (30.0, 90.0);
|
@ -1,4 +1,4 @@
|
||||
use crate::tea::model::Model;
|
||||
use crate::tea::ed_model::EdModel;
|
||||
use crate::tea::update::{move_caret_down, move_caret_left, move_caret_right, move_caret_up};
|
||||
use winit::event::{ElementState, ModifiersState, VirtualKeyCode};
|
||||
|
||||
@ -6,7 +6,7 @@ pub fn handle_keydown(
|
||||
elem_state: ElementState,
|
||||
virtual_keycode: VirtualKeyCode,
|
||||
modifiers: ModifiersState,
|
||||
model: &mut Model,
|
||||
model: &mut EdModel,
|
||||
) {
|
||||
use winit::event::VirtualKeyCode::*;
|
||||
|
||||
|
@ -8,21 +8,37 @@
|
||||
|
||||
// See this link to learn wgpu: https://sotrh.github.io/learn-wgpu/
|
||||
|
||||
use crate::error::print_err;
|
||||
extern crate pest;
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate pest_derive;
|
||||
|
||||
use crate::error::EdError::MissingGlyphDims;
|
||||
use crate::error::{print_err, EdResult};
|
||||
use crate::graphics::colors::{CARET_COLOR, CODE_COLOR, TXT_COLOR};
|
||||
use crate::graphics::lowlevel::buffer::create_rect_buffers;
|
||||
use crate::graphics::lowlevel::ortho::{init_ortho, update_ortho_buffer, OrthoResources};
|
||||
use crate::graphics::lowlevel::vertex::Vertex;
|
||||
use crate::graphics::lowlevel::ortho::update_ortho_buffer;
|
||||
use crate::graphics::lowlevel::pipelines;
|
||||
use crate::graphics::primitives::rect::Rect;
|
||||
use crate::graphics::primitives::text::{build_glyph_brush, queue_text_draw, Text};
|
||||
use crate::graphics::primitives::text::{
|
||||
build_glyph_brush, example_code_glyph_rect, queue_text_draw, Text,
|
||||
};
|
||||
use crate::graphics::style::CODE_FONT_SIZE;
|
||||
use crate::graphics::style::CODE_TXT_XY;
|
||||
use crate::selection::create_selection_rects;
|
||||
use crate::tea::{model, update};
|
||||
use crate::util::is_newline;
|
||||
use crate::tea::ed_model::EdModel;
|
||||
use crate::tea::{ed_model, update};
|
||||
use bumpalo::collections::Vec as BumpVec;
|
||||
use bumpalo::Bump;
|
||||
use model::Position;
|
||||
use cgmath::Vector2;
|
||||
use ed_model::Position;
|
||||
use pipelines::RectResources;
|
||||
use std::error::Error;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
use wgpu::{CommandEncoder, RenderPass, TextureView};
|
||||
use wgpu_glyph::GlyphBrush;
|
||||
use winit::dpi::PhysicalSize;
|
||||
use winit::event;
|
||||
use winit::event::{Event, ModifiersState};
|
||||
use winit::event_loop::ControlFlow;
|
||||
@ -103,12 +119,13 @@ fn run_event_loop() -> Result<(), Box<dyn Error>> {
|
||||
|
||||
let mut swap_chain = gpu_device.create_swap_chain(&surface, &swap_chain_descr);
|
||||
|
||||
let (rect_pipeline, ortho) = make_rect_pipeline(&gpu_device, &swap_chain_descr);
|
||||
let rect_resources = pipelines::make_rect_pipeline(&gpu_device, &swap_chain_descr);
|
||||
|
||||
let mut glyph_brush = build_glyph_brush(&gpu_device, render_format)?;
|
||||
|
||||
let is_animating = true;
|
||||
let mut ed_model = model::init_model();
|
||||
let mut ed_model = ed_model::init_model();
|
||||
ed_model.glyph_dim_rect_opt = Some(example_code_glyph_rect(&mut glyph_brush));
|
||||
let mut keyboard_modifiers = ModifiersState::empty();
|
||||
|
||||
let arena = Bump::new();
|
||||
@ -154,7 +171,7 @@ fn run_event_loop() -> Result<(), Box<dyn Error>> {
|
||||
size.width,
|
||||
size.height,
|
||||
&gpu_device,
|
||||
&ortho.buffer,
|
||||
&rect_resources.ortho.buffer,
|
||||
&cmd_queue,
|
||||
);
|
||||
}
|
||||
@ -163,7 +180,7 @@ fn run_event_loop() -> Result<(), Box<dyn Error>> {
|
||||
event: event::WindowEvent::ReceivedCharacter(ch),
|
||||
..
|
||||
} => {
|
||||
update_text_state(&mut ed_model, &ch);
|
||||
update::update_text_state(&mut ed_model, &ch);
|
||||
}
|
||||
//Keyboard Input
|
||||
Event::WindowEvent {
|
||||
@ -199,39 +216,28 @@ fn run_event_loop() -> Result<(), Box<dyn Error>> {
|
||||
.expect("Failed to acquire next SwapChainFrame")
|
||||
.output;
|
||||
|
||||
let glyph_bounds_rects =
|
||||
queue_all_text(&size, &ed_model.lines, ed_model.caret_pos, &mut glyph_brush);
|
||||
queue_all_text(
|
||||
&size,
|
||||
&ed_model.lines,
|
||||
ed_model.caret_pos,
|
||||
CODE_TXT_XY.into(),
|
||||
&mut glyph_brush,
|
||||
);
|
||||
|
||||
if let Some(selection) = ed_model.selection_opt {
|
||||
let selection_rects_res =
|
||||
create_selection_rects(selection, &glyph_bounds_rects, &arena);
|
||||
|
||||
match selection_rects_res {
|
||||
Ok(selection_rects) => {
|
||||
if !selection_rects.is_empty() {
|
||||
let rect_buffers = create_rect_buffers(
|
||||
&gpu_device,
|
||||
&mut encoder,
|
||||
&selection_rects,
|
||||
);
|
||||
|
||||
let mut render_pass = begin_render_pass(&mut encoder, &frame.view);
|
||||
|
||||
render_pass.set_pipeline(&rect_pipeline);
|
||||
render_pass.set_bind_group(0, &ortho.bind_group, &[]);
|
||||
render_pass
|
||||
.set_vertex_buffer(0, rect_buffers.vertex_buffer.slice(..));
|
||||
render_pass.set_index_buffer(rect_buffers.index_buffer.slice(..));
|
||||
render_pass.draw_indexed(0..rect_buffers.num_rects, 0, 0..1);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
begin_render_pass(&mut encoder, &frame.view);
|
||||
print_err(&e) //TODO draw error text on screen
|
||||
}
|
||||
match draw_all_rects(
|
||||
&ed_model,
|
||||
&ed_model.glyph_dim_rect_opt,
|
||||
&arena,
|
||||
&mut encoder,
|
||||
&frame.view,
|
||||
&gpu_device,
|
||||
&rect_resources,
|
||||
) {
|
||||
Ok(()) => (),
|
||||
Err(e) => {
|
||||
print_err(&e);
|
||||
begin_render_pass(&mut encoder, &frame.view);
|
||||
}
|
||||
} else {
|
||||
begin_render_pass(&mut encoder, &frame.view);
|
||||
}
|
||||
|
||||
// draw all text
|
||||
@ -265,6 +271,45 @@ fn run_event_loop() -> Result<(), Box<dyn Error>> {
|
||||
})
|
||||
}
|
||||
|
||||
fn draw_all_rects(
|
||||
ed_model: &EdModel,
|
||||
glyph_dim_rect_opt: &Option<Rect>,
|
||||
arena: &Bump,
|
||||
encoder: &mut CommandEncoder,
|
||||
texture_view: &TextureView,
|
||||
gpu_device: &wgpu::Device,
|
||||
rect_resources: &RectResources,
|
||||
) -> EdResult<()> {
|
||||
let mut all_rects: BumpVec<Rect> = BumpVec::new_in(arena);
|
||||
|
||||
let glyph_rect = if let Some(glyph_dim_rect) = glyph_dim_rect_opt {
|
||||
glyph_dim_rect
|
||||
} else {
|
||||
return Err(MissingGlyphDims {});
|
||||
};
|
||||
|
||||
if let Some(selection) = ed_model.selection_opt {
|
||||
let mut selection_rects =
|
||||
create_selection_rects(selection, &ed_model.lines, glyph_rect, &arena)?;
|
||||
|
||||
all_rects.append(&mut selection_rects);
|
||||
}
|
||||
|
||||
all_rects.push(make_caret_rect(ed_model.caret_pos, glyph_rect)?);
|
||||
|
||||
let rect_buffers = create_rect_buffers(gpu_device, encoder, &all_rects);
|
||||
|
||||
let mut render_pass = begin_render_pass(encoder, texture_view);
|
||||
|
||||
render_pass.set_pipeline(&rect_resources.pipeline);
|
||||
render_pass.set_bind_group(0, &rect_resources.ortho.bind_group, &[]);
|
||||
render_pass.set_vertex_buffer(0, rect_buffers.vertex_buffer.slice(..));
|
||||
render_pass.set_index_buffer(rect_buffers.index_buffer.slice(..));
|
||||
render_pass.draw_indexed(0..rect_buffers.num_rects, 0, 0..1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn begin_render_pass<'a>(
|
||||
encoder: &'a mut CommandEncoder,
|
||||
texture_view: &'a TextureView,
|
||||
@ -287,101 +332,53 @@ fn begin_render_pass<'a>(
|
||||
})
|
||||
}
|
||||
|
||||
fn make_rect_pipeline(
|
||||
gpu_device: &wgpu::Device,
|
||||
swap_chain_descr: &wgpu::SwapChainDescriptor,
|
||||
) -> (wgpu::RenderPipeline, OrthoResources) {
|
||||
let ortho = init_ortho(swap_chain_descr.width, swap_chain_descr.height, gpu_device);
|
||||
fn make_caret_rect(caret_pos: Position, glyph_dim_rect: &Rect) -> EdResult<Rect> {
|
||||
let caret_y =
|
||||
glyph_dim_rect.top_left_coords.y + (caret_pos.line as f32) * glyph_dim_rect.height;
|
||||
|
||||
let pipeline_layout = gpu_device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
bind_group_layouts: &[&ortho.bind_group_layout],
|
||||
push_constant_ranges: &[],
|
||||
label: Some("Rectangle pipeline layout"),
|
||||
});
|
||||
let pipeline = create_render_pipeline(
|
||||
&gpu_device,
|
||||
&pipeline_layout,
|
||||
swap_chain_descr.format,
|
||||
&[Vertex::DESC],
|
||||
wgpu::include_spirv!("graphics/shaders/rect.vert.spv"),
|
||||
wgpu::include_spirv!("graphics/shaders/rect.frag.spv"),
|
||||
);
|
||||
let caret_x =
|
||||
glyph_dim_rect.top_left_coords.x + glyph_dim_rect.width * (caret_pos.column as f32);
|
||||
|
||||
(pipeline, ortho)
|
||||
}
|
||||
|
||||
fn create_render_pipeline(
|
||||
device: &wgpu::Device,
|
||||
layout: &wgpu::PipelineLayout,
|
||||
color_format: wgpu::TextureFormat,
|
||||
vertex_descs: &[wgpu::VertexBufferDescriptor],
|
||||
vs_src: wgpu::ShaderModuleSource,
|
||||
fs_src: wgpu::ShaderModuleSource,
|
||||
) -> wgpu::RenderPipeline {
|
||||
let vs_module = device.create_shader_module(vs_src);
|
||||
let fs_module = device.create_shader_module(fs_src);
|
||||
|
||||
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: Some("Render pipeline"),
|
||||
layout: Some(&layout),
|
||||
vertex_stage: wgpu::ProgrammableStageDescriptor {
|
||||
module: &vs_module,
|
||||
entry_point: "main",
|
||||
},
|
||||
fragment_stage: Some(wgpu::ProgrammableStageDescriptor {
|
||||
module: &fs_module,
|
||||
entry_point: "main",
|
||||
}),
|
||||
rasterization_state: None,
|
||||
primitive_topology: wgpu::PrimitiveTopology::TriangleList,
|
||||
color_states: &[wgpu::ColorStateDescriptor {
|
||||
format: color_format,
|
||||
color_blend: wgpu::BlendDescriptor::REPLACE,
|
||||
alpha_blend: wgpu::BlendDescriptor::REPLACE,
|
||||
write_mask: wgpu::ColorWrite::ALL,
|
||||
}],
|
||||
depth_stencil_state: None,
|
||||
sample_count: 1,
|
||||
sample_mask: !0,
|
||||
alpha_to_coverage_enabled: false,
|
||||
vertex_state: wgpu::VertexStateDescriptor {
|
||||
index_format: wgpu::IndexFormat::Uint32,
|
||||
vertex_buffers: vertex_descs,
|
||||
},
|
||||
Ok(Rect {
|
||||
top_left_coords: (caret_x, caret_y).into(),
|
||||
height: glyph_dim_rect.height,
|
||||
width: 2.0,
|
||||
color: CARET_COLOR,
|
||||
})
|
||||
}
|
||||
|
||||
// returns bounding boxes for every glyph
|
||||
fn queue_all_text(
|
||||
size: &winit::dpi::PhysicalSize<u32>,
|
||||
size: &PhysicalSize<u32>,
|
||||
lines: &[String],
|
||||
caret_pos: Position,
|
||||
glyph_brush: &mut wgpu_glyph::GlyphBrush<()>,
|
||||
) -> Vec<Vec<Rect>> {
|
||||
code_coords: Vector2<f32>,
|
||||
glyph_brush: &mut GlyphBrush<()>,
|
||||
) {
|
||||
let area_bounds = (size.width as f32, size.height as f32).into();
|
||||
|
||||
let main_label = Text {
|
||||
position: (30.0, 30.0).into(),
|
||||
area_bounds,
|
||||
color: (0.4666, 0.2, 1.0, 1.0).into(),
|
||||
color: TXT_COLOR.into(),
|
||||
text: String::from("Enter some text:"),
|
||||
size: 40.0,
|
||||
size: CODE_FONT_SIZE,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let code_text = Text {
|
||||
position: (30.0, 90.0).into(),
|
||||
position: code_coords,
|
||||
area_bounds,
|
||||
color: (0.0, 0.05, 0.46, 1.0).into(),
|
||||
color: CODE_COLOR.into(),
|
||||
text: lines.join(""),
|
||||
size: 40.0,
|
||||
size: CODE_FONT_SIZE,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let caret_pos_label = Text {
|
||||
position: (30.0, 530.0).into(),
|
||||
position: (30.0, (size.height as f32) - 45.0).into(),
|
||||
area_bounds,
|
||||
color: (0.4666, 0.2, 1.0, 1.0).into(),
|
||||
color: TXT_COLOR.into(),
|
||||
text: format!("Ln {}, Col {}", caret_pos.line, caret_pos.column),
|
||||
size: 30.0,
|
||||
..Default::default()
|
||||
@ -391,55 +388,5 @@ fn queue_all_text(
|
||||
|
||||
queue_text_draw(&caret_pos_label, glyph_brush);
|
||||
|
||||
queue_text_draw(&code_text, glyph_brush)
|
||||
}
|
||||
|
||||
fn update_text_state(ed_model: &mut model::Model, received_char: &char) {
|
||||
ed_model.selection_opt = None;
|
||||
|
||||
match received_char {
|
||||
'\u{8}' | '\u{7f}' => {
|
||||
// In Linux, we get a '\u{8}' when you press backspace,
|
||||
// but in macOS we get '\u{7f}'.
|
||||
if let Some(last_line) = ed_model.lines.last_mut() {
|
||||
if !last_line.is_empty() {
|
||||
last_line.pop();
|
||||
} else if ed_model.lines.len() > 1 {
|
||||
ed_model.lines.pop();
|
||||
}
|
||||
ed_model.caret_pos =
|
||||
update::move_caret_left(ed_model.caret_pos, None, false, &ed_model.lines).0;
|
||||
}
|
||||
}
|
||||
'\u{e000}'..='\u{f8ff}' | '\u{f0000}'..='\u{ffffd}' | '\u{100000}'..='\u{10fffd}' => {
|
||||
// These are private use characters; ignore them.
|
||||
// See http://www.unicode.org/faq/private_use.html
|
||||
}
|
||||
ch if is_newline(ch) => {
|
||||
if let Some(last_line) = ed_model.lines.last_mut() {
|
||||
last_line.push(*received_char)
|
||||
}
|
||||
ed_model.lines.push(String::new());
|
||||
ed_model.caret_pos = Position {
|
||||
line: ed_model.caret_pos.line + 1,
|
||||
column: 0,
|
||||
};
|
||||
|
||||
ed_model.selection_opt = None;
|
||||
}
|
||||
_ => {
|
||||
let nr_lines = ed_model.lines.len();
|
||||
|
||||
if let Some(last_line) = ed_model.lines.last_mut() {
|
||||
last_line.push(*received_char);
|
||||
|
||||
ed_model.caret_pos = Position {
|
||||
line: nr_lines - 1,
|
||||
column: last_line.len(),
|
||||
};
|
||||
|
||||
ed_model.selection_opt = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
queue_text_draw(&code_text, glyph_brush);
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,17 +1,20 @@
|
||||
use crate::graphics::primitives::rect::Rect;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Model {
|
||||
pub struct EdModel {
|
||||
pub lines: Vec<String>,
|
||||
pub caret_pos: Position,
|
||||
pub selection_opt: Option<RawSelection>,
|
||||
pub glyph_dim_rect_opt: Option<Rect>,
|
||||
}
|
||||
|
||||
pub fn init_model() -> Model {
|
||||
Model {
|
||||
pub fn init_model() -> EdModel {
|
||||
EdModel {
|
||||
lines: vec![String::new()],
|
||||
caret_pos: Position { line: 0, column: 0 },
|
||||
selection_opt: None,
|
||||
glyph_dim_rect_opt: None,
|
||||
}
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
pub mod model;
|
||||
pub mod ed_model;
|
||||
pub mod update;
|
||||
|
@ -1,4 +1,5 @@
|
||||
use crate::tea::model::{Position, RawSelection};
|
||||
use super::ed_model::EdModel;
|
||||
use super::ed_model::{Position, RawSelection};
|
||||
use crate::util::is_newline;
|
||||
use std::cmp::{max, min};
|
||||
|
||||
@ -11,7 +12,12 @@ pub fn move_caret_left(
|
||||
let old_line_nr = old_caret_pos.line;
|
||||
let old_col_nr = old_caret_pos.column;
|
||||
|
||||
let (line_nr, col_nr) = if old_col_nr == 0 {
|
||||
let (line_nr, col_nr) = if old_selection_opt.is_some() && !shift_pressed {
|
||||
match old_selection_opt {
|
||||
Some(old_selection) => (old_selection.start_pos.line, old_selection.start_pos.column),
|
||||
None => unreachable!(),
|
||||
}
|
||||
} else if old_col_nr == 0 {
|
||||
if old_line_nr == 0 {
|
||||
(0, 0)
|
||||
} else if let Some(curr_line) = lines.get(old_line_nr - 1) {
|
||||
@ -30,13 +36,24 @@ pub fn move_caret_left(
|
||||
|
||||
let new_selection_opt = if shift_pressed {
|
||||
if let Some(old_selection) = old_selection_opt {
|
||||
Some(RawSelection {
|
||||
start_pos: Position {
|
||||
line: line_nr,
|
||||
column: col_nr,
|
||||
},
|
||||
end_pos: old_selection.end_pos,
|
||||
})
|
||||
if old_caret_pos >= old_selection.end_pos {
|
||||
if new_caret_pos == old_selection.start_pos {
|
||||
None
|
||||
} else {
|
||||
Some(RawSelection {
|
||||
start_pos: old_selection.start_pos,
|
||||
end_pos: new_caret_pos,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
Some(RawSelection {
|
||||
start_pos: Position {
|
||||
line: line_nr,
|
||||
column: col_nr,
|
||||
},
|
||||
end_pos: old_selection.end_pos,
|
||||
})
|
||||
}
|
||||
} else if !(old_line_nr == line_nr && old_col_nr == col_nr) {
|
||||
Some(RawSelection {
|
||||
start_pos: Position {
|
||||
@ -67,7 +84,12 @@ pub fn move_caret_right(
|
||||
let old_line_nr = old_caret_pos.line;
|
||||
let old_col_nr = old_caret_pos.column;
|
||||
|
||||
let (line_nr, col_nr) = if let Some(curr_line) = lines.get(old_line_nr) {
|
||||
let (line_nr, col_nr) = if old_selection_opt.is_some() && !shift_pressed {
|
||||
match old_selection_opt {
|
||||
Some(old_selection) => (old_selection.end_pos.line, old_selection.end_pos.column),
|
||||
None => unreachable!(),
|
||||
}
|
||||
} else if let Some(curr_line) = lines.get(old_line_nr) {
|
||||
if let Some(last_char) = curr_line.chars().last() {
|
||||
if is_newline(&last_char) {
|
||||
if old_col_nr + 1 > curr_line.len() - 1 {
|
||||
@ -94,13 +116,24 @@ pub fn move_caret_right(
|
||||
|
||||
let new_selection_opt = if shift_pressed {
|
||||
if let Some(old_selection) = old_selection_opt {
|
||||
Some(RawSelection {
|
||||
start_pos: old_selection.start_pos,
|
||||
end_pos: Position {
|
||||
line: line_nr,
|
||||
column: col_nr,
|
||||
},
|
||||
})
|
||||
if old_caret_pos <= old_selection.start_pos {
|
||||
if new_caret_pos == old_selection.end_pos {
|
||||
None
|
||||
} else {
|
||||
Some(RawSelection {
|
||||
start_pos: new_caret_pos,
|
||||
end_pos: old_selection.end_pos,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
Some(RawSelection {
|
||||
start_pos: old_selection.start_pos,
|
||||
end_pos: Position {
|
||||
line: line_nr,
|
||||
column: col_nr,
|
||||
},
|
||||
})
|
||||
}
|
||||
} else if !(old_line_nr == line_nr && old_col_nr == col_nr) {
|
||||
Some(RawSelection {
|
||||
start_pos: Position {
|
||||
@ -131,10 +164,15 @@ pub fn move_caret_up(
|
||||
let old_line_nr = old_caret_pos.line;
|
||||
let old_col_nr = old_caret_pos.column;
|
||||
|
||||
let (line_nr, col_nr) = if old_line_nr == 0 {
|
||||
(old_line_nr, old_col_nr)
|
||||
let (line_nr, col_nr) = if old_selection_opt.is_some() && !shift_pressed {
|
||||
match old_selection_opt {
|
||||
Some(old_selection) => (old_selection.start_pos.line, old_selection.start_pos.column),
|
||||
None => unreachable!(),
|
||||
}
|
||||
} else if old_line_nr == 0 {
|
||||
(old_line_nr, 0)
|
||||
} else if let Some(prev_line) = lines.get(old_line_nr - 1) {
|
||||
if prev_line.len() < old_col_nr {
|
||||
if prev_line.len() <= old_col_nr {
|
||||
(old_line_nr - 1, prev_line.len() - 1)
|
||||
} else {
|
||||
(old_line_nr - 1, old_col_nr)
|
||||
@ -150,10 +188,21 @@ pub fn move_caret_up(
|
||||
|
||||
let new_selection_opt = if shift_pressed {
|
||||
if let Some(old_selection) = old_selection_opt {
|
||||
Some(RawSelection {
|
||||
start_pos: new_caret_pos,
|
||||
end_pos: old_selection.end_pos,
|
||||
})
|
||||
if old_selection.end_pos <= old_caret_pos {
|
||||
if new_caret_pos == old_selection.start_pos {
|
||||
None
|
||||
} else {
|
||||
Some(RawSelection {
|
||||
start_pos: min(old_selection.start_pos, new_caret_pos),
|
||||
end_pos: max(old_selection.start_pos, new_caret_pos),
|
||||
})
|
||||
}
|
||||
} else {
|
||||
Some(RawSelection {
|
||||
start_pos: new_caret_pos,
|
||||
end_pos: old_selection.end_pos,
|
||||
})
|
||||
}
|
||||
} else if !(old_line_nr == line_nr && old_col_nr == col_nr) {
|
||||
Some(RawSelection {
|
||||
start_pos: min(old_caret_pos, new_caret_pos),
|
||||
@ -178,10 +227,19 @@ pub fn move_caret_down(
|
||||
let old_line_nr = old_caret_pos.line;
|
||||
let old_col_nr = old_caret_pos.column;
|
||||
|
||||
let (line_nr, col_nr) = if old_line_nr + 1 >= lines.len() {
|
||||
(old_line_nr, old_col_nr)
|
||||
let (line_nr, col_nr) = if old_selection_opt.is_some() && !shift_pressed {
|
||||
match old_selection_opt {
|
||||
Some(old_selection) => (old_selection.end_pos.line, old_selection.end_pos.column),
|
||||
None => unreachable!(),
|
||||
}
|
||||
} else if old_line_nr + 1 >= lines.len() {
|
||||
if let Some(curr_line) = lines.get(old_line_nr) {
|
||||
(old_line_nr, curr_line.len())
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
} else if let Some(next_line) = lines.get(old_line_nr + 1) {
|
||||
if next_line.len() < old_col_nr {
|
||||
if next_line.len() <= old_col_nr {
|
||||
if let Some(last_char) = next_line.chars().last() {
|
||||
if is_newline(&last_char) {
|
||||
(old_line_nr + 1, next_line.len() - 1)
|
||||
@ -205,10 +263,21 @@ pub fn move_caret_down(
|
||||
|
||||
let new_selection_opt = if shift_pressed {
|
||||
if let Some(old_selection) = old_selection_opt {
|
||||
Some(RawSelection {
|
||||
start_pos: old_selection.start_pos,
|
||||
end_pos: new_caret_pos,
|
||||
})
|
||||
if old_caret_pos <= old_selection.start_pos {
|
||||
if new_caret_pos == old_selection.end_pos {
|
||||
None
|
||||
} else {
|
||||
Some(RawSelection {
|
||||
start_pos: min(old_selection.end_pos, new_caret_pos),
|
||||
end_pos: max(old_selection.end_pos, new_caret_pos),
|
||||
})
|
||||
}
|
||||
} else {
|
||||
Some(RawSelection {
|
||||
start_pos: old_selection.start_pos,
|
||||
end_pos: new_caret_pos,
|
||||
})
|
||||
}
|
||||
} else if !(old_line_nr == line_nr && old_col_nr == col_nr) {
|
||||
Some(RawSelection {
|
||||
start_pos: min(old_caret_pos, new_caret_pos),
|
||||
@ -223,3 +292,53 @@ pub fn move_caret_down(
|
||||
|
||||
(new_caret_pos, new_selection_opt)
|
||||
}
|
||||
|
||||
pub fn update_text_state(ed_model: &mut EdModel, received_char: &char) {
|
||||
ed_model.selection_opt = None;
|
||||
|
||||
match received_char {
|
||||
'\u{8}' | '\u{7f}' => {
|
||||
// In Linux, we get a '\u{8}' when you press backspace,
|
||||
// but in macOS we get '\u{7f}'.
|
||||
if let Some(last_line) = ed_model.lines.last_mut() {
|
||||
if !last_line.is_empty() {
|
||||
last_line.pop();
|
||||
} else if ed_model.lines.len() > 1 {
|
||||
ed_model.lines.pop();
|
||||
}
|
||||
ed_model.caret_pos =
|
||||
move_caret_left(ed_model.caret_pos, None, false, &ed_model.lines).0;
|
||||
}
|
||||
}
|
||||
'\u{e000}'..='\u{f8ff}' | '\u{f0000}'..='\u{ffffd}' | '\u{100000}'..='\u{10fffd}' => {
|
||||
// These are private use characters; ignore them.
|
||||
// See http://www.unicode.org/faq/private_use.html
|
||||
}
|
||||
ch if is_newline(ch) => {
|
||||
if let Some(last_line) = ed_model.lines.last_mut() {
|
||||
last_line.push(*received_char)
|
||||
}
|
||||
ed_model.lines.push(String::new());
|
||||
ed_model.caret_pos = Position {
|
||||
line: ed_model.caret_pos.line + 1,
|
||||
column: 0,
|
||||
};
|
||||
|
||||
ed_model.selection_opt = None;
|
||||
}
|
||||
_ => {
|
||||
let nr_lines = ed_model.lines.len();
|
||||
|
||||
if let Some(last_line) = ed_model.lines.last_mut() {
|
||||
last_line.push(*received_char);
|
||||
|
||||
ed_model.caret_pos = Position {
|
||||
line: nr_lines - 1,
|
||||
column: last_line.len(),
|
||||
};
|
||||
|
||||
ed_model.selection_opt = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
pub fn is_newline(char_ref: &char) -> bool {
|
||||
let newline_codes = vec!['\u{d}'];
|
||||
let newline_codes = vec!['\u{d}', '\n'];
|
||||
|
||||
newline_codes.contains(char_ref)
|
||||
}
|
||||
|
11
editor/tests/selection.pest
Normal file
11
editor/tests/selection.pest
Normal file
@ -0,0 +1,11 @@
|
||||
text = { (ASCII_ALPHANUMERIC | " " | "\t" | "\n")* }
|
||||
|
||||
caret = {"|"}
|
||||
|
||||
optSelStart = { "["{0,1} }
|
||||
|
||||
optSelEnd = { "]"{0,1} }
|
||||
|
||||
optCaret = { caret{0,1} }
|
||||
|
||||
linesWithSelect = { SOI ~ text ~ optCaret ~ text ~ optSelStart ~ text ~ optCaret ~ text ~ optCaret ~ text ~ optSelEnd ~ text ~ optCaret ~ text ~ EOI}
|
@ -1,6 +1,6 @@
|
||||
app "closure" provides [ makeClosure ] to "./platform/"
|
||||
|
||||
makeClosure : ({} -> I64) as MyClosure
|
||||
makeClosure : ({} -> Int *) as MyClosure
|
||||
makeClosure =
|
||||
x = 42
|
||||
y = 42
|
||||
|
@ -5,7 +5,7 @@ ConsList a : [ Cons a (ConsList a), Nil ]
|
||||
empty : ConsList a
|
||||
empty = Nil
|
||||
|
||||
len : ConsList a -> I64
|
||||
len : ConsList a -> Int *
|
||||
len = \list ->
|
||||
when list is
|
||||
Cons _ rest -> 1 + len rest
|
||||
|
54
examples/effect/NQueens.roc
Normal file
54
examples/effect/NQueens.roc
Normal file
@ -0,0 +1,54 @@
|
||||
app "effect-example"
|
||||
packages { base: "thing/platform-dir" }
|
||||
imports [base.Task]
|
||||
provides [ main ] to base
|
||||
|
||||
main : Task.Task {} []
|
||||
main =
|
||||
queens 10
|
||||
|> Str.fromInt
|
||||
|> Task.putLine
|
||||
|
||||
ConsList a : [ Nil, Cons a (ConsList a) ]
|
||||
|
||||
queens = \n -> length (findSolutions n n)
|
||||
|
||||
|
||||
length : ConsList a -> I64
|
||||
length = \xs -> lengthHelp xs 0
|
||||
|
||||
lengthHelp : ConsList a, I64 -> I64
|
||||
lengthHelp = \xs, acc ->
|
||||
when xs is
|
||||
Nil -> acc
|
||||
Cons _ rest -> lengthHelp rest (1 + acc)
|
||||
|
||||
safe : I64, I64, ConsList I64 -> Bool
|
||||
safe = \queen, diagonal, xs ->
|
||||
when xs is
|
||||
Nil ->
|
||||
True
|
||||
|
||||
Cons q t ->
|
||||
queen != q && queen != q + diagonal && queen != q - diagonal && safe queen (diagonal + 1) t
|
||||
|
||||
appendSafe : I64, ConsList I64, ConsList (ConsList I64) -> ConsList (ConsList I64)
|
||||
appendSafe = \k, soln, solns ->
|
||||
if k <= 0 then
|
||||
solns
|
||||
else if safe k 1 soln then
|
||||
appendSafe (k - 1) soln (Cons (Cons k soln) solns)
|
||||
else
|
||||
appendSafe (k - 1) soln solns
|
||||
|
||||
extend = \n, acc, solns ->
|
||||
when solns is
|
||||
Nil -> acc
|
||||
Cons soln rest -> extend n (appendSafe n soln acc) rest
|
||||
|
||||
findSolutions = \n, k ->
|
||||
if k == 0 then
|
||||
Cons Nil Nil
|
||||
|
||||
else
|
||||
extend n Nil (findSolutions n (k - 1))
|
@ -21,11 +21,11 @@ singleton = \key, value ->
|
||||
Node Black key value Empty Empty
|
||||
|
||||
# {-| Determine the number of key-value pairs in the dictionary. -}
|
||||
size : Dict k v -> I64
|
||||
size : Dict k v -> Int *
|
||||
size = \dict ->
|
||||
sizeHelp 0 dict
|
||||
|
||||
sizeHelp : I64, Dict k v -> I64
|
||||
sizeHelp : Int *, Dict k v -> Int *
|
||||
sizeHelp = \n, dict ->
|
||||
when dict is
|
||||
Empty ->
|
||||
|
@ -52,7 +52,7 @@ pub fn roc_fx_getLine() -> RocStr {
|
||||
let stdin = io::stdin();
|
||||
let line1 = stdin.lock().lines().next().unwrap().unwrap();
|
||||
|
||||
RocStr::from_slice_with_capacity(line1.as_bytes(), line1.len())
|
||||
RocStr::from_slice(line1.as_bytes())
|
||||
}
|
||||
|
||||
unsafe fn call_the_closure(function_pointer: *const u8, closure_data_ptr: *const u8) -> i64 {
|
||||
|
@ -5,7 +5,7 @@ app "quicksort"
|
||||
|
||||
quicksort = \originalList ->
|
||||
|
||||
quicksortHelp : List (Num a), I64, I64 -> List (Num a)
|
||||
quicksortHelp : List (Num a), Nat, Nat -> List (Num a)
|
||||
quicksortHelp = \list, low, high ->
|
||||
if low < high then
|
||||
when partition low high list is
|
||||
@ -17,7 +17,7 @@ quicksort = \originalList ->
|
||||
list
|
||||
|
||||
|
||||
partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partition : Nat, Nat, List (Num a) -> [ Pair Nat (List (Num a)) ]
|
||||
partition = \low, high, initialList ->
|
||||
when List.get initialList high is
|
||||
Ok pivot ->
|
||||
@ -28,7 +28,7 @@ quicksort = \originalList ->
|
||||
Err _ ->
|
||||
Pair (low - 1) initialList
|
||||
|
||||
partitionHelp : I64, I64, List (Num a), I64, (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partitionHelp : Nat, Nat, List (Num c), Nat, (Num c) -> [ Pair Nat (List (Num c)) ]
|
||||
partitionHelp = \i, j, list, high, pivot ->
|
||||
if j < high then
|
||||
when List.get list j is
|
||||
@ -44,7 +44,7 @@ quicksort = \originalList ->
|
||||
Pair i list
|
||||
|
||||
|
||||
swap : I64, I64, List a -> List a
|
||||
swap : Nat, Nat, List a -> List a
|
||||
swap = \i, j, list ->
|
||||
when Pair (List.get list i) (List.get list j) is
|
||||
Pair (Ok atI) (Ok atJ) ->
|
||||
|
@ -1,12 +1,12 @@
|
||||
app "quicksort" packages { base: "./platform" } provides [ quicksort ] to base
|
||||
|
||||
quicksort : List I64 -> List I64
|
||||
quicksort : List Int * -> List Int *
|
||||
quicksort = \originalList -> helper originalList
|
||||
|
||||
helper : List I64 -> List I64
|
||||
helper : List Int * -> List Int *
|
||||
helper = \originalList ->
|
||||
|
||||
quicksortHelp : List (Num a), I64, I64 -> List (Num a)
|
||||
quicksortHelp : List (Num a), Nat, Nat -> List (Num a)
|
||||
quicksortHelp = \list, low, high ->
|
||||
if low < high then
|
||||
when partition low high list is
|
||||
@ -18,7 +18,7 @@ helper = \originalList ->
|
||||
list
|
||||
|
||||
|
||||
swap : I64, I64, List a -> List a
|
||||
swap : Nat, Nat, List a -> List a
|
||||
swap = \i, j, list ->
|
||||
when Pair (List.get list i) (List.get list j) is
|
||||
Pair (Ok atI) (Ok atJ) ->
|
||||
@ -29,7 +29,7 @@ helper = \originalList ->
|
||||
_ ->
|
||||
[]
|
||||
|
||||
partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partition : Nat, Nat, List (Num a) -> [ Pair Nat (List (Num a)) ]
|
||||
partition = \low, high, initialList ->
|
||||
when List.get initialList high is
|
||||
Ok pivot ->
|
||||
@ -41,7 +41,7 @@ helper = \originalList ->
|
||||
Pair (low - 1) initialList
|
||||
|
||||
|
||||
partitionHelp : I64, I64, List (Num a), I64, (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partitionHelp : Nat, Nat, List (Num a), Nat, (Num a) -> [ Pair Nat (List (Num a)) ]
|
||||
partitionHelp = \i, j, list, high, pivot ->
|
||||
if j < high then
|
||||
when List.get list j is
|
||||
|
75
nix/zig.nix
75
nix/zig.nix
@ -1,42 +1,37 @@
|
||||
{ pkgs, isMacOS, isAarch64 }:
|
||||
|
||||
# We require at least specific commit of Zig after the latest tagged
|
||||
# release (0.6.0), so we just download the binaries for that commit
|
||||
|
||||
let
|
||||
version = "0.6.0+0088efc4b";
|
||||
osName =
|
||||
if isMacOS
|
||||
then "macos"
|
||||
else "linux";
|
||||
archName =
|
||||
if isAarch64
|
||||
then "aarch64"
|
||||
else "x86_64";
|
||||
archiveName = "zig-${osName}-${archName}-${version}";
|
||||
sha256 =
|
||||
if isMacOS
|
||||
then "665c1a7f472cfc5e0715f0ddf6ff8409fb749ac91cbbae68c443b4a37ebd058e"
|
||||
else if isAarch64
|
||||
then "116ms44vx4xz57m9z9lsgrxd1g22qp00m5qbmklky8xdd2jmj24w"
|
||||
else "bab70ae3bd0af538022bc3ef50d8f34fa8dceac39ba7d9e5d528eee7e6d5a1cf";
|
||||
in
|
||||
pkgs.stdenv.mkDerivation {
|
||||
pname = "zig";
|
||||
version = version;
|
||||
buildInputs = [ pkgs.gzip ];
|
||||
src = pkgs.fetchurl {
|
||||
inherit sha256;
|
||||
name = "${archiveName}.tar.xz";
|
||||
url = "https://ziglang.org/builds/${archiveName}.tar.xz";
|
||||
};
|
||||
phases = [ "installPhase" ];
|
||||
installPhase = ''
|
||||
mkdir -p $out/bin
|
||||
tar -xf $src
|
||||
cp ${archiveName}/zig $out/zig
|
||||
cp -r ${archiveName}/lib $out/lib
|
||||
ln -s "$out/zig" "$out/bin/zig"
|
||||
chmod +x $out/bin/zig
|
||||
'';
|
||||
}
|
||||
if isMacOS then
|
||||
let
|
||||
version = "0.7.1";
|
||||
osName =
|
||||
if isMacOS
|
||||
then "macos"
|
||||
else "linux";
|
||||
archName =
|
||||
if isAarch64
|
||||
then "aarch64"
|
||||
else "x86_64";
|
||||
archiveName = "zig-${osName}-${archName}-${version}";
|
||||
sha256 = "845cb17562978af0cf67e3993f4e33330525eaf01ead9386df9105111e3bc519";
|
||||
in
|
||||
pkgs.stdenv.mkDerivation {
|
||||
pname = "zig";
|
||||
version = version;
|
||||
buildInputs = [ pkgs.gzip ];
|
||||
src = pkgs.fetchurl {
|
||||
inherit sha256;
|
||||
name = "${archiveName}.tar.xz";
|
||||
url = "https://ziglang.org/builds/${archiveName}.tar.xz";
|
||||
};
|
||||
phases = [ "installPhase" ];
|
||||
installPhase = ''
|
||||
mkdir -p $out/bin
|
||||
tar -xf $src
|
||||
cp ${archiveName}/zig $out/zig
|
||||
cp -r ${archiveName}/lib $out/lib
|
||||
ln -s "$out/zig" "$out/bin/zig"
|
||||
chmod +x $out/bin/zig
|
||||
'';
|
||||
}
|
||||
else
|
||||
pkgs.zig
|
||||
|
@ -99,26 +99,26 @@ impl<T> RocList<T> {
|
||||
self.get_storage_ptr() as *mut usize
|
||||
}
|
||||
|
||||
fn get_element_ptr<Q>(elements: *const Q) -> *const usize {
|
||||
fn get_element_ptr(elements: *const T) -> *const T {
|
||||
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)
|
||||
ptr.offset(1) as *const T
|
||||
} 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)
|
||||
ptr.offset(2) as *const T
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_slice_with_capacity(slice: &[T], capacity: usize) -> RocList<T>
|
||||
where
|
||||
T: Copy,
|
||||
T: Clone,
|
||||
{
|
||||
assert!(slice.len() <= capacity);
|
||||
|
||||
@ -138,25 +138,37 @@ impl<T> RocList<T> {
|
||||
let num_bytes = core::mem::size_of::<usize>() + padding + element_bytes;
|
||||
|
||||
let elements = unsafe {
|
||||
let raw_ptr = libc::malloc(num_bytes);
|
||||
let raw_ptr = libc::malloc(num_bytes) as *mut u8;
|
||||
|
||||
// pointer to the first element
|
||||
let raw_ptr = Self::get_element_ptr(raw_ptr as *mut T) as *mut T;
|
||||
|
||||
// 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);
|
||||
*(capacity_ptr.offset(-1)) = capacity;
|
||||
|
||||
{
|
||||
// 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);
|
||||
for index in 0..slice.len() {
|
||||
let source = &*source_ptr.add(index);
|
||||
let target = &mut *target_ptr.add(index);
|
||||
|
||||
// NOTE for a weird reason, it's important that we clone onto the stack
|
||||
// and explicitly forget the swapped-in value
|
||||
// cloning directly from source to target causes some garbage memory (cast to a
|
||||
// RocStr) to end up in the drop implementation of RocStr and cause havoc by
|
||||
// freeing NULL
|
||||
let mut temporary = source.clone();
|
||||
|
||||
core::mem::swap(target, &mut temporary);
|
||||
|
||||
core::mem::forget(temporary);
|
||||
}
|
||||
}
|
||||
|
||||
raw_ptr as *mut T
|
||||
raw_ptr
|
||||
};
|
||||
|
||||
RocList {
|
||||
@ -167,7 +179,7 @@ impl<T> RocList<T> {
|
||||
|
||||
pub fn from_slice(slice: &[T]) -> RocList<T>
|
||||
where
|
||||
T: Copy,
|
||||
T: Clone,
|
||||
{
|
||||
Self::from_slice_with_capacity(slice, slice.len())
|
||||
}
|
||||
@ -328,21 +340,27 @@ impl RocStr {
|
||||
(self as *mut RocStr).cast()
|
||||
}
|
||||
|
||||
pub fn from_slice_with_capacity(slice: &[u8], capacity: usize) -> RocStr {
|
||||
assert!(slice.len() <= capacity);
|
||||
fn from_slice_with_capacity_str(slice: &[u8], capacity: usize) -> RocStr {
|
||||
assert!(
|
||||
slice.len() <= capacity,
|
||||
"RocStr::from_slice_with_capacity_str length bigger than capacity {} {}",
|
||||
slice.len(),
|
||||
capacity
|
||||
);
|
||||
if capacity < core::mem::size_of::<RocStr>() {
|
||||
let mut rocstr = RocStr::empty();
|
||||
let target_ptr = rocstr.get_small_str_ptr_mut();
|
||||
let source_ptr = slice.as_ptr() as *const u8;
|
||||
for index in 0..(slice.len() as isize) {
|
||||
for index in 0..slice.len() {
|
||||
unsafe {
|
||||
*target_ptr.offset(index) = *source_ptr.offset(index);
|
||||
*target_ptr.add(index) = *source_ptr.add(index);
|
||||
}
|
||||
}
|
||||
// Write length and small string bit to last byte of length.
|
||||
let mut bytes = rocstr.length.to_ne_bytes();
|
||||
bytes[bytes.len() - 1] = capacity as u8 ^ 0b1000_0000;
|
||||
rocstr.length = usize::from_ne_bytes(bytes);
|
||||
|
||||
rocstr
|
||||
} else {
|
||||
let ptr = slice.as_ptr();
|
||||
@ -380,7 +398,7 @@ impl RocStr {
|
||||
}
|
||||
|
||||
pub fn from_slice(slice: &[u8]) -> RocStr {
|
||||
Self::from_slice_with_capacity(slice, slice.len())
|
||||
Self::from_slice_with_capacity_str(slice, slice.len())
|
||||
}
|
||||
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
@ -423,6 +441,38 @@ impl PartialEq for RocStr {
|
||||
|
||||
impl Eq for RocStr {}
|
||||
|
||||
impl Clone for RocStr {
|
||||
fn clone(&self) -> Self {
|
||||
if self.is_small_str() || self.is_empty() {
|
||||
Self {
|
||||
elements: self.elements,
|
||||
length: self.length,
|
||||
}
|
||||
} else {
|
||||
let capacity_size = core::mem::size_of::<usize>();
|
||||
let copy_length = self.length + capacity_size;
|
||||
let elements = unsafe {
|
||||
let raw = libc::malloc(copy_length);
|
||||
|
||||
libc::memcpy(
|
||||
raw,
|
||||
self.elements.offset(-(capacity_size as isize)) as *mut libc::c_void,
|
||||
copy_length,
|
||||
);
|
||||
|
||||
*(raw as *mut usize) = self.length;
|
||||
|
||||
(raw as *mut u8).add(capacity_size)
|
||||
};
|
||||
|
||||
Self {
|
||||
elements,
|
||||
length: self.length,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for RocStr {
|
||||
fn drop(&mut self) {
|
||||
if !self.is_small_str() {
|
||||
|
@ -8,10 +8,10 @@ in with {
|
||||
# Look here for information about how pin version of nixpkgs
|
||||
# → https://nixos.wiki/wiki/FAQ/Pinning_Nixpkgs
|
||||
pkgs = import (builtins.fetchGit {
|
||||
name = "nixpkgs-2020-11-24";
|
||||
name = "nixpkgs-2021-01-05";
|
||||
url = "https://github.com/nixos/nixpkgs/";
|
||||
ref = "refs/heads/nixpkgs-unstable";
|
||||
rev = "6625284c397b44bc9518a5a1567c1b5aae455c08";
|
||||
rev = "f53c431645da8e6760268092aa10f736b5cfb117";
|
||||
}) { };
|
||||
|
||||
isMacOS = currentOS == "darwin";
|
||||
|
Loading…
Reference in New Issue
Block a user