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
|
||||
|
||||
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:
|
||||
test:
|
||||
name: fmt, clippy, test, test --release
|
||||
|
@ -10,9 +18,23 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Log CPU model
|
||||
run: sudo cat /proc/cpuinfo | grep name | uniq
|
||||
|
||||
- name: Install CI Libraries
|
||||
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
|
||||
run: pushd compiler/builtins/bitcode; ./run-tests.sh; popd;
|
||||
|
||||
|
@ -28,24 +50,6 @@ jobs:
|
|||
|
||||
- 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
|
||||
name: rustfmt version
|
||||
with:
|
||||
|
@ -72,15 +76,11 @@ jobs:
|
|||
command: clippy
|
||||
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
|
||||
name: cargo test --release
|
||||
with:
|
||||
command: test
|
||||
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
|
||||
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
|
||||
|
||||
# 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,
|
||||
}
|
||||
|
||||
pub enum ToAstProblem {
|
||||
FunctionLayout,
|
||||
}
|
||||
|
||||
/// 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.
|
||||
///
|
||||
|
@ -39,7 +43,7 @@ pub unsafe fn jit_to_ast<'a>(
|
|||
home: ModuleId,
|
||||
subs: &Subs,
|
||||
ptr_bytes: u32,
|
||||
) -> Expr<'a> {
|
||||
) -> Result<Expr<'a>, ToAstProblem> {
|
||||
let env = Env {
|
||||
arena,
|
||||
subs,
|
||||
|
@ -57,55 +61,57 @@ fn jit_to_ast_help<'a>(
|
|||
main_fn_name: &str,
|
||||
layout: &Layout<'a>,
|
||||
content: &Content,
|
||||
) -> Expr<'a> {
|
||||
) -> Result<Expr<'a>, ToAstProblem> {
|
||||
match layout {
|
||||
Layout::Builtin(Builtin::Int1) => {
|
||||
run_jit_function!(lib, main_fn_name, bool, |num| bool_to_ast(
|
||||
env, num, content
|
||||
))
|
||||
}
|
||||
Layout::Builtin(Builtin::Int1) => Ok(run_jit_function!(lib, main_fn_name, bool, |num| {
|
||||
bool_to_ast(env, num, content)
|
||||
})),
|
||||
Layout::Builtin(Builtin::Int8) => {
|
||||
// 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))
|
||||
Ok(
|
||||
// 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) => {
|
||||
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,
|
||||
i64_to_ast(env.arena, num),
|
||||
content
|
||||
))
|
||||
)))
|
||||
}
|
||||
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,
|
||||
f64_to_ast(env.arena, num),
|
||||
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| {
|
||||
str_to_ast(env.arena, env.arena.alloc(string))
|
||||
})
|
||||
}
|
||||
}),
|
||||
),
|
||||
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,
|
||||
main_fn_name,
|
||||
(*const u8, usize),
|
||||
|(ptr, len): (*const u8, usize)| { list_to_ast(env, ptr, len, elem_layout, content) }
|
||||
),
|
||||
)),
|
||||
Layout::Builtin(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 {
|
||||
update: None,
|
||||
fields: &[],
|
||||
final_comments: env.arena.alloc([]),
|
||||
}
|
||||
}),
|
||||
})),
|
||||
Layout::Struct(field_layouts) => {
|
||||
let ptr_to_ast = |ptr: *const u8| match content {
|
||||
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);
|
||||
|
||||
run_jit_function_dynamic_type!(
|
||||
Ok(run_jit_function_dynamic_type!(
|
||||
lib,
|
||||
main_fn_name,
|
||||
result_stack_size as usize,
|
||||
|bytes: *const u8| { ptr_to_ast(bytes as *const u8) }
|
||||
)
|
||||
))
|
||||
}
|
||||
Layout::Union(union_layouts) => match content {
|
||||
Content::Structure(FlatType::TagUnion(tags, _)) => {
|
||||
|
@ -153,7 +159,7 @@ fn jit_to_ast_help<'a>(
|
|||
let size = layout.stack_size(env.ptr_bytes);
|
||||
match union_variant {
|
||||
UnionVariant::Wrapped(tags_and_layouts) => {
|
||||
run_jit_function_dynamic_type!(
|
||||
Ok(run_jit_function_dynamic_type!(
|
||||
lib,
|
||||
main_fn_name,
|
||||
size as usize,
|
||||
|
@ -181,7 +187,7 @@ fn jit_to_ast_help<'a>(
|
|||
|
||||
Expr::Apply(loc_tag_expr, output, CalledVia::Space)
|
||||
}
|
||||
)
|
||||
))
|
||||
}
|
||||
_ => unreachable!("any other variant would have a different layout"),
|
||||
}
|
||||
|
@ -201,7 +207,7 @@ fn jit_to_ast_help<'a>(
|
|||
}
|
||||
|
||||
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"),
|
||||
}
|
||||
|
|
|
@ -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 expr_type_str = content_to_string(content.clone(), &subs, home, &interns);
|
||||
|
||||
let (_, main_fn_layout) = procedures
|
||||
.keys()
|
||||
.find(|(s, _)| *s == main_fn_symbol)
|
||||
.unwrap()
|
||||
.clone();
|
||||
let (_, main_fn_layout) = match procedures.keys().find(|(s, _)| *s == main_fn_symbol) {
|
||||
Some(layout) => layout.clone(),
|
||||
None => {
|
||||
return Ok(ReplOutput::NoProblems {
|
||||
expr: "<function>".to_string(),
|
||||
expr_type: expr_type_str,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
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)
|
||||
.expect("Error loading compiled dylib for test");
|
||||
let answer = unsafe {
|
||||
let res_answer = unsafe {
|
||||
eval::jit_to_ast(
|
||||
&arena,
|
||||
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);
|
||||
|
||||
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 {
|
||||
expr: expr.into_bump_str().to_string(),
|
||||
|
|
|
@ -270,6 +270,41 @@ mod repl_eval {
|
|||
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]
|
||||
fn list_concat() {
|
||||
expect_success(
|
||||
|
@ -292,13 +327,16 @@ mod repl_eval {
|
|||
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]
|
||||
fn list_first() {
|
||||
expect_success(
|
||||
"List.first [ 12, 9, 6, 3 ]",
|
||||
"Ok 12 : Result (Num *) [ ListWasEmpty ]*",
|
||||
);
|
||||
expect_success(
|
||||
"List.first []",
|
||||
"Err (ListWasEmpty) : Result * [ ListWasEmpty ]*",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -307,6 +345,11 @@ mod repl_eval {
|
|||
"List.last [ 12, 9, 6, 3 ]",
|
||||
"Ok 3 : Result (Num *) [ ListWasEmpty ]*",
|
||||
);
|
||||
|
||||
expect_success(
|
||||
"List.last []",
|
||||
"Err (ListWasEmpty) : Result * [ ListWasEmpty ]*",
|
||||
);
|
||||
}
|
||||
|
||||
#[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]
|
||||
fn type_problem() {
|
||||
expect_failure(
|
||||
|
|
|
@ -75,17 +75,19 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
),
|
||||
);
|
||||
|
||||
// addChecked : Num a, Num a -> Result (Num a) [ Overflow ]*
|
||||
let overflow = SolvedType::TagUnion(
|
||||
vec![(TagName::Global("Overflow".into()), vec![])],
|
||||
Box::new(SolvedType::Wildcard),
|
||||
);
|
||||
fn overflow() -> SolvedType {
|
||||
SolvedType::TagUnion(
|
||||
vec![(TagName::Global("Overflow".into()), vec![])],
|
||||
Box::new(SolvedType::Wildcard),
|
||||
)
|
||||
}
|
||||
|
||||
// addChecked : Num a, Num a -> Result (Num a) [ Overflow ]*
|
||||
add_type(
|
||||
Symbol::NUM_ADD_CHECKED,
|
||||
top_level_function(
|
||||
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,
|
||||
top_level_function(
|
||||
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
|
||||
add_type(
|
||||
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))
|
||||
});
|
||||
|
||||
// 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
|
||||
add_type(Symbol::NUM_ABS, {
|
||||
let_tvars! { u, v, num };
|
||||
|
@ -1227,36 +1242,34 @@ fn lift(u: VarId, a: SolvedType) -> SolvedType {
|
|||
|
||||
#[inline(always)]
|
||||
fn float_type(u: VarId) -> SolvedType {
|
||||
let b_64 = builtin_aliases::binary64_type();
|
||||
let attr_b_64 = lift(u, b_64);
|
||||
let fp = builtin_aliases::floatingpoint_type(attr_b_64);
|
||||
let attr_fb = lift(u, fp);
|
||||
let num = builtin_aliases::num_type(attr_fb);
|
||||
|
||||
SolvedType::Apply(
|
||||
Symbol::ATTR_ATTR,
|
||||
vec![
|
||||
flex(u),
|
||||
SolvedType::Alias(
|
||||
Symbol::NUM_F64,
|
||||
Vec::new(),
|
||||
Box::new(builtin_aliases::num_type(SolvedType::Apply(
|
||||
Symbol::ATTR_ATTR,
|
||||
vec![flex(u), builtin_aliases::floatingpoint_type()],
|
||||
))),
|
||||
),
|
||||
SolvedType::Alias(Symbol::NUM_F64, Vec::new(), Box::new(num)),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn int_type(u: VarId) -> SolvedType {
|
||||
let signed_64 = builtin_aliases::signed64_type();
|
||||
let attr_signed_64 = lift(u, signed_64);
|
||||
let integer = builtin_aliases::integer_type(attr_signed_64);
|
||||
let attr_fb = lift(u, integer);
|
||||
let num = builtin_aliases::num_type(attr_fb);
|
||||
|
||||
SolvedType::Apply(
|
||||
Symbol::ATTR_ATTR,
|
||||
vec![
|
||||
flex(u),
|
||||
SolvedType::Alias(
|
||||
Symbol::NUM_I64,
|
||||
Vec::new(),
|
||||
Box::new(builtin_aliases::num_type(SolvedType::Apply(
|
||||
Symbol::ATTR_ATTR,
|
||||
vec![flex(u), builtin_aliases::integer_type()],
|
||||
))),
|
||||
),
|
||||
SolvedType::Alias(Symbol::NUM_I64, Vec::new(), Box::new(num)),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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_CHECKED => num_sub_checked,
|
||||
NUM_MUL => num_mul,
|
||||
NUM_MUL_WRAP => num_mul_wrap,
|
||||
NUM_MUL_CHECKED => num_mul_checked,
|
||||
NUM_GT => num_gt,
|
||||
NUM_GTE => num_gte,
|
||||
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.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
|
||||
fn num_gt(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
num_num_other_binop(symbol, var_store, LowLevel::NumGt)
|
||||
|
|
|
@ -21,7 +21,7 @@ use roc_types::types::Alias;
|
|||
pub struct Module {
|
||||
pub module_id: ModuleId,
|
||||
pub exposed_imports: MutMap<Symbol, Variable>,
|
||||
pub exposed_vars_by_symbol: Vec<(Symbol, Variable)>,
|
||||
pub exposed_symbols: MutSet<Symbol>,
|
||||
pub references: MutSet<Symbol>,
|
||||
pub aliases: MutMap<Symbol, Alias>,
|
||||
pub rigid_variables: MutMap<Variable, Lowercase>,
|
||||
|
@ -36,7 +36,6 @@ pub struct ModuleOutput {
|
|||
pub lookups: Vec<(Symbol, Variable, Region)>,
|
||||
pub problems: Vec<Problem>,
|
||||
pub ident_ids: IdentIds,
|
||||
pub exposed_vars_by_symbol: Vec<(Symbol, Variable)>,
|
||||
pub references: MutSet<Symbol>,
|
||||
}
|
||||
|
||||
|
@ -51,7 +50,7 @@ pub fn canonicalize_module_defs<'a>(
|
|||
dep_idents: MutMap<ModuleId, IdentIds>,
|
||||
aliases: MutMap<Symbol, Alias>,
|
||||
exposed_imports: MutMap<Ident, (Symbol, Region)>,
|
||||
mut exposed_symbols: MutSet<Symbol>,
|
||||
exposed_symbols: &MutSet<Symbol>,
|
||||
var_store: &mut VarStore,
|
||||
) -> Result<ModuleOutput, RuntimeError> {
|
||||
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
|
||||
// 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()) {
|
||||
(Ok(mut declarations), output) => {
|
||||
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() {
|
||||
match decl {
|
||||
Declare(def) => {
|
||||
for (symbol, variable) in def.pattern_vars.iter() {
|
||||
if exposed_symbols.contains(symbol) {
|
||||
// This is one of our exposed symbols;
|
||||
// record the corresponding variable!
|
||||
exposed_vars_by_symbol.push((*symbol, *variable));
|
||||
|
||||
for (symbol, _) in def.pattern_vars.iter() {
|
||||
if exposed_but_not_defined.contains(symbol) {
|
||||
// Remove this from exposed_symbols,
|
||||
// so that at the end of the process,
|
||||
// we can see if there were any
|
||||
// exposed symbols which did not have
|
||||
// corresponding defs.
|
||||
exposed_symbols.remove(symbol);
|
||||
exposed_but_not_defined.remove(symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
DeclareRec(defs) => {
|
||||
for def in defs {
|
||||
for (symbol, variable) in def.pattern_vars.iter() {
|
||||
if exposed_symbols.contains(symbol) {
|
||||
// This is one of our exposed symbols;
|
||||
// record the corresponding variable!
|
||||
exposed_vars_by_symbol.push((*symbol, *variable));
|
||||
|
||||
for (symbol, _) in def.pattern_vars.iter() {
|
||||
if exposed_but_not_defined.contains(symbol) {
|
||||
// Remove this from exposed_symbols,
|
||||
// so that at the end of the process,
|
||||
// we can see if there were any
|
||||
// exposed symbols which did not have
|
||||
// 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
|
||||
.pattern_vars
|
||||
.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
|
||||
// exposed symbols which did not have
|
||||
// corresponding defs.
|
||||
exposed_symbols.remove(&symbol);
|
||||
exposed_but_not_defined.remove(&symbol);
|
||||
|
||||
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
|
||||
// not, that means they were declared as exposed but there was
|
||||
// no actual declaration with that name!
|
||||
for symbol in exposed_symbols {
|
||||
for symbol in exposed_but_not_defined {
|
||||
env.problem(Problem::ExposedButNotDefined(symbol));
|
||||
|
||||
// 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,
|
||||
problems: env.problems,
|
||||
lookups,
|
||||
exposed_vars_by_symbol,
|
||||
ident_ids: env.ident_ids,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -76,33 +76,71 @@ pub fn num_float() -> Type {
|
|||
Type::Alias(
|
||||
Symbol::NUM_F64,
|
||||
vec![],
|
||||
Box::new(num_num(num_floatingpoint())),
|
||||
Box::new(num_num(num_floatingpoint(num_binary64()))),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn num_floatingpoint() -> Type {
|
||||
pub fn num_floatingpoint(range: Type) -> Type {
|
||||
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),
|
||||
);
|
||||
|
||||
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)]
|
||||
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)]
|
||||
pub fn num_integer() -> Type {
|
||||
pub fn num_signed64() -> Type {
|
||||
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),
|
||||
);
|
||||
|
||||
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)]
|
||||
|
|
|
@ -157,7 +157,7 @@ pub fn constrain_pattern(
|
|||
IntLiteral(_) => {
|
||||
state.constraints.push(Constraint::Pattern(
|
||||
region,
|
||||
PatternCategory::Float,
|
||||
PatternCategory::Int,
|
||||
builtins::num_int(),
|
||||
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 roc_can::annotation::IntroducedVariables;
|
||||
use roc_can::constraint::Constraint::{self, *};
|
||||
|
@ -166,24 +166,25 @@ fn constrain_pattern(
|
|||
}
|
||||
|
||||
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(
|
||||
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),
|
||||
));
|
||||
}
|
||||
|
||||
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(
|
||||
vec![num_uvar, int_uvar],
|
||||
vec![a, b, c],
|
||||
Constraint::Pattern(pattern.region, PatternCategory::Int, num_type, expected),
|
||||
));
|
||||
}
|
||||
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(
|
||||
vec![num_uvar, float_uvar],
|
||||
vec![a, b, c],
|
||||
Constraint::Pattern(pattern.region, PatternCategory::Float, num_type, expected),
|
||||
));
|
||||
}
|
||||
|
@ -405,41 +406,47 @@ fn constrain_pattern(
|
|||
}
|
||||
}
|
||||
|
||||
fn unique_unbound_num(
|
||||
inner_var: Variable,
|
||||
var_store: &mut VarStore,
|
||||
) -> (Variable, Variable, Type, Variable) {
|
||||
let num_var = var_store.fresh();
|
||||
fn unique_unbound_num(inner_var: Variable, var_store: &mut VarStore) -> (Variable, Variable, Type) {
|
||||
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_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_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) {
|
||||
let num_uvar = var_store.fresh();
|
||||
let val_uvar = var_store.fresh();
|
||||
fn unique_int(var_store: &mut VarStore) -> (Variable, Variable, Variable, Type) {
|
||||
let num_uvar1 = var_store.fresh();
|
||||
let num_uvar2 = var_store.fresh();
|
||||
let num_uvar3 = var_store.fresh();
|
||||
|
||||
let 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);
|
||||
let num_type = attr_type(Bool::variable(num_uvar), num_utype);
|
||||
|
||||
(num_uvar, val_uvar, num_type)
|
||||
(num_uvar1, num_uvar2, num_uvar3, attr_num)
|
||||
}
|
||||
|
||||
fn unique_int(var_store: &mut VarStore) -> (Variable, Variable, Type) {
|
||||
unique_num(var_store, num_integer())
|
||||
}
|
||||
fn unique_float(var_store: &mut VarStore) -> (Variable, Variable, Variable, Type) {
|
||||
let num_uvar1 = var_store.fresh();
|
||||
let num_uvar2 = var_store.fresh();
|
||||
let num_uvar3 = var_store.fresh();
|
||||
|
||||
fn unique_float(var_store: &mut VarStore) -> (Variable, Variable, Type) {
|
||||
unique_num(var_store, num_floatingpoint())
|
||||
let binary_64 = num_binary64();
|
||||
let attr_binary_64 = attr_type(Bool::variable(num_uvar1), binary_64);
|
||||
let fp = num_floatingpoint(attr_binary_64);
|
||||
let 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(
|
||||
|
@ -456,10 +463,10 @@ pub fn constrain_expr(
|
|||
match expr {
|
||||
Num(inner_var, _) => {
|
||||
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(
|
||||
vec![var, *inner_var, val_uvar, num_uvar, num_var],
|
||||
vec![var, *inner_var, inner_uvar, num_var],
|
||||
And(vec![
|
||||
Eq(
|
||||
Type::Variable(var),
|
||||
|
@ -472,10 +479,10 @@ pub fn constrain_expr(
|
|||
)
|
||||
}
|
||||
Int(var, _) => {
|
||||
let (num_uvar, int_uvar, num_type) = unique_int(var_store);
|
||||
let (a, b, c, num_type) = unique_int(var_store);
|
||||
|
||||
exists(
|
||||
vec![*var, num_uvar, int_uvar],
|
||||
vec![*var, a, b, c],
|
||||
And(vec![
|
||||
Eq(
|
||||
Type::Variable(*var),
|
||||
|
@ -488,10 +495,10 @@ pub fn constrain_expr(
|
|||
)
|
||||
}
|
||||
Float(var, _) => {
|
||||
let (num_uvar, float_uvar, num_type) = unique_float(var_store);
|
||||
let (a, b, c, num_type) = unique_float(var_store);
|
||||
|
||||
exists(
|
||||
vec![*var, num_uvar, float_uvar],
|
||||
vec![*var, a, b, c],
|
||||
And(vec![
|
||||
Eq(
|
||||
Type::Variable(*var),
|
||||
|
|
|
@ -9,7 +9,8 @@ use crate::llvm::build_str::{
|
|||
};
|
||||
use crate::llvm::compare::{build_eq, build_neq};
|
||||
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::{
|
||||
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)
|
||||
.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";
|
||||
|
@ -369,6 +376,7 @@ static LLVM_CEILING_F64: &str = "llvm.ceil.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_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>(
|
||||
module: &Module<'ctx>,
|
||||
|
@ -2902,7 +2910,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
|
||||
match arg_builtin {
|
||||
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 => {
|
||||
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
|
||||
| NumAddWrap | NumAddChecked | NumDivUnchecked | NumPow | NumPowInt | NumSubWrap
|
||||
| NumSubChecked => {
|
||||
| NumSubChecked | NumMulWrap | NumMulChecked => {
|
||||
debug_assert_eq!(args.len(), 2);
|
||||
|
||||
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(),
|
||||
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(),
|
||||
NumGte => bd.build_int_compare(SGE, lhs, rhs, "int_gte").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()
|
||||
}
|
||||
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(),
|
||||
NumGte => bd.build_float_compare(OGE, lhs, rhs, "float_gte").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>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
arg: IntValue<'ctx>,
|
||||
arg_layout: &Layout<'a>,
|
||||
arg_layout: &Builtin<'a>,
|
||||
op: LowLevel,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
use roc_module::low_level::LowLevel::*;
|
||||
|
@ -3500,43 +3613,13 @@ fn build_int_unary_op<'a, 'ctx, 'env>(
|
|||
let bd = env.builder;
|
||||
|
||||
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 => {
|
||||
// This is how libc's abs() is implemented - it uses no branching!
|
||||
//
|
||||
// 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",
|
||||
))
|
||||
// integer abs overflows when applied to the minimum value of a signed type
|
||||
int_abs_raise_on_overflow(env, arg, arg_layout)
|
||||
}
|
||||
NumToFloat => {
|
||||
// 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>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
arg: FloatValue<'ctx>,
|
||||
|
|
|
@ -4,7 +4,7 @@ use inkwell::context::Context;
|
|||
use inkwell::types::BasicTypeEnum::{self, *};
|
||||
use inkwell::types::{ArrayType, BasicType, FunctionType, IntType, PointerType, StructType};
|
||||
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?
|
||||
pub fn get_ptr_type<'ctx>(
|
||||
|
@ -103,8 +103,7 @@ pub fn basic_type_from_layout<'ctx>(
|
|||
layout: &Layout<'_>,
|
||||
ptr_bytes: u32,
|
||||
) -> BasicTypeEnum<'ctx> {
|
||||
use roc_mono::layout::Builtin::*;
|
||||
use roc_mono::layout::Layout::*;
|
||||
use Layout::*;
|
||||
|
||||
match layout {
|
||||
FunctionPointer(args, ret_layout) => {
|
||||
|
@ -147,22 +146,33 @@ pub fn basic_type_from_layout<'ctx>(
|
|||
.as_basic_type_enum()
|
||||
}
|
||||
|
||||
Builtin(builtin) => match builtin {
|
||||
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(),
|
||||
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)),
|
||||
},
|
||||
Builtin(builtin) => basic_type_from_builtin(arena, context, builtin, ptr_bytes),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn basic_type_from_builtin<'ctx>(
|
||||
_arena: &Bump,
|
||||
context: &'ctx Context,
|
||||
builtin: &Builtin<'_>,
|
||||
ptr_bytes: u32,
|
||||
) -> BasicTypeEnum<'ctx> {
|
||||
use Builtin::*;
|
||||
|
||||
match builtin {
|
||||
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(),
|
||||
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() {
|
||||
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 Num.maxFloat", f64::MAX, f64);
|
||||
//assert_evals_to!("Num.abs Num.minFloat", -f64::MIN, f64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -52,6 +54,24 @@ mod gen_num {
|
|||
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 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]
|
||||
|
@ -533,6 +553,24 @@ mod gen_num {
|
|||
#[test]
|
||||
fn int_negate() {
|
||||
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]
|
||||
|
@ -555,7 +593,7 @@ mod gen_num {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
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 5
|
||||
|
@ -778,7 +816,7 @@ mod gen_num {
|
|||
indoc!(
|
||||
r#"
|
||||
-1.7976931348623157e308 - 1.7976931348623157e308
|
||||
"#
|
||||
"#
|
||||
),
|
||||
0.0,
|
||||
f64
|
||||
|
@ -790,12 +828,12 @@ mod gen_num {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when Num.subChecked 1 2 is
|
||||
when Num.subChecked 5 2 is
|
||||
Ok v -> v
|
||||
_ -> -1
|
||||
"#
|
||||
),
|
||||
-1,
|
||||
3,
|
||||
i64
|
||||
);
|
||||
|
||||
|
@ -838,4 +876,127 @@ mod gen_num {
|
|||
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
|
||||
);
|
||||
}
|
||||
|
||||
#[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)
|
||||
);
|
||||
}
|
||||
|
||||
#[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::pattern::Pattern;
|
||||
use roc_can::scope::Scope;
|
||||
use roc_collections::all::SendMap;
|
||||
use roc_collections::all::{MutSet, SendMap};
|
||||
use roc_module::ident::TagName;
|
||||
use roc_module::operator::CalledVia;
|
||||
use roc_module::symbol::Symbol;
|
||||
|
@ -48,7 +48,7 @@ pub fn build_effect_builtins(
|
|||
scope: &mut Scope,
|
||||
effect_symbol: Symbol,
|
||||
var_store: &mut VarStore,
|
||||
exposed_vars_by_symbol: &mut Vec<(Symbol, Variable)>,
|
||||
exposed_symbols: &mut MutSet<Symbol>,
|
||||
declarations: &mut Vec<Declaration>,
|
||||
) {
|
||||
for (_, f) in BUILTIN_EFFECT_FUNCTIONS.iter() {
|
||||
|
@ -60,7 +60,7 @@ pub fn build_effect_builtins(
|
|||
var_store,
|
||||
);
|
||||
|
||||
exposed_vars_by_symbol.push((symbol, def.expr_var));
|
||||
exposed_symbols.insert(symbol);
|
||||
declarations.push(Declaration::Declare(def));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ use roc_module::symbol::{
|
|||
use roc_mono::ir::{
|
||||
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::header::{
|
||||
ExposesEntry, ImportsEntry, PackageEntry, PackageOrPath, PlatformHeader, To, TypedIdent,
|
||||
|
@ -663,7 +663,7 @@ enum Msg<'a> {
|
|||
},
|
||||
FinishedAllTypeChecking {
|
||||
solved_subs: Solved<Subs>,
|
||||
exposed_vars_by_symbol: Vec<(Symbol, Variable)>,
|
||||
exposed_vars_by_symbol: MutMap<Symbol, Variable>,
|
||||
documentation: MutMap<ModuleId, ModuleDocumentation>,
|
||||
},
|
||||
FoundSpecializations {
|
||||
|
@ -1638,9 +1638,12 @@ fn update<'a>(
|
|||
};
|
||||
|
||||
if is_host_exposed {
|
||||
state
|
||||
.exposed_to_host
|
||||
.extend(solved_module.exposed_vars_by_symbol.iter().copied());
|
||||
state.exposed_to_host.extend(
|
||||
solved_module
|
||||
.exposed_vars_by_symbol
|
||||
.iter()
|
||||
.map(|(k, v)| (*k, *v)),
|
||||
);
|
||||
}
|
||||
|
||||
if module_id == state.root_id && state.goal_phase == Phase::SolveTypes {
|
||||
|
@ -1904,7 +1907,7 @@ fn finish_specialization<'a>(
|
|||
fn finish<'a>(
|
||||
state: State<'a>,
|
||||
solved: Solved<Subs>,
|
||||
exposed_vars_by_symbol: Vec<(Symbol, Variable)>,
|
||||
exposed_vars_by_symbol: MutMap<Symbol, Variable>,
|
||||
documentation: MutMap<ModuleId, ModuleDocumentation>,
|
||||
) -> LoadedModule {
|
||||
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 {
|
||||
exposed_vars_by_symbol,
|
||||
exposed_symbols,
|
||||
aliases,
|
||||
rigid_variables,
|
||||
..
|
||||
|
@ -2800,6 +2803,9 @@ fn run_solve<'a>(
|
|||
let (solved_subs, solved_env, problems) =
|
||||
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 =
|
||||
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 exposed_vars_by_symbol = {
|
||||
let mut exposed_vars_by_symbol = Vec::new();
|
||||
let exposed_symbols: MutSet<Symbol> = {
|
||||
let mut exposed_symbols = MutSet::default();
|
||||
|
||||
{
|
||||
for (ident, ann) in effect_entries {
|
||||
|
@ -3035,7 +3041,7 @@ fn fabricate_effects_module<'a>(
|
|||
annotation,
|
||||
);
|
||||
|
||||
exposed_vars_by_symbol.push((symbol, def.expr_var));
|
||||
exposed_symbols.insert(symbol);
|
||||
|
||||
declarations.push(Declaration::Declare(def));
|
||||
}
|
||||
|
@ -3047,11 +3053,11 @@ fn fabricate_effects_module<'a>(
|
|||
&mut scope,
|
||||
effect_symbol,
|
||||
&mut var_store,
|
||||
&mut exposed_vars_by_symbol,
|
||||
&mut exposed_symbols,
|
||||
&mut declarations,
|
||||
);
|
||||
|
||||
exposed_vars_by_symbol
|
||||
exposed_symbols
|
||||
};
|
||||
|
||||
use roc_can::module::ModuleOutput;
|
||||
|
@ -3063,7 +3069,6 @@ fn fabricate_effects_module<'a>(
|
|||
lookups: Vec::new(),
|
||||
problems: can_env.problems,
|
||||
ident_ids: can_env.ident_ids,
|
||||
exposed_vars_by_symbol,
|
||||
references: MutSet::default(),
|
||||
};
|
||||
|
||||
|
@ -3072,7 +3077,7 @@ fn fabricate_effects_module<'a>(
|
|||
let module = Module {
|
||||
module_id,
|
||||
exposed_imports: module_output.exposed_imports,
|
||||
exposed_vars_by_symbol: module_output.exposed_vars_by_symbol,
|
||||
exposed_symbols,
|
||||
references: module_output.references,
|
||||
aliases: module_output.aliases,
|
||||
rigid_variables: module_output.rigid_variables,
|
||||
|
@ -3182,7 +3187,7 @@ fn canonicalize_and_constrain<'a>(
|
|||
dep_idents,
|
||||
aliases,
|
||||
exposed_imports,
|
||||
exposed_symbols,
|
||||
&exposed_symbols,
|
||||
&mut var_store,
|
||||
);
|
||||
let canonicalize_end = SystemTime::now();
|
||||
|
@ -3196,7 +3201,7 @@ fn canonicalize_and_constrain<'a>(
|
|||
let module = Module {
|
||||
module_id,
|
||||
exposed_imports: module_output.exposed_imports,
|
||||
exposed_vars_by_symbol: module_output.exposed_vars_by_symbol,
|
||||
exposed_symbols,
|
||||
references: module_output.references,
|
||||
aliases: module_output.aliases,
|
||||
rigid_variables: module_output.rigid_variables,
|
||||
|
@ -3503,9 +3508,20 @@ fn add_def_to_module<'a>(
|
|||
mono_env.subs,
|
||||
) {
|
||||
Ok(l) => l,
|
||||
Err(err) => {
|
||||
// a host-exposed function is not monomorphized
|
||||
todo!("The host-exposed function {:?} does not have a valid layout (e.g. maybe the function wasn't monomorphic): {:?}", symbol, err)
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -3537,9 +3553,29 @@ fn add_def_to_module<'a>(
|
|||
// get specialized!
|
||||
if is_exposed {
|
||||
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(
|
||||
symbol,
|
||||
|
|
|
@ -8,7 +8,7 @@ use roc_can::expected::Expected;
|
|||
use roc_can::expr::{canonicalize_expr, Expr, Output};
|
||||
use roc_can::operator;
|
||||
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::module::{constrain_imported_values, Import};
|
||||
use roc_module::ident::Ident;
|
||||
|
@ -37,7 +37,7 @@ pub fn infer_expr(
|
|||
) -> (Content, Subs) {
|
||||
let env = solve::Env {
|
||||
aliases: MutMap::default(),
|
||||
vars_by_symbol: SendMap::default(),
|
||||
vars_by_symbol: MutMap::default(),
|
||||
};
|
||||
let (solved, _) = solve::run(&env, problems, subs, constraint);
|
||||
|
||||
|
|
|
@ -34,6 +34,8 @@ pub enum LowLevel {
|
|||
NumSubWrap,
|
||||
NumSubChecked,
|
||||
NumMul,
|
||||
NumMulWrap,
|
||||
NumMulChecked,
|
||||
NumGt,
|
||||
NumGte,
|
||||
NumLt,
|
||||
|
|
|
@ -797,9 +797,35 @@ define_builtins! {
|
|||
54 NUM_ATAN: "atan"
|
||||
55 NUM_ACOS: "acos"
|
||||
56 NUM_ASIN: "asin"
|
||||
57 NUM_BITWISE_AND: "bitwiseAnd"
|
||||
58 NUM_SUB_WRAP: "subWrap"
|
||||
59 NUM_SUB_CHECKED: "subChecked"
|
||||
57 NUM_AT_SIGNED128: "@Signed128"
|
||||
58 NUM_SIGNED128: "Signed128" imported
|
||||
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" => {
|
||||
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]),
|
||||
|
||||
Eq | NotEq | And | Or | NumAdd | NumAddWrap | NumAddChecked | NumSub | NumSubWrap
|
||||
| NumSubChecked | NumMul | NumGt | NumGte | NumLt | NumLte | NumCompare
|
||||
| NumDivUnchecked | NumRemUnchecked | NumPow | NumPowInt | NumBitwiseAnd => {
|
||||
| NumSubChecked | NumMul | NumMulWrap | NumMulChecked | NumGt | NumGte | NumLt | NumLte
|
||||
| NumCompare | NumDivUnchecked | NumRemUnchecked | NumPow | NumPowInt | NumBitwiseAnd => {
|
||||
arena.alloc_slice_copy(&[irrelevant, irrelevant])
|
||||
}
|
||||
|
||||
|
|
|
@ -1508,7 +1508,6 @@ pub fn specialize_all<'a>(
|
|||
));
|
||||
|
||||
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);
|
||||
|
||||
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
|
||||
let layout = layout_cache
|
||||
.from_var(env.arena, var, env.subs)
|
||||
.unwrap_or_else(|err| {
|
||||
panic!("TODO turn fn_var into a RuntimeError {:?}", err)
|
||||
});
|
||||
let layout = match layout_cache.from_var(env.arena, var, env.subs) {
|
||||
Ok(cached) => cached,
|
||||
Err(LayoutProblem::UnresolvedTypeVar(_)) => {
|
||||
// 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);
|
||||
|
||||
|
@ -3575,9 +3584,23 @@ pub fn with_hole<'a>(
|
|||
let arg_symbols = arg_symbols.into_bump_slice();
|
||||
|
||||
// layout of the return type
|
||||
let layout = layout_cache
|
||||
.from_var(env.arena, ret_var, env.subs)
|
||||
.unwrap_or_else(|err| todo!("TODO turn fn_var into a RuntimeError {:?}", err));
|
||||
let layout = match layout_cache.from_var(env.arena, ret_var, env.subs) {
|
||||
Ok(cached) => cached,
|
||||
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);
|
||||
|
||||
|
@ -3654,7 +3677,7 @@ pub fn from_can<'a>(
|
|||
let mut stmt = from_can(env, branch_var, final_else.value, procs, layout_cache);
|
||||
|
||||
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);
|
||||
|
||||
stmt = Stmt::Cond {
|
||||
|
@ -3667,15 +3690,14 @@ pub fn from_can<'a>(
|
|||
ret_layout: ret_layout.clone(),
|
||||
};
|
||||
|
||||
// add condition
|
||||
stmt = with_hole(
|
||||
stmt = assign_to_symbol(
|
||||
env,
|
||||
loc_cond.value,
|
||||
cond_var,
|
||||
procs,
|
||||
layout_cache,
|
||||
cond_var,
|
||||
loc_cond,
|
||||
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
|
||||
// function). Register it with the current variable, then create a function pointer
|
||||
// to it in the IR.
|
||||
let layout = layout_cache
|
||||
.from_var(env.arena, arg_var, env.subs)
|
||||
.expect("creating layout does not fail");
|
||||
let res_layout = layout_cache.from_var(env.arena, arg_var, env.subs);
|
||||
|
||||
// we have three kinds of functions really. Plain functions, closures by capture,
|
||||
// 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 captured = partial_proc.captured_symbols.clone();
|
||||
|
||||
match layout {
|
||||
Layout::Closure(argument_layouts, closure_layout, ret_layout) if captures => {
|
||||
match res_layout {
|
||||
Ok(Layout::Closure(argument_layouts, closure_layout, ret_layout)) if captures => {
|
||||
// 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
|
||||
|
||||
|
@ -4958,7 +4978,7 @@ fn reuse_function_symbol<'a>(
|
|||
|
||||
stmt
|
||||
}
|
||||
_ => {
|
||||
Ok(layout) => {
|
||||
procs.insert_passed_by_name(
|
||||
env,
|
||||
arg_var,
|
||||
|
@ -4974,6 +4994,17 @@ fn reuse_function_symbol<'a>(
|
|||
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 {
|
||||
match subs.get_without_compacting(var).content {
|
||||
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
|
||||
}
|
||||
Content::FlexVar(_) => {
|
||||
|
@ -5840,7 +5873,9 @@ pub fn num_argument_to_int_or_float(subs: &Subs, var: Variable) -> IntOrFloat {
|
|||
IntOrFloat::IntType
|
||||
}
|
||||
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
|
||||
}
|
||||
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
|
||||
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 => {
|
||||
panic!(
|
||||
"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();
|
||||
|
||||
if tags_vec[0].0 != TagName::Private(Symbol::NUM_AT_NUM) {
|
||||
let opt_rec_var = None;
|
||||
let variant = union_sorted_tags_help(arena, tags_vec, opt_rec_var, subs);
|
||||
match tags_vec.get(0) {
|
||||
Some((tag_name, arguments)) if *tag_name == TagName::Private(Symbol::NUM_AT_NUM) => {
|
||||
debug_assert_eq!(arguments.len(), 1);
|
||||
|
||||
match variant {
|
||||
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);
|
||||
let var = arguments.iter().next().unwrap();
|
||||
|
||||
for (_, tag_layout) in tags {
|
||||
tag_layouts.push(tag_layout);
|
||||
unwrap_num_tag(subs, *var).expect("invalid Num argument")
|
||||
}
|
||||
_ => {
|
||||
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, _) => {
|
||||
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))
|
||||
}
|
||||
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))
|
||||
}
|
||||
Content::FlexVar(_) | Content::RigidVar(_) => {
|
||||
|
|
|
@ -8,7 +8,7 @@ use roc_can::expected::Expected;
|
|||
use roc_can::expr::{canonicalize_expr, Expr, Output};
|
||||
use roc_can::operator;
|
||||
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::module::{constrain_imported_values, Import};
|
||||
use roc_module::ident::Ident;
|
||||
|
@ -37,7 +37,7 @@ pub fn infer_expr(
|
|||
) -> (Content, Subs) {
|
||||
let env = solve::Env {
|
||||
aliases: MutMap::default(),
|
||||
vars_by_symbol: SendMap::default(),
|
||||
vars_by_symbol: MutMap::default(),
|
||||
};
|
||||
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
|
||||
NoImplementation,
|
||||
|
||||
/// cases where the `[]` value (or equivalently, `forall a. a`) pops up
|
||||
VoidValue,
|
||||
|
||||
ExposedButNotDefined(Symbol),
|
||||
}
|
||||
|
||||
|
|
|
@ -338,6 +338,12 @@ fn pretty_runtime_error<'b>(
|
|||
runtime_error: RuntimeError,
|
||||
) -> RocDocBuilder<'b> {
|
||||
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 {
|
||||
original_region,
|
||||
shadow,
|
||||
|
|
|
@ -1643,6 +1643,17 @@ fn to_diff<'b>(
|
|||
ErrorType::Type(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,
|
||||
};
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ use roc_can::expected::Expected;
|
|||
use roc_can::expr::{canonicalize_expr, Expr, Output};
|
||||
use roc_can::operator;
|
||||
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::module::{constrain_imported_values, Import};
|
||||
use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds};
|
||||
|
@ -35,7 +35,7 @@ pub fn infer_expr(
|
|||
) -> (Content, Subs) {
|
||||
let env = solve::Env {
|
||||
aliases: MutMap::default(),
|
||||
vars_by_symbol: SendMap::default(),
|
||||
vars_by_symbol: MutMap::default(),
|
||||
};
|
||||
let (solved, _) = solve::run(&env, problems, subs, constraint);
|
||||
|
||||
|
|
|
@ -448,8 +448,8 @@ mod test_reporting {
|
|||
|
||||
baz
|
||||
Str
|
||||
main
|
||||
U8
|
||||
F64
|
||||
"#
|
||||
),
|
||||
)
|
||||
|
@ -620,9 +620,9 @@ mod test_reporting {
|
|||
|
||||
these names seem close though:
|
||||
|
||||
Result
|
||||
Num
|
||||
Set
|
||||
Result
|
||||
U8
|
||||
"#
|
||||
),
|
||||
|
@ -1374,7 +1374,7 @@ mod test_reporting {
|
|||
Bool
|
||||
U8
|
||||
F64
|
||||
Num
|
||||
Str
|
||||
"#
|
||||
),
|
||||
)
|
||||
|
@ -2094,7 +2094,7 @@ mod test_reporting {
|
|||
|
||||
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:
|
||||
|
||||
Num Integer
|
||||
Num (Integer Signed64)
|
||||
|
||||
Tip: You can convert between Int and Float using functions like
|
||||
`Num.toFloat` and `Num.round`.
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use crate::solve;
|
||||
use roc_can::constraint::Constraint;
|
||||
use roc_can::module::Module;
|
||||
use roc_collections::all::{MutMap, SendMap};
|
||||
use roc_collections::all::MutMap;
|
||||
use roc_module::ident::Lowercase;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_types::solved_types::{Solved, SolvedType};
|
||||
|
@ -12,37 +11,10 @@ use roc_types::types::Alias;
|
|||
pub struct SolvedModule {
|
||||
pub solved_types: MutMap<Symbol, SolvedType>,
|
||||
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 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(
|
||||
aliases: MutMap<Symbol, Alias>,
|
||||
rigid_variables: MutMap<Variable, Lowercase>,
|
||||
|
@ -50,7 +22,7 @@ pub fn run_solve(
|
|||
var_store: VarStore,
|
||||
) -> (Solved<Subs>, solve::Env, Vec<solve::TypeError>) {
|
||||
let env = solve::Env {
|
||||
vars_by_symbol: SendMap::default(),
|
||||
vars_by_symbol: MutMap::default(),
|
||||
aliases,
|
||||
};
|
||||
|
||||
|
@ -73,7 +45,7 @@ pub fn run_solve(
|
|||
pub fn make_solved_types(
|
||||
solved_env: &solve::Env,
|
||||
solved_subs: &Solved<Subs>,
|
||||
exposed_vars_by_symbol: &[(Symbol, Variable)],
|
||||
exposed_vars_by_symbol: &MutMap<Symbol, Variable>,
|
||||
) -> MutMap<Symbol, SolvedType> {
|
||||
let mut solved_types = MutMap::default();
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use roc_can::constraint::Constraint::{self, *};
|
||||
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::symbol::Symbol;
|
||||
use roc_region::all::{Located, Region};
|
||||
|
@ -73,7 +73,7 @@ pub enum TypeError {
|
|||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Env {
|
||||
pub vars_by_symbol: SendMap<Symbol, Variable>,
|
||||
pub vars_by_symbol: MutMap<Symbol, Variable>,
|
||||
pub aliases: MutMap<Symbol, Alias>,
|
||||
}
|
||||
|
||||
|
@ -138,24 +138,9 @@ pub fn run(
|
|||
mut subs: Subs,
|
||||
constraint: &Constraint,
|
||||
) -> (Solved<Subs>, Env) {
|
||||
let mut pools = Pools::default();
|
||||
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,
|
||||
);
|
||||
let env = run_in_place(env, problems, &mut subs, constraint);
|
||||
|
||||
(Solved(subs), state.env)
|
||||
(Solved(subs), env)
|
||||
}
|
||||
|
||||
/// 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::operator;
|
||||
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::module::{constrain_imported_values, Import};
|
||||
use roc_module::ident::Ident;
|
||||
|
@ -37,7 +37,7 @@ pub fn infer_expr(
|
|||
) -> (Content, Subs) {
|
||||
let env = solve::Env {
|
||||
aliases: MutMap::default(),
|
||||
vars_by_symbol: SendMap::default(),
|
||||
vars_by_symbol: MutMap::default(),
|
||||
};
|
||||
let (solved, _) = solve::run(&env, problems, subs, constraint);
|
||||
|
||||
|
|
|
@ -1326,7 +1326,7 @@ mod solve_expr {
|
|||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
int : Num.Num Num.Integer
|
||||
int : Num.Num (Num.Integer Num.Signed64)
|
||||
|
||||
int
|
||||
"#
|
||||
|
@ -1339,7 +1339,7 @@ mod solve_expr {
|
|||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
int : Num.Num Num.Integer
|
||||
int : Num.Num (Num.Integer Num.Signed64)
|
||||
int = 5
|
||||
|
||||
int
|
||||
|
@ -1353,7 +1353,7 @@ mod solve_expr {
|
|||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
int : Num Integer
|
||||
int : Num (Integer Signed64)
|
||||
|
||||
int
|
||||
"#
|
||||
|
@ -1366,7 +1366,7 @@ mod solve_expr {
|
|||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
int : Num Integer
|
||||
int : Num (Integer Signed64)
|
||||
int = 5
|
||||
|
||||
int
|
||||
|
@ -1931,7 +1931,7 @@ mod solve_expr {
|
|||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
float : Num.Num Num.FloatingPoint
|
||||
float : Num.Num (Num.FloatingPoint Num.Binary64)
|
||||
|
||||
float
|
||||
"#
|
||||
|
@ -1944,7 +1944,7 @@ mod solve_expr {
|
|||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
float : Num.Num Num.FloatingPoint
|
||||
float : Num.Num (Num.FloatingPoint Num.Binary64)
|
||||
float = 5.5
|
||||
|
||||
float
|
||||
|
@ -1958,7 +1958,7 @@ mod solve_expr {
|
|||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
float : Num FloatingPoint
|
||||
float : Num (FloatingPoint Binary64)
|
||||
|
||||
float
|
||||
"#
|
||||
|
@ -1971,7 +1971,7 @@ mod solve_expr {
|
|||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
float : Num FloatingPoint
|
||||
float : Num (FloatingPoint Binary64)
|
||||
float = 5.5
|
||||
|
||||
float
|
||||
|
@ -2216,7 +2216,7 @@ mod solve_expr {
|
|||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
x : Num.Num Num.Integer
|
||||
x : Num.Num (Num.Integer Num.Signed64)
|
||||
x =
|
||||
when 2 is
|
||||
3 -> 4
|
||||
|
@ -2428,7 +2428,7 @@ mod solve_expr {
|
|||
r#"
|
||||
Foo a : { foo : a }
|
||||
|
||||
v : Foo (Num.Num Num.Integer)
|
||||
v : Foo (Num.Num (Num.Integer Num.Signed64))
|
||||
v = { foo: 42 }
|
||||
|
||||
v
|
||||
|
@ -2492,7 +2492,7 @@ mod solve_expr {
|
|||
r#"
|
||||
Peano : [ S Peano, Z ]
|
||||
|
||||
length : Peano -> Num.Num Num.Integer
|
||||
length : Peano -> Num.Num (Num.Integer Num.Signed64)
|
||||
length = \peano ->
|
||||
when peano is
|
||||
Z -> 0
|
||||
|
@ -2592,10 +2592,10 @@ mod solve_expr {
|
|||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
r : { x : (Num.Num Num.Integer) }
|
||||
r : { x : (Num.Num (Num.Integer Signed64)) }
|
||||
r = { x : 1 }
|
||||
|
||||
s : { left : { x : Num.Num Num.FloatingPoint } }
|
||||
s : { left : { x : Num.Num (Num.FloatingPoint Num.Binary64) } }
|
||||
s = { left: { x : 3.14 } }
|
||||
|
||||
when 0 is
|
||||
|
@ -2757,7 +2757,7 @@ mod solve_expr {
|
|||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
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
|
||||
|
@ -2772,7 +2772,7 @@ mod solve_expr {
|
|||
infer_eq(
|
||||
indoc!(
|
||||
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 } = { x : "foo", y : 3.14 }
|
||||
|
@ -2830,7 +2830,7 @@ mod solve_expr {
|
|||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
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 } = { x : "foo", y : 3.14 }
|
||||
|
|
|
@ -1118,7 +1118,7 @@ mod solve_uniq_expr {
|
|||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
x : Num.Num Num.Integer
|
||||
x : Num.Num (Num.Integer Num.Signed64)
|
||||
x = 4
|
||||
|
||||
x
|
||||
|
@ -1368,7 +1368,7 @@ mod solve_uniq_expr {
|
|||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
x : Num.Num Num.Integer
|
||||
x : I64
|
||||
x =
|
||||
when 2 is
|
||||
3 -> 4
|
||||
|
@ -1816,7 +1816,7 @@ mod solve_uniq_expr {
|
|||
infer_eq(
|
||||
indoc!(
|
||||
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
|
||||
|
@ -2662,7 +2662,7 @@ mod solve_uniq_expr {
|
|||
f
|
||||
"#
|
||||
),
|
||||
"Attr * (Attr a I64, Attr b I64 -> Attr c I64)",
|
||||
"Attr * (Attr b I64, Attr c I64 -> Attr d I64)",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3150,7 +3150,7 @@ mod solve_uniq_expr {
|
|||
indoc!(
|
||||
r#"
|
||||
empty : List I64
|
||||
empty =
|
||||
empty =
|
||||
[]
|
||||
|
||||
List.walkBackwards empty (\a, b -> a + b) 0
|
||||
|
@ -3159,4 +3159,55 @@ mod solve_uniq_expr {
|
|||
"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(
|
||||
Symbol::NUM_INTEGER,
|
||||
BuiltinAlias {
|
||||
region: Region::zero(),
|
||||
vars: Vec::new(),
|
||||
typ: integer_alias_content(),
|
||||
vars: vec![Located::at(Region::zero(), "range".into())],
|
||||
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(
|
||||
Symbol::NUM_I128,
|
||||
BuiltinAlias {
|
||||
region: Region::zero(),
|
||||
vars: Vec::new(),
|
||||
typ: int_alias_content(),
|
||||
typ: int_alias_content(signed128_type()),
|
||||
},
|
||||
);
|
||||
|
||||
// U128 : Num Integer
|
||||
// U128 : Num (Integer Unsigned128)
|
||||
add_alias(
|
||||
Symbol::NUM_U128,
|
||||
BuiltinAlias {
|
||||
region: Region::zero(),
|
||||
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(
|
||||
Symbol::NUM_I64,
|
||||
BuiltinAlias {
|
||||
region: Region::zero(),
|
||||
vars: Vec::new(),
|
||||
typ: int_alias_content(),
|
||||
typ: int_alias_content(signed64_type()),
|
||||
},
|
||||
);
|
||||
|
||||
// U64 : Num Integer
|
||||
// U64 : Num (Integer Unsigned64)
|
||||
add_alias(
|
||||
Symbol::NUM_U64,
|
||||
BuiltinAlias {
|
||||
region: Region::zero(),
|
||||
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(
|
||||
Symbol::NUM_I32,
|
||||
BuiltinAlias {
|
||||
region: Region::zero(),
|
||||
vars: Vec::new(),
|
||||
typ: int_alias_content(),
|
||||
typ: int_alias_content(signed32_type()),
|
||||
},
|
||||
);
|
||||
|
||||
// U32 : Num Integer
|
||||
// U32 : Num (Integer Unsigned32)
|
||||
add_alias(
|
||||
Symbol::NUM_U32,
|
||||
BuiltinAlias {
|
||||
region: Region::zero(),
|
||||
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(
|
||||
Symbol::NUM_I16,
|
||||
BuiltinAlias {
|
||||
region: Region::zero(),
|
||||
vars: Vec::new(),
|
||||
typ: int_alias_content(),
|
||||
typ: int_alias_content(signed16_type()),
|
||||
},
|
||||
);
|
||||
|
||||
// U16 : Num Integer
|
||||
// U16 : Num (Integer Unsigned16)
|
||||
add_alias(
|
||||
Symbol::NUM_U16,
|
||||
BuiltinAlias {
|
||||
region: Region::zero(),
|
||||
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(
|
||||
Symbol::NUM_I8,
|
||||
BuiltinAlias {
|
||||
region: Region::zero(),
|
||||
vars: Vec::new(),
|
||||
typ: int_alias_content(),
|
||||
typ: int_alias_content(signed8_type()),
|
||||
},
|
||||
);
|
||||
|
||||
// U8 : Num Integer
|
||||
// U8 : Num (Integer Unsigned8)
|
||||
add_alias(
|
||||
Symbol::NUM_U8,
|
||||
BuiltinAlias {
|
||||
region: Region::zero(),
|
||||
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(
|
||||
Symbol::NUM_FLOATINGPOINT,
|
||||
BuiltinAlias {
|
||||
region: Region::zero(),
|
||||
vars: Vec::new(),
|
||||
typ: floatingpoint_alias_content(),
|
||||
vars: vec![Located::at(Region::zero(), "range".into())],
|
||||
typ: floatingpoint_alias_content(flex(TVAR1)),
|
||||
},
|
||||
);
|
||||
|
||||
// F64 : Num FloatingPoint
|
||||
// F64 : Num (FloatingPoint Binary64)
|
||||
add_alias(
|
||||
Symbol::NUM_F64,
|
||||
BuiltinAlias {
|
||||
region: Region::zero(),
|
||||
vars: Vec::new(),
|
||||
typ: float_alias_content(),
|
||||
typ: float_alias_content(binary64_type()),
|
||||
},
|
||||
);
|
||||
|
||||
// F32 : Num FloatingPoint
|
||||
// F32 : Num (FloatingPoint Binary32)
|
||||
add_alias(
|
||||
Symbol::NUM_F32,
|
||||
BuiltinAlias {
|
||||
region: Region::zero(),
|
||||
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
|
||||
|
||||
#[inline(always)]
|
||||
pub fn floatingpoint_type() -> SolvedType {
|
||||
pub fn floatingpoint_type(range: SolvedType) -> SolvedType {
|
||||
SolvedType::Alias(
|
||||
Symbol::NUM_FLOATINGPOINT,
|
||||
Vec::new(),
|
||||
Box::new(floatingpoint_alias_content()),
|
||||
vec![("range".into(), range.clone())],
|
||||
Box::new(floatingpoint_alias_content(range)),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn floatingpoint_alias_content() -> SolvedType {
|
||||
single_private_tag(Symbol::NUM_AT_FLOATINGPOINT, Vec::new())
|
||||
fn floatingpoint_alias_content(range: SolvedType) -> SolvedType {
|
||||
single_private_tag(Symbol::NUM_AT_FLOATINGPOINT, vec![range])
|
||||
}
|
||||
|
||||
// FLOAT
|
||||
|
||||
#[inline(always)]
|
||||
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)]
|
||||
fn float_alias_content() -> SolvedType {
|
||||
num_type(floatingpoint_type())
|
||||
fn float_alias_content(typ: SolvedType) -> SolvedType {
|
||||
num_type(floatingpoint_type(typ))
|
||||
}
|
||||
|
||||
// INT
|
||||
|
||||
#[inline(always)]
|
||||
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)]
|
||||
fn int_alias_content() -> SolvedType {
|
||||
num_type(integer_type())
|
||||
fn int_alias_content(range: SolvedType) -> SolvedType {
|
||||
num_type(integer_type(range))
|
||||
}
|
||||
|
||||
// INTEGER
|
||||
|
||||
#[inline(always)]
|
||||
pub fn integer_type() -> SolvedType {
|
||||
pub fn integer_type(range: SolvedType) -> SolvedType {
|
||||
SolvedType::Alias(
|
||||
Symbol::NUM_INTEGER,
|
||||
Vec::new(),
|
||||
Box::new(integer_alias_content()),
|
||||
vec![("range".into(), range.clone())],
|
||||
Box::new(integer_alias_content(range)),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn integer_alias_content() -> SolvedType {
|
||||
single_private_tag(Symbol::NUM_AT_INTEGER, Vec::new())
|
||||
fn integer_alias_content(range: SolvedType) -> SolvedType {
|
||||
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)]
|
||||
|
@ -351,7 +597,7 @@ pub fn dict_type(key: SolvedType, value: SolvedType) -> SolvedType {
|
|||
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(
|
||||
vec![(TagName::Private(symbol), type_arguments)],
|
||||
Box::new(SolvedType::EmptyTagUnion),
|
||||
|
|
|
@ -753,10 +753,10 @@ fn write_apply(
|
|||
|
||||
match &arg_content {
|
||||
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");
|
||||
}
|
||||
Symbol::NUM_FLOATINGPOINT if nested_args.is_empty() => {
|
||||
Symbol::NUM_FLOATINGPOINT if nested_args.len() == 1 => {
|
||||
buf.push_str("F64");
|
||||
}
|
||||
Symbol::ATTR_ATTR => match nested_args
|
||||
|
@ -767,10 +767,10 @@ fn write_apply(
|
|||
double_nested_symbol,
|
||||
double_nested_args,
|
||||
))) => 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");
|
||||
}
|
||||
Symbol::NUM_FLOATINGPOINT if double_nested_args.is_empty() => {
|
||||
Symbol::NUM_FLOATINGPOINT if double_nested_args.len() == 1 => {
|
||||
buf.push_str("F64");
|
||||
}
|
||||
_ => default_case(subs, arg_content),
|
||||
|
|
|
@ -198,7 +198,7 @@ fn unify_alias(
|
|||
|
||||
problems
|
||||
} else {
|
||||
mismatch!()
|
||||
mismatch!("{}", symbol)
|
||||
}
|
||||
} else {
|
||||
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::operator;
|
||||
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::module::{constrain_imported_values, Import};
|
||||
use roc_module::ident::Ident;
|
||||
|
@ -37,7 +37,7 @@ pub fn infer_expr(
|
|||
) -> (Content, Subs) {
|
||||
let env = solve::Env {
|
||||
aliases: MutMap::default(),
|
||||
vars_by_symbol: SendMap::default(),
|
||||
vars_by_symbol: MutMap::default(),
|
||||
};
|
||||
let (solved, _) = solve::run(&env, problems, subs, constraint);
|
||||
|
||||
|
|
|
@ -3,6 +3,6 @@ app "effect-example"
|
|||
imports [base.Task]
|
||||
provides [ main ] to base
|
||||
|
||||
main : Task.Task {} F64
|
||||
main : Task.Task {} []
|
||||
main =
|
||||
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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue