mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-01 15:51:12 +00:00
Merge remote-tracking branch 'origin/trunk' into editor-ast-module
This commit is contained in:
commit
264b6884f0
43 changed files with 1481 additions and 439 deletions
50
.github/workflows/ci.yml
vendored
50
.github/workflows/ci.yml
vendored
|
@ -2,6 +2,14 @@ on: [pull_request]
|
||||||
|
|
||||||
name: CI
|
name: CI
|
||||||
|
|
||||||
|
env:
|
||||||
|
RUSTC_WRAPPER: /usr/local/bin/sccache
|
||||||
|
SCCACHE_BUCKET: ${{ secrets.SCCACHE_BUCKET }}
|
||||||
|
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
|
SCCACHE_S3_USE_SSL: ${{ secrets.SCCACHE_S3_USE_SSL }}
|
||||||
|
SCCACHE_REGION: ${{ secrets.SCCACHE_REGION }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
name: fmt, clippy, test, test --release
|
name: fmt, clippy, test, test --release
|
||||||
|
@ -10,9 +18,23 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Log CPU model
|
||||||
|
run: sudo cat /proc/cpuinfo | grep name | uniq
|
||||||
|
|
||||||
- name: Install CI Libraries
|
- name: Install CI Libraries
|
||||||
run: sudo ./ci/install-ci-libraries.sh 10
|
run: sudo ./ci/install-ci-libraries.sh 10
|
||||||
|
|
||||||
|
- name: scope sccache to CPU model
|
||||||
|
run: |
|
||||||
|
export SCCACHE_S3_KEY_PREFIX=`sudo cat /proc/cpuinfo | grep name | uniq | awk -F: {'print $2'} | awk -F@ {'print$1'} | xargs | sed -e 's/\s/_/g'`
|
||||||
|
echo "SCCACHE_S3_KEY_PREFIX=$SCCACHE_S3_KEY_PREFIX" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: print SCCACHE_S3_KEY_PREFIX
|
||||||
|
run: echo $SCCACHE_S3_KEY_PREFIX
|
||||||
|
|
||||||
|
- name: sccache version
|
||||||
|
run: /usr/local/bin/sccache -V
|
||||||
|
|
||||||
- name: Run Zig tests
|
- name: Run Zig tests
|
||||||
run: pushd compiler/builtins/bitcode; ./run-tests.sh; popd;
|
run: pushd compiler/builtins/bitcode; ./run-tests.sh; popd;
|
||||||
|
|
||||||
|
@ -28,24 +50,6 @@ jobs:
|
||||||
|
|
||||||
- run: rustup component add rustfmt
|
- run: rustup component add rustfmt
|
||||||
|
|
||||||
- name: Cache cargo registry
|
|
||||||
uses: actions/cache@v1
|
|
||||||
with:
|
|
||||||
path: ~/.cargo/registry
|
|
||||||
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
|
|
||||||
|
|
||||||
- name: Cache cargo index
|
|
||||||
uses: actions/cache@v1
|
|
||||||
with:
|
|
||||||
path: ~/.cargo/git
|
|
||||||
key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}
|
|
||||||
|
|
||||||
- name: Cache compiled valgrind
|
|
||||||
uses: actions/cache@v1
|
|
||||||
with:
|
|
||||||
path: ~/valgrind-3.6.1/
|
|
||||||
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
|
|
||||||
|
|
||||||
- uses: actions-rs/cargo@v1
|
- uses: actions-rs/cargo@v1
|
||||||
name: rustfmt version
|
name: rustfmt version
|
||||||
with:
|
with:
|
||||||
|
@ -72,15 +76,11 @@ jobs:
|
||||||
command: clippy
|
command: clippy
|
||||||
args: -- -D warnings
|
args: -- -D warnings
|
||||||
|
|
||||||
- uses: actions-rs/cargo@v1
|
|
||||||
name: cargo test -- --ignored
|
|
||||||
continue-on-error: true
|
|
||||||
with:
|
|
||||||
command: test
|
|
||||||
args: -- --ignored
|
|
||||||
|
|
||||||
- uses: actions-rs/cargo@v1
|
- uses: actions-rs/cargo@v1
|
||||||
name: cargo test --release
|
name: cargo test --release
|
||||||
with:
|
with:
|
||||||
command: test
|
command: test
|
||||||
args: --release
|
args: --release
|
||||||
|
|
||||||
|
- name: sccache stats
|
||||||
|
run: /usr/local/bin/sccache --show-stats
|
||||||
|
|
|
@ -79,3 +79,8 @@ valgrind --version
|
||||||
wget -c https://ziglang.org/builds/zig-linux-x86_64-0.6.0+0088efc4b.tar.xz --no-check-certificate
|
wget -c https://ziglang.org/builds/zig-linux-x86_64-0.6.0+0088efc4b.tar.xz --no-check-certificate
|
||||||
tar -xf zig-linux-x86_64-0.6.0+0088efc4b.tar.xz
|
tar -xf zig-linux-x86_64-0.6.0+0088efc4b.tar.xz
|
||||||
ln -s "$PWD/zig-linux-x86_64-0.6.0+0088efc4b/zig" /usr/local/bin/zig
|
ln -s "$PWD/zig-linux-x86_64-0.6.0+0088efc4b/zig" /usr/local/bin/zig
|
||||||
|
|
||||||
|
# test sccache
|
||||||
|
./ci/sccache -V
|
||||||
|
# copy sccache to prevent current working dir problems
|
||||||
|
cp ./ci/sccache /usr/local/bin/sccache
|
||||||
|
|
BIN
ci/sccache
Executable file
BIN
ci/sccache
Executable file
Binary file not shown.
|
@ -20,6 +20,10 @@ struct Env<'a, 'env> {
|
||||||
home: ModuleId,
|
home: ModuleId,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum ToAstProblem {
|
||||||
|
FunctionLayout,
|
||||||
|
}
|
||||||
|
|
||||||
/// JIT execute the given main function, and then wrap its results in an Expr
|
/// JIT execute the given main function, and then wrap its results in an Expr
|
||||||
/// so we can display them to the user using the formatter.
|
/// so we can display them to the user using the formatter.
|
||||||
///
|
///
|
||||||
|
@ -39,7 +43,7 @@ pub unsafe fn jit_to_ast<'a>(
|
||||||
home: ModuleId,
|
home: ModuleId,
|
||||||
subs: &Subs,
|
subs: &Subs,
|
||||||
ptr_bytes: u32,
|
ptr_bytes: u32,
|
||||||
) -> Expr<'a> {
|
) -> Result<Expr<'a>, ToAstProblem> {
|
||||||
let env = Env {
|
let env = Env {
|
||||||
arena,
|
arena,
|
||||||
subs,
|
subs,
|
||||||
|
@ -57,55 +61,57 @@ fn jit_to_ast_help<'a>(
|
||||||
main_fn_name: &str,
|
main_fn_name: &str,
|
||||||
layout: &Layout<'a>,
|
layout: &Layout<'a>,
|
||||||
content: &Content,
|
content: &Content,
|
||||||
) -> Expr<'a> {
|
) -> Result<Expr<'a>, ToAstProblem> {
|
||||||
match layout {
|
match layout {
|
||||||
Layout::Builtin(Builtin::Int1) => {
|
Layout::Builtin(Builtin::Int1) => Ok(run_jit_function!(lib, main_fn_name, bool, |num| {
|
||||||
run_jit_function!(lib, main_fn_name, bool, |num| bool_to_ast(
|
bool_to_ast(env, num, content)
|
||||||
env, num, content
|
})),
|
||||||
))
|
|
||||||
}
|
|
||||||
Layout::Builtin(Builtin::Int8) => {
|
Layout::Builtin(Builtin::Int8) => {
|
||||||
// NOTE: this is does not handle 8-bit numbers yet
|
Ok(
|
||||||
run_jit_function!(lib, main_fn_name, u8, |num| byte_to_ast(env, num, content))
|
// NOTE: this is does not handle 8-bit numbers yet
|
||||||
|
run_jit_function!(lib, main_fn_name, u8, |num| byte_to_ast(env, num, content)),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Layout::Builtin(Builtin::Int64) => {
|
Layout::Builtin(Builtin::Int64) => {
|
||||||
run_jit_function!(lib, main_fn_name, i64, |num| num_to_ast(
|
Ok(run_jit_function!(lib, main_fn_name, i64, |num| num_to_ast(
|
||||||
env,
|
env,
|
||||||
i64_to_ast(env.arena, num),
|
i64_to_ast(env.arena, num),
|
||||||
content
|
content
|
||||||
))
|
)))
|
||||||
}
|
}
|
||||||
Layout::Builtin(Builtin::Float64) => {
|
Layout::Builtin(Builtin::Float64) => {
|
||||||
run_jit_function!(lib, main_fn_name, f64, |num| num_to_ast(
|
Ok(run_jit_function!(lib, main_fn_name, f64, |num| num_to_ast(
|
||||||
env,
|
env,
|
||||||
f64_to_ast(env.arena, num),
|
f64_to_ast(env.arena, num),
|
||||||
content
|
content
|
||||||
))
|
)))
|
||||||
}
|
}
|
||||||
Layout::Builtin(Builtin::Str) | Layout::Builtin(Builtin::EmptyStr) => {
|
Layout::Builtin(Builtin::Str) | Layout::Builtin(Builtin::EmptyStr) => Ok(
|
||||||
run_jit_function!(lib, main_fn_name, &'static str, |string: &'static str| {
|
run_jit_function!(lib, main_fn_name, &'static str, |string: &'static str| {
|
||||||
str_to_ast(env.arena, env.arena.alloc(string))
|
str_to_ast(env.arena, env.arena.alloc(string))
|
||||||
})
|
}),
|
||||||
}
|
),
|
||||||
Layout::Builtin(Builtin::EmptyList) => {
|
Layout::Builtin(Builtin::EmptyList) => {
|
||||||
run_jit_function!(lib, main_fn_name, &'static str, |_| { Expr::List(&[]) })
|
Ok(run_jit_function!(lib, main_fn_name, &'static str, |_| {
|
||||||
|
Expr::List(&[])
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
Layout::Builtin(Builtin::List(_, elem_layout)) => run_jit_function!(
|
Layout::Builtin(Builtin::List(_, elem_layout)) => Ok(run_jit_function!(
|
||||||
lib,
|
lib,
|
||||||
main_fn_name,
|
main_fn_name,
|
||||||
(*const u8, usize),
|
(*const u8, usize),
|
||||||
|(ptr, len): (*const u8, usize)| { list_to_ast(env, ptr, len, elem_layout, content) }
|
|(ptr, len): (*const u8, usize)| { list_to_ast(env, ptr, len, elem_layout, content) }
|
||||||
),
|
)),
|
||||||
Layout::Builtin(other) => {
|
Layout::Builtin(other) => {
|
||||||
todo!("add support for rendering builtin {:?} to the REPL", other)
|
todo!("add support for rendering builtin {:?} to the REPL", other)
|
||||||
}
|
}
|
||||||
Layout::PhantomEmptyStruct => run_jit_function!(lib, main_fn_name, &u8, |_| {
|
Layout::PhantomEmptyStruct => Ok(run_jit_function!(lib, main_fn_name, &u8, |_| {
|
||||||
Expr::Record {
|
Expr::Record {
|
||||||
update: None,
|
update: None,
|
||||||
fields: &[],
|
fields: &[],
|
||||||
final_comments: env.arena.alloc([]),
|
final_comments: env.arena.alloc([]),
|
||||||
}
|
}
|
||||||
}),
|
})),
|
||||||
Layout::Struct(field_layouts) => {
|
Layout::Struct(field_layouts) => {
|
||||||
let ptr_to_ast = |ptr: *const u8| match content {
|
let ptr_to_ast = |ptr: *const u8| match content {
|
||||||
Content::Structure(FlatType::Record(fields, _)) => {
|
Content::Structure(FlatType::Record(fields, _)) => {
|
||||||
|
@ -134,12 +140,12 @@ fn jit_to_ast_help<'a>(
|
||||||
|
|
||||||
let result_stack_size = layout.stack_size(env.ptr_bytes);
|
let result_stack_size = layout.stack_size(env.ptr_bytes);
|
||||||
|
|
||||||
run_jit_function_dynamic_type!(
|
Ok(run_jit_function_dynamic_type!(
|
||||||
lib,
|
lib,
|
||||||
main_fn_name,
|
main_fn_name,
|
||||||
result_stack_size as usize,
|
result_stack_size as usize,
|
||||||
|bytes: *const u8| { ptr_to_ast(bytes as *const u8) }
|
|bytes: *const u8| { ptr_to_ast(bytes as *const u8) }
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
Layout::Union(union_layouts) => match content {
|
Layout::Union(union_layouts) => match content {
|
||||||
Content::Structure(FlatType::TagUnion(tags, _)) => {
|
Content::Structure(FlatType::TagUnion(tags, _)) => {
|
||||||
|
@ -153,7 +159,7 @@ fn jit_to_ast_help<'a>(
|
||||||
let size = layout.stack_size(env.ptr_bytes);
|
let size = layout.stack_size(env.ptr_bytes);
|
||||||
match union_variant {
|
match union_variant {
|
||||||
UnionVariant::Wrapped(tags_and_layouts) => {
|
UnionVariant::Wrapped(tags_and_layouts) => {
|
||||||
run_jit_function_dynamic_type!(
|
Ok(run_jit_function_dynamic_type!(
|
||||||
lib,
|
lib,
|
||||||
main_fn_name,
|
main_fn_name,
|
||||||
size as usize,
|
size as usize,
|
||||||
|
@ -181,7 +187,7 @@ fn jit_to_ast_help<'a>(
|
||||||
|
|
||||||
Expr::Apply(loc_tag_expr, output, CalledVia::Space)
|
Expr::Apply(loc_tag_expr, output, CalledVia::Space)
|
||||||
}
|
}
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
_ => unreachable!("any other variant would have a different layout"),
|
_ => unreachable!("any other variant would have a different layout"),
|
||||||
}
|
}
|
||||||
|
@ -201,7 +207,7 @@ fn jit_to_ast_help<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
Layout::FunctionPointer(_, _) | Layout::Closure(_, _, _) => {
|
Layout::FunctionPointer(_, _) | Layout::Closure(_, _, _) => {
|
||||||
todo!("add support for rendering functions in the REPL")
|
Err(ToAstProblem::FunctionLayout)
|
||||||
}
|
}
|
||||||
Layout::Pointer(_) => todo!("add support for rendering pointers in the REPL"),
|
Layout::Pointer(_) => todo!("add support for rendering pointers in the REPL"),
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,11 +131,15 @@ pub fn gen_and_eval(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<R
|
||||||
let content = subs.get(main_fn_var).content;
|
let content = subs.get(main_fn_var).content;
|
||||||
let expr_type_str = content_to_string(content.clone(), &subs, home, &interns);
|
let expr_type_str = content_to_string(content.clone(), &subs, home, &interns);
|
||||||
|
|
||||||
let (_, main_fn_layout) = procedures
|
let (_, main_fn_layout) = match procedures.keys().find(|(s, _)| *s == main_fn_symbol) {
|
||||||
.keys()
|
Some(layout) => layout.clone(),
|
||||||
.find(|(s, _)| *s == main_fn_symbol)
|
None => {
|
||||||
.unwrap()
|
return Ok(ReplOutput::NoProblems {
|
||||||
.clone();
|
expr: "<function>".to_string(),
|
||||||
|
expr_type: expr_type_str,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let ptr_bytes = target.pointer_width().unwrap().bytes() as u32;
|
let ptr_bytes = target.pointer_width().unwrap().bytes() as u32;
|
||||||
|
|
||||||
|
@ -249,7 +253,7 @@ pub fn gen_and_eval(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<R
|
||||||
|
|
||||||
let lib = module_to_dylib(&env.module, &target, opt_level)
|
let lib = module_to_dylib(&env.module, &target, opt_level)
|
||||||
.expect("Error loading compiled dylib for test");
|
.expect("Error loading compiled dylib for test");
|
||||||
let answer = unsafe {
|
let res_answer = unsafe {
|
||||||
eval::jit_to_ast(
|
eval::jit_to_ast(
|
||||||
&arena,
|
&arena,
|
||||||
lib,
|
lib,
|
||||||
|
@ -264,7 +268,15 @@ pub fn gen_and_eval(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<R
|
||||||
};
|
};
|
||||||
let mut expr = bumpalo::collections::String::new_in(&arena);
|
let mut expr = bumpalo::collections::String::new_in(&arena);
|
||||||
|
|
||||||
answer.format_with_options(&mut expr, Parens::NotNeeded, Newlines::Yes, 0);
|
use eval::ToAstProblem::*;
|
||||||
|
match res_answer {
|
||||||
|
Ok(answer) => {
|
||||||
|
answer.format_with_options(&mut expr, Parens::NotNeeded, Newlines::Yes, 0);
|
||||||
|
}
|
||||||
|
Err(FunctionLayout) => {
|
||||||
|
expr.push_str("<function>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(ReplOutput::NoProblems {
|
Ok(ReplOutput::NoProblems {
|
||||||
expr: expr.into_bump_str().to_string(),
|
expr: expr.into_bump_str().to_string(),
|
||||||
|
|
|
@ -270,6 +270,41 @@ mod repl_eval {
|
||||||
expect_success("Num.subWrap Num.minInt 1", "9223372036854775807 : I64");
|
expect_success("Num.subWrap Num.minInt 1", "9223372036854775807 : I64");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn num_mul_wrap() {
|
||||||
|
expect_success("Num.mulWrap Num.maxInt 2", "-2 : I64");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn num_add_checked() {
|
||||||
|
expect_success("Num.addChecked 1 1", "Ok 2 : Result (Num *) [ Overflow ]*");
|
||||||
|
expect_success(
|
||||||
|
"Num.addChecked Num.maxInt 1",
|
||||||
|
"Err (Overflow) : Result I64 [ Overflow ]*",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn num_sub_checked() {
|
||||||
|
expect_success("Num.subChecked 1 1", "Ok 0 : Result (Num *) [ Overflow ]*");
|
||||||
|
expect_success(
|
||||||
|
"Num.subChecked Num.minInt 1",
|
||||||
|
"Err (Overflow) : Result I64 [ Overflow ]*",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn num_mul_checked() {
|
||||||
|
expect_success(
|
||||||
|
"Num.mulChecked 20 2",
|
||||||
|
"Ok 40 : Result (Num *) [ Overflow ]*",
|
||||||
|
);
|
||||||
|
expect_success(
|
||||||
|
"Num.mulChecked Num.maxInt 2",
|
||||||
|
"Err (Overflow) : Result I64 [ Overflow ]*",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn list_concat() {
|
fn list_concat() {
|
||||||
expect_success(
|
expect_success(
|
||||||
|
@ -292,13 +327,16 @@ mod repl_eval {
|
||||||
expect_success("List.sum [ 1.1, 2.2, 3.3 ]", "6.6 : F64");
|
expect_success("List.sum [ 1.1, 2.2, 3.3 ]", "6.6 : F64");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO add test cases for empty lists once error messages in the repl are correct
|
|
||||||
#[test]
|
#[test]
|
||||||
fn list_first() {
|
fn list_first() {
|
||||||
expect_success(
|
expect_success(
|
||||||
"List.first [ 12, 9, 6, 3 ]",
|
"List.first [ 12, 9, 6, 3 ]",
|
||||||
"Ok 12 : Result (Num *) [ ListWasEmpty ]*",
|
"Ok 12 : Result (Num *) [ ListWasEmpty ]*",
|
||||||
);
|
);
|
||||||
|
expect_success(
|
||||||
|
"List.first []",
|
||||||
|
"Err (ListWasEmpty) : Result * [ ListWasEmpty ]*",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -307,6 +345,11 @@ mod repl_eval {
|
||||||
"List.last [ 12, 9, 6, 3 ]",
|
"List.last [ 12, 9, 6, 3 ]",
|
||||||
"Ok 3 : Result (Num *) [ ListWasEmpty ]*",
|
"Ok 3 : Result (Num *) [ ListWasEmpty ]*",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
expect_success(
|
||||||
|
"List.last []",
|
||||||
|
"Err (ListWasEmpty) : Result * [ ListWasEmpty ]*",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -427,6 +470,40 @@ mod repl_eval {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn identity_lambda() {
|
||||||
|
// Even though this gets unwrapped at runtime, the repl should still
|
||||||
|
// report it as a record
|
||||||
|
expect_success("\\x -> x", "<function> : a -> a");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn stdlib_function() {
|
||||||
|
// Even though this gets unwrapped at runtime, the repl should still
|
||||||
|
// report it as a record
|
||||||
|
expect_success("Num.abs", "<function> : Num a -> Num a");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn too_few_args() {
|
||||||
|
expect_failure(
|
||||||
|
"Num.add 2",
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
── TOO FEW ARGS ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
The add function expects 2 arguments, but it got only 1:
|
||||||
|
|
||||||
|
4│ Num.add 2
|
||||||
|
^^^^^^^
|
||||||
|
|
||||||
|
Roc does not allow functions to be partially applied. Use a closure to
|
||||||
|
make partial application explicit.
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn type_problem() {
|
fn type_problem() {
|
||||||
expect_failure(
|
expect_failure(
|
||||||
|
|
|
@ -75,17 +75,19 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
// addChecked : Num a, Num a -> Result (Num a) [ Overflow ]*
|
fn overflow() -> SolvedType {
|
||||||
let overflow = SolvedType::TagUnion(
|
SolvedType::TagUnion(
|
||||||
vec![(TagName::Global("Overflow".into()), vec![])],
|
vec![(TagName::Global("Overflow".into()), vec![])],
|
||||||
Box::new(SolvedType::Wildcard),
|
Box::new(SolvedType::Wildcard),
|
||||||
);
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// addChecked : Num a, Num a -> Result (Num a) [ Overflow ]*
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::NUM_ADD_CHECKED,
|
Symbol::NUM_ADD_CHECKED,
|
||||||
top_level_function(
|
top_level_function(
|
||||||
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
|
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
|
||||||
Box::new(result_type(num_type(flex(TVAR1)), overflow.clone())),
|
Box::new(result_type(num_type(flex(TVAR1)), overflow())),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -115,7 +117,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
Symbol::NUM_SUB_CHECKED,
|
Symbol::NUM_SUB_CHECKED,
|
||||||
top_level_function(
|
top_level_function(
|
||||||
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
|
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
|
||||||
Box::new(result_type(num_type(flex(TVAR1)), overflow)),
|
Box::new(result_type(num_type(flex(TVAR1)), overflow())),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -128,6 +130,21 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// mulWrap : Int, Int -> Int
|
||||||
|
add_type(
|
||||||
|
Symbol::NUM_MUL_WRAP,
|
||||||
|
top_level_function(vec![int_type(), int_type()], Box::new(int_type())),
|
||||||
|
);
|
||||||
|
|
||||||
|
// mulChecked : Num a, Num a -> Result (Num a) [ Overflow ]*
|
||||||
|
add_type(
|
||||||
|
Symbol::NUM_MUL_CHECKED,
|
||||||
|
top_level_function(
|
||||||
|
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
|
||||||
|
Box::new(result_type(num_type(flex(TVAR1)), overflow())),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
// abs : Num a -> Num a
|
// abs : Num a -> Num a
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::NUM_ABS,
|
Symbol::NUM_ABS,
|
||||||
|
|
|
@ -197,6 +197,21 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
unique_function(vec![num_type(u, num), num_type(v, num)], num_type(w, num))
|
unique_function(vec![num_type(u, num), num_type(v, num)], num_type(w, num))
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 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))
|
||||||
|
});
|
||||||
|
|
||||||
|
// mulChecked : Num a, Num a -> Result (Num a) [ Overflow ]*
|
||||||
|
add_type(Symbol::NUM_MUL_CHECKED, {
|
||||||
|
let_tvars! { u, v, w, num, result, star };
|
||||||
|
unique_function(
|
||||||
|
vec![num_type(u, num), num_type(v, num)],
|
||||||
|
result_type(result, num_type(w, num), lift(star, overflow())),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
// abs : Num a -> Num a
|
// abs : Num a -> Num a
|
||||||
add_type(Symbol::NUM_ABS, {
|
add_type(Symbol::NUM_ABS, {
|
||||||
let_tvars! { u, v, num };
|
let_tvars! { u, v, num };
|
||||||
|
@ -1227,36 +1242,34 @@ fn lift(u: VarId, a: SolvedType) -> SolvedType {
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn float_type(u: VarId) -> SolvedType {
|
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 attr_fb = lift(u, fp);
|
||||||
|
let num = builtin_aliases::num_type(attr_fb);
|
||||||
|
|
||||||
SolvedType::Apply(
|
SolvedType::Apply(
|
||||||
Symbol::ATTR_ATTR,
|
Symbol::ATTR_ATTR,
|
||||||
vec![
|
vec![
|
||||||
flex(u),
|
flex(u),
|
||||||
SolvedType::Alias(
|
SolvedType::Alias(Symbol::NUM_F64, Vec::new(), Box::new(num)),
|
||||||
Symbol::NUM_F64,
|
|
||||||
Vec::new(),
|
|
||||||
Box::new(builtin_aliases::num_type(SolvedType::Apply(
|
|
||||||
Symbol::ATTR_ATTR,
|
|
||||||
vec![flex(u), builtin_aliases::floatingpoint_type()],
|
|
||||||
))),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn int_type(u: VarId) -> SolvedType {
|
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);
|
||||||
|
let attr_fb = lift(u, integer);
|
||||||
|
let num = builtin_aliases::num_type(attr_fb);
|
||||||
|
|
||||||
SolvedType::Apply(
|
SolvedType::Apply(
|
||||||
Symbol::ATTR_ATTR,
|
Symbol::ATTR_ATTR,
|
||||||
vec![
|
vec![
|
||||||
flex(u),
|
flex(u),
|
||||||
SolvedType::Alias(
|
SolvedType::Alias(Symbol::NUM_I64, Vec::new(), Box::new(num)),
|
||||||
Symbol::NUM_I64,
|
|
||||||
Vec::new(),
|
|
||||||
Box::new(builtin_aliases::num_type(SolvedType::Apply(
|
|
||||||
Symbol::ATTR_ATTR,
|
|
||||||
vec![flex(u), builtin_aliases::integer_type()],
|
|
||||||
))),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,6 +85,8 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
|
||||||
NUM_SUB_WRAP => num_sub_wrap,
|
NUM_SUB_WRAP => num_sub_wrap,
|
||||||
NUM_SUB_CHECKED => num_sub_checked,
|
NUM_SUB_CHECKED => num_sub_checked,
|
||||||
NUM_MUL => num_mul,
|
NUM_MUL => num_mul,
|
||||||
|
NUM_MUL_WRAP => num_mul_wrap,
|
||||||
|
NUM_MUL_CHECKED => num_mul_checked,
|
||||||
NUM_GT => num_gt,
|
NUM_GT => num_gt,
|
||||||
NUM_GTE => num_gte,
|
NUM_GTE => num_gte,
|
||||||
NUM_LT => num_lt,
|
NUM_LT => num_lt,
|
||||||
|
@ -573,6 +575,100 @@ fn num_mul(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
num_binop(symbol, var_store, LowLevel::NumMul)
|
num_binop(symbol, var_store, LowLevel::NumMul)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Num.mulWrap : Int, Int -> Int
|
||||||
|
fn num_mul_wrap(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
|
num_binop(symbol, var_store, LowLevel::NumMulWrap)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Num.mulChecked : Num a, Num a -> Result (Num a) [ Overflow ]*
|
||||||
|
fn num_mul_checked(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
|
let bool_var = var_store.fresh();
|
||||||
|
let num_var_1 = var_store.fresh();
|
||||||
|
let num_var_2 = var_store.fresh();
|
||||||
|
let num_var_3 = var_store.fresh();
|
||||||
|
let ret_var = var_store.fresh();
|
||||||
|
let record_var = var_store.fresh();
|
||||||
|
|
||||||
|
// let arg_3 = RunLowLevel NumMulChecked arg_1 arg_2
|
||||||
|
//
|
||||||
|
// if arg_3.b then
|
||||||
|
// # overflow
|
||||||
|
// Err Overflow
|
||||||
|
// else
|
||||||
|
// # all is well
|
||||||
|
// Ok arg_3.a
|
||||||
|
|
||||||
|
let cont = If {
|
||||||
|
branch_var: ret_var,
|
||||||
|
cond_var: bool_var,
|
||||||
|
branches: vec![(
|
||||||
|
// if-condition
|
||||||
|
no_region(
|
||||||
|
// arg_3.b
|
||||||
|
Access {
|
||||||
|
record_var,
|
||||||
|
ext_var: var_store.fresh(),
|
||||||
|
field: "b".into(),
|
||||||
|
field_var: var_store.fresh(),
|
||||||
|
loc_expr: Box::new(no_region(Var(Symbol::ARG_3))),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// overflow!
|
||||||
|
no_region(tag(
|
||||||
|
"Err",
|
||||||
|
vec![tag("Overflow", Vec::new(), var_store)],
|
||||||
|
var_store,
|
||||||
|
)),
|
||||||
|
)],
|
||||||
|
final_else: Box::new(
|
||||||
|
// all is well
|
||||||
|
no_region(
|
||||||
|
// Ok arg_3.a
|
||||||
|
tag(
|
||||||
|
"Ok",
|
||||||
|
vec![
|
||||||
|
// arg_3.a
|
||||||
|
Access {
|
||||||
|
record_var,
|
||||||
|
ext_var: var_store.fresh(),
|
||||||
|
field: "a".into(),
|
||||||
|
field_var: num_var_3,
|
||||||
|
loc_expr: Box::new(no_region(Var(Symbol::ARG_3))),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
var_store,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
// arg_3 = RunLowLevel NumMulChecked arg_1 arg_2
|
||||||
|
let def = crate::def::Def {
|
||||||
|
loc_pattern: no_region(Pattern::Identifier(Symbol::ARG_3)),
|
||||||
|
loc_expr: no_region(RunLowLevel {
|
||||||
|
op: LowLevel::NumMulChecked,
|
||||||
|
args: vec![
|
||||||
|
(num_var_1, Var(Symbol::ARG_1)),
|
||||||
|
(num_var_2, Var(Symbol::ARG_2)),
|
||||||
|
],
|
||||||
|
ret_var: record_var,
|
||||||
|
}),
|
||||||
|
expr_var: record_var,
|
||||||
|
pattern_vars: SendMap::default(),
|
||||||
|
annotation: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let body = LetNonRec(Box::new(def), Box::new(no_region(cont)), ret_var);
|
||||||
|
|
||||||
|
defn(
|
||||||
|
symbol,
|
||||||
|
vec![(num_var_1, Symbol::ARG_1), (num_var_2, Symbol::ARG_2)],
|
||||||
|
var_store,
|
||||||
|
body,
|
||||||
|
ret_var,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// Num.isGt : Num a, Num a -> Bool
|
/// Num.isGt : Num a, Num a -> Bool
|
||||||
fn num_gt(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
fn num_gt(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
num_num_other_binop(symbol, var_store, LowLevel::NumGt)
|
num_num_other_binop(symbol, var_store, LowLevel::NumGt)
|
||||||
|
|
|
@ -21,7 +21,7 @@ use roc_types::types::Alias;
|
||||||
pub struct Module {
|
pub struct Module {
|
||||||
pub module_id: ModuleId,
|
pub module_id: ModuleId,
|
||||||
pub exposed_imports: MutMap<Symbol, Variable>,
|
pub exposed_imports: MutMap<Symbol, Variable>,
|
||||||
pub exposed_vars_by_symbol: Vec<(Symbol, Variable)>,
|
pub exposed_symbols: MutSet<Symbol>,
|
||||||
pub references: MutSet<Symbol>,
|
pub references: MutSet<Symbol>,
|
||||||
pub aliases: MutMap<Symbol, Alias>,
|
pub aliases: MutMap<Symbol, Alias>,
|
||||||
pub rigid_variables: MutMap<Variable, Lowercase>,
|
pub rigid_variables: MutMap<Variable, Lowercase>,
|
||||||
|
@ -36,7 +36,6 @@ pub struct ModuleOutput {
|
||||||
pub lookups: Vec<(Symbol, Variable, Region)>,
|
pub lookups: Vec<(Symbol, Variable, Region)>,
|
||||||
pub problems: Vec<Problem>,
|
pub problems: Vec<Problem>,
|
||||||
pub ident_ids: IdentIds,
|
pub ident_ids: IdentIds,
|
||||||
pub exposed_vars_by_symbol: Vec<(Symbol, Variable)>,
|
|
||||||
pub references: MutSet<Symbol>,
|
pub references: MutSet<Symbol>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +50,7 @@ pub fn canonicalize_module_defs<'a>(
|
||||||
dep_idents: MutMap<ModuleId, IdentIds>,
|
dep_idents: MutMap<ModuleId, IdentIds>,
|
||||||
aliases: MutMap<Symbol, Alias>,
|
aliases: MutMap<Symbol, Alias>,
|
||||||
exposed_imports: MutMap<Ident, (Symbol, Region)>,
|
exposed_imports: MutMap<Ident, (Symbol, Region)>,
|
||||||
mut exposed_symbols: MutSet<Symbol>,
|
exposed_symbols: &MutSet<Symbol>,
|
||||||
var_store: &mut VarStore,
|
var_store: &mut VarStore,
|
||||||
) -> Result<ModuleOutput, RuntimeError> {
|
) -> Result<ModuleOutput, RuntimeError> {
|
||||||
let mut can_exposed_imports = MutMap::default();
|
let mut can_exposed_imports = MutMap::default();
|
||||||
|
@ -166,45 +165,39 @@ pub fn canonicalize_module_defs<'a>(
|
||||||
// NOTE previously we inserted builtin defs into the list of defs here
|
// NOTE previously we inserted builtin defs into the list of defs here
|
||||||
// this is now done later, in file.rs.
|
// this is now done later, in file.rs.
|
||||||
|
|
||||||
|
// assume all exposed symbols are not actually defined in the module
|
||||||
|
// then as we walk the module and encounter the definitions, remove
|
||||||
|
// symbols from this set
|
||||||
|
let mut exposed_but_not_defined = exposed_symbols.clone();
|
||||||
|
|
||||||
match sort_can_defs(&mut env, defs, Output::default()) {
|
match sort_can_defs(&mut env, defs, Output::default()) {
|
||||||
(Ok(mut declarations), output) => {
|
(Ok(mut declarations), output) => {
|
||||||
use crate::def::Declaration::*;
|
use crate::def::Declaration::*;
|
||||||
|
|
||||||
// Record the variables for all exposed symbols.
|
|
||||||
let mut exposed_vars_by_symbol = Vec::with_capacity(exposed_symbols.len());
|
|
||||||
|
|
||||||
for decl in declarations.iter() {
|
for decl in declarations.iter() {
|
||||||
match decl {
|
match decl {
|
||||||
Declare(def) => {
|
Declare(def) => {
|
||||||
for (symbol, variable) in def.pattern_vars.iter() {
|
for (symbol, _) in def.pattern_vars.iter() {
|
||||||
if exposed_symbols.contains(symbol) {
|
if exposed_but_not_defined.contains(symbol) {
|
||||||
// This is one of our exposed symbols;
|
|
||||||
// record the corresponding variable!
|
|
||||||
exposed_vars_by_symbol.push((*symbol, *variable));
|
|
||||||
|
|
||||||
// Remove this from exposed_symbols,
|
// Remove this from exposed_symbols,
|
||||||
// so that at the end of the process,
|
// so that at the end of the process,
|
||||||
// we can see if there were any
|
// we can see if there were any
|
||||||
// exposed symbols which did not have
|
// exposed symbols which did not have
|
||||||
// corresponding defs.
|
// corresponding defs.
|
||||||
exposed_symbols.remove(symbol);
|
exposed_but_not_defined.remove(symbol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DeclareRec(defs) => {
|
DeclareRec(defs) => {
|
||||||
for def in defs {
|
for def in defs {
|
||||||
for (symbol, variable) in def.pattern_vars.iter() {
|
for (symbol, _) in def.pattern_vars.iter() {
|
||||||
if exposed_symbols.contains(symbol) {
|
if exposed_but_not_defined.contains(symbol) {
|
||||||
// This is one of our exposed symbols;
|
|
||||||
// record the corresponding variable!
|
|
||||||
exposed_vars_by_symbol.push((*symbol, *variable));
|
|
||||||
|
|
||||||
// Remove this from exposed_symbols,
|
// Remove this from exposed_symbols,
|
||||||
// so that at the end of the process,
|
// so that at the end of the process,
|
||||||
// we can see if there were any
|
// we can see if there were any
|
||||||
// exposed symbols which did not have
|
// exposed symbols which did not have
|
||||||
// corresponding defs.
|
// corresponding defs.
|
||||||
exposed_symbols.remove(symbol);
|
exposed_but_not_defined.remove(symbol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -219,7 +212,7 @@ pub fn canonicalize_module_defs<'a>(
|
||||||
debug_assert!(def
|
debug_assert!(def
|
||||||
.pattern_vars
|
.pattern_vars
|
||||||
.iter()
|
.iter()
|
||||||
.all(|(symbol, _)| !exposed_symbols.contains(symbol)));
|
.all(|(symbol, _)| !exposed_but_not_defined.contains(symbol)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -232,7 +225,7 @@ pub fn canonicalize_module_defs<'a>(
|
||||||
// we can see if there were any
|
// we can see if there were any
|
||||||
// exposed symbols which did not have
|
// exposed symbols which did not have
|
||||||
// corresponding defs.
|
// corresponding defs.
|
||||||
exposed_symbols.remove(&symbol);
|
exposed_but_not_defined.remove(&symbol);
|
||||||
|
|
||||||
aliases.insert(symbol, alias);
|
aliases.insert(symbol, alias);
|
||||||
}
|
}
|
||||||
|
@ -241,7 +234,7 @@ pub fn canonicalize_module_defs<'a>(
|
||||||
// exposed_symbols and added to exposed_vars_by_symbol. If any were
|
// exposed_symbols and added to exposed_vars_by_symbol. If any were
|
||||||
// not, that means they were declared as exposed but there was
|
// not, that means they were declared as exposed but there was
|
||||||
// no actual declaration with that name!
|
// no actual declaration with that name!
|
||||||
for symbol in exposed_symbols {
|
for symbol in exposed_but_not_defined {
|
||||||
env.problem(Problem::ExposedButNotDefined(symbol));
|
env.problem(Problem::ExposedButNotDefined(symbol));
|
||||||
|
|
||||||
// In case this exposed value is referenced by other modules,
|
// In case this exposed value is referenced by other modules,
|
||||||
|
@ -305,7 +298,6 @@ pub fn canonicalize_module_defs<'a>(
|
||||||
exposed_imports: can_exposed_imports,
|
exposed_imports: can_exposed_imports,
|
||||||
problems: env.problems,
|
problems: env.problems,
|
||||||
lookups,
|
lookups,
|
||||||
exposed_vars_by_symbol,
|
|
||||||
ident_ids: env.ident_ids,
|
ident_ids: env.ident_ids,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,33 +76,71 @@ pub fn num_float() -> Type {
|
||||||
Type::Alias(
|
Type::Alias(
|
||||||
Symbol::NUM_F64,
|
Symbol::NUM_F64,
|
||||||
vec![],
|
vec![],
|
||||||
Box::new(num_num(num_floatingpoint())),
|
Box::new(num_num(num_floatingpoint(num_binary64()))),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn num_floatingpoint() -> Type {
|
pub fn num_floatingpoint(range: Type) -> Type {
|
||||||
let alias_content = Type::TagUnion(
|
let alias_content = Type::TagUnion(
|
||||||
vec![(TagName::Private(Symbol::NUM_AT_FLOATINGPOINT), vec![])],
|
vec![(
|
||||||
|
TagName::Private(Symbol::NUM_AT_FLOATINGPOINT),
|
||||||
|
vec![range.clone()],
|
||||||
|
)],
|
||||||
Box::new(Type::EmptyTagUnion),
|
Box::new(Type::EmptyTagUnion),
|
||||||
);
|
);
|
||||||
|
|
||||||
Type::Alias(Symbol::NUM_FLOATINGPOINT, vec![], Box::new(alias_content))
|
Type::Alias(
|
||||||
|
Symbol::NUM_FLOATINGPOINT,
|
||||||
|
vec![("range".into(), range)],
|
||||||
|
Box::new(alias_content),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn num_binary64() -> Type {
|
||||||
|
let alias_content = Type::TagUnion(
|
||||||
|
vec![(TagName::Private(Symbol::NUM_AT_BINARY64), vec![])],
|
||||||
|
Box::new(Type::EmptyTagUnion),
|
||||||
|
);
|
||||||
|
|
||||||
|
Type::Alias(Symbol::NUM_BINARY64, vec![], Box::new(alias_content))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn num_int() -> Type {
|
pub fn num_int() -> Type {
|
||||||
Type::Alias(Symbol::NUM_I64, vec![], Box::new(num_num(num_integer())))
|
Type::Alias(
|
||||||
|
Symbol::NUM_I64,
|
||||||
|
vec![],
|
||||||
|
Box::new(num_num(num_integer(num_signed64()))),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn num_integer() -> Type {
|
pub fn num_signed64() -> Type {
|
||||||
let alias_content = Type::TagUnion(
|
let alias_content = Type::TagUnion(
|
||||||
vec![(TagName::Private(Symbol::NUM_AT_INTEGER), vec![])],
|
vec![(TagName::Private(Symbol::NUM_AT_SIGNED64), vec![])],
|
||||||
Box::new(Type::EmptyTagUnion),
|
Box::new(Type::EmptyTagUnion),
|
||||||
);
|
);
|
||||||
|
|
||||||
Type::Alias(Symbol::NUM_INTEGER, vec![], Box::new(alias_content))
|
Type::Alias(Symbol::NUM_SIGNED64, vec![], Box::new(alias_content))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn num_integer(range: Type) -> Type {
|
||||||
|
let alias_content = Type::TagUnion(
|
||||||
|
vec![(
|
||||||
|
TagName::Private(Symbol::NUM_AT_INTEGER),
|
||||||
|
vec![range.clone()],
|
||||||
|
)],
|
||||||
|
Box::new(Type::EmptyTagUnion),
|
||||||
|
);
|
||||||
|
|
||||||
|
Type::Alias(
|
||||||
|
Symbol::NUM_INTEGER,
|
||||||
|
vec![("range".into(), range)],
|
||||||
|
Box::new(alias_content),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
|
|
@ -157,7 +157,7 @@ pub fn constrain_pattern(
|
||||||
IntLiteral(_) => {
|
IntLiteral(_) => {
|
||||||
state.constraints.push(Constraint::Pattern(
|
state.constraints.push(Constraint::Pattern(
|
||||||
region,
|
region,
|
||||||
PatternCategory::Float,
|
PatternCategory::Int,
|
||||||
builtins::num_int(),
|
builtins::num_int(),
|
||||||
expected,
|
expected,
|
||||||
));
|
));
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::builtins::{num_floatingpoint, num_integer, num_num};
|
use crate::builtins::{num_binary64, num_floatingpoint, num_integer, num_num, num_signed64};
|
||||||
use crate::expr::{exists, Info};
|
use crate::expr::{exists, Info};
|
||||||
use roc_can::annotation::IntroducedVariables;
|
use roc_can::annotation::IntroducedVariables;
|
||||||
use roc_can::constraint::Constraint::{self, *};
|
use roc_can::constraint::Constraint::{self, *};
|
||||||
|
@ -166,24 +166,25 @@ fn constrain_pattern(
|
||||||
}
|
}
|
||||||
|
|
||||||
NumLiteral(inner_var, _) => {
|
NumLiteral(inner_var, _) => {
|
||||||
let (num_uvar, val_uvar, num_type, num_var) = unique_unbound_num(*inner_var, var_store);
|
let (inner_uvar, num_var, num_type) = unique_unbound_num(*inner_var, var_store);
|
||||||
state.constraints.push(exists(
|
state.constraints.push(exists(
|
||||||
vec![val_uvar, num_uvar, num_var, *inner_var],
|
vec![num_var, inner_uvar, *inner_var],
|
||||||
Constraint::Pattern(pattern.region, PatternCategory::Num, num_type, expected),
|
Constraint::Pattern(pattern.region, PatternCategory::Num, num_type, expected),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
IntLiteral(_) => {
|
IntLiteral(_) => {
|
||||||
let (num_uvar, int_uvar, num_type) = unique_int(var_store);
|
let (a, b, c, num_type) = unique_int(var_store);
|
||||||
state.constraints.push(exists(
|
state.constraints.push(exists(
|
||||||
vec![num_uvar, int_uvar],
|
vec![a, b, c],
|
||||||
Constraint::Pattern(pattern.region, PatternCategory::Int, num_type, expected),
|
Constraint::Pattern(pattern.region, PatternCategory::Int, num_type, expected),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
FloatLiteral(_) => {
|
FloatLiteral(_) => {
|
||||||
let (num_uvar, float_uvar, num_type) = unique_float(var_store);
|
let (a, b, c, num_type) = unique_float(var_store);
|
||||||
|
|
||||||
state.constraints.push(exists(
|
state.constraints.push(exists(
|
||||||
vec![num_uvar, float_uvar],
|
vec![a, b, c],
|
||||||
Constraint::Pattern(pattern.region, PatternCategory::Float, num_type, expected),
|
Constraint::Pattern(pattern.region, PatternCategory::Float, num_type, expected),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -405,41 +406,47 @@ fn constrain_pattern(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unique_unbound_num(
|
fn unique_unbound_num(inner_var: Variable, var_store: &mut VarStore) -> (Variable, Variable, Type) {
|
||||||
inner_var: Variable,
|
|
||||||
var_store: &mut VarStore,
|
|
||||||
) -> (Variable, Variable, Type, Variable) {
|
|
||||||
let num_var = var_store.fresh();
|
|
||||||
let num_uvar = var_store.fresh();
|
let num_uvar = var_store.fresh();
|
||||||
let val_uvar = var_store.fresh();
|
let inner_uvar = var_store.fresh();
|
||||||
|
|
||||||
let val_type = Type::Variable(inner_var);
|
let val_type = Type::Variable(inner_var);
|
||||||
let val_utype = attr_type(Bool::variable(val_uvar), val_type);
|
let val_utype = attr_type(Bool::variable(inner_uvar), val_type);
|
||||||
|
|
||||||
let num_utype = num_num(val_utype);
|
let num_utype = num_num(val_utype);
|
||||||
let num_type = attr_type(Bool::variable(num_uvar), num_utype);
|
let num_type = attr_type(Bool::variable(num_uvar), num_utype);
|
||||||
|
|
||||||
(num_uvar, val_uvar, num_type, num_var)
|
(inner_uvar, num_uvar, num_type)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unique_num(var_store: &mut VarStore, val_type: Type) -> (Variable, Variable, Type) {
|
fn unique_int(var_store: &mut VarStore) -> (Variable, Variable, Variable, Type) {
|
||||||
let num_uvar = var_store.fresh();
|
let num_uvar1 = var_store.fresh();
|
||||||
let val_uvar = var_store.fresh();
|
let num_uvar2 = var_store.fresh();
|
||||||
|
let num_uvar3 = var_store.fresh();
|
||||||
|
|
||||||
let val_utype = attr_type(Bool::variable(val_uvar), val_type);
|
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 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);
|
||||||
|
|
||||||
let num_utype = num_num(val_utype);
|
(num_uvar1, num_uvar2, num_uvar3, attr_num)
|
||||||
let num_type = attr_type(Bool::variable(num_uvar), num_utype);
|
|
||||||
|
|
||||||
(num_uvar, val_uvar, num_type)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unique_int(var_store: &mut VarStore) -> (Variable, Variable, Type) {
|
fn unique_float(var_store: &mut VarStore) -> (Variable, Variable, Variable, Type) {
|
||||||
unique_num(var_store, num_integer())
|
let num_uvar1 = var_store.fresh();
|
||||||
}
|
let num_uvar2 = var_store.fresh();
|
||||||
|
let num_uvar3 = var_store.fresh();
|
||||||
|
|
||||||
fn unique_float(var_store: &mut VarStore) -> (Variable, Variable, Type) {
|
let binary_64 = num_binary64();
|
||||||
unique_num(var_store, num_floatingpoint())
|
let attr_binary_64 = attr_type(Bool::variable(num_uvar1), binary_64);
|
||||||
|
let fp = num_floatingpoint(attr_binary_64);
|
||||||
|
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);
|
||||||
|
|
||||||
|
(num_uvar1, num_uvar2, num_uvar3, attr_num)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn constrain_expr(
|
pub fn constrain_expr(
|
||||||
|
@ -456,10 +463,10 @@ pub fn constrain_expr(
|
||||||
match expr {
|
match expr {
|
||||||
Num(inner_var, _) => {
|
Num(inner_var, _) => {
|
||||||
let var = var_store.fresh();
|
let var = var_store.fresh();
|
||||||
let (num_uvar, val_uvar, num_type, num_var) = unique_unbound_num(*inner_var, var_store);
|
let (inner_uvar, num_var, num_type) = unique_unbound_num(*inner_var, var_store);
|
||||||
|
|
||||||
exists(
|
exists(
|
||||||
vec![var, *inner_var, val_uvar, num_uvar, num_var],
|
vec![var, *inner_var, inner_uvar, num_var],
|
||||||
And(vec![
|
And(vec![
|
||||||
Eq(
|
Eq(
|
||||||
Type::Variable(var),
|
Type::Variable(var),
|
||||||
|
@ -472,10 +479,10 @@ pub fn constrain_expr(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Int(var, _) => {
|
Int(var, _) => {
|
||||||
let (num_uvar, int_uvar, num_type) = unique_int(var_store);
|
let (a, b, c, num_type) = unique_int(var_store);
|
||||||
|
|
||||||
exists(
|
exists(
|
||||||
vec![*var, num_uvar, int_uvar],
|
vec![*var, a, b, c],
|
||||||
And(vec![
|
And(vec![
|
||||||
Eq(
|
Eq(
|
||||||
Type::Variable(*var),
|
Type::Variable(*var),
|
||||||
|
@ -488,10 +495,10 @@ pub fn constrain_expr(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Float(var, _) => {
|
Float(var, _) => {
|
||||||
let (num_uvar, float_uvar, num_type) = unique_float(var_store);
|
let (a, b, c, num_type) = unique_float(var_store);
|
||||||
|
|
||||||
exists(
|
exists(
|
||||||
vec![*var, num_uvar, float_uvar],
|
vec![*var, a, b, c],
|
||||||
And(vec![
|
And(vec![
|
||||||
Eq(
|
Eq(
|
||||||
Type::Variable(*var),
|
Type::Variable(*var),
|
||||||
|
|
|
@ -9,7 +9,8 @@ use crate::llvm::build_str::{
|
||||||
};
|
};
|
||||||
use crate::llvm::compare::{build_eq, build_neq};
|
use crate::llvm::compare::{build_eq, build_neq};
|
||||||
use crate::llvm::convert::{
|
use crate::llvm::convert::{
|
||||||
basic_type_from_layout, block_of_memory, collection, get_fn_type, get_ptr_type, ptr_int,
|
basic_type_from_builtin, basic_type_from_layout, block_of_memory, collection, get_fn_type,
|
||||||
|
get_ptr_type, ptr_int,
|
||||||
};
|
};
|
||||||
use crate::llvm::refcounting::{
|
use crate::llvm::refcounting::{
|
||||||
decrement_refcount_layout, increment_refcount_layout, refcount_is_one_comparison,
|
decrement_refcount_layout, increment_refcount_layout, refcount_is_one_comparison,
|
||||||
|
@ -355,6 +356,12 @@ fn add_intrinsics<'ctx>(ctx: &'ctx Context, module: &Module<'ctx>) {
|
||||||
ctx.struct_type(&fields, false)
|
ctx.struct_type(&fields, false)
|
||||||
.fn_type(&[i64_type.into(), i64_type.into()], false)
|
.fn_type(&[i64_type.into(), i64_type.into()], false)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
add_intrinsic(module, LLVM_SMUL_WITH_OVERFLOW_I64, {
|
||||||
|
let fields = [i64_type.into(), i1_type.into()];
|
||||||
|
ctx.struct_type(&fields, false)
|
||||||
|
.fn_type(&[i64_type.into(), i64_type.into()], false)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static LLVM_MEMSET_I64: &str = "llvm.memset.p0i8.i64";
|
static LLVM_MEMSET_I64: &str = "llvm.memset.p0i8.i64";
|
||||||
|
@ -369,6 +376,7 @@ static LLVM_CEILING_F64: &str = "llvm.ceil.f64";
|
||||||
static LLVM_FLOOR_F64: &str = "llvm.floor.f64";
|
static LLVM_FLOOR_F64: &str = "llvm.floor.f64";
|
||||||
pub static LLVM_SADD_WITH_OVERFLOW_I64: &str = "llvm.sadd.with.overflow.i64";
|
pub static LLVM_SADD_WITH_OVERFLOW_I64: &str = "llvm.sadd.with.overflow.i64";
|
||||||
pub static LLVM_SSUB_WITH_OVERFLOW_I64: &str = "llvm.ssub.with.overflow.i64";
|
pub static LLVM_SSUB_WITH_OVERFLOW_I64: &str = "llvm.ssub.with.overflow.i64";
|
||||||
|
pub static LLVM_SMUL_WITH_OVERFLOW_I64: &str = "llvm.smul.with.overflow.i64";
|
||||||
|
|
||||||
fn add_intrinsic<'ctx>(
|
fn add_intrinsic<'ctx>(
|
||||||
module: &Module<'ctx>,
|
module: &Module<'ctx>,
|
||||||
|
@ -2902,7 +2910,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
match arg_builtin {
|
match arg_builtin {
|
||||||
Int128 | Int64 | Int32 | Int16 | Int8 => {
|
Int128 | Int64 | Int32 | Int16 | Int8 => {
|
||||||
build_int_unary_op(env, arg.into_int_value(), arg_layout, op)
|
build_int_unary_op(env, arg.into_int_value(), arg_builtin, op)
|
||||||
}
|
}
|
||||||
Float128 | Float64 | Float32 | Float16 => {
|
Float128 | Float64 | Float32 | Float16 => {
|
||||||
build_float_unary_op(env, arg.into_float_value(), op)
|
build_float_unary_op(env, arg.into_float_value(), op)
|
||||||
|
@ -3003,7 +3011,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
NumAdd | NumSub | NumMul | NumLt | NumLte | NumGt | NumGte | NumRemUnchecked
|
NumAdd | NumSub | NumMul | NumLt | NumLte | NumGt | NumGte | NumRemUnchecked
|
||||||
| NumAddWrap | NumAddChecked | NumDivUnchecked | NumPow | NumPowInt | NumSubWrap
|
| NumAddWrap | NumAddChecked | NumDivUnchecked | NumPow | NumPowInt | NumSubWrap
|
||||||
| NumSubChecked => {
|
| NumSubChecked | NumMulWrap | NumMulChecked => {
|
||||||
debug_assert_eq!(args.len(), 2);
|
debug_assert_eq!(args.len(), 2);
|
||||||
|
|
||||||
let (lhs_arg, lhs_layout) = load_symbol_and_layout(env, scope, &args[0]);
|
let (lhs_arg, lhs_layout) = load_symbol_and_layout(env, scope, &args[0]);
|
||||||
|
@ -3259,7 +3267,37 @@ fn build_int_binop<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
NumSubWrap => bd.build_int_sub(lhs, rhs, "sub_int").into(),
|
NumSubWrap => bd.build_int_sub(lhs, rhs, "sub_int").into(),
|
||||||
NumSubChecked => env.call_intrinsic(LLVM_SSUB_WITH_OVERFLOW_I64, &[lhs.into(), rhs.into()]),
|
NumSubChecked => env.call_intrinsic(LLVM_SSUB_WITH_OVERFLOW_I64, &[lhs.into(), rhs.into()]),
|
||||||
NumMul => bd.build_int_mul(lhs, rhs, "mul_int").into(),
|
NumMul => {
|
||||||
|
let context = env.context;
|
||||||
|
let result = env
|
||||||
|
.call_intrinsic(LLVM_SMUL_WITH_OVERFLOW_I64, &[lhs.into(), rhs.into()])
|
||||||
|
.into_struct_value();
|
||||||
|
|
||||||
|
let mul_result = bd.build_extract_value(result, 0, "mul_result").unwrap();
|
||||||
|
let has_overflowed = bd.build_extract_value(result, 1, "has_overflowed").unwrap();
|
||||||
|
|
||||||
|
let condition = bd.build_int_compare(
|
||||||
|
IntPredicate::EQ,
|
||||||
|
has_overflowed.into_int_value(),
|
||||||
|
context.bool_type().const_zero(),
|
||||||
|
"has_not_overflowed",
|
||||||
|
);
|
||||||
|
|
||||||
|
let then_block = context.append_basic_block(parent, "then_block");
|
||||||
|
let throw_block = context.append_basic_block(parent, "throw_block");
|
||||||
|
|
||||||
|
bd.build_conditional_branch(condition, then_block, throw_block);
|
||||||
|
|
||||||
|
bd.position_at_end(throw_block);
|
||||||
|
|
||||||
|
throw_exception(env, "integer multiplication overflowed!");
|
||||||
|
|
||||||
|
bd.position_at_end(then_block);
|
||||||
|
|
||||||
|
mul_result
|
||||||
|
}
|
||||||
|
NumMulWrap => bd.build_int_mul(lhs, rhs, "mul_int").into(),
|
||||||
|
NumMulChecked => env.call_intrinsic(LLVM_SMUL_WITH_OVERFLOW_I64, &[lhs.into(), rhs.into()]),
|
||||||
NumGt => bd.build_int_compare(SGT, lhs, rhs, "int_gt").into(),
|
NumGt => bd.build_int_compare(SGT, lhs, rhs, "int_gt").into(),
|
||||||
NumGte => bd.build_int_compare(SGE, lhs, rhs, "int_gte").into(),
|
NumGte => bd.build_int_compare(SGE, lhs, rhs, "int_gte").into(),
|
||||||
NumLt => bd.build_int_compare(SLT, lhs, rhs, "int_lt").into(),
|
NumLt => bd.build_int_compare(SLT, lhs, rhs, "int_lt").into(),
|
||||||
|
@ -3475,7 +3513,55 @@ fn build_float_binop<'a, 'ctx, 'env>(
|
||||||
struct_value.into()
|
struct_value.into()
|
||||||
}
|
}
|
||||||
NumSubWrap => unreachable!("wrapping subtraction is not defined on floats"),
|
NumSubWrap => unreachable!("wrapping subtraction is not defined on floats"),
|
||||||
NumMul => bd.build_float_mul(lhs, rhs, "mul_float").into(),
|
NumMul => {
|
||||||
|
let builder = env.builder;
|
||||||
|
let context = env.context;
|
||||||
|
|
||||||
|
let result = bd.build_float_mul(lhs, rhs, "mul_float");
|
||||||
|
|
||||||
|
let is_finite =
|
||||||
|
call_bitcode_fn(env, &[result.into()], &bitcode::NUM_IS_FINITE).into_int_value();
|
||||||
|
|
||||||
|
let then_block = context.append_basic_block(parent, "then_block");
|
||||||
|
let throw_block = context.append_basic_block(parent, "throw_block");
|
||||||
|
|
||||||
|
builder.build_conditional_branch(is_finite, then_block, throw_block);
|
||||||
|
|
||||||
|
builder.position_at_end(throw_block);
|
||||||
|
|
||||||
|
throw_exception(env, "float multiplication overflowed!");
|
||||||
|
|
||||||
|
builder.position_at_end(then_block);
|
||||||
|
|
||||||
|
result.into()
|
||||||
|
}
|
||||||
|
NumMulChecked => {
|
||||||
|
let context = env.context;
|
||||||
|
|
||||||
|
let result = bd.build_float_mul(lhs, rhs, "mul_float");
|
||||||
|
|
||||||
|
let is_finite =
|
||||||
|
call_bitcode_fn(env, &[result.into()], &bitcode::NUM_IS_FINITE).into_int_value();
|
||||||
|
let is_infinite = bd.build_not(is_finite, "negate");
|
||||||
|
|
||||||
|
let struct_type = context.struct_type(
|
||||||
|
&[context.f64_type().into(), context.bool_type().into()],
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
let struct_value = {
|
||||||
|
let v1 = struct_type.const_zero();
|
||||||
|
let v2 = bd.build_insert_value(v1, result, 0, "set_result").unwrap();
|
||||||
|
let v3 = bd
|
||||||
|
.build_insert_value(v2, is_infinite, 1, "set_is_infinite")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
v3.into_struct_value()
|
||||||
|
};
|
||||||
|
|
||||||
|
struct_value.into()
|
||||||
|
}
|
||||||
|
NumMulWrap => unreachable!("wrapping multiplication is not defined on floats"),
|
||||||
NumGt => bd.build_float_compare(OGT, lhs, rhs, "float_gt").into(),
|
NumGt => bd.build_float_compare(OGT, lhs, rhs, "float_gt").into(),
|
||||||
NumGte => bd.build_float_compare(OGE, lhs, rhs, "float_gte").into(),
|
NumGte => bd.build_float_compare(OGE, lhs, rhs, "float_gte").into(),
|
||||||
NumLt => bd.build_float_compare(OLT, lhs, rhs, "float_lt").into(),
|
NumLt => bd.build_float_compare(OLT, lhs, rhs, "float_lt").into(),
|
||||||
|
@ -3489,10 +3575,37 @@ fn build_float_binop<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn int_type_signed_min(int_type: IntType) -> IntValue {
|
||||||
|
let width = int_type.get_bit_width();
|
||||||
|
|
||||||
|
debug_assert!(width <= 128);
|
||||||
|
let shift = 128 - width as usize;
|
||||||
|
|
||||||
|
if shift < 64 {
|
||||||
|
let min = i128::MIN >> shift;
|
||||||
|
let a = min as u64;
|
||||||
|
let b = (min >> 64) as u64;
|
||||||
|
|
||||||
|
int_type.const_int_arbitrary_precision(&[b, a])
|
||||||
|
} else {
|
||||||
|
int_type.const_int((i128::MIN >> shift) as u64, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn builtin_to_int_type<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
builtin: &Builtin<'a>,
|
||||||
|
) -> IntType<'ctx> {
|
||||||
|
let result = basic_type_from_builtin(env.arena, env.context, builtin, env.ptr_bytes);
|
||||||
|
debug_assert!(result.is_int_type());
|
||||||
|
|
||||||
|
result.into_int_type()
|
||||||
|
}
|
||||||
|
|
||||||
fn build_int_unary_op<'a, 'ctx, 'env>(
|
fn build_int_unary_op<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
arg: IntValue<'ctx>,
|
arg: IntValue<'ctx>,
|
||||||
arg_layout: &Layout<'a>,
|
arg_layout: &Builtin<'a>,
|
||||||
op: LowLevel,
|
op: LowLevel,
|
||||||
) -> BasicValueEnum<'ctx> {
|
) -> BasicValueEnum<'ctx> {
|
||||||
use roc_module::low_level::LowLevel::*;
|
use roc_module::low_level::LowLevel::*;
|
||||||
|
@ -3500,43 +3613,13 @@ fn build_int_unary_op<'a, 'ctx, 'env>(
|
||||||
let bd = env.builder;
|
let bd = env.builder;
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
NumNeg => bd.build_int_neg(arg, "negate_int").into(),
|
NumNeg => {
|
||||||
|
// integer abs overflows when applied to the minimum value of a signed type
|
||||||
|
int_neg_raise_on_overflow(env, arg, arg_layout)
|
||||||
|
}
|
||||||
NumAbs => {
|
NumAbs => {
|
||||||
// This is how libc's abs() is implemented - it uses no branching!
|
// integer abs overflows when applied to the minimum value of a signed type
|
||||||
//
|
int_abs_raise_on_overflow(env, arg, arg_layout)
|
||||||
// abs = \arg ->
|
|
||||||
// shifted = arg >>> 63
|
|
||||||
//
|
|
||||||
// (xor arg shifted) - shifted
|
|
||||||
|
|
||||||
let ctx = env.context;
|
|
||||||
let shifted_name = "abs_shift_right";
|
|
||||||
let shifted_alloca = {
|
|
||||||
let bits_to_shift = ((arg_layout.stack_size(env.ptr_bytes) as u64) * 8) - 1;
|
|
||||||
let shift_val = ctx.i64_type().const_int(bits_to_shift, false);
|
|
||||||
let shifted = bd.build_right_shift(arg, shift_val, true, shifted_name);
|
|
||||||
let alloca = bd.build_alloca(
|
|
||||||
basic_type_from_layout(env.arena, ctx, arg_layout, env.ptr_bytes),
|
|
||||||
"#int_abs_help",
|
|
||||||
);
|
|
||||||
|
|
||||||
// shifted = arg >>> 63
|
|
||||||
bd.build_store(alloca, shifted);
|
|
||||||
|
|
||||||
alloca
|
|
||||||
};
|
|
||||||
|
|
||||||
let xored_arg = bd.build_xor(
|
|
||||||
arg,
|
|
||||||
bd.build_load(shifted_alloca, shifted_name).into_int_value(),
|
|
||||||
"xor_arg_shifted",
|
|
||||||
);
|
|
||||||
|
|
||||||
BasicValueEnum::IntValue(bd.build_int_sub(
|
|
||||||
xored_arg,
|
|
||||||
bd.build_load(shifted_alloca, shifted_name).into_int_value(),
|
|
||||||
"sub_xored_shifted",
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
NumToFloat => {
|
NumToFloat => {
|
||||||
// This is an Int, so we need to convert it.
|
// This is an Int, so we need to convert it.
|
||||||
|
@ -3553,6 +3636,109 @@ fn build_int_unary_op<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn int_neg_raise_on_overflow<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
arg: IntValue<'ctx>,
|
||||||
|
builtin: &Builtin<'a>,
|
||||||
|
) -> BasicValueEnum<'ctx> {
|
||||||
|
let builder = env.builder;
|
||||||
|
|
||||||
|
let min_val = int_type_signed_min(builtin_to_int_type(env, builtin));
|
||||||
|
let condition = builder.build_int_compare(IntPredicate::EQ, arg, min_val, "is_min_val");
|
||||||
|
|
||||||
|
let block = env.builder.get_insert_block().expect("to be in a function");
|
||||||
|
let parent = block.get_parent().expect("to be in a function");
|
||||||
|
let then_block = env.context.append_basic_block(parent, "then");
|
||||||
|
let else_block = env.context.append_basic_block(parent, "else");
|
||||||
|
|
||||||
|
env.builder
|
||||||
|
.build_conditional_branch(condition, then_block, else_block);
|
||||||
|
|
||||||
|
builder.position_at_end(then_block);
|
||||||
|
|
||||||
|
throw_exception(
|
||||||
|
env,
|
||||||
|
"integer negation overflowed because its argument is the minimum value",
|
||||||
|
);
|
||||||
|
|
||||||
|
builder.position_at_end(else_block);
|
||||||
|
|
||||||
|
builder.build_int_neg(arg, "negate_int").into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn int_abs_raise_on_overflow<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
arg: IntValue<'ctx>,
|
||||||
|
builtin: &Builtin<'a>,
|
||||||
|
) -> BasicValueEnum<'ctx> {
|
||||||
|
let builder = env.builder;
|
||||||
|
|
||||||
|
let min_val = int_type_signed_min(builtin_to_int_type(env, builtin));
|
||||||
|
let condition = builder.build_int_compare(IntPredicate::EQ, arg, min_val, "is_min_val");
|
||||||
|
|
||||||
|
let block = env.builder.get_insert_block().expect("to be in a function");
|
||||||
|
let parent = block.get_parent().expect("to be in a function");
|
||||||
|
let then_block = env.context.append_basic_block(parent, "then");
|
||||||
|
let else_block = env.context.append_basic_block(parent, "else");
|
||||||
|
|
||||||
|
env.builder
|
||||||
|
.build_conditional_branch(condition, then_block, else_block);
|
||||||
|
|
||||||
|
builder.position_at_end(then_block);
|
||||||
|
|
||||||
|
throw_exception(
|
||||||
|
env,
|
||||||
|
"integer absolute overflowed because its argument is the minimum value",
|
||||||
|
);
|
||||||
|
|
||||||
|
builder.position_at_end(else_block);
|
||||||
|
|
||||||
|
int_abs_with_overflow(env, arg, builtin)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn int_abs_with_overflow<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
arg: IntValue<'ctx>,
|
||||||
|
arg_layout: &Builtin<'a>,
|
||||||
|
) -> BasicValueEnum<'ctx> {
|
||||||
|
// This is how libc's abs() is implemented - it uses no branching!
|
||||||
|
//
|
||||||
|
// abs = \arg ->
|
||||||
|
// shifted = arg >>> 63
|
||||||
|
//
|
||||||
|
// (xor arg shifted) - shifted
|
||||||
|
|
||||||
|
let bd = env.builder;
|
||||||
|
let ctx = env.context;
|
||||||
|
let shifted_name = "abs_shift_right";
|
||||||
|
let shifted_alloca = {
|
||||||
|
let bits_to_shift = ((arg_layout.stack_size(env.ptr_bytes) as u64) * 8) - 1;
|
||||||
|
let shift_val = ctx.i64_type().const_int(bits_to_shift, false);
|
||||||
|
let shifted = bd.build_right_shift(arg, shift_val, true, shifted_name);
|
||||||
|
let alloca = bd.build_alloca(
|
||||||
|
basic_type_from_builtin(env.arena, ctx, arg_layout, env.ptr_bytes),
|
||||||
|
"#int_abs_help",
|
||||||
|
);
|
||||||
|
|
||||||
|
// shifted = arg >>> 63
|
||||||
|
bd.build_store(alloca, shifted);
|
||||||
|
|
||||||
|
alloca
|
||||||
|
};
|
||||||
|
|
||||||
|
let xored_arg = bd.build_xor(
|
||||||
|
arg,
|
||||||
|
bd.build_load(shifted_alloca, shifted_name).into_int_value(),
|
||||||
|
"xor_arg_shifted",
|
||||||
|
);
|
||||||
|
|
||||||
|
BasicValueEnum::IntValue(bd.build_int_sub(
|
||||||
|
xored_arg,
|
||||||
|
bd.build_load(shifted_alloca, shifted_name).into_int_value(),
|
||||||
|
"sub_xored_shifted",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
fn build_float_unary_op<'a, 'ctx, 'env>(
|
fn build_float_unary_op<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
arg: FloatValue<'ctx>,
|
arg: FloatValue<'ctx>,
|
||||||
|
|
|
@ -4,7 +4,7 @@ use inkwell::context::Context;
|
||||||
use inkwell::types::BasicTypeEnum::{self, *};
|
use inkwell::types::BasicTypeEnum::{self, *};
|
||||||
use inkwell::types::{ArrayType, BasicType, FunctionType, IntType, PointerType, StructType};
|
use inkwell::types::{ArrayType, BasicType, FunctionType, IntType, PointerType, StructType};
|
||||||
use inkwell::AddressSpace;
|
use inkwell::AddressSpace;
|
||||||
use roc_mono::layout::Layout;
|
use roc_mono::layout::{Builtin, Layout};
|
||||||
|
|
||||||
/// TODO could this be added to Inkwell itself as a method on BasicValueEnum?
|
/// TODO could this be added to Inkwell itself as a method on BasicValueEnum?
|
||||||
pub fn get_ptr_type<'ctx>(
|
pub fn get_ptr_type<'ctx>(
|
||||||
|
@ -103,8 +103,7 @@ pub fn basic_type_from_layout<'ctx>(
|
||||||
layout: &Layout<'_>,
|
layout: &Layout<'_>,
|
||||||
ptr_bytes: u32,
|
ptr_bytes: u32,
|
||||||
) -> BasicTypeEnum<'ctx> {
|
) -> BasicTypeEnum<'ctx> {
|
||||||
use roc_mono::layout::Builtin::*;
|
use Layout::*;
|
||||||
use roc_mono::layout::Layout::*;
|
|
||||||
|
|
||||||
match layout {
|
match layout {
|
||||||
FunctionPointer(args, ret_layout) => {
|
FunctionPointer(args, ret_layout) => {
|
||||||
|
@ -147,22 +146,33 @@ pub fn basic_type_from_layout<'ctx>(
|
||||||
.as_basic_type_enum()
|
.as_basic_type_enum()
|
||||||
}
|
}
|
||||||
|
|
||||||
Builtin(builtin) => match builtin {
|
Builtin(builtin) => basic_type_from_builtin(arena, context, builtin, ptr_bytes),
|
||||||
Int128 => context.i128_type().as_basic_type_enum(),
|
}
|
||||||
Int64 => context.i64_type().as_basic_type_enum(),
|
}
|
||||||
Int32 => context.i32_type().as_basic_type_enum(),
|
|
||||||
Int16 => context.i16_type().as_basic_type_enum(),
|
pub fn basic_type_from_builtin<'ctx>(
|
||||||
Int8 => context.i8_type().as_basic_type_enum(),
|
_arena: &Bump,
|
||||||
Int1 => context.bool_type().as_basic_type_enum(),
|
context: &'ctx Context,
|
||||||
Float128 => context.f128_type().as_basic_type_enum(),
|
builtin: &Builtin<'_>,
|
||||||
Float64 => context.f64_type().as_basic_type_enum(),
|
ptr_bytes: u32,
|
||||||
Float32 => context.f32_type().as_basic_type_enum(),
|
) -> BasicTypeEnum<'ctx> {
|
||||||
Float16 => context.f16_type().as_basic_type_enum(),
|
use Builtin::*;
|
||||||
Dict(_, _) | EmptyDict => panic!("TODO layout_to_basic_type for Builtin::Dict"),
|
|
||||||
Set(_) | EmptySet => panic!("TODO layout_to_basic_type for Builtin::Set"),
|
match builtin {
|
||||||
List(_, _) | Str | EmptyStr => collection(context, ptr_bytes).into(),
|
Int128 => context.i128_type().as_basic_type_enum(),
|
||||||
EmptyList => BasicTypeEnum::StructType(collection(context, ptr_bytes)),
|
Int64 => context.i64_type().as_basic_type_enum(),
|
||||||
},
|
Int32 => context.i32_type().as_basic_type_enum(),
|
||||||
|
Int16 => context.i16_type().as_basic_type_enum(),
|
||||||
|
Int8 => context.i8_type().as_basic_type_enum(),
|
||||||
|
Int1 => context.bool_type().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(),
|
||||||
|
Float16 => context.f16_type().as_basic_type_enum(),
|
||||||
|
Dict(_, _) | EmptyDict => panic!("TODO layout_to_basic_type for Builtin::Dict"),
|
||||||
|
Set(_) | EmptySet => panic!("TODO layout_to_basic_type for Builtin::Set"),
|
||||||
|
List(_, _) | Str | EmptyStr => collection(context, ptr_bytes).into(),
|
||||||
|
EmptyList => BasicTypeEnum::StructType(collection(context, ptr_bytes)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,8 @@ mod gen_num {
|
||||||
fn f64_abs() {
|
fn f64_abs() {
|
||||||
assert_evals_to!("Num.abs -4.7", 4.7, f64);
|
assert_evals_to!("Num.abs -4.7", 4.7, f64);
|
||||||
assert_evals_to!("Num.abs 5.8", 5.8, f64);
|
assert_evals_to!("Num.abs 5.8", 5.8, f64);
|
||||||
|
//assert_evals_to!("Num.abs Num.maxFloat", f64::MAX, f64);
|
||||||
|
//assert_evals_to!("Num.abs Num.minFloat", -f64::MIN, f64);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -52,6 +54,24 @@ mod gen_num {
|
||||||
assert_evals_to!("Num.abs 1", 1, i64);
|
assert_evals_to!("Num.abs 1", 1, i64);
|
||||||
assert_evals_to!("Num.abs 9_000_000_000_000", 9_000_000_000_000, i64);
|
assert_evals_to!("Num.abs 9_000_000_000_000", 9_000_000_000_000, i64);
|
||||||
assert_evals_to!("Num.abs -9_000_000_000_000", 9_000_000_000_000, i64);
|
assert_evals_to!("Num.abs -9_000_000_000_000", 9_000_000_000_000, i64);
|
||||||
|
assert_evals_to!("Num.abs Num.maxInt", i64::MAX, i64);
|
||||||
|
assert_evals_to!("Num.abs (Num.minInt + 1)", -(i64::MIN + 1), i64);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(
|
||||||
|
expected = r#"Roc failed with message: "integer absolute overflowed because its argument is the minimum value"#
|
||||||
|
)]
|
||||||
|
fn abs_min_int_overflow() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
Num.abs Num.minInt
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
0,
|
||||||
|
i64
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -533,6 +553,24 @@ mod gen_num {
|
||||||
#[test]
|
#[test]
|
||||||
fn int_negate() {
|
fn int_negate() {
|
||||||
assert_evals_to!("Num.neg 123", -123, i64);
|
assert_evals_to!("Num.neg 123", -123, i64);
|
||||||
|
assert_evals_to!("Num.neg Num.maxInt", -i64::MAX, i64);
|
||||||
|
assert_evals_to!("Num.neg (Num.minInt + 1)", i64::MAX, i64);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(
|
||||||
|
expected = r#"Roc failed with message: "integer negation overflowed because its argument is the minimum value"#
|
||||||
|
)]
|
||||||
|
fn neg_min_int_overflow() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
Num.neg Num.minInt
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
0,
|
||||||
|
i64
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -555,7 +593,7 @@ mod gen_num {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
always42 : Num.Num Num.Integer -> Num.Num Num.Integer
|
always42 : Num.Num (Num.Integer Num.Signed64) -> Num.Num (Num.Integer Num.Signed64)
|
||||||
always42 = \_ -> 42
|
always42 = \_ -> 42
|
||||||
|
|
||||||
always42 5
|
always42 5
|
||||||
|
@ -778,7 +816,7 @@ mod gen_num {
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
-1.7976931348623157e308 - 1.7976931348623157e308
|
-1.7976931348623157e308 - 1.7976931348623157e308
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
0.0,
|
0.0,
|
||||||
f64
|
f64
|
||||||
|
@ -790,12 +828,12 @@ mod gen_num {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
when Num.subChecked 1 2 is
|
when Num.subChecked 5 2 is
|
||||||
Ok v -> v
|
Ok v -> v
|
||||||
_ -> -1
|
_ -> -1
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
-1,
|
3,
|
||||||
i64
|
i64
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -838,4 +876,127 @@ mod gen_num {
|
||||||
f64
|
f64
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(expected = r#"Roc failed with message: "integer multiplication overflowed!"#)]
|
||||||
|
fn int_positive_mul_overflow() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
9_223_372_036_854_775_807 * 2
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
0,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(expected = r#"Roc failed with message: "integer multiplication overflowed!"#)]
|
||||||
|
fn int_negative_mul_overflow() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
(-9_223_372_036_854_775_808) * 2
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
0,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(expected = r#"Roc failed with message: "float multiplication overflowed!"#)]
|
||||||
|
fn float_positive_mul_overflow() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
1.7976931348623157e308 * 2
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
0.0,
|
||||||
|
f64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(expected = r#"Roc failed with message: "float multiplication overflowed!"#)]
|
||||||
|
fn float_negative_mul_overflow() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
-1.7976931348623157e308 * 2
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
0.0,
|
||||||
|
f64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn int_mul_wrap() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
Num.mulWrap Num.maxInt 2
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
-2,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn int_mul_checked() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
when Num.mulChecked 20 2 is
|
||||||
|
Ok v -> v
|
||||||
|
_ -> -1
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
40,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
when Num.mulChecked Num.maxInt 2 is
|
||||||
|
Err Overflow -> -1
|
||||||
|
Ok v -> v
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
-1,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn float_mul_checked() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
when Num.mulChecked 20.0 2.0 is
|
||||||
|
Ok v -> v
|
||||||
|
Err Overflow -> -1.0
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
40.0,
|
||||||
|
f64
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
when Num.mulChecked 1.7976931348623157e308 2 is
|
||||||
|
Err Overflow -> -1
|
||||||
|
Ok v -> v
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
-1.0,
|
||||||
|
f64
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1732,4 +1732,20 @@ mod gen_primitives {
|
||||||
|_| 0
|
|_| 0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hof_conditional() {
|
||||||
|
// exposed issue with the if condition being just a symbol
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
passTrue = \f -> f True
|
||||||
|
|
||||||
|
passTrue (\trueVal -> if trueVal then False else True)
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
0,
|
||||||
|
u8
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -920,4 +920,25 @@ mod gen_tags {
|
||||||
(i64, i64)
|
(i64, i64)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn result_never() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r"#
|
||||||
|
res : Result I64 []
|
||||||
|
res = Ok 4
|
||||||
|
|
||||||
|
# we should provide this in the stdlib
|
||||||
|
never : [] -> a
|
||||||
|
|
||||||
|
when res is
|
||||||
|
Ok v -> v
|
||||||
|
Err empty -> never empty
|
||||||
|
#"
|
||||||
|
),
|
||||||
|
4,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ use roc_can::env::Env;
|
||||||
use roc_can::expr::{Expr, Recursive};
|
use roc_can::expr::{Expr, Recursive};
|
||||||
use roc_can::pattern::Pattern;
|
use roc_can::pattern::Pattern;
|
||||||
use roc_can::scope::Scope;
|
use roc_can::scope::Scope;
|
||||||
use roc_collections::all::SendMap;
|
use roc_collections::all::{MutSet, SendMap};
|
||||||
use roc_module::ident::TagName;
|
use roc_module::ident::TagName;
|
||||||
use roc_module::operator::CalledVia;
|
use roc_module::operator::CalledVia;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
|
@ -48,7 +48,7 @@ pub fn build_effect_builtins(
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
effect_symbol: Symbol,
|
effect_symbol: Symbol,
|
||||||
var_store: &mut VarStore,
|
var_store: &mut VarStore,
|
||||||
exposed_vars_by_symbol: &mut Vec<(Symbol, Variable)>,
|
exposed_symbols: &mut MutSet<Symbol>,
|
||||||
declarations: &mut Vec<Declaration>,
|
declarations: &mut Vec<Declaration>,
|
||||||
) {
|
) {
|
||||||
for (_, f) in BUILTIN_EFFECT_FUNCTIONS.iter() {
|
for (_, f) in BUILTIN_EFFECT_FUNCTIONS.iter() {
|
||||||
|
@ -60,7 +60,7 @@ pub fn build_effect_builtins(
|
||||||
var_store,
|
var_store,
|
||||||
);
|
);
|
||||||
|
|
||||||
exposed_vars_by_symbol.push((symbol, def.expr_var));
|
exposed_symbols.insert(symbol);
|
||||||
declarations.push(Declaration::Declare(def));
|
declarations.push(Declaration::Declare(def));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ use roc_module::symbol::{
|
||||||
use roc_mono::ir::{
|
use roc_mono::ir::{
|
||||||
CapturedSymbols, ExternalSpecializations, PartialProc, PendingSpecialization, Proc, Procs,
|
CapturedSymbols, ExternalSpecializations, PartialProc, PendingSpecialization, Proc, Procs,
|
||||||
};
|
};
|
||||||
use roc_mono::layout::{Layout, LayoutCache};
|
use roc_mono::layout::{Layout, LayoutCache, LayoutProblem};
|
||||||
use roc_parse::ast::{self, Attempting, StrLiteral, TypeAnnotation};
|
use roc_parse::ast::{self, Attempting, StrLiteral, TypeAnnotation};
|
||||||
use roc_parse::header::{
|
use roc_parse::header::{
|
||||||
ExposesEntry, ImportsEntry, PackageEntry, PackageOrPath, PlatformHeader, To, TypedIdent,
|
ExposesEntry, ImportsEntry, PackageEntry, PackageOrPath, PlatformHeader, To, TypedIdent,
|
||||||
|
@ -663,7 +663,7 @@ enum Msg<'a> {
|
||||||
},
|
},
|
||||||
FinishedAllTypeChecking {
|
FinishedAllTypeChecking {
|
||||||
solved_subs: Solved<Subs>,
|
solved_subs: Solved<Subs>,
|
||||||
exposed_vars_by_symbol: Vec<(Symbol, Variable)>,
|
exposed_vars_by_symbol: MutMap<Symbol, Variable>,
|
||||||
documentation: MutMap<ModuleId, ModuleDocumentation>,
|
documentation: MutMap<ModuleId, ModuleDocumentation>,
|
||||||
},
|
},
|
||||||
FoundSpecializations {
|
FoundSpecializations {
|
||||||
|
@ -1638,9 +1638,12 @@ fn update<'a>(
|
||||||
};
|
};
|
||||||
|
|
||||||
if is_host_exposed {
|
if is_host_exposed {
|
||||||
state
|
state.exposed_to_host.extend(
|
||||||
.exposed_to_host
|
solved_module
|
||||||
.extend(solved_module.exposed_vars_by_symbol.iter().copied());
|
.exposed_vars_by_symbol
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| (*k, *v)),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if module_id == state.root_id && state.goal_phase == Phase::SolveTypes {
|
if module_id == state.root_id && state.goal_phase == Phase::SolveTypes {
|
||||||
|
@ -1904,7 +1907,7 @@ fn finish_specialization<'a>(
|
||||||
fn finish<'a>(
|
fn finish<'a>(
|
||||||
state: State<'a>,
|
state: State<'a>,
|
||||||
solved: Solved<Subs>,
|
solved: Solved<Subs>,
|
||||||
exposed_vars_by_symbol: Vec<(Symbol, Variable)>,
|
exposed_vars_by_symbol: MutMap<Symbol, Variable>,
|
||||||
documentation: MutMap<ModuleId, ModuleDocumentation>,
|
documentation: MutMap<ModuleId, ModuleDocumentation>,
|
||||||
) -> LoadedModule {
|
) -> LoadedModule {
|
||||||
let module_ids = Arc::try_unwrap(state.arc_modules)
|
let module_ids = Arc::try_unwrap(state.arc_modules)
|
||||||
|
@ -2791,7 +2794,7 @@ fn run_solve<'a>(
|
||||||
let module_id = module.module_id;
|
let module_id = module.module_id;
|
||||||
|
|
||||||
let Module {
|
let Module {
|
||||||
exposed_vars_by_symbol,
|
exposed_symbols,
|
||||||
aliases,
|
aliases,
|
||||||
rigid_variables,
|
rigid_variables,
|
||||||
..
|
..
|
||||||
|
@ -2800,6 +2803,9 @@ fn run_solve<'a>(
|
||||||
let (solved_subs, solved_env, problems) =
|
let (solved_subs, solved_env, problems) =
|
||||||
roc_solve::module::run_solve(aliases, rigid_variables, constraint, var_store);
|
roc_solve::module::run_solve(aliases, rigid_variables, constraint, var_store);
|
||||||
|
|
||||||
|
let mut exposed_vars_by_symbol: MutMap<Symbol, Variable> = solved_env.vars_by_symbol.clone();
|
||||||
|
exposed_vars_by_symbol.retain(|k, _| exposed_symbols.contains(k));
|
||||||
|
|
||||||
let solved_types =
|
let solved_types =
|
||||||
roc_solve::module::make_solved_types(&solved_env, &solved_subs, &exposed_vars_by_symbol);
|
roc_solve::module::make_solved_types(&solved_env, &solved_subs, &exposed_vars_by_symbol);
|
||||||
|
|
||||||
|
@ -3001,8 +3007,8 @@ fn fabricate_effects_module<'a>(
|
||||||
|
|
||||||
let mut declarations = Vec::new();
|
let mut declarations = Vec::new();
|
||||||
|
|
||||||
let exposed_vars_by_symbol = {
|
let exposed_symbols: MutSet<Symbol> = {
|
||||||
let mut exposed_vars_by_symbol = Vec::new();
|
let mut exposed_symbols = MutSet::default();
|
||||||
|
|
||||||
{
|
{
|
||||||
for (ident, ann) in effect_entries {
|
for (ident, ann) in effect_entries {
|
||||||
|
@ -3035,7 +3041,7 @@ fn fabricate_effects_module<'a>(
|
||||||
annotation,
|
annotation,
|
||||||
);
|
);
|
||||||
|
|
||||||
exposed_vars_by_symbol.push((symbol, def.expr_var));
|
exposed_symbols.insert(symbol);
|
||||||
|
|
||||||
declarations.push(Declaration::Declare(def));
|
declarations.push(Declaration::Declare(def));
|
||||||
}
|
}
|
||||||
|
@ -3047,11 +3053,11 @@ fn fabricate_effects_module<'a>(
|
||||||
&mut scope,
|
&mut scope,
|
||||||
effect_symbol,
|
effect_symbol,
|
||||||
&mut var_store,
|
&mut var_store,
|
||||||
&mut exposed_vars_by_symbol,
|
&mut exposed_symbols,
|
||||||
&mut declarations,
|
&mut declarations,
|
||||||
);
|
);
|
||||||
|
|
||||||
exposed_vars_by_symbol
|
exposed_symbols
|
||||||
};
|
};
|
||||||
|
|
||||||
use roc_can::module::ModuleOutput;
|
use roc_can::module::ModuleOutput;
|
||||||
|
@ -3063,7 +3069,6 @@ fn fabricate_effects_module<'a>(
|
||||||
lookups: Vec::new(),
|
lookups: Vec::new(),
|
||||||
problems: can_env.problems,
|
problems: can_env.problems,
|
||||||
ident_ids: can_env.ident_ids,
|
ident_ids: can_env.ident_ids,
|
||||||
exposed_vars_by_symbol,
|
|
||||||
references: MutSet::default(),
|
references: MutSet::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3072,7 +3077,7 @@ fn fabricate_effects_module<'a>(
|
||||||
let module = Module {
|
let module = Module {
|
||||||
module_id,
|
module_id,
|
||||||
exposed_imports: module_output.exposed_imports,
|
exposed_imports: module_output.exposed_imports,
|
||||||
exposed_vars_by_symbol: module_output.exposed_vars_by_symbol,
|
exposed_symbols,
|
||||||
references: module_output.references,
|
references: module_output.references,
|
||||||
aliases: module_output.aliases,
|
aliases: module_output.aliases,
|
||||||
rigid_variables: module_output.rigid_variables,
|
rigid_variables: module_output.rigid_variables,
|
||||||
|
@ -3182,7 +3187,7 @@ fn canonicalize_and_constrain<'a>(
|
||||||
dep_idents,
|
dep_idents,
|
||||||
aliases,
|
aliases,
|
||||||
exposed_imports,
|
exposed_imports,
|
||||||
exposed_symbols,
|
&exposed_symbols,
|
||||||
&mut var_store,
|
&mut var_store,
|
||||||
);
|
);
|
||||||
let canonicalize_end = SystemTime::now();
|
let canonicalize_end = SystemTime::now();
|
||||||
|
@ -3196,7 +3201,7 @@ fn canonicalize_and_constrain<'a>(
|
||||||
let module = Module {
|
let module = Module {
|
||||||
module_id,
|
module_id,
|
||||||
exposed_imports: module_output.exposed_imports,
|
exposed_imports: module_output.exposed_imports,
|
||||||
exposed_vars_by_symbol: module_output.exposed_vars_by_symbol,
|
exposed_symbols,
|
||||||
references: module_output.references,
|
references: module_output.references,
|
||||||
aliases: module_output.aliases,
|
aliases: module_output.aliases,
|
||||||
rigid_variables: module_output.rigid_variables,
|
rigid_variables: module_output.rigid_variables,
|
||||||
|
@ -3503,9 +3508,20 @@ fn add_def_to_module<'a>(
|
||||||
mono_env.subs,
|
mono_env.subs,
|
||||||
) {
|
) {
|
||||||
Ok(l) => l,
|
Ok(l) => l,
|
||||||
Err(err) => {
|
Err(LayoutProblem::Erroneous) => {
|
||||||
// a host-exposed function is not monomorphized
|
let message = "top level function has erroneous type";
|
||||||
todo!("The host-exposed function {:?} does not have a valid layout (e.g. maybe the function wasn't monomorphic): {:?}", symbol, err)
|
procs.runtime_errors.insert(symbol, message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Err(LayoutProblem::UnresolvedTypeVar(v)) => {
|
||||||
|
let message = format!(
|
||||||
|
"top level function has unresolved type variable {:?}",
|
||||||
|
v
|
||||||
|
);
|
||||||
|
procs
|
||||||
|
.runtime_errors
|
||||||
|
.insert(symbol, mono_env.arena.alloc(message));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3537,9 +3553,29 @@ fn add_def_to_module<'a>(
|
||||||
// get specialized!
|
// get specialized!
|
||||||
if is_exposed {
|
if is_exposed {
|
||||||
let annotation = def.expr_var;
|
let annotation = def.expr_var;
|
||||||
let layout = layout_cache.from_var(mono_env.arena, annotation, mono_env.subs).unwrap_or_else(|err|
|
|
||||||
todo!("TODO gracefully handle the situation where we expose a function to the host which doesn't have a valid layout (e.g. maybe the function wasn't monomorphic): {:?}", err)
|
let layout = match layout_cache.from_var(
|
||||||
);
|
mono_env.arena,
|
||||||
|
annotation,
|
||||||
|
mono_env.subs,
|
||||||
|
) {
|
||||||
|
Ok(l) => l,
|
||||||
|
Err(LayoutProblem::Erroneous) => {
|
||||||
|
let message = "top level function has erroneous type";
|
||||||
|
procs.runtime_errors.insert(symbol, message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Err(LayoutProblem::UnresolvedTypeVar(v)) => {
|
||||||
|
let message = format!(
|
||||||
|
"top level function has unresolved type variable {:?}",
|
||||||
|
v
|
||||||
|
);
|
||||||
|
procs
|
||||||
|
.runtime_errors
|
||||||
|
.insert(symbol, mono_env.arena.alloc(message));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
procs.insert_exposed(
|
procs.insert_exposed(
|
||||||
symbol,
|
symbol,
|
||||||
|
|
|
@ -8,7 +8,7 @@ use roc_can::expected::Expected;
|
||||||
use roc_can::expr::{canonicalize_expr, Expr, Output};
|
use roc_can::expr::{canonicalize_expr, Expr, Output};
|
||||||
use roc_can::operator;
|
use roc_can::operator;
|
||||||
use roc_can::scope::Scope;
|
use roc_can::scope::Scope;
|
||||||
use roc_collections::all::{ImMap, ImSet, MutMap, SendMap, SendSet};
|
use roc_collections::all::{ImMap, ImSet, MutMap, SendSet};
|
||||||
use roc_constrain::expr::constrain_expr;
|
use roc_constrain::expr::constrain_expr;
|
||||||
use roc_constrain::module::{constrain_imported_values, Import};
|
use roc_constrain::module::{constrain_imported_values, Import};
|
||||||
use roc_module::ident::Ident;
|
use roc_module::ident::Ident;
|
||||||
|
@ -37,7 +37,7 @@ pub fn infer_expr(
|
||||||
) -> (Content, Subs) {
|
) -> (Content, Subs) {
|
||||||
let env = solve::Env {
|
let env = solve::Env {
|
||||||
aliases: MutMap::default(),
|
aliases: MutMap::default(),
|
||||||
vars_by_symbol: SendMap::default(),
|
vars_by_symbol: MutMap::default(),
|
||||||
};
|
};
|
||||||
let (solved, _) = solve::run(&env, problems, subs, constraint);
|
let (solved, _) = solve::run(&env, problems, subs, constraint);
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,8 @@ pub enum LowLevel {
|
||||||
NumSubWrap,
|
NumSubWrap,
|
||||||
NumSubChecked,
|
NumSubChecked,
|
||||||
NumMul,
|
NumMul,
|
||||||
|
NumMulWrap,
|
||||||
|
NumMulChecked,
|
||||||
NumGt,
|
NumGt,
|
||||||
NumGte,
|
NumGte,
|
||||||
NumLt,
|
NumLt,
|
||||||
|
|
|
@ -797,9 +797,35 @@ define_builtins! {
|
||||||
54 NUM_ATAN: "atan"
|
54 NUM_ATAN: "atan"
|
||||||
55 NUM_ACOS: "acos"
|
55 NUM_ACOS: "acos"
|
||||||
56 NUM_ASIN: "asin"
|
56 NUM_ASIN: "asin"
|
||||||
57 NUM_BITWISE_AND: "bitwiseAnd"
|
57 NUM_AT_SIGNED128: "@Signed128"
|
||||||
58 NUM_SUB_WRAP: "subWrap"
|
58 NUM_SIGNED128: "Signed128" imported
|
||||||
59 NUM_SUB_CHECKED: "subChecked"
|
59 NUM_AT_SIGNED64: "@Signed64"
|
||||||
|
60 NUM_SIGNED64: "Signed64" imported
|
||||||
|
61 NUM_AT_SIGNED32: "@Signed32"
|
||||||
|
62 NUM_SIGNED32: "Signed32" imported
|
||||||
|
63 NUM_AT_SIGNED16: "@Signed16"
|
||||||
|
64 NUM_SIGNED16: "Signed16" imported
|
||||||
|
65 NUM_AT_SIGNED8: "@Signed8"
|
||||||
|
66 NUM_SIGNED8: "Signed8" imported
|
||||||
|
67 NUM_AT_UNSIGNED128: "@Unsigned128"
|
||||||
|
68 NUM_UNSIGNED128: "Unsigned128" imported
|
||||||
|
69 NUM_AT_UNSIGNED64: "@Unsigned64"
|
||||||
|
70 NUM_UNSIGNED64: "Unsigned64" imported
|
||||||
|
71 NUM_AT_UNSIGNED32: "@Unsigned32"
|
||||||
|
72 NUM_UNSIGNED32: "Unsigned32" imported
|
||||||
|
73 NUM_AT_UNSIGNED16: "@Unsigned16"
|
||||||
|
74 NUM_UNSIGNED16: "Unsigned16" imported
|
||||||
|
75 NUM_AT_UNSIGNED8: "@Unsigned8"
|
||||||
|
76 NUM_UNSIGNED8: "Unsigned8" imported
|
||||||
|
77 NUM_AT_BINARY64: "@Binary64"
|
||||||
|
78 NUM_BINARY64: "Binary64" imported
|
||||||
|
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"
|
||||||
}
|
}
|
||||||
2 BOOL: "Bool" => {
|
2 BOOL: "Bool" => {
|
||||||
0 BOOL_BOOL: "Bool" imported // the Bool.Bool type alias
|
0 BOOL_BOOL: "Bool" imported // the Bool.Bool type alias
|
||||||
|
|
|
@ -540,8 +540,8 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
||||||
ListSum => arena.alloc_slice_copy(&[borrowed]),
|
ListSum => arena.alloc_slice_copy(&[borrowed]),
|
||||||
|
|
||||||
Eq | NotEq | And | Or | NumAdd | NumAddWrap | NumAddChecked | NumSub | NumSubWrap
|
Eq | NotEq | And | Or | NumAdd | NumAddWrap | NumAddChecked | NumSub | NumSubWrap
|
||||||
| NumSubChecked | NumMul | NumGt | NumGte | NumLt | NumLte | NumCompare
|
| NumSubChecked | NumMul | NumMulWrap | NumMulChecked | NumGt | NumGte | NumLt | NumLte
|
||||||
| NumDivUnchecked | NumRemUnchecked | NumPow | NumPowInt | NumBitwiseAnd => {
|
| NumCompare | NumDivUnchecked | NumRemUnchecked | NumPow | NumPowInt | NumBitwiseAnd => {
|
||||||
arena.alloc_slice_copy(&[irrelevant, irrelevant])
|
arena.alloc_slice_copy(&[irrelevant, irrelevant])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1508,7 +1508,6 @@ pub fn specialize_all<'a>(
|
||||||
));
|
));
|
||||||
|
|
||||||
procs.runtime_errors.insert(name, error_msg);
|
procs.runtime_errors.insert(name, error_msg);
|
||||||
panic!("failed to specialize {:?}", name);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2585,13 +2584,23 @@ pub fn with_hole<'a>(
|
||||||
|
|
||||||
let mut field_symbols_temp = Vec::with_capacity_in(args.len(), env.arena);
|
let mut field_symbols_temp = Vec::with_capacity_in(args.len(), env.arena);
|
||||||
|
|
||||||
for (var, arg) in args.drain(..) {
|
for (var, mut arg) in args.drain(..) {
|
||||||
// Layout will unpack this unwrapped tack if it only has one (non-zero-sized) field
|
// Layout will unpack this unwrapped tack if it only has one (non-zero-sized) field
|
||||||
let layout = layout_cache
|
let layout = match layout_cache.from_var(env.arena, var, env.subs) {
|
||||||
.from_var(env.arena, var, env.subs)
|
Ok(cached) => cached,
|
||||||
.unwrap_or_else(|err| {
|
Err(LayoutProblem::UnresolvedTypeVar(_)) => {
|
||||||
panic!("TODO turn fn_var into a RuntimeError {:?}", err)
|
// this argument has type `forall a. a`, which is isomorphic to
|
||||||
});
|
// the empty type (Void, Never, the empty tag union `[]`)
|
||||||
|
use roc_can::expr::Expr;
|
||||||
|
use roc_problem::can::RuntimeError;
|
||||||
|
arg.value = Expr::RuntimeError(RuntimeError::VoidValue);
|
||||||
|
Layout::Struct(&[])
|
||||||
|
}
|
||||||
|
Err(LayoutProblem::Erroneous) => {
|
||||||
|
// something went very wrong
|
||||||
|
panic!("TODO turn fn_var into a RuntimeError")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let alignment = layout.alignment_bytes(8);
|
let alignment = layout.alignment_bytes(8);
|
||||||
|
|
||||||
|
@ -3575,9 +3584,23 @@ pub fn with_hole<'a>(
|
||||||
let arg_symbols = arg_symbols.into_bump_slice();
|
let arg_symbols = arg_symbols.into_bump_slice();
|
||||||
|
|
||||||
// layout of the return type
|
// layout of the return type
|
||||||
let layout = layout_cache
|
let layout = match layout_cache.from_var(env.arena, ret_var, env.subs) {
|
||||||
.from_var(env.arena, ret_var, env.subs)
|
Ok(cached) => cached,
|
||||||
.unwrap_or_else(|err| todo!("TODO turn fn_var into a RuntimeError {:?}", err));
|
Err(LayoutProblem::UnresolvedTypeVar(_)) => {
|
||||||
|
return Stmt::RuntimeError(env.arena.alloc(format!(
|
||||||
|
"UnresolvedTypeVar {} line {}",
|
||||||
|
file!(),
|
||||||
|
line!()
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
Err(LayoutProblem::Erroneous) => {
|
||||||
|
return Stmt::RuntimeError(env.arena.alloc(format!(
|
||||||
|
"Erroneous {} line {}",
|
||||||
|
file!(),
|
||||||
|
line!()
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let result = Stmt::Let(assigned, Expr::RunLowLevel(op, arg_symbols), layout, hole);
|
let result = Stmt::Let(assigned, Expr::RunLowLevel(op, arg_symbols), layout, hole);
|
||||||
|
|
||||||
|
@ -3654,7 +3677,7 @@ pub fn from_can<'a>(
|
||||||
let mut stmt = from_can(env, branch_var, final_else.value, procs, layout_cache);
|
let mut stmt = from_can(env, branch_var, final_else.value, procs, layout_cache);
|
||||||
|
|
||||||
for (loc_cond, loc_then) in branches.into_iter().rev() {
|
for (loc_cond, loc_then) in branches.into_iter().rev() {
|
||||||
let branching_symbol = env.unique_symbol();
|
let branching_symbol = possible_reuse_symbol(env, procs, &loc_cond.value);
|
||||||
let then = from_can(env, branch_var, loc_then.value, procs, layout_cache);
|
let then = from_can(env, branch_var, loc_then.value, procs, layout_cache);
|
||||||
|
|
||||||
stmt = Stmt::Cond {
|
stmt = Stmt::Cond {
|
||||||
|
@ -3667,15 +3690,14 @@ pub fn from_can<'a>(
|
||||||
ret_layout: ret_layout.clone(),
|
ret_layout: ret_layout.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// add condition
|
stmt = assign_to_symbol(
|
||||||
stmt = with_hole(
|
|
||||||
env,
|
env,
|
||||||
loc_cond.value,
|
|
||||||
cond_var,
|
|
||||||
procs,
|
procs,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
|
cond_var,
|
||||||
|
loc_cond,
|
||||||
branching_symbol,
|
branching_symbol,
|
||||||
env.arena.alloc(stmt),
|
stmt,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4869,9 +4891,7 @@ fn reuse_function_symbol<'a>(
|
||||||
// this symbol is a function, that is used by-name (e.g. as an argument to another
|
// this symbol is a function, that is used by-name (e.g. as an argument to another
|
||||||
// function). Register it with the current variable, then create a function pointer
|
// function). Register it with the current variable, then create a function pointer
|
||||||
// to it in the IR.
|
// to it in the IR.
|
||||||
let layout = layout_cache
|
let res_layout = layout_cache.from_var(env.arena, arg_var, env.subs);
|
||||||
.from_var(env.arena, arg_var, env.subs)
|
|
||||||
.expect("creating layout does not fail");
|
|
||||||
|
|
||||||
// we have three kinds of functions really. Plain functions, closures by capture,
|
// we have three kinds of functions really. Plain functions, closures by capture,
|
||||||
// and closures by unification. Here we record whether this function captures
|
// and closures by unification. Here we record whether this function captures
|
||||||
|
@ -4879,8 +4899,8 @@ fn reuse_function_symbol<'a>(
|
||||||
let captures = partial_proc.captured_symbols.captures();
|
let captures = partial_proc.captured_symbols.captures();
|
||||||
let captured = partial_proc.captured_symbols.clone();
|
let captured = partial_proc.captured_symbols.clone();
|
||||||
|
|
||||||
match layout {
|
match res_layout {
|
||||||
Layout::Closure(argument_layouts, closure_layout, ret_layout) if captures => {
|
Ok(Layout::Closure(argument_layouts, closure_layout, ret_layout)) if captures => {
|
||||||
// this is a closure by capture, meaning it itself captures local variables.
|
// this is a closure by capture, meaning it itself captures local variables.
|
||||||
// we've defined the closure as a (function_ptr, closure_data) pair already
|
// we've defined the closure as a (function_ptr, closure_data) pair already
|
||||||
|
|
||||||
|
@ -4958,7 +4978,7 @@ fn reuse_function_symbol<'a>(
|
||||||
|
|
||||||
stmt
|
stmt
|
||||||
}
|
}
|
||||||
_ => {
|
Ok(layout) => {
|
||||||
procs.insert_passed_by_name(
|
procs.insert_passed_by_name(
|
||||||
env,
|
env,
|
||||||
arg_var,
|
arg_var,
|
||||||
|
@ -4974,6 +4994,17 @@ fn reuse_function_symbol<'a>(
|
||||||
env.arena.alloc(result),
|
env.arena.alloc(result),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Err(LayoutProblem::Erroneous) => {
|
||||||
|
let message = format!("The {:?} symbol has an erroneous type", symbol);
|
||||||
|
Stmt::RuntimeError(env.arena.alloc(message))
|
||||||
|
}
|
||||||
|
Err(LayoutProblem::UnresolvedTypeVar(v)) => {
|
||||||
|
let message = format!(
|
||||||
|
"The {:?} symbol contains a unresolved type var {:?}",
|
||||||
|
symbol, v
|
||||||
|
);
|
||||||
|
Stmt::RuntimeError(env.arena.alloc(message))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5832,7 +5863,9 @@ pub enum IntOrFloat {
|
||||||
pub fn num_argument_to_int_or_float(subs: &Subs, var: Variable) -> IntOrFloat {
|
pub fn num_argument_to_int_or_float(subs: &Subs, var: Variable) -> IntOrFloat {
|
||||||
match subs.get_without_compacting(var).content {
|
match subs.get_without_compacting(var).content {
|
||||||
Content::Alias(Symbol::NUM_INTEGER, args, _) => {
|
Content::Alias(Symbol::NUM_INTEGER, args, _) => {
|
||||||
debug_assert!(args.is_empty());
|
debug_assert!(args.len() == 1);
|
||||||
|
|
||||||
|
// TODO: we probably need to match on the type of the arg
|
||||||
IntOrFloat::IntType
|
IntOrFloat::IntType
|
||||||
}
|
}
|
||||||
Content::FlexVar(_) => {
|
Content::FlexVar(_) => {
|
||||||
|
@ -5840,7 +5873,9 @@ pub fn num_argument_to_int_or_float(subs: &Subs, var: Variable) -> IntOrFloat {
|
||||||
IntOrFloat::IntType
|
IntOrFloat::IntType
|
||||||
}
|
}
|
||||||
Content::Alias(Symbol::NUM_FLOATINGPOINT, args, _) => {
|
Content::Alias(Symbol::NUM_FLOATINGPOINT, args, _) => {
|
||||||
debug_assert!(args.is_empty());
|
debug_assert!(args.len() == 1);
|
||||||
|
|
||||||
|
// TODO: we probably need to match on the type of the arg
|
||||||
IntOrFloat::FloatType
|
IntOrFloat::FloatType
|
||||||
}
|
}
|
||||||
Content::Structure(FlatType::Apply(Symbol::ATTR_ATTR, attr_args)) => {
|
Content::Structure(FlatType::Apply(Symbol::ATTR_ATTR, attr_args)) => {
|
||||||
|
@ -5849,6 +5884,11 @@ pub fn num_argument_to_int_or_float(subs: &Subs, var: Variable) -> IntOrFloat {
|
||||||
// Recurse on the second argument
|
// Recurse on the second argument
|
||||||
num_argument_to_int_or_float(subs, attr_args[1])
|
num_argument_to_int_or_float(subs, attr_args[1])
|
||||||
}
|
}
|
||||||
|
Content::Alias(Symbol::NUM_F64, args, _) | Content::Alias(Symbol::NUM_F32, args, _) => {
|
||||||
|
debug_assert!(args.is_empty());
|
||||||
|
|
||||||
|
IntOrFloat::FloatType
|
||||||
|
}
|
||||||
other => {
|
other => {
|
||||||
panic!(
|
panic!(
|
||||||
"Unrecognized Num type argument for var {:?} with Content: {:?}",
|
"Unrecognized Num type argument for var {:?} with Content: {:?}",
|
||||||
|
|
|
@ -1196,39 +1196,40 @@ pub fn layout_from_tag_union<'a>(
|
||||||
|
|
||||||
let tags_vec: std::vec::Vec<_> = tags.into_iter().collect();
|
let tags_vec: std::vec::Vec<_> = tags.into_iter().collect();
|
||||||
|
|
||||||
if tags_vec[0].0 != TagName::Private(Symbol::NUM_AT_NUM) {
|
match tags_vec.get(0) {
|
||||||
let opt_rec_var = None;
|
Some((tag_name, arguments)) if *tag_name == TagName::Private(Symbol::NUM_AT_NUM) => {
|
||||||
let variant = union_sorted_tags_help(arena, tags_vec, opt_rec_var, subs);
|
debug_assert_eq!(arguments.len(), 1);
|
||||||
|
|
||||||
match variant {
|
let var = arguments.iter().next().unwrap();
|
||||||
Never => panic!("TODO gracefully handle trying to instantiate Never"),
|
|
||||||
Unit | UnitWithArguments => Layout::Struct(&[]),
|
|
||||||
BoolUnion { .. } => Layout::Builtin(Builtin::Int1),
|
|
||||||
ByteUnion(_) => Layout::Builtin(Builtin::Int8),
|
|
||||||
Unwrapped(mut field_layouts) => {
|
|
||||||
if field_layouts.len() == 1 {
|
|
||||||
field_layouts.pop().unwrap()
|
|
||||||
} else {
|
|
||||||
Layout::Struct(field_layouts.into_bump_slice())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Wrapped(tags) => {
|
|
||||||
let mut tag_layouts = Vec::with_capacity_in(tags.len(), arena);
|
|
||||||
|
|
||||||
for (_, tag_layout) in tags {
|
unwrap_num_tag(subs, *var).expect("invalid Num argument")
|
||||||
tag_layouts.push(tag_layout);
|
}
|
||||||
|
_ => {
|
||||||
|
let opt_rec_var = None;
|
||||||
|
let variant = union_sorted_tags_help(arena, tags_vec, opt_rec_var, subs);
|
||||||
|
|
||||||
|
match variant {
|
||||||
|
Never => Layout::Union(&[]),
|
||||||
|
Unit | UnitWithArguments => Layout::Struct(&[]),
|
||||||
|
BoolUnion { .. } => Layout::Builtin(Builtin::Int1),
|
||||||
|
ByteUnion(_) => Layout::Builtin(Builtin::Int8),
|
||||||
|
Unwrapped(mut field_layouts) => {
|
||||||
|
if field_layouts.len() == 1 {
|
||||||
|
field_layouts.pop().unwrap()
|
||||||
|
} else {
|
||||||
|
Layout::Struct(field_layouts.into_bump_slice())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Wrapped(tags) => {
|
||||||
|
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())
|
||||||
}
|
}
|
||||||
Layout::Union(tag_layouts.into_bump_slice())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
let arguments = &tags_vec[0].1;
|
|
||||||
|
|
||||||
debug_assert_eq!(arguments.len(), 1);
|
|
||||||
|
|
||||||
let var = arguments.iter().next().unwrap();
|
|
||||||
|
|
||||||
unwrap_num_tag(subs, *var).expect("invalid Num argument")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1296,11 +1297,17 @@ fn unwrap_num_tag<'a>(subs: &Subs, var: Variable) -> Result<Layout<'a>, LayoutPr
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Content::Alias(Symbol::NUM_INTEGER, args, _) => {
|
Content::Alias(Symbol::NUM_INTEGER, args, _) => {
|
||||||
debug_assert!(args.is_empty());
|
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))
|
Ok(Layout::Builtin(Builtin::Int64))
|
||||||
}
|
}
|
||||||
Content::Alias(Symbol::NUM_FLOATINGPOINT, args, _) => {
|
Content::Alias(Symbol::NUM_FLOATINGPOINT, args, _) => {
|
||||||
debug_assert!(args.is_empty());
|
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))
|
Ok(Layout::Builtin(Builtin::Float64))
|
||||||
}
|
}
|
||||||
Content::FlexVar(_) | Content::RigidVar(_) => {
|
Content::FlexVar(_) | Content::RigidVar(_) => {
|
||||||
|
|
|
@ -8,7 +8,7 @@ use roc_can::expected::Expected;
|
||||||
use roc_can::expr::{canonicalize_expr, Expr, Output};
|
use roc_can::expr::{canonicalize_expr, Expr, Output};
|
||||||
use roc_can::operator;
|
use roc_can::operator;
|
||||||
use roc_can::scope::Scope;
|
use roc_can::scope::Scope;
|
||||||
use roc_collections::all::{ImMap, ImSet, MutMap, SendMap, SendSet};
|
use roc_collections::all::{ImMap, ImSet, MutMap, SendSet};
|
||||||
use roc_constrain::expr::constrain_expr;
|
use roc_constrain::expr::constrain_expr;
|
||||||
use roc_constrain::module::{constrain_imported_values, Import};
|
use roc_constrain::module::{constrain_imported_values, Import};
|
||||||
use roc_module::ident::Ident;
|
use roc_module::ident::Ident;
|
||||||
|
@ -37,7 +37,7 @@ pub fn infer_expr(
|
||||||
) -> (Content, Subs) {
|
) -> (Content, Subs) {
|
||||||
let env = solve::Env {
|
let env = solve::Env {
|
||||||
aliases: MutMap::default(),
|
aliases: MutMap::default(),
|
||||||
vars_by_symbol: SendMap::default(),
|
vars_by_symbol: MutMap::default(),
|
||||||
};
|
};
|
||||||
let (solved, _) = solve::run(&env, problems, subs, constraint);
|
let (solved, _) = solve::run(&env, problems, subs, constraint);
|
||||||
|
|
||||||
|
|
|
@ -147,6 +147,9 @@ pub enum RuntimeError {
|
||||||
/// When the author specifies a type annotation but no implementation
|
/// When the author specifies a type annotation but no implementation
|
||||||
NoImplementation,
|
NoImplementation,
|
||||||
|
|
||||||
|
/// cases where the `[]` value (or equivalently, `forall a. a`) pops up
|
||||||
|
VoidValue,
|
||||||
|
|
||||||
ExposedButNotDefined(Symbol),
|
ExposedButNotDefined(Symbol),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -338,6 +338,12 @@ fn pretty_runtime_error<'b>(
|
||||||
runtime_error: RuntimeError,
|
runtime_error: RuntimeError,
|
||||||
) -> RocDocBuilder<'b> {
|
) -> RocDocBuilder<'b> {
|
||||||
match runtime_error {
|
match runtime_error {
|
||||||
|
RuntimeError::VoidValue => {
|
||||||
|
// is used to communicate to the compiler that
|
||||||
|
// a branch is unreachable; this should never reach a user
|
||||||
|
unreachable!("")
|
||||||
|
}
|
||||||
|
|
||||||
RuntimeError::Shadowing {
|
RuntimeError::Shadowing {
|
||||||
original_region,
|
original_region,
|
||||||
shadow,
|
shadow,
|
||||||
|
|
|
@ -1643,6 +1643,17 @@ fn to_diff<'b>(
|
||||||
ErrorType::Type(Symbol::NUM_F64, _) => true,
|
ErrorType::Type(Symbol::NUM_F64, _) => true,
|
||||||
ErrorType::Alias(Symbol::NUM_F64, _, _) => true,
|
ErrorType::Alias(Symbol::NUM_F64, _, _) => true,
|
||||||
|
|
||||||
|
ErrorType::Type(Symbol::NUM_NUM, args) => match &args.get(0) {
|
||||||
|
Some(ErrorType::Type(Symbol::NUM_FLOATINGPOINT, _)) => true,
|
||||||
|
Some(ErrorType::Alias(Symbol::NUM_FLOATINGPOINT, _, _)) => true,
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
ErrorType::Alias(Symbol::NUM_NUM, args, _) => match &args.get(0) {
|
||||||
|
Some((_, ErrorType::Type(Symbol::NUM_FLOATINGPOINT, _))) => true,
|
||||||
|
Some((_, ErrorType::Alias(Symbol::NUM_FLOATINGPOINT, _, _))) => true,
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ use roc_can::expected::Expected;
|
||||||
use roc_can::expr::{canonicalize_expr, Expr, Output};
|
use roc_can::expr::{canonicalize_expr, Expr, Output};
|
||||||
use roc_can::operator;
|
use roc_can::operator;
|
||||||
use roc_can::scope::Scope;
|
use roc_can::scope::Scope;
|
||||||
use roc_collections::all::{ImMap, MutMap, SendMap, SendSet};
|
use roc_collections::all::{ImMap, MutMap, SendSet};
|
||||||
use roc_constrain::expr::constrain_expr;
|
use roc_constrain::expr::constrain_expr;
|
||||||
use roc_constrain::module::{constrain_imported_values, Import};
|
use roc_constrain::module::{constrain_imported_values, Import};
|
||||||
use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds};
|
use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds};
|
||||||
|
@ -35,7 +35,7 @@ pub fn infer_expr(
|
||||||
) -> (Content, Subs) {
|
) -> (Content, Subs) {
|
||||||
let env = solve::Env {
|
let env = solve::Env {
|
||||||
aliases: MutMap::default(),
|
aliases: MutMap::default(),
|
||||||
vars_by_symbol: SendMap::default(),
|
vars_by_symbol: MutMap::default(),
|
||||||
};
|
};
|
||||||
let (solved, _) = solve::run(&env, problems, subs, constraint);
|
let (solved, _) = solve::run(&env, problems, subs, constraint);
|
||||||
|
|
||||||
|
|
|
@ -448,8 +448,8 @@ mod test_reporting {
|
||||||
|
|
||||||
baz
|
baz
|
||||||
Str
|
Str
|
||||||
main
|
|
||||||
U8
|
U8
|
||||||
|
F64
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -620,9 +620,9 @@ mod test_reporting {
|
||||||
|
|
||||||
these names seem close though:
|
these names seem close though:
|
||||||
|
|
||||||
|
Result
|
||||||
Num
|
Num
|
||||||
Set
|
Set
|
||||||
Result
|
|
||||||
U8
|
U8
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
|
@ -1374,7 +1374,7 @@ mod test_reporting {
|
||||||
Bool
|
Bool
|
||||||
U8
|
U8
|
||||||
F64
|
F64
|
||||||
Num
|
Str
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -2094,7 +2094,7 @@ mod test_reporting {
|
||||||
|
|
||||||
But `add` needs the 2nd argument to be:
|
But `add` needs the 2nd argument to be:
|
||||||
|
|
||||||
Num Integer
|
Num (Integer Signed64)
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -2123,7 +2123,7 @@ mod test_reporting {
|
||||||
|
|
||||||
But `add` needs the 2nd argument to be:
|
But `add` needs the 2nd argument to be:
|
||||||
|
|
||||||
Num Integer
|
Num (Integer Signed64)
|
||||||
|
|
||||||
Tip: You can convert between Int and Float using functions like
|
Tip: You can convert between Int and Float using functions like
|
||||||
`Num.toFloat` and `Num.round`.
|
`Num.toFloat` and `Num.round`.
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use crate::solve;
|
use crate::solve;
|
||||||
use roc_can::constraint::Constraint;
|
use roc_can::constraint::Constraint;
|
||||||
use roc_can::module::Module;
|
use roc_collections::all::MutMap;
|
||||||
use roc_collections::all::{MutMap, SendMap};
|
|
||||||
use roc_module::ident::Lowercase;
|
use roc_module::ident::Lowercase;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_types::solved_types::{Solved, SolvedType};
|
use roc_types::solved_types::{Solved, SolvedType};
|
||||||
|
@ -12,37 +11,10 @@ use roc_types::types::Alias;
|
||||||
pub struct SolvedModule {
|
pub struct SolvedModule {
|
||||||
pub solved_types: MutMap<Symbol, SolvedType>,
|
pub solved_types: MutMap<Symbol, SolvedType>,
|
||||||
pub aliases: MutMap<Symbol, Alias>,
|
pub aliases: MutMap<Symbol, Alias>,
|
||||||
pub exposed_vars_by_symbol: Vec<(Symbol, Variable)>,
|
pub exposed_vars_by_symbol: MutMap<Symbol, Variable>,
|
||||||
pub problems: Vec<solve::TypeError>,
|
pub problems: Vec<solve::TypeError>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn solve_module(
|
|
||||||
module: Module,
|
|
||||||
constraint: Constraint,
|
|
||||||
var_store: VarStore,
|
|
||||||
) -> (Solved<Subs>, SolvedModule) {
|
|
||||||
let Module {
|
|
||||||
exposed_vars_by_symbol,
|
|
||||||
aliases,
|
|
||||||
rigid_variables,
|
|
||||||
..
|
|
||||||
} = module;
|
|
||||||
|
|
||||||
let (solved_subs, solved_env, problems) =
|
|
||||||
run_solve(aliases, rigid_variables, constraint, var_store);
|
|
||||||
|
|
||||||
let solved_types = make_solved_types(&solved_env, &solved_subs, &exposed_vars_by_symbol);
|
|
||||||
|
|
||||||
let solved_module = SolvedModule {
|
|
||||||
exposed_vars_by_symbol,
|
|
||||||
solved_types,
|
|
||||||
problems,
|
|
||||||
aliases: solved_env.aliases,
|
|
||||||
};
|
|
||||||
|
|
||||||
(solved_subs, solved_module)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run_solve(
|
pub fn run_solve(
|
||||||
aliases: MutMap<Symbol, Alias>,
|
aliases: MutMap<Symbol, Alias>,
|
||||||
rigid_variables: MutMap<Variable, Lowercase>,
|
rigid_variables: MutMap<Variable, Lowercase>,
|
||||||
|
@ -50,7 +22,7 @@ pub fn run_solve(
|
||||||
var_store: VarStore,
|
var_store: VarStore,
|
||||||
) -> (Solved<Subs>, solve::Env, Vec<solve::TypeError>) {
|
) -> (Solved<Subs>, solve::Env, Vec<solve::TypeError>) {
|
||||||
let env = solve::Env {
|
let env = solve::Env {
|
||||||
vars_by_symbol: SendMap::default(),
|
vars_by_symbol: MutMap::default(),
|
||||||
aliases,
|
aliases,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -73,7 +45,7 @@ pub fn run_solve(
|
||||||
pub fn make_solved_types(
|
pub fn make_solved_types(
|
||||||
solved_env: &solve::Env,
|
solved_env: &solve::Env,
|
||||||
solved_subs: &Solved<Subs>,
|
solved_subs: &Solved<Subs>,
|
||||||
exposed_vars_by_symbol: &[(Symbol, Variable)],
|
exposed_vars_by_symbol: &MutMap<Symbol, Variable>,
|
||||||
) -> MutMap<Symbol, SolvedType> {
|
) -> MutMap<Symbol, SolvedType> {
|
||||||
let mut solved_types = MutMap::default();
|
let mut solved_types = MutMap::default();
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use roc_can::constraint::Constraint::{self, *};
|
use roc_can::constraint::Constraint::{self, *};
|
||||||
use roc_can::expected::{Expected, PExpected};
|
use roc_can::expected::{Expected, PExpected};
|
||||||
use roc_collections::all::{ImMap, MutMap, SendMap};
|
use roc_collections::all::{ImMap, MutMap};
|
||||||
use roc_module::ident::TagName;
|
use roc_module::ident::TagName;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_region::all::{Located, Region};
|
use roc_region::all::{Located, Region};
|
||||||
|
@ -73,7 +73,7 @@ pub enum TypeError {
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct Env {
|
pub struct Env {
|
||||||
pub vars_by_symbol: SendMap<Symbol, Variable>,
|
pub vars_by_symbol: MutMap<Symbol, Variable>,
|
||||||
pub aliases: MutMap<Symbol, Alias>,
|
pub aliases: MutMap<Symbol, Alias>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,24 +138,9 @@ pub fn run(
|
||||||
mut subs: Subs,
|
mut subs: Subs,
|
||||||
constraint: &Constraint,
|
constraint: &Constraint,
|
||||||
) -> (Solved<Subs>, Env) {
|
) -> (Solved<Subs>, Env) {
|
||||||
let mut pools = Pools::default();
|
let env = run_in_place(env, problems, &mut subs, constraint);
|
||||||
let state = State {
|
|
||||||
env: env.clone(),
|
|
||||||
mark: Mark::NONE.next(),
|
|
||||||
};
|
|
||||||
let rank = Rank::toplevel();
|
|
||||||
let state = solve(
|
|
||||||
env,
|
|
||||||
state,
|
|
||||||
rank,
|
|
||||||
&mut pools,
|
|
||||||
problems,
|
|
||||||
&mut MutMap::default(),
|
|
||||||
&mut subs,
|
|
||||||
constraint,
|
|
||||||
);
|
|
||||||
|
|
||||||
(Solved(subs), state.env)
|
(Solved(subs), env)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Modify an existing subs in-place instead
|
/// Modify an existing subs in-place instead
|
||||||
|
|
|
@ -8,7 +8,7 @@ use roc_can::expected::Expected;
|
||||||
use roc_can::expr::{canonicalize_expr, Expr, Output};
|
use roc_can::expr::{canonicalize_expr, Expr, Output};
|
||||||
use roc_can::operator;
|
use roc_can::operator;
|
||||||
use roc_can::scope::Scope;
|
use roc_can::scope::Scope;
|
||||||
use roc_collections::all::{ImMap, ImSet, MutMap, SendMap, SendSet};
|
use roc_collections::all::{ImMap, ImSet, MutMap, SendSet};
|
||||||
use roc_constrain::expr::constrain_expr;
|
use roc_constrain::expr::constrain_expr;
|
||||||
use roc_constrain::module::{constrain_imported_values, Import};
|
use roc_constrain::module::{constrain_imported_values, Import};
|
||||||
use roc_module::ident::Ident;
|
use roc_module::ident::Ident;
|
||||||
|
@ -37,7 +37,7 @@ pub fn infer_expr(
|
||||||
) -> (Content, Subs) {
|
) -> (Content, Subs) {
|
||||||
let env = solve::Env {
|
let env = solve::Env {
|
||||||
aliases: MutMap::default(),
|
aliases: MutMap::default(),
|
||||||
vars_by_symbol: SendMap::default(),
|
vars_by_symbol: MutMap::default(),
|
||||||
};
|
};
|
||||||
let (solved, _) = solve::run(&env, problems, subs, constraint);
|
let (solved, _) = solve::run(&env, problems, subs, constraint);
|
||||||
|
|
||||||
|
|
|
@ -1326,7 +1326,7 @@ mod solve_expr {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
int : Num.Num Num.Integer
|
int : Num.Num (Num.Integer Num.Signed64)
|
||||||
|
|
||||||
int
|
int
|
||||||
"#
|
"#
|
||||||
|
@ -1339,7 +1339,7 @@ mod solve_expr {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
int : Num.Num Num.Integer
|
int : Num.Num (Num.Integer Num.Signed64)
|
||||||
int = 5
|
int = 5
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -1353,7 +1353,7 @@ mod solve_expr {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
int : Num Integer
|
int : Num (Integer Signed64)
|
||||||
|
|
||||||
int
|
int
|
||||||
"#
|
"#
|
||||||
|
@ -1366,7 +1366,7 @@ mod solve_expr {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
int : Num Integer
|
int : Num (Integer Signed64)
|
||||||
int = 5
|
int = 5
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -1931,7 +1931,7 @@ mod solve_expr {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
float : Num.Num Num.FloatingPoint
|
float : Num.Num (Num.FloatingPoint Num.Binary64)
|
||||||
|
|
||||||
float
|
float
|
||||||
"#
|
"#
|
||||||
|
@ -1944,7 +1944,7 @@ mod solve_expr {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
float : Num.Num Num.FloatingPoint
|
float : Num.Num (Num.FloatingPoint Num.Binary64)
|
||||||
float = 5.5
|
float = 5.5
|
||||||
|
|
||||||
float
|
float
|
||||||
|
@ -1958,7 +1958,7 @@ mod solve_expr {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
float : Num FloatingPoint
|
float : Num (FloatingPoint Binary64)
|
||||||
|
|
||||||
float
|
float
|
||||||
"#
|
"#
|
||||||
|
@ -1971,7 +1971,7 @@ mod solve_expr {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
float : Num FloatingPoint
|
float : Num (FloatingPoint Binary64)
|
||||||
float = 5.5
|
float = 5.5
|
||||||
|
|
||||||
float
|
float
|
||||||
|
@ -2216,7 +2216,7 @@ mod solve_expr {
|
||||||
infer_eq_without_problem(
|
infer_eq_without_problem(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
x : Num.Num Num.Integer
|
x : Num.Num (Num.Integer Num.Signed64)
|
||||||
x =
|
x =
|
||||||
when 2 is
|
when 2 is
|
||||||
3 -> 4
|
3 -> 4
|
||||||
|
@ -2428,7 +2428,7 @@ mod solve_expr {
|
||||||
r#"
|
r#"
|
||||||
Foo a : { foo : a }
|
Foo a : { foo : a }
|
||||||
|
|
||||||
v : Foo (Num.Num Num.Integer)
|
v : Foo (Num.Num (Num.Integer Num.Signed64))
|
||||||
v = { foo: 42 }
|
v = { foo: 42 }
|
||||||
|
|
||||||
v
|
v
|
||||||
|
@ -2492,7 +2492,7 @@ mod solve_expr {
|
||||||
r#"
|
r#"
|
||||||
Peano : [ S Peano, Z ]
|
Peano : [ S Peano, Z ]
|
||||||
|
|
||||||
length : Peano -> Num.Num Num.Integer
|
length : Peano -> Num.Num (Num.Integer Num.Signed64)
|
||||||
length = \peano ->
|
length = \peano ->
|
||||||
when peano is
|
when peano is
|
||||||
Z -> 0
|
Z -> 0
|
||||||
|
@ -2592,10 +2592,10 @@ mod solve_expr {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
r : { x : (Num.Num Num.Integer) }
|
r : { x : (Num.Num (Num.Integer Signed64)) }
|
||||||
r = { x : 1 }
|
r = { x : 1 }
|
||||||
|
|
||||||
s : { left : { x : Num.Num Num.FloatingPoint } }
|
s : { left : { x : Num.Num (Num.FloatingPoint Num.Binary64) } }
|
||||||
s = { left: { x : 3.14 } }
|
s = { left: { x : 3.14 } }
|
||||||
|
|
||||||
when 0 is
|
when 0 is
|
||||||
|
@ -2757,7 +2757,7 @@ mod solve_expr {
|
||||||
infer_eq_without_problem(
|
infer_eq_without_problem(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
{ x, y } : { x : Str.Str, y : Num.Num Num.FloatingPoint }
|
{ x, y } : { x : Str.Str, y : Num.Num (Num.FloatingPoint Num.Binary64) }
|
||||||
{ x, y } = { x : "foo", y : 3.14 }
|
{ x, y } = { x : "foo", y : 3.14 }
|
||||||
|
|
||||||
x
|
x
|
||||||
|
@ -2772,7 +2772,7 @@ mod solve_expr {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
Foo : { x : Str.Str, y : Num.Num Num.FloatingPoint }
|
Foo : { x : Str.Str, y : Num.Num (Num.FloatingPoint Num.Binary64) }
|
||||||
|
|
||||||
{ x, y } : Foo
|
{ x, y } : Foo
|
||||||
{ x, y } = { x : "foo", y : 3.14 }
|
{ x, y } = { x : "foo", y : 3.14 }
|
||||||
|
@ -2830,7 +2830,7 @@ mod solve_expr {
|
||||||
infer_eq_without_problem(
|
infer_eq_without_problem(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
Foo : { x : Str.Str, y : Num.Num Num.FloatingPoint }
|
Foo : { x : Str.Str, y : Num.Num (Num.FloatingPoint Num.Binary64) }
|
||||||
|
|
||||||
{ x, y } : Foo
|
{ x, y } : Foo
|
||||||
{ x, y } = { x : "foo", y : 3.14 }
|
{ x, y } = { x : "foo", y : 3.14 }
|
||||||
|
|
|
@ -1118,7 +1118,7 @@ mod solve_uniq_expr {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
x : Num.Num Num.Integer
|
x : Num.Num (Num.Integer Num.Signed64)
|
||||||
x = 4
|
x = 4
|
||||||
|
|
||||||
x
|
x
|
||||||
|
@ -1368,7 +1368,7 @@ mod solve_uniq_expr {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
x : Num.Num Num.Integer
|
x : I64
|
||||||
x =
|
x =
|
||||||
when 2 is
|
when 2 is
|
||||||
3 -> 4
|
3 -> 4
|
||||||
|
@ -1816,7 +1816,7 @@ mod solve_uniq_expr {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
{ x, y } : { x : Str.Str, y : Num.Num Num.FloatingPoint }
|
{ x, y } : { x : Str.Str, y : F64 }
|
||||||
{ x, y } = { x : "foo", y : 3.14 }
|
{ x, y } = { x : "foo", y : 3.14 }
|
||||||
|
|
||||||
x
|
x
|
||||||
|
@ -2662,7 +2662,7 @@ mod solve_uniq_expr {
|
||||||
f
|
f
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * (Attr a I64, Attr b I64 -> Attr c I64)",
|
"Attr * (Attr b I64, Attr c I64 -> Attr d I64)",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3159,4 +3159,55 @@ mod solve_uniq_expr {
|
||||||
"Attr a I64",
|
"Attr a I64",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_set_out_of_bounds_num() {
|
||||||
|
infer_eq(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
List.set [2] 1337 0
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"Attr * (List (Attr * (Num (Attr * *))))",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_set_out_of_bounds_int() {
|
||||||
|
infer_eq(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
List.set [0x2] 1337 0
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"Attr * (List (Attr * I64))",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_set_out_of_bounds_float() {
|
||||||
|
infer_eq(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
List.set [0.2] 1337 0
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"Attr * (List (Attr * F64))",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn list_set_out_of_bounds_int_int() {
|
||||||
|
// the unification of an integer list with a new integer element is a problem
|
||||||
|
// same for floats, but it's fine with the unspecified Num
|
||||||
|
infer_eq(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
List.set [0x2] 1337 0x1
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"Attr * (List (Attr a I64))",
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,143 +38,213 @@ pub fn aliases() -> MutMap<Symbol, BuiltinAlias> {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// Integer : [ @Integer ]
|
// Integer range : [ @Integer range ]
|
||||||
add_alias(
|
add_alias(
|
||||||
Symbol::NUM_INTEGER,
|
Symbol::NUM_INTEGER,
|
||||||
BuiltinAlias {
|
BuiltinAlias {
|
||||||
region: Region::zero(),
|
region: Region::zero(),
|
||||||
vars: Vec::new(),
|
vars: vec![Located::at(Region::zero(), "range".into())],
|
||||||
typ: integer_alias_content(),
|
typ: integer_alias_content(flex(TVAR1)),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// I128 Num Integer
|
// Signed128 : [ @Signed128 ]
|
||||||
|
add_alias(
|
||||||
|
Symbol::NUM_SIGNED128,
|
||||||
|
BuiltinAlias {
|
||||||
|
region: Region::zero(),
|
||||||
|
vars: vec![],
|
||||||
|
typ: signed128_alias_content(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// I128 : Num (Integer Signed128)
|
||||||
add_alias(
|
add_alias(
|
||||||
Symbol::NUM_I128,
|
Symbol::NUM_I128,
|
||||||
BuiltinAlias {
|
BuiltinAlias {
|
||||||
region: Region::zero(),
|
region: Region::zero(),
|
||||||
vars: Vec::new(),
|
vars: Vec::new(),
|
||||||
typ: int_alias_content(),
|
typ: int_alias_content(signed128_type()),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// U128 : Num Integer
|
// U128 : Num (Integer Unsigned128)
|
||||||
add_alias(
|
add_alias(
|
||||||
Symbol::NUM_U128,
|
Symbol::NUM_U128,
|
||||||
BuiltinAlias {
|
BuiltinAlias {
|
||||||
region: Region::zero(),
|
region: Region::zero(),
|
||||||
vars: Vec::new(),
|
vars: Vec::new(),
|
||||||
typ: int_alias_content(),
|
typ: int_alias_content(unsigned128_type()),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// I64 Num Integer
|
// Signed64 : [ @Signed64 ]
|
||||||
|
add_alias(
|
||||||
|
Symbol::NUM_SIGNED64,
|
||||||
|
BuiltinAlias {
|
||||||
|
region: Region::zero(),
|
||||||
|
vars: vec![],
|
||||||
|
typ: signed64_alias_content(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// I64 : Num (Integer Signed64)
|
||||||
add_alias(
|
add_alias(
|
||||||
Symbol::NUM_I64,
|
Symbol::NUM_I64,
|
||||||
BuiltinAlias {
|
BuiltinAlias {
|
||||||
region: Region::zero(),
|
region: Region::zero(),
|
||||||
vars: Vec::new(),
|
vars: Vec::new(),
|
||||||
typ: int_alias_content(),
|
typ: int_alias_content(signed64_type()),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// U64 : Num Integer
|
// U64 : Num (Integer Unsigned64)
|
||||||
add_alias(
|
add_alias(
|
||||||
Symbol::NUM_U64,
|
Symbol::NUM_U64,
|
||||||
BuiltinAlias {
|
BuiltinAlias {
|
||||||
region: Region::zero(),
|
region: Region::zero(),
|
||||||
vars: Vec::new(),
|
vars: Vec::new(),
|
||||||
typ: int_alias_content(),
|
typ: int_alias_content(unsigned64_type()),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// I32 Num Integer
|
// Signed32 : [ @Signed32 ]
|
||||||
|
add_alias(
|
||||||
|
Symbol::NUM_SIGNED32,
|
||||||
|
BuiltinAlias {
|
||||||
|
region: Region::zero(),
|
||||||
|
vars: vec![],
|
||||||
|
typ: signed32_alias_content(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// I32 : Num (Integer Signed32)
|
||||||
add_alias(
|
add_alias(
|
||||||
Symbol::NUM_I32,
|
Symbol::NUM_I32,
|
||||||
BuiltinAlias {
|
BuiltinAlias {
|
||||||
region: Region::zero(),
|
region: Region::zero(),
|
||||||
vars: Vec::new(),
|
vars: Vec::new(),
|
||||||
typ: int_alias_content(),
|
typ: int_alias_content(signed32_type()),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// U32 : Num Integer
|
// U32 : Num (Integer Unsigned32)
|
||||||
add_alias(
|
add_alias(
|
||||||
Symbol::NUM_U32,
|
Symbol::NUM_U32,
|
||||||
BuiltinAlias {
|
BuiltinAlias {
|
||||||
region: Region::zero(),
|
region: Region::zero(),
|
||||||
vars: Vec::new(),
|
vars: Vec::new(),
|
||||||
typ: int_alias_content(),
|
typ: int_alias_content(unsigned32_type()),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// I16 Num Integer
|
// Signed16 : [ @Signed16 ]
|
||||||
|
add_alias(
|
||||||
|
Symbol::NUM_SIGNED16,
|
||||||
|
BuiltinAlias {
|
||||||
|
region: Region::zero(),
|
||||||
|
vars: vec![],
|
||||||
|
typ: signed16_alias_content(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// I16 : Num (Integer Signed16)
|
||||||
add_alias(
|
add_alias(
|
||||||
Symbol::NUM_I16,
|
Symbol::NUM_I16,
|
||||||
BuiltinAlias {
|
BuiltinAlias {
|
||||||
region: Region::zero(),
|
region: Region::zero(),
|
||||||
vars: Vec::new(),
|
vars: Vec::new(),
|
||||||
typ: int_alias_content(),
|
typ: int_alias_content(signed16_type()),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// U16 : Num Integer
|
// U16 : Num (Integer Unsigned16)
|
||||||
add_alias(
|
add_alias(
|
||||||
Symbol::NUM_U16,
|
Symbol::NUM_U16,
|
||||||
BuiltinAlias {
|
BuiltinAlias {
|
||||||
region: Region::zero(),
|
region: Region::zero(),
|
||||||
vars: Vec::new(),
|
vars: Vec::new(),
|
||||||
typ: int_alias_content(),
|
typ: int_alias_content(unsigned16_type()),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// I8 Num Integer
|
// Signed8 : [ @Signed8 ]
|
||||||
|
add_alias(
|
||||||
|
Symbol::NUM_SIGNED8,
|
||||||
|
BuiltinAlias {
|
||||||
|
region: Region::zero(),
|
||||||
|
vars: vec![],
|
||||||
|
typ: signed8_alias_content(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// I8 : Num (Integer Signed8)
|
||||||
add_alias(
|
add_alias(
|
||||||
Symbol::NUM_I8,
|
Symbol::NUM_I8,
|
||||||
BuiltinAlias {
|
BuiltinAlias {
|
||||||
region: Region::zero(),
|
region: Region::zero(),
|
||||||
vars: Vec::new(),
|
vars: Vec::new(),
|
||||||
typ: int_alias_content(),
|
typ: int_alias_content(signed8_type()),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// U8 : Num Integer
|
// U8 : Num (Integer Unsigned8)
|
||||||
add_alias(
|
add_alias(
|
||||||
Symbol::NUM_U8,
|
Symbol::NUM_U8,
|
||||||
BuiltinAlias {
|
BuiltinAlias {
|
||||||
region: Region::zero(),
|
region: Region::zero(),
|
||||||
vars: Vec::new(),
|
vars: Vec::new(),
|
||||||
typ: int_alias_content(),
|
typ: int_alias_content(unsigned8_type()),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// FloatingPoint : [ @FloatingPoint ]
|
// Binary64 : [ @Binary64 ]
|
||||||
|
add_alias(
|
||||||
|
Symbol::NUM_BINARY64,
|
||||||
|
BuiltinAlias {
|
||||||
|
region: Region::zero(),
|
||||||
|
vars: vec![],
|
||||||
|
typ: binary64_alias_content(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Binary32 : [ @Binary32 ]
|
||||||
|
add_alias(
|
||||||
|
Symbol::NUM_BINARY32,
|
||||||
|
BuiltinAlias {
|
||||||
|
region: Region::zero(),
|
||||||
|
vars: vec![],
|
||||||
|
typ: binary32_alias_content(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// FloatingPoint range : [ @FloatingPoint range ]
|
||||||
add_alias(
|
add_alias(
|
||||||
Symbol::NUM_FLOATINGPOINT,
|
Symbol::NUM_FLOATINGPOINT,
|
||||||
BuiltinAlias {
|
BuiltinAlias {
|
||||||
region: Region::zero(),
|
region: Region::zero(),
|
||||||
vars: Vec::new(),
|
vars: vec![Located::at(Region::zero(), "range".into())],
|
||||||
typ: floatingpoint_alias_content(),
|
typ: floatingpoint_alias_content(flex(TVAR1)),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// F64 : Num FloatingPoint
|
// F64 : Num (FloatingPoint Binary64)
|
||||||
add_alias(
|
add_alias(
|
||||||
Symbol::NUM_F64,
|
Symbol::NUM_F64,
|
||||||
BuiltinAlias {
|
BuiltinAlias {
|
||||||
region: Region::zero(),
|
region: Region::zero(),
|
||||||
vars: Vec::new(),
|
vars: Vec::new(),
|
||||||
typ: float_alias_content(),
|
typ: float_alias_content(binary64_type()),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// F32 : Num FloatingPoint
|
// F32 : Num (FloatingPoint Binary32)
|
||||||
add_alias(
|
add_alias(
|
||||||
Symbol::NUM_F32,
|
Symbol::NUM_F32,
|
||||||
BuiltinAlias {
|
BuiltinAlias {
|
||||||
region: Region::zero(),
|
region: Region::zero(),
|
||||||
vars: Vec::new(),
|
vars: Vec::new(),
|
||||||
typ: float_alias_content(),
|
typ: float_alias_content(binary32_type()),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -226,57 +296,233 @@ fn num_alias_content(range: SolvedType) -> SolvedType {
|
||||||
// FLOATING POINT
|
// FLOATING POINT
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn floatingpoint_type() -> SolvedType {
|
pub fn floatingpoint_type(range: SolvedType) -> SolvedType {
|
||||||
SolvedType::Alias(
|
SolvedType::Alias(
|
||||||
Symbol::NUM_FLOATINGPOINT,
|
Symbol::NUM_FLOATINGPOINT,
|
||||||
Vec::new(),
|
vec![("range".into(), range.clone())],
|
||||||
Box::new(floatingpoint_alias_content()),
|
Box::new(floatingpoint_alias_content(range)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn floatingpoint_alias_content() -> SolvedType {
|
fn floatingpoint_alias_content(range: SolvedType) -> SolvedType {
|
||||||
single_private_tag(Symbol::NUM_AT_FLOATINGPOINT, Vec::new())
|
single_private_tag(Symbol::NUM_AT_FLOATINGPOINT, vec![range])
|
||||||
}
|
}
|
||||||
|
|
||||||
// FLOAT
|
// FLOAT
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn float_type() -> SolvedType {
|
pub fn float_type() -> SolvedType {
|
||||||
SolvedType::Alias(Symbol::NUM_F64, Vec::new(), Box::new(float_alias_content()))
|
SolvedType::Alias(
|
||||||
|
Symbol::NUM_F64,
|
||||||
|
Vec::new(),
|
||||||
|
Box::new(float_alias_content(binary64_type())),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn float_alias_content() -> SolvedType {
|
fn float_alias_content(typ: SolvedType) -> SolvedType {
|
||||||
num_type(floatingpoint_type())
|
num_type(floatingpoint_type(typ))
|
||||||
}
|
}
|
||||||
|
|
||||||
// INT
|
// INT
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn int_type() -> SolvedType {
|
pub fn int_type() -> SolvedType {
|
||||||
SolvedType::Alias(Symbol::NUM_I64, Vec::new(), Box::new(int_alias_content()))
|
SolvedType::Alias(
|
||||||
|
Symbol::NUM_I64,
|
||||||
|
Vec::new(),
|
||||||
|
Box::new(int_alias_content(signed64_type())),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn int_alias_content() -> SolvedType {
|
fn int_alias_content(range: SolvedType) -> SolvedType {
|
||||||
num_type(integer_type())
|
num_type(integer_type(range))
|
||||||
}
|
}
|
||||||
|
|
||||||
// INTEGER
|
// INTEGER
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn integer_type() -> SolvedType {
|
pub fn integer_type(range: SolvedType) -> SolvedType {
|
||||||
SolvedType::Alias(
|
SolvedType::Alias(
|
||||||
Symbol::NUM_INTEGER,
|
Symbol::NUM_INTEGER,
|
||||||
Vec::new(),
|
vec![("range".into(), range.clone())],
|
||||||
Box::new(integer_alias_content()),
|
Box::new(integer_alias_content(range)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn integer_alias_content() -> SolvedType {
|
fn integer_alias_content(range: SolvedType) -> SolvedType {
|
||||||
single_private_tag(Symbol::NUM_AT_INTEGER, Vec::new())
|
single_private_tag(Symbol::NUM_AT_INTEGER, vec![range])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn binary64_type() -> SolvedType {
|
||||||
|
SolvedType::Alias(
|
||||||
|
Symbol::NUM_BINARY64,
|
||||||
|
vec![],
|
||||||
|
Box::new(binary64_alias_content()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn binary64_alias_content() -> SolvedType {
|
||||||
|
single_private_tag(Symbol::NUM_AT_BINARY64, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn binary32_type() -> SolvedType {
|
||||||
|
SolvedType::Alias(
|
||||||
|
Symbol::NUM_BINARY32,
|
||||||
|
vec![],
|
||||||
|
Box::new(binary32_alias_content()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn binary32_alias_content() -> SolvedType {
|
||||||
|
single_private_tag(Symbol::NUM_AT_BINARY32, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn signed128_type() -> SolvedType {
|
||||||
|
SolvedType::Alias(
|
||||||
|
Symbol::NUM_SIGNED128,
|
||||||
|
vec![],
|
||||||
|
Box::new(signed128_alias_content()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn signed128_alias_content() -> SolvedType {
|
||||||
|
single_private_tag(Symbol::NUM_AT_SIGNED128, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn signed64_type() -> SolvedType {
|
||||||
|
SolvedType::Alias(
|
||||||
|
Symbol::NUM_SIGNED64,
|
||||||
|
vec![],
|
||||||
|
Box::new(signed64_alias_content()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn signed64_alias_content() -> SolvedType {
|
||||||
|
single_private_tag(Symbol::NUM_AT_SIGNED64, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn signed32_type() -> SolvedType {
|
||||||
|
SolvedType::Alias(
|
||||||
|
Symbol::NUM_SIGNED32,
|
||||||
|
vec![],
|
||||||
|
Box::new(signed32_alias_content()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn signed32_alias_content() -> SolvedType {
|
||||||
|
single_private_tag(Symbol::NUM_AT_SIGNED32, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn signed16_type() -> SolvedType {
|
||||||
|
SolvedType::Alias(
|
||||||
|
Symbol::NUM_SIGNED16,
|
||||||
|
vec![],
|
||||||
|
Box::new(signed16_alias_content()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn signed16_alias_content() -> SolvedType {
|
||||||
|
single_private_tag(Symbol::NUM_AT_SIGNED16, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn signed8_type() -> SolvedType {
|
||||||
|
SolvedType::Alias(
|
||||||
|
Symbol::NUM_SIGNED8,
|
||||||
|
vec![],
|
||||||
|
Box::new(signed8_alias_content()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn signed8_alias_content() -> SolvedType {
|
||||||
|
single_private_tag(Symbol::NUM_AT_SIGNED8, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn unsigned128_type() -> SolvedType {
|
||||||
|
SolvedType::Alias(
|
||||||
|
Symbol::NUM_UNSIGNED128,
|
||||||
|
vec![],
|
||||||
|
Box::new(unsigned128_alias_content()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn unsigned128_alias_content() -> SolvedType {
|
||||||
|
single_private_tag(Symbol::NUM_AT_UNSIGNED128, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn unsigned64_type() -> SolvedType {
|
||||||
|
SolvedType::Alias(
|
||||||
|
Symbol::NUM_UNSIGNED64,
|
||||||
|
vec![],
|
||||||
|
Box::new(unsigned64_alias_content()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn unsigned64_alias_content() -> SolvedType {
|
||||||
|
single_private_tag(Symbol::NUM_AT_UNSIGNED64, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn unsigned32_type() -> SolvedType {
|
||||||
|
SolvedType::Alias(
|
||||||
|
Symbol::NUM_UNSIGNED32,
|
||||||
|
vec![],
|
||||||
|
Box::new(unsigned32_alias_content()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn unsigned32_alias_content() -> SolvedType {
|
||||||
|
single_private_tag(Symbol::NUM_AT_UNSIGNED32, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn unsigned16_type() -> SolvedType {
|
||||||
|
SolvedType::Alias(
|
||||||
|
Symbol::NUM_UNSIGNED16,
|
||||||
|
vec![],
|
||||||
|
Box::new(unsigned16_alias_content()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn unsigned16_alias_content() -> SolvedType {
|
||||||
|
single_private_tag(Symbol::NUM_AT_UNSIGNED16, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn unsigned8_type() -> SolvedType {
|
||||||
|
SolvedType::Alias(
|
||||||
|
Symbol::NUM_UNSIGNED8,
|
||||||
|
vec![],
|
||||||
|
Box::new(unsigned8_alias_content()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn unsigned8_alias_content() -> SolvedType {
|
||||||
|
single_private_tag(Symbol::NUM_AT_UNSIGNED8, vec![])
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -351,7 +597,7 @@ pub fn dict_type(key: SolvedType, value: SolvedType) -> SolvedType {
|
||||||
SolvedType::Apply(Symbol::DICT_DICT, vec![key, value])
|
SolvedType::Apply(Symbol::DICT_DICT, vec![key, value])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn single_private_tag(symbol: Symbol, type_arguments: Vec<SolvedType>) -> SolvedType {
|
pub fn single_private_tag(symbol: Symbol, type_arguments: Vec<SolvedType>) -> SolvedType {
|
||||||
SolvedType::TagUnion(
|
SolvedType::TagUnion(
|
||||||
vec![(TagName::Private(symbol), type_arguments)],
|
vec![(TagName::Private(symbol), type_arguments)],
|
||||||
Box::new(SolvedType::EmptyTagUnion),
|
Box::new(SolvedType::EmptyTagUnion),
|
||||||
|
|
|
@ -753,10 +753,10 @@ fn write_apply(
|
||||||
|
|
||||||
match &arg_content {
|
match &arg_content {
|
||||||
Content::Structure(FlatType::Apply(symbol, nested_args)) => match *symbol {
|
Content::Structure(FlatType::Apply(symbol, nested_args)) => match *symbol {
|
||||||
Symbol::NUM_INTEGER if nested_args.is_empty() => {
|
Symbol::NUM_INTEGER if nested_args.len() == 1 => {
|
||||||
buf.push_str("I64");
|
buf.push_str("I64");
|
||||||
}
|
}
|
||||||
Symbol::NUM_FLOATINGPOINT if nested_args.is_empty() => {
|
Symbol::NUM_FLOATINGPOINT if nested_args.len() == 1 => {
|
||||||
buf.push_str("F64");
|
buf.push_str("F64");
|
||||||
}
|
}
|
||||||
Symbol::ATTR_ATTR => match nested_args
|
Symbol::ATTR_ATTR => match nested_args
|
||||||
|
@ -767,10 +767,10 @@ fn write_apply(
|
||||||
double_nested_symbol,
|
double_nested_symbol,
|
||||||
double_nested_args,
|
double_nested_args,
|
||||||
))) => match double_nested_symbol {
|
))) => match double_nested_symbol {
|
||||||
Symbol::NUM_INTEGER if double_nested_args.is_empty() => {
|
Symbol::NUM_INTEGER if double_nested_args.len() == 1 => {
|
||||||
buf.push_str("I64");
|
buf.push_str("I64");
|
||||||
}
|
}
|
||||||
Symbol::NUM_FLOATINGPOINT if double_nested_args.is_empty() => {
|
Symbol::NUM_FLOATINGPOINT if double_nested_args.len() == 1 => {
|
||||||
buf.push_str("F64");
|
buf.push_str("F64");
|
||||||
}
|
}
|
||||||
_ => default_case(subs, arg_content),
|
_ => default_case(subs, arg_content),
|
||||||
|
|
|
@ -198,7 +198,7 @@ fn unify_alias(
|
||||||
|
|
||||||
problems
|
problems
|
||||||
} else {
|
} else {
|
||||||
mismatch!()
|
mismatch!("{}", symbol)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
unify_pool(subs, pool, real_var, *other_real_var)
|
unify_pool(subs, pool, real_var, *other_real_var)
|
||||||
|
|
|
@ -8,7 +8,7 @@ use roc_can::expected::Expected;
|
||||||
use roc_can::expr::{canonicalize_expr, Expr, Output};
|
use roc_can::expr::{canonicalize_expr, Expr, Output};
|
||||||
use roc_can::operator;
|
use roc_can::operator;
|
||||||
use roc_can::scope::Scope;
|
use roc_can::scope::Scope;
|
||||||
use roc_collections::all::{ImMap, ImSet, MutMap, SendMap, SendSet};
|
use roc_collections::all::{ImMap, ImSet, MutMap, SendSet};
|
||||||
use roc_constrain::expr::constrain_expr;
|
use roc_constrain::expr::constrain_expr;
|
||||||
use roc_constrain::module::{constrain_imported_values, Import};
|
use roc_constrain::module::{constrain_imported_values, Import};
|
||||||
use roc_module::ident::Ident;
|
use roc_module::ident::Ident;
|
||||||
|
@ -37,7 +37,7 @@ pub fn infer_expr(
|
||||||
) -> (Content, Subs) {
|
) -> (Content, Subs) {
|
||||||
let env = solve::Env {
|
let env = solve::Env {
|
||||||
aliases: MutMap::default(),
|
aliases: MutMap::default(),
|
||||||
vars_by_symbol: SendMap::default(),
|
vars_by_symbol: MutMap::default(),
|
||||||
};
|
};
|
||||||
let (solved, _) = solve::run(&env, problems, subs, constraint);
|
let (solved, _) = solve::run(&env, problems, subs, constraint);
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,6 @@ app "effect-example"
|
||||||
imports [base.Task]
|
imports [base.Task]
|
||||||
provides [ main ] to base
|
provides [ main ] to base
|
||||||
|
|
||||||
main : Task.Task {} F64
|
main : Task.Task {} []
|
||||||
main =
|
main =
|
||||||
Task.after Task.getLine \lineThisThing -> Task.putLine lineThisThing
|
Task.after Task.getLine \lineThisThing -> Task.putLine lineThisThing
|
||||||
|
|
|
@ -12,5 +12,5 @@ platform folkertdev/foo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
mainForHost : Task.Task {} F64 as Fx
|
mainForHost : Task.Task {} [] as Fx
|
||||||
mainForHost = main
|
mainForHost = main
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue