Merge remote-tracking branch 'origin/trunk' into editor-ast-module

This commit is contained in:
Folkert 2020-12-27 15:37:27 +01:00
commit 264b6884f0
43 changed files with 1481 additions and 439 deletions

View file

@ -2,6 +2,14 @@ on: [pull_request]
name: CI name: CI
env:
RUSTC_WRAPPER: /usr/local/bin/sccache
SCCACHE_BUCKET: ${{ secrets.SCCACHE_BUCKET }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
SCCACHE_S3_USE_SSL: ${{ secrets.SCCACHE_S3_USE_SSL }}
SCCACHE_REGION: ${{ secrets.SCCACHE_REGION }}
jobs: jobs:
test: test:
name: fmt, clippy, test, test --release name: fmt, clippy, test, test --release
@ -10,9 +18,23 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Log CPU model
run: sudo cat /proc/cpuinfo | grep name | uniq
- name: Install CI Libraries - name: Install CI Libraries
run: sudo ./ci/install-ci-libraries.sh 10 run: sudo ./ci/install-ci-libraries.sh 10
- name: scope sccache to CPU model
run: |
export SCCACHE_S3_KEY_PREFIX=`sudo cat /proc/cpuinfo | grep name | uniq | awk -F: {'print $2'} | awk -F@ {'print$1'} | xargs | sed -e 's/\s/_/g'`
echo "SCCACHE_S3_KEY_PREFIX=$SCCACHE_S3_KEY_PREFIX" >> $GITHUB_ENV
- name: print SCCACHE_S3_KEY_PREFIX
run: echo $SCCACHE_S3_KEY_PREFIX
- name: sccache version
run: /usr/local/bin/sccache -V
- name: Run Zig tests - name: Run Zig tests
run: pushd compiler/builtins/bitcode; ./run-tests.sh; popd; run: pushd compiler/builtins/bitcode; ./run-tests.sh; popd;
@ -28,24 +50,6 @@ jobs:
- run: rustup component add rustfmt - run: rustup component add rustfmt
- name: Cache cargo registry
uses: actions/cache@v1
with:
path: ~/.cargo/registry
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
- name: Cache cargo index
uses: actions/cache@v1
with:
path: ~/.cargo/git
key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}
- name: Cache compiled valgrind
uses: actions/cache@v1
with:
path: ~/valgrind-3.6.1/
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
- uses: actions-rs/cargo@v1 - uses: actions-rs/cargo@v1
name: rustfmt version name: rustfmt version
with: with:
@ -72,15 +76,11 @@ jobs:
command: clippy command: clippy
args: -- -D warnings args: -- -D warnings
- uses: actions-rs/cargo@v1
name: cargo test -- --ignored
continue-on-error: true
with:
command: test
args: -- --ignored
- uses: actions-rs/cargo@v1 - uses: actions-rs/cargo@v1
name: cargo test --release name: cargo test --release
with: with:
command: test command: test
args: --release args: --release
- name: sccache stats
run: /usr/local/bin/sccache --show-stats

View file

@ -79,3 +79,8 @@ valgrind --version
wget -c https://ziglang.org/builds/zig-linux-x86_64-0.6.0+0088efc4b.tar.xz --no-check-certificate wget -c https://ziglang.org/builds/zig-linux-x86_64-0.6.0+0088efc4b.tar.xz --no-check-certificate
tar -xf zig-linux-x86_64-0.6.0+0088efc4b.tar.xz tar -xf zig-linux-x86_64-0.6.0+0088efc4b.tar.xz
ln -s "$PWD/zig-linux-x86_64-0.6.0+0088efc4b/zig" /usr/local/bin/zig ln -s "$PWD/zig-linux-x86_64-0.6.0+0088efc4b/zig" /usr/local/bin/zig
# test sccache
./ci/sccache -V
# copy sccache to prevent current working dir problems
cp ./ci/sccache /usr/local/bin/sccache

BIN
ci/sccache Executable file

Binary file not shown.

View file

@ -20,6 +20,10 @@ struct Env<'a, 'env> {
home: ModuleId, home: ModuleId,
} }
pub enum ToAstProblem {
FunctionLayout,
}
/// JIT execute the given main function, and then wrap its results in an Expr /// JIT execute the given main function, and then wrap its results in an Expr
/// so we can display them to the user using the formatter. /// so we can display them to the user using the formatter.
/// ///
@ -39,7 +43,7 @@ pub unsafe fn jit_to_ast<'a>(
home: ModuleId, home: ModuleId,
subs: &Subs, subs: &Subs,
ptr_bytes: u32, ptr_bytes: u32,
) -> Expr<'a> { ) -> Result<Expr<'a>, ToAstProblem> {
let env = Env { let env = Env {
arena, arena,
subs, subs,
@ -57,55 +61,57 @@ fn jit_to_ast_help<'a>(
main_fn_name: &str, main_fn_name: &str,
layout: &Layout<'a>, layout: &Layout<'a>,
content: &Content, content: &Content,
) -> Expr<'a> { ) -> Result<Expr<'a>, ToAstProblem> {
match layout { match layout {
Layout::Builtin(Builtin::Int1) => { Layout::Builtin(Builtin::Int1) => Ok(run_jit_function!(lib, main_fn_name, bool, |num| {
run_jit_function!(lib, main_fn_name, bool, |num| bool_to_ast( bool_to_ast(env, num, content)
env, num, content })),
))
}
Layout::Builtin(Builtin::Int8) => { Layout::Builtin(Builtin::Int8) => {
// NOTE: this is does not handle 8-bit numbers yet Ok(
run_jit_function!(lib, main_fn_name, u8, |num| byte_to_ast(env, num, content)) // NOTE: this is does not handle 8-bit numbers yet
run_jit_function!(lib, main_fn_name, u8, |num| byte_to_ast(env, num, content)),
)
} }
Layout::Builtin(Builtin::Int64) => { Layout::Builtin(Builtin::Int64) => {
run_jit_function!(lib, main_fn_name, i64, |num| num_to_ast( Ok(run_jit_function!(lib, main_fn_name, i64, |num| num_to_ast(
env, env,
i64_to_ast(env.arena, num), i64_to_ast(env.arena, num),
content content
)) )))
} }
Layout::Builtin(Builtin::Float64) => { Layout::Builtin(Builtin::Float64) => {
run_jit_function!(lib, main_fn_name, f64, |num| num_to_ast( Ok(run_jit_function!(lib, main_fn_name, f64, |num| num_to_ast(
env, env,
f64_to_ast(env.arena, num), f64_to_ast(env.arena, num),
content content
)) )))
} }
Layout::Builtin(Builtin::Str) | Layout::Builtin(Builtin::EmptyStr) => { Layout::Builtin(Builtin::Str) | Layout::Builtin(Builtin::EmptyStr) => Ok(
run_jit_function!(lib, main_fn_name, &'static str, |string: &'static str| { run_jit_function!(lib, main_fn_name, &'static str, |string: &'static str| {
str_to_ast(env.arena, env.arena.alloc(string)) str_to_ast(env.arena, env.arena.alloc(string))
}) }),
} ),
Layout::Builtin(Builtin::EmptyList) => { Layout::Builtin(Builtin::EmptyList) => {
run_jit_function!(lib, main_fn_name, &'static str, |_| { Expr::List(&[]) }) Ok(run_jit_function!(lib, main_fn_name, &'static str, |_| {
Expr::List(&[])
}))
} }
Layout::Builtin(Builtin::List(_, elem_layout)) => run_jit_function!( Layout::Builtin(Builtin::List(_, elem_layout)) => Ok(run_jit_function!(
lib, lib,
main_fn_name, main_fn_name,
(*const u8, usize), (*const u8, usize),
|(ptr, len): (*const u8, usize)| { list_to_ast(env, ptr, len, elem_layout, content) } |(ptr, len): (*const u8, usize)| { list_to_ast(env, ptr, len, elem_layout, content) }
), )),
Layout::Builtin(other) => { Layout::Builtin(other) => {
todo!("add support for rendering builtin {:?} to the REPL", other) todo!("add support for rendering builtin {:?} to the REPL", other)
} }
Layout::PhantomEmptyStruct => run_jit_function!(lib, main_fn_name, &u8, |_| { Layout::PhantomEmptyStruct => Ok(run_jit_function!(lib, main_fn_name, &u8, |_| {
Expr::Record { Expr::Record {
update: None, update: None,
fields: &[], fields: &[],
final_comments: env.arena.alloc([]), final_comments: env.arena.alloc([]),
} }
}), })),
Layout::Struct(field_layouts) => { Layout::Struct(field_layouts) => {
let ptr_to_ast = |ptr: *const u8| match content { let ptr_to_ast = |ptr: *const u8| match content {
Content::Structure(FlatType::Record(fields, _)) => { Content::Structure(FlatType::Record(fields, _)) => {
@ -134,12 +140,12 @@ fn jit_to_ast_help<'a>(
let result_stack_size = layout.stack_size(env.ptr_bytes); let result_stack_size = layout.stack_size(env.ptr_bytes);
run_jit_function_dynamic_type!( Ok(run_jit_function_dynamic_type!(
lib, lib,
main_fn_name, main_fn_name,
result_stack_size as usize, result_stack_size as usize,
|bytes: *const u8| { ptr_to_ast(bytes as *const u8) } |bytes: *const u8| { ptr_to_ast(bytes as *const u8) }
) ))
} }
Layout::Union(union_layouts) => match content { Layout::Union(union_layouts) => match content {
Content::Structure(FlatType::TagUnion(tags, _)) => { Content::Structure(FlatType::TagUnion(tags, _)) => {
@ -153,7 +159,7 @@ fn jit_to_ast_help<'a>(
let size = layout.stack_size(env.ptr_bytes); let size = layout.stack_size(env.ptr_bytes);
match union_variant { match union_variant {
UnionVariant::Wrapped(tags_and_layouts) => { UnionVariant::Wrapped(tags_and_layouts) => {
run_jit_function_dynamic_type!( Ok(run_jit_function_dynamic_type!(
lib, lib,
main_fn_name, main_fn_name,
size as usize, size as usize,
@ -181,7 +187,7 @@ fn jit_to_ast_help<'a>(
Expr::Apply(loc_tag_expr, output, CalledVia::Space) Expr::Apply(loc_tag_expr, output, CalledVia::Space)
} }
) ))
} }
_ => unreachable!("any other variant would have a different layout"), _ => unreachable!("any other variant would have a different layout"),
} }
@ -201,7 +207,7 @@ fn jit_to_ast_help<'a>(
} }
Layout::FunctionPointer(_, _) | Layout::Closure(_, _, _) => { Layout::FunctionPointer(_, _) | Layout::Closure(_, _, _) => {
todo!("add support for rendering functions in the REPL") Err(ToAstProblem::FunctionLayout)
} }
Layout::Pointer(_) => todo!("add support for rendering pointers in the REPL"), Layout::Pointer(_) => todo!("add support for rendering pointers in the REPL"),
} }

View file

@ -131,11 +131,15 @@ pub fn gen_and_eval(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<R
let content = subs.get(main_fn_var).content; let content = subs.get(main_fn_var).content;
let expr_type_str = content_to_string(content.clone(), &subs, home, &interns); let expr_type_str = content_to_string(content.clone(), &subs, home, &interns);
let (_, main_fn_layout) = procedures let (_, main_fn_layout) = match procedures.keys().find(|(s, _)| *s == main_fn_symbol) {
.keys() Some(layout) => layout.clone(),
.find(|(s, _)| *s == main_fn_symbol) None => {
.unwrap() return Ok(ReplOutput::NoProblems {
.clone(); expr: "<function>".to_string(),
expr_type: expr_type_str,
});
}
};
let ptr_bytes = target.pointer_width().unwrap().bytes() as u32; let ptr_bytes = target.pointer_width().unwrap().bytes() as u32;
@ -249,7 +253,7 @@ pub fn gen_and_eval(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<R
let lib = module_to_dylib(&env.module, &target, opt_level) let lib = module_to_dylib(&env.module, &target, opt_level)
.expect("Error loading compiled dylib for test"); .expect("Error loading compiled dylib for test");
let answer = unsafe { let res_answer = unsafe {
eval::jit_to_ast( eval::jit_to_ast(
&arena, &arena,
lib, lib,
@ -264,7 +268,15 @@ pub fn gen_and_eval(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<R
}; };
let mut expr = bumpalo::collections::String::new_in(&arena); let mut expr = bumpalo::collections::String::new_in(&arena);
answer.format_with_options(&mut expr, Parens::NotNeeded, Newlines::Yes, 0); use eval::ToAstProblem::*;
match res_answer {
Ok(answer) => {
answer.format_with_options(&mut expr, Parens::NotNeeded, Newlines::Yes, 0);
}
Err(FunctionLayout) => {
expr.push_str("<function>");
}
}
Ok(ReplOutput::NoProblems { Ok(ReplOutput::NoProblems {
expr: expr.into_bump_str().to_string(), expr: expr.into_bump_str().to_string(),

View file

@ -270,6 +270,41 @@ mod repl_eval {
expect_success("Num.subWrap Num.minInt 1", "9223372036854775807 : I64"); expect_success("Num.subWrap Num.minInt 1", "9223372036854775807 : I64");
} }
#[test]
fn num_mul_wrap() {
expect_success("Num.mulWrap Num.maxInt 2", "-2 : I64");
}
#[test]
fn num_add_checked() {
expect_success("Num.addChecked 1 1", "Ok 2 : Result (Num *) [ Overflow ]*");
expect_success(
"Num.addChecked Num.maxInt 1",
"Err (Overflow) : Result I64 [ Overflow ]*",
);
}
#[test]
fn num_sub_checked() {
expect_success("Num.subChecked 1 1", "Ok 0 : Result (Num *) [ Overflow ]*");
expect_success(
"Num.subChecked Num.minInt 1",
"Err (Overflow) : Result I64 [ Overflow ]*",
);
}
#[test]
fn num_mul_checked() {
expect_success(
"Num.mulChecked 20 2",
"Ok 40 : Result (Num *) [ Overflow ]*",
);
expect_success(
"Num.mulChecked Num.maxInt 2",
"Err (Overflow) : Result I64 [ Overflow ]*",
);
}
#[test] #[test]
fn list_concat() { fn list_concat() {
expect_success( expect_success(
@ -292,13 +327,16 @@ mod repl_eval {
expect_success("List.sum [ 1.1, 2.2, 3.3 ]", "6.6 : F64"); expect_success("List.sum [ 1.1, 2.2, 3.3 ]", "6.6 : F64");
} }
// TODO add test cases for empty lists once error messages in the repl are correct
#[test] #[test]
fn list_first() { fn list_first() {
expect_success( expect_success(
"List.first [ 12, 9, 6, 3 ]", "List.first [ 12, 9, 6, 3 ]",
"Ok 12 : Result (Num *) [ ListWasEmpty ]*", "Ok 12 : Result (Num *) [ ListWasEmpty ]*",
); );
expect_success(
"List.first []",
"Err (ListWasEmpty) : Result * [ ListWasEmpty ]*",
);
} }
#[test] #[test]
@ -307,6 +345,11 @@ mod repl_eval {
"List.last [ 12, 9, 6, 3 ]", "List.last [ 12, 9, 6, 3 ]",
"Ok 3 : Result (Num *) [ ListWasEmpty ]*", "Ok 3 : Result (Num *) [ ListWasEmpty ]*",
); );
expect_success(
"List.last []",
"Err (ListWasEmpty) : Result * [ ListWasEmpty ]*",
);
} }
#[test] #[test]
@ -427,6 +470,40 @@ mod repl_eval {
); );
} }
#[test]
fn identity_lambda() {
// Even though this gets unwrapped at runtime, the repl should still
// report it as a record
expect_success("\\x -> x", "<function> : a -> a");
}
#[test]
fn stdlib_function() {
// Even though this gets unwrapped at runtime, the repl should still
// report it as a record
expect_success("Num.abs", "<function> : Num a -> Num a");
}
#[test]
fn too_few_args() {
expect_failure(
"Num.add 2",
indoc!(
r#"
TOO FEW ARGS
The add function expects 2 arguments, but it got only 1:
4 Num.add 2
^^^^^^^
Roc does not allow functions to be partially applied. Use a closure to
make partial application explicit.
"#
),
);
}
#[test] #[test]
fn type_problem() { fn type_problem() {
expect_failure( expect_failure(

View file

@ -75,17 +75,19 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
), ),
); );
// addChecked : Num a, Num a -> Result (Num a) [ Overflow ]* fn overflow() -> SolvedType {
let overflow = SolvedType::TagUnion( SolvedType::TagUnion(
vec![(TagName::Global("Overflow".into()), vec![])], vec![(TagName::Global("Overflow".into()), vec![])],
Box::new(SolvedType::Wildcard), Box::new(SolvedType::Wildcard),
); )
}
// addChecked : Num a, Num a -> Result (Num a) [ Overflow ]*
add_type( add_type(
Symbol::NUM_ADD_CHECKED, Symbol::NUM_ADD_CHECKED,
top_level_function( top_level_function(
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))], vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
Box::new(result_type(num_type(flex(TVAR1)), overflow.clone())), Box::new(result_type(num_type(flex(TVAR1)), overflow())),
), ),
); );
@ -115,7 +117,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
Symbol::NUM_SUB_CHECKED, Symbol::NUM_SUB_CHECKED,
top_level_function( top_level_function(
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))], vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
Box::new(result_type(num_type(flex(TVAR1)), overflow)), Box::new(result_type(num_type(flex(TVAR1)), overflow())),
), ),
); );
@ -128,6 +130,21 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
), ),
); );
// mulWrap : Int, Int -> Int
add_type(
Symbol::NUM_MUL_WRAP,
top_level_function(vec![int_type(), int_type()], Box::new(int_type())),
);
// mulChecked : Num a, Num a -> Result (Num a) [ Overflow ]*
add_type(
Symbol::NUM_MUL_CHECKED,
top_level_function(
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
Box::new(result_type(num_type(flex(TVAR1)), overflow())),
),
);
// abs : Num a -> Num a // abs : Num a -> Num a
add_type( add_type(
Symbol::NUM_ABS, Symbol::NUM_ABS,

View file

@ -197,6 +197,21 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
unique_function(vec![num_type(u, num), num_type(v, num)], num_type(w, num)) unique_function(vec![num_type(u, num), num_type(v, num)], num_type(w, num))
}); });
// mulWrap : Int, Int -> Int
add_type(Symbol::NUM_MUL_WRAP, {
let_tvars! { u, v, w };
unique_function(vec![int_type(u), int_type(v)], int_type(w))
});
// mulChecked : Num a, Num a -> Result (Num a) [ Overflow ]*
add_type(Symbol::NUM_MUL_CHECKED, {
let_tvars! { u, v, w, num, result, star };
unique_function(
vec![num_type(u, num), num_type(v, num)],
result_type(result, num_type(w, num), lift(star, overflow())),
)
});
// abs : Num a -> Num a // abs : Num a -> Num a
add_type(Symbol::NUM_ABS, { add_type(Symbol::NUM_ABS, {
let_tvars! { u, v, num }; let_tvars! { u, v, num };
@ -1227,36 +1242,34 @@ fn lift(u: VarId, a: SolvedType) -> SolvedType {
#[inline(always)] #[inline(always)]
fn float_type(u: VarId) -> SolvedType { fn float_type(u: VarId) -> SolvedType {
let b_64 = builtin_aliases::binary64_type();
let attr_b_64 = lift(u, b_64);
let fp = builtin_aliases::floatingpoint_type(attr_b_64);
let attr_fb = lift(u, fp);
let num = builtin_aliases::num_type(attr_fb);
SolvedType::Apply( SolvedType::Apply(
Symbol::ATTR_ATTR, Symbol::ATTR_ATTR,
vec![ vec![
flex(u), flex(u),
SolvedType::Alias( SolvedType::Alias(Symbol::NUM_F64, Vec::new(), Box::new(num)),
Symbol::NUM_F64,
Vec::new(),
Box::new(builtin_aliases::num_type(SolvedType::Apply(
Symbol::ATTR_ATTR,
vec![flex(u), builtin_aliases::floatingpoint_type()],
))),
),
], ],
) )
} }
#[inline(always)] #[inline(always)]
fn int_type(u: VarId) -> SolvedType { fn int_type(u: VarId) -> SolvedType {
let signed_64 = builtin_aliases::signed64_type();
let attr_signed_64 = lift(u, signed_64);
let integer = builtin_aliases::integer_type(attr_signed_64);
let attr_fb = lift(u, integer);
let num = builtin_aliases::num_type(attr_fb);
SolvedType::Apply( SolvedType::Apply(
Symbol::ATTR_ATTR, Symbol::ATTR_ATTR,
vec![ vec![
flex(u), flex(u),
SolvedType::Alias( SolvedType::Alias(Symbol::NUM_I64, Vec::new(), Box::new(num)),
Symbol::NUM_I64,
Vec::new(),
Box::new(builtin_aliases::num_type(SolvedType::Apply(
Symbol::ATTR_ATTR,
vec![flex(u), builtin_aliases::integer_type()],
))),
),
], ],
) )
} }

View file

@ -85,6 +85,8 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
NUM_SUB_WRAP => num_sub_wrap, NUM_SUB_WRAP => num_sub_wrap,
NUM_SUB_CHECKED => num_sub_checked, NUM_SUB_CHECKED => num_sub_checked,
NUM_MUL => num_mul, NUM_MUL => num_mul,
NUM_MUL_WRAP => num_mul_wrap,
NUM_MUL_CHECKED => num_mul_checked,
NUM_GT => num_gt, NUM_GT => num_gt,
NUM_GTE => num_gte, NUM_GTE => num_gte,
NUM_LT => num_lt, NUM_LT => num_lt,
@ -573,6 +575,100 @@ fn num_mul(symbol: Symbol, var_store: &mut VarStore) -> Def {
num_binop(symbol, var_store, LowLevel::NumMul) num_binop(symbol, var_store, LowLevel::NumMul)
} }
/// Num.mulWrap : Int, Int -> Int
fn num_mul_wrap(symbol: Symbol, var_store: &mut VarStore) -> Def {
num_binop(symbol, var_store, LowLevel::NumMulWrap)
}
/// Num.mulChecked : Num a, Num a -> Result (Num a) [ Overflow ]*
fn num_mul_checked(symbol: Symbol, var_store: &mut VarStore) -> Def {
let bool_var = var_store.fresh();
let num_var_1 = var_store.fresh();
let num_var_2 = var_store.fresh();
let num_var_3 = var_store.fresh();
let ret_var = var_store.fresh();
let record_var = var_store.fresh();
// let arg_3 = RunLowLevel NumMulChecked arg_1 arg_2
//
// if arg_3.b then
// # overflow
// Err Overflow
// else
// # all is well
// Ok arg_3.a
let cont = If {
branch_var: ret_var,
cond_var: bool_var,
branches: vec![(
// if-condition
no_region(
// arg_3.b
Access {
record_var,
ext_var: var_store.fresh(),
field: "b".into(),
field_var: var_store.fresh(),
loc_expr: Box::new(no_region(Var(Symbol::ARG_3))),
},
),
// overflow!
no_region(tag(
"Err",
vec![tag("Overflow", Vec::new(), var_store)],
var_store,
)),
)],
final_else: Box::new(
// all is well
no_region(
// Ok arg_3.a
tag(
"Ok",
vec![
// arg_3.a
Access {
record_var,
ext_var: var_store.fresh(),
field: "a".into(),
field_var: num_var_3,
loc_expr: Box::new(no_region(Var(Symbol::ARG_3))),
},
],
var_store,
),
),
),
};
// arg_3 = RunLowLevel NumMulChecked arg_1 arg_2
let def = crate::def::Def {
loc_pattern: no_region(Pattern::Identifier(Symbol::ARG_3)),
loc_expr: no_region(RunLowLevel {
op: LowLevel::NumMulChecked,
args: vec![
(num_var_1, Var(Symbol::ARG_1)),
(num_var_2, Var(Symbol::ARG_2)),
],
ret_var: record_var,
}),
expr_var: record_var,
pattern_vars: SendMap::default(),
annotation: None,
};
let body = LetNonRec(Box::new(def), Box::new(no_region(cont)), ret_var);
defn(
symbol,
vec![(num_var_1, Symbol::ARG_1), (num_var_2, Symbol::ARG_2)],
var_store,
body,
ret_var,
)
}
/// Num.isGt : Num a, Num a -> Bool /// Num.isGt : Num a, Num a -> Bool
fn num_gt(symbol: Symbol, var_store: &mut VarStore) -> Def { fn num_gt(symbol: Symbol, var_store: &mut VarStore) -> Def {
num_num_other_binop(symbol, var_store, LowLevel::NumGt) num_num_other_binop(symbol, var_store, LowLevel::NumGt)

View file

@ -21,7 +21,7 @@ use roc_types::types::Alias;
pub struct Module { pub struct Module {
pub module_id: ModuleId, pub module_id: ModuleId,
pub exposed_imports: MutMap<Symbol, Variable>, pub exposed_imports: MutMap<Symbol, Variable>,
pub exposed_vars_by_symbol: Vec<(Symbol, Variable)>, pub exposed_symbols: MutSet<Symbol>,
pub references: MutSet<Symbol>, pub references: MutSet<Symbol>,
pub aliases: MutMap<Symbol, Alias>, pub aliases: MutMap<Symbol, Alias>,
pub rigid_variables: MutMap<Variable, Lowercase>, pub rigid_variables: MutMap<Variable, Lowercase>,
@ -36,7 +36,6 @@ pub struct ModuleOutput {
pub lookups: Vec<(Symbol, Variable, Region)>, pub lookups: Vec<(Symbol, Variable, Region)>,
pub problems: Vec<Problem>, pub problems: Vec<Problem>,
pub ident_ids: IdentIds, pub ident_ids: IdentIds,
pub exposed_vars_by_symbol: Vec<(Symbol, Variable)>,
pub references: MutSet<Symbol>, pub references: MutSet<Symbol>,
} }
@ -51,7 +50,7 @@ pub fn canonicalize_module_defs<'a>(
dep_idents: MutMap<ModuleId, IdentIds>, dep_idents: MutMap<ModuleId, IdentIds>,
aliases: MutMap<Symbol, Alias>, aliases: MutMap<Symbol, Alias>,
exposed_imports: MutMap<Ident, (Symbol, Region)>, exposed_imports: MutMap<Ident, (Symbol, Region)>,
mut exposed_symbols: MutSet<Symbol>, exposed_symbols: &MutSet<Symbol>,
var_store: &mut VarStore, var_store: &mut VarStore,
) -> Result<ModuleOutput, RuntimeError> { ) -> Result<ModuleOutput, RuntimeError> {
let mut can_exposed_imports = MutMap::default(); let mut can_exposed_imports = MutMap::default();
@ -166,45 +165,39 @@ pub fn canonicalize_module_defs<'a>(
// NOTE previously we inserted builtin defs into the list of defs here // NOTE previously we inserted builtin defs into the list of defs here
// this is now done later, in file.rs. // this is now done later, in file.rs.
// assume all exposed symbols are not actually defined in the module
// then as we walk the module and encounter the definitions, remove
// symbols from this set
let mut exposed_but_not_defined = exposed_symbols.clone();
match sort_can_defs(&mut env, defs, Output::default()) { match sort_can_defs(&mut env, defs, Output::default()) {
(Ok(mut declarations), output) => { (Ok(mut declarations), output) => {
use crate::def::Declaration::*; use crate::def::Declaration::*;
// Record the variables for all exposed symbols.
let mut exposed_vars_by_symbol = Vec::with_capacity(exposed_symbols.len());
for decl in declarations.iter() { for decl in declarations.iter() {
match decl { match decl {
Declare(def) => { Declare(def) => {
for (symbol, variable) in def.pattern_vars.iter() { for (symbol, _) in def.pattern_vars.iter() {
if exposed_symbols.contains(symbol) { if exposed_but_not_defined.contains(symbol) {
// This is one of our exposed symbols;
// record the corresponding variable!
exposed_vars_by_symbol.push((*symbol, *variable));
// Remove this from exposed_symbols, // Remove this from exposed_symbols,
// so that at the end of the process, // so that at the end of the process,
// we can see if there were any // we can see if there were any
// exposed symbols which did not have // exposed symbols which did not have
// corresponding defs. // corresponding defs.
exposed_symbols.remove(symbol); exposed_but_not_defined.remove(symbol);
} }
} }
} }
DeclareRec(defs) => { DeclareRec(defs) => {
for def in defs { for def in defs {
for (symbol, variable) in def.pattern_vars.iter() { for (symbol, _) in def.pattern_vars.iter() {
if exposed_symbols.contains(symbol) { if exposed_but_not_defined.contains(symbol) {
// This is one of our exposed symbols;
// record the corresponding variable!
exposed_vars_by_symbol.push((*symbol, *variable));
// Remove this from exposed_symbols, // Remove this from exposed_symbols,
// so that at the end of the process, // so that at the end of the process,
// we can see if there were any // we can see if there were any
// exposed symbols which did not have // exposed symbols which did not have
// corresponding defs. // corresponding defs.
exposed_symbols.remove(symbol); exposed_but_not_defined.remove(symbol);
} }
} }
} }
@ -219,7 +212,7 @@ pub fn canonicalize_module_defs<'a>(
debug_assert!(def debug_assert!(def
.pattern_vars .pattern_vars
.iter() .iter()
.all(|(symbol, _)| !exposed_symbols.contains(symbol))); .all(|(symbol, _)| !exposed_but_not_defined.contains(symbol)));
} }
} }
} }
@ -232,7 +225,7 @@ pub fn canonicalize_module_defs<'a>(
// we can see if there were any // we can see if there were any
// exposed symbols which did not have // exposed symbols which did not have
// corresponding defs. // corresponding defs.
exposed_symbols.remove(&symbol); exposed_but_not_defined.remove(&symbol);
aliases.insert(symbol, alias); aliases.insert(symbol, alias);
} }
@ -241,7 +234,7 @@ pub fn canonicalize_module_defs<'a>(
// exposed_symbols and added to exposed_vars_by_symbol. If any were // exposed_symbols and added to exposed_vars_by_symbol. If any were
// not, that means they were declared as exposed but there was // not, that means they were declared as exposed but there was
// no actual declaration with that name! // no actual declaration with that name!
for symbol in exposed_symbols { for symbol in exposed_but_not_defined {
env.problem(Problem::ExposedButNotDefined(symbol)); env.problem(Problem::ExposedButNotDefined(symbol));
// In case this exposed value is referenced by other modules, // In case this exposed value is referenced by other modules,
@ -305,7 +298,6 @@ pub fn canonicalize_module_defs<'a>(
exposed_imports: can_exposed_imports, exposed_imports: can_exposed_imports,
problems: env.problems, problems: env.problems,
lookups, lookups,
exposed_vars_by_symbol,
ident_ids: env.ident_ids, ident_ids: env.ident_ids,
}) })
} }

View file

@ -76,33 +76,71 @@ pub fn num_float() -> Type {
Type::Alias( Type::Alias(
Symbol::NUM_F64, Symbol::NUM_F64,
vec![], vec![],
Box::new(num_num(num_floatingpoint())), Box::new(num_num(num_floatingpoint(num_binary64()))),
) )
} }
#[inline(always)] #[inline(always)]
pub fn num_floatingpoint() -> Type { pub fn num_floatingpoint(range: Type) -> Type {
let alias_content = Type::TagUnion( let alias_content = Type::TagUnion(
vec![(TagName::Private(Symbol::NUM_AT_FLOATINGPOINT), vec![])], vec![(
TagName::Private(Symbol::NUM_AT_FLOATINGPOINT),
vec![range.clone()],
)],
Box::new(Type::EmptyTagUnion), Box::new(Type::EmptyTagUnion),
); );
Type::Alias(Symbol::NUM_FLOATINGPOINT, vec![], Box::new(alias_content)) Type::Alias(
Symbol::NUM_FLOATINGPOINT,
vec![("range".into(), range)],
Box::new(alias_content),
)
}
#[inline(always)]
pub fn num_binary64() -> Type {
let alias_content = Type::TagUnion(
vec![(TagName::Private(Symbol::NUM_AT_BINARY64), vec![])],
Box::new(Type::EmptyTagUnion),
);
Type::Alias(Symbol::NUM_BINARY64, vec![], Box::new(alias_content))
} }
#[inline(always)] #[inline(always)]
pub fn num_int() -> Type { pub fn num_int() -> Type {
Type::Alias(Symbol::NUM_I64, vec![], Box::new(num_num(num_integer()))) Type::Alias(
Symbol::NUM_I64,
vec![],
Box::new(num_num(num_integer(num_signed64()))),
)
} }
#[inline(always)] #[inline(always)]
pub fn num_integer() -> Type { pub fn num_signed64() -> Type {
let alias_content = Type::TagUnion( let alias_content = Type::TagUnion(
vec![(TagName::Private(Symbol::NUM_AT_INTEGER), vec![])], vec![(TagName::Private(Symbol::NUM_AT_SIGNED64), vec![])],
Box::new(Type::EmptyTagUnion), Box::new(Type::EmptyTagUnion),
); );
Type::Alias(Symbol::NUM_INTEGER, vec![], Box::new(alias_content)) Type::Alias(Symbol::NUM_SIGNED64, vec![], Box::new(alias_content))
}
#[inline(always)]
pub fn num_integer(range: Type) -> Type {
let alias_content = Type::TagUnion(
vec![(
TagName::Private(Symbol::NUM_AT_INTEGER),
vec![range.clone()],
)],
Box::new(Type::EmptyTagUnion),
);
Type::Alias(
Symbol::NUM_INTEGER,
vec![("range".into(), range)],
Box::new(alias_content),
)
} }
#[inline(always)] #[inline(always)]

View file

@ -157,7 +157,7 @@ pub fn constrain_pattern(
IntLiteral(_) => { IntLiteral(_) => {
state.constraints.push(Constraint::Pattern( state.constraints.push(Constraint::Pattern(
region, region,
PatternCategory::Float, PatternCategory::Int,
builtins::num_int(), builtins::num_int(),
expected, expected,
)); ));

View file

@ -1,4 +1,4 @@
use crate::builtins::{num_floatingpoint, num_integer, num_num}; use crate::builtins::{num_binary64, num_floatingpoint, num_integer, num_num, num_signed64};
use crate::expr::{exists, Info}; use crate::expr::{exists, Info};
use roc_can::annotation::IntroducedVariables; use roc_can::annotation::IntroducedVariables;
use roc_can::constraint::Constraint::{self, *}; use roc_can::constraint::Constraint::{self, *};
@ -166,24 +166,25 @@ fn constrain_pattern(
} }
NumLiteral(inner_var, _) => { NumLiteral(inner_var, _) => {
let (num_uvar, val_uvar, num_type, num_var) = unique_unbound_num(*inner_var, var_store); let (inner_uvar, num_var, num_type) = unique_unbound_num(*inner_var, var_store);
state.constraints.push(exists( state.constraints.push(exists(
vec![val_uvar, num_uvar, num_var, *inner_var], vec![num_var, inner_uvar, *inner_var],
Constraint::Pattern(pattern.region, PatternCategory::Num, num_type, expected), Constraint::Pattern(pattern.region, PatternCategory::Num, num_type, expected),
)); ));
} }
IntLiteral(_) => { IntLiteral(_) => {
let (num_uvar, int_uvar, num_type) = unique_int(var_store); let (a, b, c, num_type) = unique_int(var_store);
state.constraints.push(exists( state.constraints.push(exists(
vec![num_uvar, int_uvar], vec![a, b, c],
Constraint::Pattern(pattern.region, PatternCategory::Int, num_type, expected), Constraint::Pattern(pattern.region, PatternCategory::Int, num_type, expected),
)); ));
} }
FloatLiteral(_) => { FloatLiteral(_) => {
let (num_uvar, float_uvar, num_type) = unique_float(var_store); let (a, b, c, num_type) = unique_float(var_store);
state.constraints.push(exists( state.constraints.push(exists(
vec![num_uvar, float_uvar], vec![a, b, c],
Constraint::Pattern(pattern.region, PatternCategory::Float, num_type, expected), Constraint::Pattern(pattern.region, PatternCategory::Float, num_type, expected),
)); ));
} }
@ -405,41 +406,47 @@ fn constrain_pattern(
} }
} }
fn unique_unbound_num( fn unique_unbound_num(inner_var: Variable, var_store: &mut VarStore) -> (Variable, Variable, Type) {
inner_var: Variable,
var_store: &mut VarStore,
) -> (Variable, Variable, Type, Variable) {
let num_var = var_store.fresh();
let num_uvar = var_store.fresh(); let num_uvar = var_store.fresh();
let val_uvar = var_store.fresh(); let inner_uvar = var_store.fresh();
let val_type = Type::Variable(inner_var); let val_type = Type::Variable(inner_var);
let val_utype = attr_type(Bool::variable(val_uvar), val_type); let val_utype = attr_type(Bool::variable(inner_uvar), val_type);
let num_utype = num_num(val_utype); let num_utype = num_num(val_utype);
let num_type = attr_type(Bool::variable(num_uvar), num_utype); let num_type = attr_type(Bool::variable(num_uvar), num_utype);
(num_uvar, val_uvar, num_type, num_var) (inner_uvar, num_uvar, num_type)
} }
fn unique_num(var_store: &mut VarStore, val_type: Type) -> (Variable, Variable, Type) { fn unique_int(var_store: &mut VarStore) -> (Variable, Variable, Variable, Type) {
let num_uvar = var_store.fresh(); let num_uvar1 = var_store.fresh();
let val_uvar = var_store.fresh(); let num_uvar2 = var_store.fresh();
let num_uvar3 = var_store.fresh();
let val_utype = attr_type(Bool::variable(val_uvar), val_type); let signed_64 = num_signed64();
let attr_signed_64 = attr_type(Bool::variable(num_uvar1), signed_64);
let integer = num_integer(attr_signed_64);
let attr_int = attr_type(Bool::variable(num_uvar2), integer);
let num = num_num(attr_int);
let attr_num = attr_type(Bool::variable(num_uvar3), num);
let num_utype = num_num(val_utype); (num_uvar1, num_uvar2, num_uvar3, attr_num)
let num_type = attr_type(Bool::variable(num_uvar), num_utype);
(num_uvar, val_uvar, num_type)
} }
fn unique_int(var_store: &mut VarStore) -> (Variable, Variable, Type) { fn unique_float(var_store: &mut VarStore) -> (Variable, Variable, Variable, Type) {
unique_num(var_store, num_integer()) let num_uvar1 = var_store.fresh();
} let num_uvar2 = var_store.fresh();
let num_uvar3 = var_store.fresh();
fn unique_float(var_store: &mut VarStore) -> (Variable, Variable, Type) { let binary_64 = num_binary64();
unique_num(var_store, num_floatingpoint()) let attr_binary_64 = attr_type(Bool::variable(num_uvar1), binary_64);
let fp = num_floatingpoint(attr_binary_64);
let attr_fp = attr_type(Bool::variable(num_uvar2), fp);
let num = num_num(attr_fp);
let attr_num = attr_type(Bool::variable(num_uvar3), num);
(num_uvar1, num_uvar2, num_uvar3, attr_num)
} }
pub fn constrain_expr( pub fn constrain_expr(
@ -456,10 +463,10 @@ pub fn constrain_expr(
match expr { match expr {
Num(inner_var, _) => { Num(inner_var, _) => {
let var = var_store.fresh(); let var = var_store.fresh();
let (num_uvar, val_uvar, num_type, num_var) = unique_unbound_num(*inner_var, var_store); let (inner_uvar, num_var, num_type) = unique_unbound_num(*inner_var, var_store);
exists( exists(
vec![var, *inner_var, val_uvar, num_uvar, num_var], vec![var, *inner_var, inner_uvar, num_var],
And(vec![ And(vec![
Eq( Eq(
Type::Variable(var), Type::Variable(var),
@ -472,10 +479,10 @@ pub fn constrain_expr(
) )
} }
Int(var, _) => { Int(var, _) => {
let (num_uvar, int_uvar, num_type) = unique_int(var_store); let (a, b, c, num_type) = unique_int(var_store);
exists( exists(
vec![*var, num_uvar, int_uvar], vec![*var, a, b, c],
And(vec![ And(vec![
Eq( Eq(
Type::Variable(*var), Type::Variable(*var),
@ -488,10 +495,10 @@ pub fn constrain_expr(
) )
} }
Float(var, _) => { Float(var, _) => {
let (num_uvar, float_uvar, num_type) = unique_float(var_store); let (a, b, c, num_type) = unique_float(var_store);
exists( exists(
vec![*var, num_uvar, float_uvar], vec![*var, a, b, c],
And(vec![ And(vec![
Eq( Eq(
Type::Variable(*var), Type::Variable(*var),

View file

@ -9,7 +9,8 @@ use crate::llvm::build_str::{
}; };
use crate::llvm::compare::{build_eq, build_neq}; use crate::llvm::compare::{build_eq, build_neq};
use crate::llvm::convert::{ use crate::llvm::convert::{
basic_type_from_layout, block_of_memory, collection, get_fn_type, get_ptr_type, ptr_int, basic_type_from_builtin, basic_type_from_layout, block_of_memory, collection, get_fn_type,
get_ptr_type, ptr_int,
}; };
use crate::llvm::refcounting::{ use crate::llvm::refcounting::{
decrement_refcount_layout, increment_refcount_layout, refcount_is_one_comparison, decrement_refcount_layout, increment_refcount_layout, refcount_is_one_comparison,
@ -355,6 +356,12 @@ fn add_intrinsics<'ctx>(ctx: &'ctx Context, module: &Module<'ctx>) {
ctx.struct_type(&fields, false) ctx.struct_type(&fields, false)
.fn_type(&[i64_type.into(), i64_type.into()], false) .fn_type(&[i64_type.into(), i64_type.into()], false)
}); });
add_intrinsic(module, LLVM_SMUL_WITH_OVERFLOW_I64, {
let fields = [i64_type.into(), i1_type.into()];
ctx.struct_type(&fields, false)
.fn_type(&[i64_type.into(), i64_type.into()], false)
});
} }
static LLVM_MEMSET_I64: &str = "llvm.memset.p0i8.i64"; static LLVM_MEMSET_I64: &str = "llvm.memset.p0i8.i64";
@ -369,6 +376,7 @@ static LLVM_CEILING_F64: &str = "llvm.ceil.f64";
static LLVM_FLOOR_F64: &str = "llvm.floor.f64"; static LLVM_FLOOR_F64: &str = "llvm.floor.f64";
pub static LLVM_SADD_WITH_OVERFLOW_I64: &str = "llvm.sadd.with.overflow.i64"; pub static LLVM_SADD_WITH_OVERFLOW_I64: &str = "llvm.sadd.with.overflow.i64";
pub static LLVM_SSUB_WITH_OVERFLOW_I64: &str = "llvm.ssub.with.overflow.i64"; pub static LLVM_SSUB_WITH_OVERFLOW_I64: &str = "llvm.ssub.with.overflow.i64";
pub static LLVM_SMUL_WITH_OVERFLOW_I64: &str = "llvm.smul.with.overflow.i64";
fn add_intrinsic<'ctx>( fn add_intrinsic<'ctx>(
module: &Module<'ctx>, module: &Module<'ctx>,
@ -2902,7 +2910,7 @@ fn run_low_level<'a, 'ctx, 'env>(
match arg_builtin { match arg_builtin {
Int128 | Int64 | Int32 | Int16 | Int8 => { Int128 | Int64 | Int32 | Int16 | Int8 => {
build_int_unary_op(env, arg.into_int_value(), arg_layout, op) build_int_unary_op(env, arg.into_int_value(), arg_builtin, op)
} }
Float128 | Float64 | Float32 | Float16 => { Float128 | Float64 | Float32 | Float16 => {
build_float_unary_op(env, arg.into_float_value(), op) build_float_unary_op(env, arg.into_float_value(), op)
@ -3003,7 +3011,7 @@ fn run_low_level<'a, 'ctx, 'env>(
NumAdd | NumSub | NumMul | NumLt | NumLte | NumGt | NumGte | NumRemUnchecked NumAdd | NumSub | NumMul | NumLt | NumLte | NumGt | NumGte | NumRemUnchecked
| NumAddWrap | NumAddChecked | NumDivUnchecked | NumPow | NumPowInt | NumSubWrap | NumAddWrap | NumAddChecked | NumDivUnchecked | NumPow | NumPowInt | NumSubWrap
| NumSubChecked => { | NumSubChecked | NumMulWrap | NumMulChecked => {
debug_assert_eq!(args.len(), 2); debug_assert_eq!(args.len(), 2);
let (lhs_arg, lhs_layout) = load_symbol_and_layout(env, scope, &args[0]); let (lhs_arg, lhs_layout) = load_symbol_and_layout(env, scope, &args[0]);
@ -3259,7 +3267,37 @@ fn build_int_binop<'a, 'ctx, 'env>(
} }
NumSubWrap => bd.build_int_sub(lhs, rhs, "sub_int").into(), NumSubWrap => bd.build_int_sub(lhs, rhs, "sub_int").into(),
NumSubChecked => env.call_intrinsic(LLVM_SSUB_WITH_OVERFLOW_I64, &[lhs.into(), rhs.into()]), NumSubChecked => env.call_intrinsic(LLVM_SSUB_WITH_OVERFLOW_I64, &[lhs.into(), rhs.into()]),
NumMul => bd.build_int_mul(lhs, rhs, "mul_int").into(), NumMul => {
let context = env.context;
let result = env
.call_intrinsic(LLVM_SMUL_WITH_OVERFLOW_I64, &[lhs.into(), rhs.into()])
.into_struct_value();
let mul_result = bd.build_extract_value(result, 0, "mul_result").unwrap();
let has_overflowed = bd.build_extract_value(result, 1, "has_overflowed").unwrap();
let condition = bd.build_int_compare(
IntPredicate::EQ,
has_overflowed.into_int_value(),
context.bool_type().const_zero(),
"has_not_overflowed",
);
let then_block = context.append_basic_block(parent, "then_block");
let throw_block = context.append_basic_block(parent, "throw_block");
bd.build_conditional_branch(condition, then_block, throw_block);
bd.position_at_end(throw_block);
throw_exception(env, "integer multiplication overflowed!");
bd.position_at_end(then_block);
mul_result
}
NumMulWrap => bd.build_int_mul(lhs, rhs, "mul_int").into(),
NumMulChecked => env.call_intrinsic(LLVM_SMUL_WITH_OVERFLOW_I64, &[lhs.into(), rhs.into()]),
NumGt => bd.build_int_compare(SGT, lhs, rhs, "int_gt").into(), NumGt => bd.build_int_compare(SGT, lhs, rhs, "int_gt").into(),
NumGte => bd.build_int_compare(SGE, lhs, rhs, "int_gte").into(), NumGte => bd.build_int_compare(SGE, lhs, rhs, "int_gte").into(),
NumLt => bd.build_int_compare(SLT, lhs, rhs, "int_lt").into(), NumLt => bd.build_int_compare(SLT, lhs, rhs, "int_lt").into(),
@ -3475,7 +3513,55 @@ fn build_float_binop<'a, 'ctx, 'env>(
struct_value.into() struct_value.into()
} }
NumSubWrap => unreachable!("wrapping subtraction is not defined on floats"), NumSubWrap => unreachable!("wrapping subtraction is not defined on floats"),
NumMul => bd.build_float_mul(lhs, rhs, "mul_float").into(), NumMul => {
let builder = env.builder;
let context = env.context;
let result = bd.build_float_mul(lhs, rhs, "mul_float");
let is_finite =
call_bitcode_fn(env, &[result.into()], &bitcode::NUM_IS_FINITE).into_int_value();
let then_block = context.append_basic_block(parent, "then_block");
let throw_block = context.append_basic_block(parent, "throw_block");
builder.build_conditional_branch(is_finite, then_block, throw_block);
builder.position_at_end(throw_block);
throw_exception(env, "float multiplication overflowed!");
builder.position_at_end(then_block);
result.into()
}
NumMulChecked => {
let context = env.context;
let result = bd.build_float_mul(lhs, rhs, "mul_float");
let is_finite =
call_bitcode_fn(env, &[result.into()], &bitcode::NUM_IS_FINITE).into_int_value();
let is_infinite = bd.build_not(is_finite, "negate");
let struct_type = context.struct_type(
&[context.f64_type().into(), context.bool_type().into()],
false,
);
let struct_value = {
let v1 = struct_type.const_zero();
let v2 = bd.build_insert_value(v1, result, 0, "set_result").unwrap();
let v3 = bd
.build_insert_value(v2, is_infinite, 1, "set_is_infinite")
.unwrap();
v3.into_struct_value()
};
struct_value.into()
}
NumMulWrap => unreachable!("wrapping multiplication is not defined on floats"),
NumGt => bd.build_float_compare(OGT, lhs, rhs, "float_gt").into(), NumGt => bd.build_float_compare(OGT, lhs, rhs, "float_gt").into(),
NumGte => bd.build_float_compare(OGE, lhs, rhs, "float_gte").into(), NumGte => bd.build_float_compare(OGE, lhs, rhs, "float_gte").into(),
NumLt => bd.build_float_compare(OLT, lhs, rhs, "float_lt").into(), NumLt => bd.build_float_compare(OLT, lhs, rhs, "float_lt").into(),
@ -3489,10 +3575,37 @@ fn build_float_binop<'a, 'ctx, 'env>(
} }
} }
fn int_type_signed_min(int_type: IntType) -> IntValue {
let width = int_type.get_bit_width();
debug_assert!(width <= 128);
let shift = 128 - width as usize;
if shift < 64 {
let min = i128::MIN >> shift;
let a = min as u64;
let b = (min >> 64) as u64;
int_type.const_int_arbitrary_precision(&[b, a])
} else {
int_type.const_int((i128::MIN >> shift) as u64, false)
}
}
fn builtin_to_int_type<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
builtin: &Builtin<'a>,
) -> IntType<'ctx> {
let result = basic_type_from_builtin(env.arena, env.context, builtin, env.ptr_bytes);
debug_assert!(result.is_int_type());
result.into_int_type()
}
fn build_int_unary_op<'a, 'ctx, 'env>( fn build_int_unary_op<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
arg: IntValue<'ctx>, arg: IntValue<'ctx>,
arg_layout: &Layout<'a>, arg_layout: &Builtin<'a>,
op: LowLevel, op: LowLevel,
) -> BasicValueEnum<'ctx> { ) -> BasicValueEnum<'ctx> {
use roc_module::low_level::LowLevel::*; use roc_module::low_level::LowLevel::*;
@ -3500,43 +3613,13 @@ fn build_int_unary_op<'a, 'ctx, 'env>(
let bd = env.builder; let bd = env.builder;
match op { match op {
NumNeg => bd.build_int_neg(arg, "negate_int").into(), NumNeg => {
// integer abs overflows when applied to the minimum value of a signed type
int_neg_raise_on_overflow(env, arg, arg_layout)
}
NumAbs => { NumAbs => {
// This is how libc's abs() is implemented - it uses no branching! // integer abs overflows when applied to the minimum value of a signed type
// int_abs_raise_on_overflow(env, arg, arg_layout)
// abs = \arg ->
// shifted = arg >>> 63
//
// (xor arg shifted) - shifted
let ctx = env.context;
let shifted_name = "abs_shift_right";
let shifted_alloca = {
let bits_to_shift = ((arg_layout.stack_size(env.ptr_bytes) as u64) * 8) - 1;
let shift_val = ctx.i64_type().const_int(bits_to_shift, false);
let shifted = bd.build_right_shift(arg, shift_val, true, shifted_name);
let alloca = bd.build_alloca(
basic_type_from_layout(env.arena, ctx, arg_layout, env.ptr_bytes),
"#int_abs_help",
);
// shifted = arg >>> 63
bd.build_store(alloca, shifted);
alloca
};
let xored_arg = bd.build_xor(
arg,
bd.build_load(shifted_alloca, shifted_name).into_int_value(),
"xor_arg_shifted",
);
BasicValueEnum::IntValue(bd.build_int_sub(
xored_arg,
bd.build_load(shifted_alloca, shifted_name).into_int_value(),
"sub_xored_shifted",
))
} }
NumToFloat => { NumToFloat => {
// This is an Int, so we need to convert it. // This is an Int, so we need to convert it.
@ -3553,6 +3636,109 @@ fn build_int_unary_op<'a, 'ctx, 'env>(
} }
} }
fn int_neg_raise_on_overflow<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
arg: IntValue<'ctx>,
builtin: &Builtin<'a>,
) -> BasicValueEnum<'ctx> {
let builder = env.builder;
let min_val = int_type_signed_min(builtin_to_int_type(env, builtin));
let condition = builder.build_int_compare(IntPredicate::EQ, arg, min_val, "is_min_val");
let block = env.builder.get_insert_block().expect("to be in a function");
let parent = block.get_parent().expect("to be in a function");
let then_block = env.context.append_basic_block(parent, "then");
let else_block = env.context.append_basic_block(parent, "else");
env.builder
.build_conditional_branch(condition, then_block, else_block);
builder.position_at_end(then_block);
throw_exception(
env,
"integer negation overflowed because its argument is the minimum value",
);
builder.position_at_end(else_block);
builder.build_int_neg(arg, "negate_int").into()
}
fn int_abs_raise_on_overflow<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
arg: IntValue<'ctx>,
builtin: &Builtin<'a>,
) -> BasicValueEnum<'ctx> {
let builder = env.builder;
let min_val = int_type_signed_min(builtin_to_int_type(env, builtin));
let condition = builder.build_int_compare(IntPredicate::EQ, arg, min_val, "is_min_val");
let block = env.builder.get_insert_block().expect("to be in a function");
let parent = block.get_parent().expect("to be in a function");
let then_block = env.context.append_basic_block(parent, "then");
let else_block = env.context.append_basic_block(parent, "else");
env.builder
.build_conditional_branch(condition, then_block, else_block);
builder.position_at_end(then_block);
throw_exception(
env,
"integer absolute overflowed because its argument is the minimum value",
);
builder.position_at_end(else_block);
int_abs_with_overflow(env, arg, builtin)
}
fn int_abs_with_overflow<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
arg: IntValue<'ctx>,
arg_layout: &Builtin<'a>,
) -> BasicValueEnum<'ctx> {
// This is how libc's abs() is implemented - it uses no branching!
//
// abs = \arg ->
// shifted = arg >>> 63
//
// (xor arg shifted) - shifted
let bd = env.builder;
let ctx = env.context;
let shifted_name = "abs_shift_right";
let shifted_alloca = {
let bits_to_shift = ((arg_layout.stack_size(env.ptr_bytes) as u64) * 8) - 1;
let shift_val = ctx.i64_type().const_int(bits_to_shift, false);
let shifted = bd.build_right_shift(arg, shift_val, true, shifted_name);
let alloca = bd.build_alloca(
basic_type_from_builtin(env.arena, ctx, arg_layout, env.ptr_bytes),
"#int_abs_help",
);
// shifted = arg >>> 63
bd.build_store(alloca, shifted);
alloca
};
let xored_arg = bd.build_xor(
arg,
bd.build_load(shifted_alloca, shifted_name).into_int_value(),
"xor_arg_shifted",
);
BasicValueEnum::IntValue(bd.build_int_sub(
xored_arg,
bd.build_load(shifted_alloca, shifted_name).into_int_value(),
"sub_xored_shifted",
))
}
fn build_float_unary_op<'a, 'ctx, 'env>( fn build_float_unary_op<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
arg: FloatValue<'ctx>, arg: FloatValue<'ctx>,

View file

@ -4,7 +4,7 @@ use inkwell::context::Context;
use inkwell::types::BasicTypeEnum::{self, *}; use inkwell::types::BasicTypeEnum::{self, *};
use inkwell::types::{ArrayType, BasicType, FunctionType, IntType, PointerType, StructType}; use inkwell::types::{ArrayType, BasicType, FunctionType, IntType, PointerType, StructType};
use inkwell::AddressSpace; use inkwell::AddressSpace;
use roc_mono::layout::Layout; use roc_mono::layout::{Builtin, Layout};
/// TODO could this be added to Inkwell itself as a method on BasicValueEnum? /// TODO could this be added to Inkwell itself as a method on BasicValueEnum?
pub fn get_ptr_type<'ctx>( pub fn get_ptr_type<'ctx>(
@ -103,8 +103,7 @@ pub fn basic_type_from_layout<'ctx>(
layout: &Layout<'_>, layout: &Layout<'_>,
ptr_bytes: u32, ptr_bytes: u32,
) -> BasicTypeEnum<'ctx> { ) -> BasicTypeEnum<'ctx> {
use roc_mono::layout::Builtin::*; use Layout::*;
use roc_mono::layout::Layout::*;
match layout { match layout {
FunctionPointer(args, ret_layout) => { FunctionPointer(args, ret_layout) => {
@ -147,22 +146,33 @@ pub fn basic_type_from_layout<'ctx>(
.as_basic_type_enum() .as_basic_type_enum()
} }
Builtin(builtin) => match builtin { Builtin(builtin) => basic_type_from_builtin(arena, context, builtin, ptr_bytes),
Int128 => context.i128_type().as_basic_type_enum(), }
Int64 => context.i64_type().as_basic_type_enum(), }
Int32 => context.i32_type().as_basic_type_enum(),
Int16 => context.i16_type().as_basic_type_enum(), pub fn basic_type_from_builtin<'ctx>(
Int8 => context.i8_type().as_basic_type_enum(), _arena: &Bump,
Int1 => context.bool_type().as_basic_type_enum(), context: &'ctx Context,
Float128 => context.f128_type().as_basic_type_enum(), builtin: &Builtin<'_>,
Float64 => context.f64_type().as_basic_type_enum(), ptr_bytes: u32,
Float32 => context.f32_type().as_basic_type_enum(), ) -> BasicTypeEnum<'ctx> {
Float16 => context.f16_type().as_basic_type_enum(), use Builtin::*;
Dict(_, _) | EmptyDict => panic!("TODO layout_to_basic_type for Builtin::Dict"),
Set(_) | EmptySet => panic!("TODO layout_to_basic_type for Builtin::Set"), match builtin {
List(_, _) | Str | EmptyStr => collection(context, ptr_bytes).into(), Int128 => context.i128_type().as_basic_type_enum(),
EmptyList => BasicTypeEnum::StructType(collection(context, ptr_bytes)), Int64 => context.i64_type().as_basic_type_enum(),
}, Int32 => context.i32_type().as_basic_type_enum(),
Int16 => context.i16_type().as_basic_type_enum(),
Int8 => context.i8_type().as_basic_type_enum(),
Int1 => context.bool_type().as_basic_type_enum(),
Float128 => context.f128_type().as_basic_type_enum(),
Float64 => context.f64_type().as_basic_type_enum(),
Float32 => context.f32_type().as_basic_type_enum(),
Float16 => context.f16_type().as_basic_type_enum(),
Dict(_, _) | EmptyDict => panic!("TODO layout_to_basic_type for Builtin::Dict"),
Set(_) | EmptySet => panic!("TODO layout_to_basic_type for Builtin::Set"),
List(_, _) | Str | EmptyStr => collection(context, ptr_bytes).into(),
EmptyList => BasicTypeEnum::StructType(collection(context, ptr_bytes)),
} }
} }

View file

@ -40,6 +40,8 @@ mod gen_num {
fn f64_abs() { fn f64_abs() {
assert_evals_to!("Num.abs -4.7", 4.7, f64); assert_evals_to!("Num.abs -4.7", 4.7, f64);
assert_evals_to!("Num.abs 5.8", 5.8, f64); assert_evals_to!("Num.abs 5.8", 5.8, f64);
//assert_evals_to!("Num.abs Num.maxFloat", f64::MAX, f64);
//assert_evals_to!("Num.abs Num.minFloat", -f64::MIN, f64);
} }
#[test] #[test]
@ -52,6 +54,24 @@ mod gen_num {
assert_evals_to!("Num.abs 1", 1, i64); assert_evals_to!("Num.abs 1", 1, i64);
assert_evals_to!("Num.abs 9_000_000_000_000", 9_000_000_000_000, i64); assert_evals_to!("Num.abs 9_000_000_000_000", 9_000_000_000_000, i64);
assert_evals_to!("Num.abs -9_000_000_000_000", 9_000_000_000_000, i64); assert_evals_to!("Num.abs -9_000_000_000_000", 9_000_000_000_000, i64);
assert_evals_to!("Num.abs Num.maxInt", i64::MAX, i64);
assert_evals_to!("Num.abs (Num.minInt + 1)", -(i64::MIN + 1), i64);
}
#[test]
#[should_panic(
expected = r#"Roc failed with message: "integer absolute overflowed because its argument is the minimum value"#
)]
fn abs_min_int_overflow() {
assert_evals_to!(
indoc!(
r#"
Num.abs Num.minInt
"#
),
0,
i64
);
} }
#[test] #[test]
@ -533,6 +553,24 @@ mod gen_num {
#[test] #[test]
fn int_negate() { fn int_negate() {
assert_evals_to!("Num.neg 123", -123, i64); assert_evals_to!("Num.neg 123", -123, i64);
assert_evals_to!("Num.neg Num.maxInt", -i64::MAX, i64);
assert_evals_to!("Num.neg (Num.minInt + 1)", i64::MAX, i64);
}
#[test]
#[should_panic(
expected = r#"Roc failed with message: "integer negation overflowed because its argument is the minimum value"#
)]
fn neg_min_int_overflow() {
assert_evals_to!(
indoc!(
r#"
Num.neg Num.minInt
"#
),
0,
i64
);
} }
#[test] #[test]
@ -555,7 +593,7 @@ mod gen_num {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
always42 : Num.Num Num.Integer -> Num.Num Num.Integer always42 : Num.Num (Num.Integer Num.Signed64) -> Num.Num (Num.Integer Num.Signed64)
always42 = \_ -> 42 always42 = \_ -> 42
always42 5 always42 5
@ -778,7 +816,7 @@ mod gen_num {
indoc!( indoc!(
r#" r#"
-1.7976931348623157e308 - 1.7976931348623157e308 -1.7976931348623157e308 - 1.7976931348623157e308
"# "#
), ),
0.0, 0.0,
f64 f64
@ -790,12 +828,12 @@ mod gen_num {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
when Num.subChecked 1 2 is when Num.subChecked 5 2 is
Ok v -> v Ok v -> v
_ -> -1 _ -> -1
"# "#
), ),
-1, 3,
i64 i64
); );
@ -838,4 +876,127 @@ mod gen_num {
f64 f64
); );
} }
#[test]
#[should_panic(expected = r#"Roc failed with message: "integer multiplication overflowed!"#)]
fn int_positive_mul_overflow() {
assert_evals_to!(
indoc!(
r#"
9_223_372_036_854_775_807 * 2
"#
),
0,
i64
);
}
#[test]
#[should_panic(expected = r#"Roc failed with message: "integer multiplication overflowed!"#)]
fn int_negative_mul_overflow() {
assert_evals_to!(
indoc!(
r#"
(-9_223_372_036_854_775_808) * 2
"#
),
0,
i64
);
}
#[test]
#[should_panic(expected = r#"Roc failed with message: "float multiplication overflowed!"#)]
fn float_positive_mul_overflow() {
assert_evals_to!(
indoc!(
r#"
1.7976931348623157e308 * 2
"#
),
0.0,
f64
);
}
#[test]
#[should_panic(expected = r#"Roc failed with message: "float multiplication overflowed!"#)]
fn float_negative_mul_overflow() {
assert_evals_to!(
indoc!(
r#"
-1.7976931348623157e308 * 2
"#
),
0.0,
f64
);
}
#[test]
fn int_mul_wrap() {
assert_evals_to!(
indoc!(
r#"
Num.mulWrap Num.maxInt 2
"#
),
-2,
i64
);
}
#[test]
fn int_mul_checked() {
assert_evals_to!(
indoc!(
r#"
when Num.mulChecked 20 2 is
Ok v -> v
_ -> -1
"#
),
40,
i64
);
assert_evals_to!(
indoc!(
r#"
when Num.mulChecked Num.maxInt 2 is
Err Overflow -> -1
Ok v -> v
"#
),
-1,
i64
);
}
#[test]
fn float_mul_checked() {
assert_evals_to!(
indoc!(
r#"
when Num.mulChecked 20.0 2.0 is
Ok v -> v
Err Overflow -> -1.0
"#
),
40.0,
f64
);
assert_evals_to!(
indoc!(
r#"
when Num.mulChecked 1.7976931348623157e308 2 is
Err Overflow -> -1
Ok v -> v
"#
),
-1.0,
f64
);
}
} }

View file

@ -1732,4 +1732,20 @@ mod gen_primitives {
|_| 0 |_| 0
); );
} }
#[test]
fn hof_conditional() {
// exposed issue with the if condition being just a symbol
assert_evals_to!(
indoc!(
r#"
passTrue = \f -> f True
passTrue (\trueVal -> if trueVal then False else True)
"#
),
0,
u8
);
}
} }

View file

@ -920,4 +920,25 @@ mod gen_tags {
(i64, i64) (i64, i64)
); );
} }
#[test]
fn result_never() {
assert_evals_to!(
indoc!(
r"#
res : Result I64 []
res = Ok 4
# we should provide this in the stdlib
never : [] -> a
when res is
Ok v -> v
Err empty -> never empty
#"
),
4,
i64
);
}
} }

View file

@ -3,7 +3,7 @@ use roc_can::env::Env;
use roc_can::expr::{Expr, Recursive}; use roc_can::expr::{Expr, Recursive};
use roc_can::pattern::Pattern; use roc_can::pattern::Pattern;
use roc_can::scope::Scope; use roc_can::scope::Scope;
use roc_collections::all::SendMap; use roc_collections::all::{MutSet, SendMap};
use roc_module::ident::TagName; use roc_module::ident::TagName;
use roc_module::operator::CalledVia; use roc_module::operator::CalledVia;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
@ -48,7 +48,7 @@ pub fn build_effect_builtins(
scope: &mut Scope, scope: &mut Scope,
effect_symbol: Symbol, effect_symbol: Symbol,
var_store: &mut VarStore, var_store: &mut VarStore,
exposed_vars_by_symbol: &mut Vec<(Symbol, Variable)>, exposed_symbols: &mut MutSet<Symbol>,
declarations: &mut Vec<Declaration>, declarations: &mut Vec<Declaration>,
) { ) {
for (_, f) in BUILTIN_EFFECT_FUNCTIONS.iter() { for (_, f) in BUILTIN_EFFECT_FUNCTIONS.iter() {
@ -60,7 +60,7 @@ pub fn build_effect_builtins(
var_store, var_store,
); );
exposed_vars_by_symbol.push((symbol, def.expr_var)); exposed_symbols.insert(symbol);
declarations.push(Declaration::Declare(def)); declarations.push(Declaration::Declare(def));
} }
} }

View file

@ -20,7 +20,7 @@ use roc_module::symbol::{
use roc_mono::ir::{ use roc_mono::ir::{
CapturedSymbols, ExternalSpecializations, PartialProc, PendingSpecialization, Proc, Procs, CapturedSymbols, ExternalSpecializations, PartialProc, PendingSpecialization, Proc, Procs,
}; };
use roc_mono::layout::{Layout, LayoutCache}; use roc_mono::layout::{Layout, LayoutCache, LayoutProblem};
use roc_parse::ast::{self, Attempting, StrLiteral, TypeAnnotation}; use roc_parse::ast::{self, Attempting, StrLiteral, TypeAnnotation};
use roc_parse::header::{ use roc_parse::header::{
ExposesEntry, ImportsEntry, PackageEntry, PackageOrPath, PlatformHeader, To, TypedIdent, ExposesEntry, ImportsEntry, PackageEntry, PackageOrPath, PlatformHeader, To, TypedIdent,
@ -663,7 +663,7 @@ enum Msg<'a> {
}, },
FinishedAllTypeChecking { FinishedAllTypeChecking {
solved_subs: Solved<Subs>, solved_subs: Solved<Subs>,
exposed_vars_by_symbol: Vec<(Symbol, Variable)>, exposed_vars_by_symbol: MutMap<Symbol, Variable>,
documentation: MutMap<ModuleId, ModuleDocumentation>, documentation: MutMap<ModuleId, ModuleDocumentation>,
}, },
FoundSpecializations { FoundSpecializations {
@ -1638,9 +1638,12 @@ fn update<'a>(
}; };
if is_host_exposed { if is_host_exposed {
state state.exposed_to_host.extend(
.exposed_to_host solved_module
.extend(solved_module.exposed_vars_by_symbol.iter().copied()); .exposed_vars_by_symbol
.iter()
.map(|(k, v)| (*k, *v)),
);
} }
if module_id == state.root_id && state.goal_phase == Phase::SolveTypes { if module_id == state.root_id && state.goal_phase == Phase::SolveTypes {
@ -1904,7 +1907,7 @@ fn finish_specialization<'a>(
fn finish<'a>( fn finish<'a>(
state: State<'a>, state: State<'a>,
solved: Solved<Subs>, solved: Solved<Subs>,
exposed_vars_by_symbol: Vec<(Symbol, Variable)>, exposed_vars_by_symbol: MutMap<Symbol, Variable>,
documentation: MutMap<ModuleId, ModuleDocumentation>, documentation: MutMap<ModuleId, ModuleDocumentation>,
) -> LoadedModule { ) -> LoadedModule {
let module_ids = Arc::try_unwrap(state.arc_modules) let module_ids = Arc::try_unwrap(state.arc_modules)
@ -2791,7 +2794,7 @@ fn run_solve<'a>(
let module_id = module.module_id; let module_id = module.module_id;
let Module { let Module {
exposed_vars_by_symbol, exposed_symbols,
aliases, aliases,
rigid_variables, rigid_variables,
.. ..
@ -2800,6 +2803,9 @@ fn run_solve<'a>(
let (solved_subs, solved_env, problems) = let (solved_subs, solved_env, problems) =
roc_solve::module::run_solve(aliases, rigid_variables, constraint, var_store); roc_solve::module::run_solve(aliases, rigid_variables, constraint, var_store);
let mut exposed_vars_by_symbol: MutMap<Symbol, Variable> = solved_env.vars_by_symbol.clone();
exposed_vars_by_symbol.retain(|k, _| exposed_symbols.contains(k));
let solved_types = let solved_types =
roc_solve::module::make_solved_types(&solved_env, &solved_subs, &exposed_vars_by_symbol); roc_solve::module::make_solved_types(&solved_env, &solved_subs, &exposed_vars_by_symbol);
@ -3001,8 +3007,8 @@ fn fabricate_effects_module<'a>(
let mut declarations = Vec::new(); let mut declarations = Vec::new();
let exposed_vars_by_symbol = { let exposed_symbols: MutSet<Symbol> = {
let mut exposed_vars_by_symbol = Vec::new(); let mut exposed_symbols = MutSet::default();
{ {
for (ident, ann) in effect_entries { for (ident, ann) in effect_entries {
@ -3035,7 +3041,7 @@ fn fabricate_effects_module<'a>(
annotation, annotation,
); );
exposed_vars_by_symbol.push((symbol, def.expr_var)); exposed_symbols.insert(symbol);
declarations.push(Declaration::Declare(def)); declarations.push(Declaration::Declare(def));
} }
@ -3047,11 +3053,11 @@ fn fabricate_effects_module<'a>(
&mut scope, &mut scope,
effect_symbol, effect_symbol,
&mut var_store, &mut var_store,
&mut exposed_vars_by_symbol, &mut exposed_symbols,
&mut declarations, &mut declarations,
); );
exposed_vars_by_symbol exposed_symbols
}; };
use roc_can::module::ModuleOutput; use roc_can::module::ModuleOutput;
@ -3063,7 +3069,6 @@ fn fabricate_effects_module<'a>(
lookups: Vec::new(), lookups: Vec::new(),
problems: can_env.problems, problems: can_env.problems,
ident_ids: can_env.ident_ids, ident_ids: can_env.ident_ids,
exposed_vars_by_symbol,
references: MutSet::default(), references: MutSet::default(),
}; };
@ -3072,7 +3077,7 @@ fn fabricate_effects_module<'a>(
let module = Module { let module = Module {
module_id, module_id,
exposed_imports: module_output.exposed_imports, exposed_imports: module_output.exposed_imports,
exposed_vars_by_symbol: module_output.exposed_vars_by_symbol, exposed_symbols,
references: module_output.references, references: module_output.references,
aliases: module_output.aliases, aliases: module_output.aliases,
rigid_variables: module_output.rigid_variables, rigid_variables: module_output.rigid_variables,
@ -3182,7 +3187,7 @@ fn canonicalize_and_constrain<'a>(
dep_idents, dep_idents,
aliases, aliases,
exposed_imports, exposed_imports,
exposed_symbols, &exposed_symbols,
&mut var_store, &mut var_store,
); );
let canonicalize_end = SystemTime::now(); let canonicalize_end = SystemTime::now();
@ -3196,7 +3201,7 @@ fn canonicalize_and_constrain<'a>(
let module = Module { let module = Module {
module_id, module_id,
exposed_imports: module_output.exposed_imports, exposed_imports: module_output.exposed_imports,
exposed_vars_by_symbol: module_output.exposed_vars_by_symbol, exposed_symbols,
references: module_output.references, references: module_output.references,
aliases: module_output.aliases, aliases: module_output.aliases,
rigid_variables: module_output.rigid_variables, rigid_variables: module_output.rigid_variables,
@ -3503,9 +3508,20 @@ fn add_def_to_module<'a>(
mono_env.subs, mono_env.subs,
) { ) {
Ok(l) => l, Ok(l) => l,
Err(err) => { Err(LayoutProblem::Erroneous) => {
// a host-exposed function is not monomorphized let message = "top level function has erroneous type";
todo!("The host-exposed function {:?} does not have a valid layout (e.g. maybe the function wasn't monomorphic): {:?}", symbol, err) procs.runtime_errors.insert(symbol, message);
return;
}
Err(LayoutProblem::UnresolvedTypeVar(v)) => {
let message = format!(
"top level function has unresolved type variable {:?}",
v
);
procs
.runtime_errors
.insert(symbol, mono_env.arena.alloc(message));
return;
} }
}; };
@ -3537,9 +3553,29 @@ fn add_def_to_module<'a>(
// get specialized! // get specialized!
if is_exposed { if is_exposed {
let annotation = def.expr_var; let annotation = def.expr_var;
let layout = layout_cache.from_var(mono_env.arena, annotation, mono_env.subs).unwrap_or_else(|err|
todo!("TODO gracefully handle the situation where we expose a function to the host which doesn't have a valid layout (e.g. maybe the function wasn't monomorphic): {:?}", err) let layout = match layout_cache.from_var(
); mono_env.arena,
annotation,
mono_env.subs,
) {
Ok(l) => l,
Err(LayoutProblem::Erroneous) => {
let message = "top level function has erroneous type";
procs.runtime_errors.insert(symbol, message);
return;
}
Err(LayoutProblem::UnresolvedTypeVar(v)) => {
let message = format!(
"top level function has unresolved type variable {:?}",
v
);
procs
.runtime_errors
.insert(symbol, mono_env.arena.alloc(message));
return;
}
};
procs.insert_exposed( procs.insert_exposed(
symbol, symbol,

View file

@ -8,7 +8,7 @@ use roc_can::expected::Expected;
use roc_can::expr::{canonicalize_expr, Expr, Output}; use roc_can::expr::{canonicalize_expr, Expr, Output};
use roc_can::operator; use roc_can::operator;
use roc_can::scope::Scope; use roc_can::scope::Scope;
use roc_collections::all::{ImMap, ImSet, MutMap, SendMap, SendSet}; use roc_collections::all::{ImMap, ImSet, MutMap, SendSet};
use roc_constrain::expr::constrain_expr; use roc_constrain::expr::constrain_expr;
use roc_constrain::module::{constrain_imported_values, Import}; use roc_constrain::module::{constrain_imported_values, Import};
use roc_module::ident::Ident; use roc_module::ident::Ident;
@ -37,7 +37,7 @@ pub fn infer_expr(
) -> (Content, Subs) { ) -> (Content, Subs) {
let env = solve::Env { let env = solve::Env {
aliases: MutMap::default(), aliases: MutMap::default(),
vars_by_symbol: SendMap::default(), vars_by_symbol: MutMap::default(),
}; };
let (solved, _) = solve::run(&env, problems, subs, constraint); let (solved, _) = solve::run(&env, problems, subs, constraint);

View file

@ -34,6 +34,8 @@ pub enum LowLevel {
NumSubWrap, NumSubWrap,
NumSubChecked, NumSubChecked,
NumMul, NumMul,
NumMulWrap,
NumMulChecked,
NumGt, NumGt,
NumGte, NumGte,
NumLt, NumLt,

View file

@ -797,9 +797,35 @@ define_builtins! {
54 NUM_ATAN: "atan" 54 NUM_ATAN: "atan"
55 NUM_ACOS: "acos" 55 NUM_ACOS: "acos"
56 NUM_ASIN: "asin" 56 NUM_ASIN: "asin"
57 NUM_BITWISE_AND: "bitwiseAnd" 57 NUM_AT_SIGNED128: "@Signed128"
58 NUM_SUB_WRAP: "subWrap" 58 NUM_SIGNED128: "Signed128" imported
59 NUM_SUB_CHECKED: "subChecked" 59 NUM_AT_SIGNED64: "@Signed64"
60 NUM_SIGNED64: "Signed64" imported
61 NUM_AT_SIGNED32: "@Signed32"
62 NUM_SIGNED32: "Signed32" imported
63 NUM_AT_SIGNED16: "@Signed16"
64 NUM_SIGNED16: "Signed16" imported
65 NUM_AT_SIGNED8: "@Signed8"
66 NUM_SIGNED8: "Signed8" imported
67 NUM_AT_UNSIGNED128: "@Unsigned128"
68 NUM_UNSIGNED128: "Unsigned128" imported
69 NUM_AT_UNSIGNED64: "@Unsigned64"
70 NUM_UNSIGNED64: "Unsigned64" imported
71 NUM_AT_UNSIGNED32: "@Unsigned32"
72 NUM_UNSIGNED32: "Unsigned32" imported
73 NUM_AT_UNSIGNED16: "@Unsigned16"
74 NUM_UNSIGNED16: "Unsigned16" imported
75 NUM_AT_UNSIGNED8: "@Unsigned8"
76 NUM_UNSIGNED8: "Unsigned8" imported
77 NUM_AT_BINARY64: "@Binary64"
78 NUM_BINARY64: "Binary64" imported
79 NUM_AT_BINARY32: "@Binary32"
80 NUM_BINARY32: "Binary32" imported
81 NUM_BITWISE_AND: "bitwiseAnd"
82 NUM_SUB_WRAP: "subWrap"
83 NUM_SUB_CHECKED: "subChecked"
84 NUM_MUL_WRAP: "mulWrap"
85 NUM_MUL_CHECKED: "mulChecked"
} }
2 BOOL: "Bool" => { 2 BOOL: "Bool" => {
0 BOOL_BOOL: "Bool" imported // the Bool.Bool type alias 0 BOOL_BOOL: "Bool" imported // the Bool.Bool type alias

View file

@ -540,8 +540,8 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
ListSum => arena.alloc_slice_copy(&[borrowed]), ListSum => arena.alloc_slice_copy(&[borrowed]),
Eq | NotEq | And | Or | NumAdd | NumAddWrap | NumAddChecked | NumSub | NumSubWrap Eq | NotEq | And | Or | NumAdd | NumAddWrap | NumAddChecked | NumSub | NumSubWrap
| NumSubChecked | NumMul | NumGt | NumGte | NumLt | NumLte | NumCompare | NumSubChecked | NumMul | NumMulWrap | NumMulChecked | NumGt | NumGte | NumLt | NumLte
| NumDivUnchecked | NumRemUnchecked | NumPow | NumPowInt | NumBitwiseAnd => { | NumCompare | NumDivUnchecked | NumRemUnchecked | NumPow | NumPowInt | NumBitwiseAnd => {
arena.alloc_slice_copy(&[irrelevant, irrelevant]) arena.alloc_slice_copy(&[irrelevant, irrelevant])
} }

View file

@ -1508,7 +1508,6 @@ pub fn specialize_all<'a>(
)); ));
procs.runtime_errors.insert(name, error_msg); procs.runtime_errors.insert(name, error_msg);
panic!("failed to specialize {:?}", name);
} }
} }
} }
@ -2585,13 +2584,23 @@ pub fn with_hole<'a>(
let mut field_symbols_temp = Vec::with_capacity_in(args.len(), env.arena); let mut field_symbols_temp = Vec::with_capacity_in(args.len(), env.arena);
for (var, arg) in args.drain(..) { for (var, mut arg) in args.drain(..) {
// Layout will unpack this unwrapped tack if it only has one (non-zero-sized) field // Layout will unpack this unwrapped tack if it only has one (non-zero-sized) field
let layout = layout_cache let layout = match layout_cache.from_var(env.arena, var, env.subs) {
.from_var(env.arena, var, env.subs) Ok(cached) => cached,
.unwrap_or_else(|err| { Err(LayoutProblem::UnresolvedTypeVar(_)) => {
panic!("TODO turn fn_var into a RuntimeError {:?}", err) // this argument has type `forall a. a`, which is isomorphic to
}); // the empty type (Void, Never, the empty tag union `[]`)
use roc_can::expr::Expr;
use roc_problem::can::RuntimeError;
arg.value = Expr::RuntimeError(RuntimeError::VoidValue);
Layout::Struct(&[])
}
Err(LayoutProblem::Erroneous) => {
// something went very wrong
panic!("TODO turn fn_var into a RuntimeError")
}
};
let alignment = layout.alignment_bytes(8); let alignment = layout.alignment_bytes(8);
@ -3575,9 +3584,23 @@ pub fn with_hole<'a>(
let arg_symbols = arg_symbols.into_bump_slice(); let arg_symbols = arg_symbols.into_bump_slice();
// layout of the return type // layout of the return type
let layout = layout_cache let layout = match layout_cache.from_var(env.arena, ret_var, env.subs) {
.from_var(env.arena, ret_var, env.subs) Ok(cached) => cached,
.unwrap_or_else(|err| todo!("TODO turn fn_var into a RuntimeError {:?}", err)); Err(LayoutProblem::UnresolvedTypeVar(_)) => {
return Stmt::RuntimeError(env.arena.alloc(format!(
"UnresolvedTypeVar {} line {}",
file!(),
line!()
)));
}
Err(LayoutProblem::Erroneous) => {
return Stmt::RuntimeError(env.arena.alloc(format!(
"Erroneous {} line {}",
file!(),
line!()
)));
}
};
let result = Stmt::Let(assigned, Expr::RunLowLevel(op, arg_symbols), layout, hole); let result = Stmt::Let(assigned, Expr::RunLowLevel(op, arg_symbols), layout, hole);
@ -3654,7 +3677,7 @@ pub fn from_can<'a>(
let mut stmt = from_can(env, branch_var, final_else.value, procs, layout_cache); let mut stmt = from_can(env, branch_var, final_else.value, procs, layout_cache);
for (loc_cond, loc_then) in branches.into_iter().rev() { for (loc_cond, loc_then) in branches.into_iter().rev() {
let branching_symbol = env.unique_symbol(); let branching_symbol = possible_reuse_symbol(env, procs, &loc_cond.value);
let then = from_can(env, branch_var, loc_then.value, procs, layout_cache); let then = from_can(env, branch_var, loc_then.value, procs, layout_cache);
stmt = Stmt::Cond { stmt = Stmt::Cond {
@ -3667,15 +3690,14 @@ pub fn from_can<'a>(
ret_layout: ret_layout.clone(), ret_layout: ret_layout.clone(),
}; };
// add condition stmt = assign_to_symbol(
stmt = with_hole(
env, env,
loc_cond.value,
cond_var,
procs, procs,
layout_cache, layout_cache,
cond_var,
loc_cond,
branching_symbol, branching_symbol,
env.arena.alloc(stmt), stmt,
); );
} }
@ -4869,9 +4891,7 @@ fn reuse_function_symbol<'a>(
// this symbol is a function, that is used by-name (e.g. as an argument to another // this symbol is a function, that is used by-name (e.g. as an argument to another
// function). Register it with the current variable, then create a function pointer // function). Register it with the current variable, then create a function pointer
// to it in the IR. // to it in the IR.
let layout = layout_cache let res_layout = layout_cache.from_var(env.arena, arg_var, env.subs);
.from_var(env.arena, arg_var, env.subs)
.expect("creating layout does not fail");
// we have three kinds of functions really. Plain functions, closures by capture, // we have three kinds of functions really. Plain functions, closures by capture,
// and closures by unification. Here we record whether this function captures // and closures by unification. Here we record whether this function captures
@ -4879,8 +4899,8 @@ fn reuse_function_symbol<'a>(
let captures = partial_proc.captured_symbols.captures(); let captures = partial_proc.captured_symbols.captures();
let captured = partial_proc.captured_symbols.clone(); let captured = partial_proc.captured_symbols.clone();
match layout { match res_layout {
Layout::Closure(argument_layouts, closure_layout, ret_layout) if captures => { Ok(Layout::Closure(argument_layouts, closure_layout, ret_layout)) if captures => {
// this is a closure by capture, meaning it itself captures local variables. // this is a closure by capture, meaning it itself captures local variables.
// we've defined the closure as a (function_ptr, closure_data) pair already // we've defined the closure as a (function_ptr, closure_data) pair already
@ -4958,7 +4978,7 @@ fn reuse_function_symbol<'a>(
stmt stmt
} }
_ => { Ok(layout) => {
procs.insert_passed_by_name( procs.insert_passed_by_name(
env, env,
arg_var, arg_var,
@ -4974,6 +4994,17 @@ fn reuse_function_symbol<'a>(
env.arena.alloc(result), env.arena.alloc(result),
) )
} }
Err(LayoutProblem::Erroneous) => {
let message = format!("The {:?} symbol has an erroneous type", symbol);
Stmt::RuntimeError(env.arena.alloc(message))
}
Err(LayoutProblem::UnresolvedTypeVar(v)) => {
let message = format!(
"The {:?} symbol contains a unresolved type var {:?}",
symbol, v
);
Stmt::RuntimeError(env.arena.alloc(message))
}
} }
} }
} }
@ -5832,7 +5863,9 @@ pub enum IntOrFloat {
pub fn num_argument_to_int_or_float(subs: &Subs, var: Variable) -> IntOrFloat { pub fn num_argument_to_int_or_float(subs: &Subs, var: Variable) -> IntOrFloat {
match subs.get_without_compacting(var).content { match subs.get_without_compacting(var).content {
Content::Alias(Symbol::NUM_INTEGER, args, _) => { Content::Alias(Symbol::NUM_INTEGER, args, _) => {
debug_assert!(args.is_empty()); debug_assert!(args.len() == 1);
// TODO: we probably need to match on the type of the arg
IntOrFloat::IntType IntOrFloat::IntType
} }
Content::FlexVar(_) => { Content::FlexVar(_) => {
@ -5840,7 +5873,9 @@ pub fn num_argument_to_int_or_float(subs: &Subs, var: Variable) -> IntOrFloat {
IntOrFloat::IntType IntOrFloat::IntType
} }
Content::Alias(Symbol::NUM_FLOATINGPOINT, args, _) => { Content::Alias(Symbol::NUM_FLOATINGPOINT, args, _) => {
debug_assert!(args.is_empty()); debug_assert!(args.len() == 1);
// TODO: we probably need to match on the type of the arg
IntOrFloat::FloatType IntOrFloat::FloatType
} }
Content::Structure(FlatType::Apply(Symbol::ATTR_ATTR, attr_args)) => { Content::Structure(FlatType::Apply(Symbol::ATTR_ATTR, attr_args)) => {
@ -5849,6 +5884,11 @@ pub fn num_argument_to_int_or_float(subs: &Subs, var: Variable) -> IntOrFloat {
// Recurse on the second argument // Recurse on the second argument
num_argument_to_int_or_float(subs, attr_args[1]) num_argument_to_int_or_float(subs, attr_args[1])
} }
Content::Alias(Symbol::NUM_F64, args, _) | Content::Alias(Symbol::NUM_F32, args, _) => {
debug_assert!(args.is_empty());
IntOrFloat::FloatType
}
other => { other => {
panic!( panic!(
"Unrecognized Num type argument for var {:?} with Content: {:?}", "Unrecognized Num type argument for var {:?} with Content: {:?}",

View file

@ -1196,39 +1196,40 @@ pub fn layout_from_tag_union<'a>(
let tags_vec: std::vec::Vec<_> = tags.into_iter().collect(); let tags_vec: std::vec::Vec<_> = tags.into_iter().collect();
if tags_vec[0].0 != TagName::Private(Symbol::NUM_AT_NUM) { match tags_vec.get(0) {
let opt_rec_var = None; Some((tag_name, arguments)) if *tag_name == TagName::Private(Symbol::NUM_AT_NUM) => {
let variant = union_sorted_tags_help(arena, tags_vec, opt_rec_var, subs); debug_assert_eq!(arguments.len(), 1);
match variant { let var = arguments.iter().next().unwrap();
Never => panic!("TODO gracefully handle trying to instantiate Never"),
Unit | UnitWithArguments => Layout::Struct(&[]),
BoolUnion { .. } => Layout::Builtin(Builtin::Int1),
ByteUnion(_) => Layout::Builtin(Builtin::Int8),
Unwrapped(mut field_layouts) => {
if field_layouts.len() == 1 {
field_layouts.pop().unwrap()
} else {
Layout::Struct(field_layouts.into_bump_slice())
}
}
Wrapped(tags) => {
let mut tag_layouts = Vec::with_capacity_in(tags.len(), arena);
for (_, tag_layout) in tags { unwrap_num_tag(subs, *var).expect("invalid Num argument")
tag_layouts.push(tag_layout); }
_ => {
let opt_rec_var = None;
let variant = union_sorted_tags_help(arena, tags_vec, opt_rec_var, subs);
match variant {
Never => Layout::Union(&[]),
Unit | UnitWithArguments => Layout::Struct(&[]),
BoolUnion { .. } => Layout::Builtin(Builtin::Int1),
ByteUnion(_) => Layout::Builtin(Builtin::Int8),
Unwrapped(mut field_layouts) => {
if field_layouts.len() == 1 {
field_layouts.pop().unwrap()
} else {
Layout::Struct(field_layouts.into_bump_slice())
}
}
Wrapped(tags) => {
let mut tag_layouts = Vec::with_capacity_in(tags.len(), arena);
for (_, tag_layout) in tags {
tag_layouts.push(tag_layout);
}
Layout::Union(tag_layouts.into_bump_slice())
} }
Layout::Union(tag_layouts.into_bump_slice())
} }
} }
} else {
let arguments = &tags_vec[0].1;
debug_assert_eq!(arguments.len(), 1);
let var = arguments.iter().next().unwrap();
unwrap_num_tag(subs, *var).expect("invalid Num argument")
} }
} }
@ -1296,11 +1297,17 @@ fn unwrap_num_tag<'a>(subs: &Subs, var: Variable) -> Result<Layout<'a>, LayoutPr
} }
}, },
Content::Alias(Symbol::NUM_INTEGER, args, _) => { Content::Alias(Symbol::NUM_INTEGER, args, _) => {
debug_assert!(args.is_empty()); debug_assert!(args.len() == 1);
// TODO: we probably need to match on the type of the arg
// and return the correct builtin ex: Builtin::{Int32, Int16}
Ok(Layout::Builtin(Builtin::Int64)) Ok(Layout::Builtin(Builtin::Int64))
} }
Content::Alias(Symbol::NUM_FLOATINGPOINT, args, _) => { Content::Alias(Symbol::NUM_FLOATINGPOINT, args, _) => {
debug_assert!(args.is_empty()); debug_assert!(args.len() == 1);
// TODO: we probably need to match on the type of the arg
// and return the correct builtin ex: Builtin::Float32
Ok(Layout::Builtin(Builtin::Float64)) Ok(Layout::Builtin(Builtin::Float64))
} }
Content::FlexVar(_) | Content::RigidVar(_) => { Content::FlexVar(_) | Content::RigidVar(_) => {

View file

@ -8,7 +8,7 @@ use roc_can::expected::Expected;
use roc_can::expr::{canonicalize_expr, Expr, Output}; use roc_can::expr::{canonicalize_expr, Expr, Output};
use roc_can::operator; use roc_can::operator;
use roc_can::scope::Scope; use roc_can::scope::Scope;
use roc_collections::all::{ImMap, ImSet, MutMap, SendMap, SendSet}; use roc_collections::all::{ImMap, ImSet, MutMap, SendSet};
use roc_constrain::expr::constrain_expr; use roc_constrain::expr::constrain_expr;
use roc_constrain::module::{constrain_imported_values, Import}; use roc_constrain::module::{constrain_imported_values, Import};
use roc_module::ident::Ident; use roc_module::ident::Ident;
@ -37,7 +37,7 @@ pub fn infer_expr(
) -> (Content, Subs) { ) -> (Content, Subs) {
let env = solve::Env { let env = solve::Env {
aliases: MutMap::default(), aliases: MutMap::default(),
vars_by_symbol: SendMap::default(), vars_by_symbol: MutMap::default(),
}; };
let (solved, _) = solve::run(&env, problems, subs, constraint); let (solved, _) = solve::run(&env, problems, subs, constraint);

View file

@ -147,6 +147,9 @@ pub enum RuntimeError {
/// When the author specifies a type annotation but no implementation /// When the author specifies a type annotation but no implementation
NoImplementation, NoImplementation,
/// cases where the `[]` value (or equivalently, `forall a. a`) pops up
VoidValue,
ExposedButNotDefined(Symbol), ExposedButNotDefined(Symbol),
} }

View file

@ -338,6 +338,12 @@ fn pretty_runtime_error<'b>(
runtime_error: RuntimeError, runtime_error: RuntimeError,
) -> RocDocBuilder<'b> { ) -> RocDocBuilder<'b> {
match runtime_error { match runtime_error {
RuntimeError::VoidValue => {
// is used to communicate to the compiler that
// a branch is unreachable; this should never reach a user
unreachable!("")
}
RuntimeError::Shadowing { RuntimeError::Shadowing {
original_region, original_region,
shadow, shadow,

View file

@ -1643,6 +1643,17 @@ fn to_diff<'b>(
ErrorType::Type(Symbol::NUM_F64, _) => true, ErrorType::Type(Symbol::NUM_F64, _) => true,
ErrorType::Alias(Symbol::NUM_F64, _, _) => true, ErrorType::Alias(Symbol::NUM_F64, _, _) => true,
ErrorType::Type(Symbol::NUM_NUM, args) => match &args.get(0) {
Some(ErrorType::Type(Symbol::NUM_FLOATINGPOINT, _)) => true,
Some(ErrorType::Alias(Symbol::NUM_FLOATINGPOINT, _, _)) => true,
_ => false,
},
ErrorType::Alias(Symbol::NUM_NUM, args, _) => match &args.get(0) {
Some((_, ErrorType::Type(Symbol::NUM_FLOATINGPOINT, _))) => true,
Some((_, ErrorType::Alias(Symbol::NUM_FLOATINGPOINT, _, _))) => true,
_ => false,
},
_ => false, _ => false,
}; };

View file

@ -7,7 +7,7 @@ use roc_can::expected::Expected;
use roc_can::expr::{canonicalize_expr, Expr, Output}; use roc_can::expr::{canonicalize_expr, Expr, Output};
use roc_can::operator; use roc_can::operator;
use roc_can::scope::Scope; use roc_can::scope::Scope;
use roc_collections::all::{ImMap, MutMap, SendMap, SendSet}; use roc_collections::all::{ImMap, MutMap, SendSet};
use roc_constrain::expr::constrain_expr; use roc_constrain::expr::constrain_expr;
use roc_constrain::module::{constrain_imported_values, Import}; use roc_constrain::module::{constrain_imported_values, Import};
use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds}; use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds};
@ -35,7 +35,7 @@ pub fn infer_expr(
) -> (Content, Subs) { ) -> (Content, Subs) {
let env = solve::Env { let env = solve::Env {
aliases: MutMap::default(), aliases: MutMap::default(),
vars_by_symbol: SendMap::default(), vars_by_symbol: MutMap::default(),
}; };
let (solved, _) = solve::run(&env, problems, subs, constraint); let (solved, _) = solve::run(&env, problems, subs, constraint);

View file

@ -448,8 +448,8 @@ mod test_reporting {
baz baz
Str Str
main
U8 U8
F64
"# "#
), ),
) )
@ -620,9 +620,9 @@ mod test_reporting {
these names seem close though: these names seem close though:
Result
Num Num
Set Set
Result
U8 U8
"# "#
), ),
@ -1374,7 +1374,7 @@ mod test_reporting {
Bool Bool
U8 U8
F64 F64
Num Str
"# "#
), ),
) )
@ -2094,7 +2094,7 @@ mod test_reporting {
But `add` needs the 2nd argument to be: But `add` needs the 2nd argument to be:
Num Integer Num (Integer Signed64)
"# "#
), ),
) )
@ -2123,7 +2123,7 @@ mod test_reporting {
But `add` needs the 2nd argument to be: But `add` needs the 2nd argument to be:
Num Integer Num (Integer Signed64)
Tip: You can convert between Int and Float using functions like Tip: You can convert between Int and Float using functions like
`Num.toFloat` and `Num.round`. `Num.toFloat` and `Num.round`.

View file

@ -1,7 +1,6 @@
use crate::solve; use crate::solve;
use roc_can::constraint::Constraint; use roc_can::constraint::Constraint;
use roc_can::module::Module; use roc_collections::all::MutMap;
use roc_collections::all::{MutMap, SendMap};
use roc_module::ident::Lowercase; use roc_module::ident::Lowercase;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_types::solved_types::{Solved, SolvedType}; use roc_types::solved_types::{Solved, SolvedType};
@ -12,37 +11,10 @@ use roc_types::types::Alias;
pub struct SolvedModule { pub struct SolvedModule {
pub solved_types: MutMap<Symbol, SolvedType>, pub solved_types: MutMap<Symbol, SolvedType>,
pub aliases: MutMap<Symbol, Alias>, pub aliases: MutMap<Symbol, Alias>,
pub exposed_vars_by_symbol: Vec<(Symbol, Variable)>, pub exposed_vars_by_symbol: MutMap<Symbol, Variable>,
pub problems: Vec<solve::TypeError>, pub problems: Vec<solve::TypeError>,
} }
pub fn solve_module(
module: Module,
constraint: Constraint,
var_store: VarStore,
) -> (Solved<Subs>, SolvedModule) {
let Module {
exposed_vars_by_symbol,
aliases,
rigid_variables,
..
} = module;
let (solved_subs, solved_env, problems) =
run_solve(aliases, rigid_variables, constraint, var_store);
let solved_types = make_solved_types(&solved_env, &solved_subs, &exposed_vars_by_symbol);
let solved_module = SolvedModule {
exposed_vars_by_symbol,
solved_types,
problems,
aliases: solved_env.aliases,
};
(solved_subs, solved_module)
}
pub fn run_solve( pub fn run_solve(
aliases: MutMap<Symbol, Alias>, aliases: MutMap<Symbol, Alias>,
rigid_variables: MutMap<Variable, Lowercase>, rigid_variables: MutMap<Variable, Lowercase>,
@ -50,7 +22,7 @@ pub fn run_solve(
var_store: VarStore, var_store: VarStore,
) -> (Solved<Subs>, solve::Env, Vec<solve::TypeError>) { ) -> (Solved<Subs>, solve::Env, Vec<solve::TypeError>) {
let env = solve::Env { let env = solve::Env {
vars_by_symbol: SendMap::default(), vars_by_symbol: MutMap::default(),
aliases, aliases,
}; };
@ -73,7 +45,7 @@ pub fn run_solve(
pub fn make_solved_types( pub fn make_solved_types(
solved_env: &solve::Env, solved_env: &solve::Env,
solved_subs: &Solved<Subs>, solved_subs: &Solved<Subs>,
exposed_vars_by_symbol: &[(Symbol, Variable)], exposed_vars_by_symbol: &MutMap<Symbol, Variable>,
) -> MutMap<Symbol, SolvedType> { ) -> MutMap<Symbol, SolvedType> {
let mut solved_types = MutMap::default(); let mut solved_types = MutMap::default();

View file

@ -1,6 +1,6 @@
use roc_can::constraint::Constraint::{self, *}; use roc_can::constraint::Constraint::{self, *};
use roc_can::expected::{Expected, PExpected}; use roc_can::expected::{Expected, PExpected};
use roc_collections::all::{ImMap, MutMap, SendMap}; use roc_collections::all::{ImMap, MutMap};
use roc_module::ident::TagName; use roc_module::ident::TagName;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_region::all::{Located, Region}; use roc_region::all::{Located, Region};
@ -73,7 +73,7 @@ pub enum TypeError {
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct Env { pub struct Env {
pub vars_by_symbol: SendMap<Symbol, Variable>, pub vars_by_symbol: MutMap<Symbol, Variable>,
pub aliases: MutMap<Symbol, Alias>, pub aliases: MutMap<Symbol, Alias>,
} }
@ -138,24 +138,9 @@ pub fn run(
mut subs: Subs, mut subs: Subs,
constraint: &Constraint, constraint: &Constraint,
) -> (Solved<Subs>, Env) { ) -> (Solved<Subs>, Env) {
let mut pools = Pools::default(); let env = run_in_place(env, problems, &mut subs, constraint);
let state = State {
env: env.clone(),
mark: Mark::NONE.next(),
};
let rank = Rank::toplevel();
let state = solve(
env,
state,
rank,
&mut pools,
problems,
&mut MutMap::default(),
&mut subs,
constraint,
);
(Solved(subs), state.env) (Solved(subs), env)
} }
/// Modify an existing subs in-place instead /// Modify an existing subs in-place instead

View file

@ -8,7 +8,7 @@ use roc_can::expected::Expected;
use roc_can::expr::{canonicalize_expr, Expr, Output}; use roc_can::expr::{canonicalize_expr, Expr, Output};
use roc_can::operator; use roc_can::operator;
use roc_can::scope::Scope; use roc_can::scope::Scope;
use roc_collections::all::{ImMap, ImSet, MutMap, SendMap, SendSet}; use roc_collections::all::{ImMap, ImSet, MutMap, SendSet};
use roc_constrain::expr::constrain_expr; use roc_constrain::expr::constrain_expr;
use roc_constrain::module::{constrain_imported_values, Import}; use roc_constrain::module::{constrain_imported_values, Import};
use roc_module::ident::Ident; use roc_module::ident::Ident;
@ -37,7 +37,7 @@ pub fn infer_expr(
) -> (Content, Subs) { ) -> (Content, Subs) {
let env = solve::Env { let env = solve::Env {
aliases: MutMap::default(), aliases: MutMap::default(),
vars_by_symbol: SendMap::default(), vars_by_symbol: MutMap::default(),
}; };
let (solved, _) = solve::run(&env, problems, subs, constraint); let (solved, _) = solve::run(&env, problems, subs, constraint);

View file

@ -1326,7 +1326,7 @@ mod solve_expr {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
int : Num.Num Num.Integer int : Num.Num (Num.Integer Num.Signed64)
int int
"# "#
@ -1339,7 +1339,7 @@ mod solve_expr {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
int : Num.Num Num.Integer int : Num.Num (Num.Integer Num.Signed64)
int = 5 int = 5
int int
@ -1353,7 +1353,7 @@ mod solve_expr {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
int : Num Integer int : Num (Integer Signed64)
int int
"# "#
@ -1366,7 +1366,7 @@ mod solve_expr {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
int : Num Integer int : Num (Integer Signed64)
int = 5 int = 5
int int
@ -1931,7 +1931,7 @@ mod solve_expr {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
float : Num.Num Num.FloatingPoint float : Num.Num (Num.FloatingPoint Num.Binary64)
float float
"# "#
@ -1944,7 +1944,7 @@ mod solve_expr {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
float : Num.Num Num.FloatingPoint float : Num.Num (Num.FloatingPoint Num.Binary64)
float = 5.5 float = 5.5
float float
@ -1958,7 +1958,7 @@ mod solve_expr {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
float : Num FloatingPoint float : Num (FloatingPoint Binary64)
float float
"# "#
@ -1971,7 +1971,7 @@ mod solve_expr {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
float : Num FloatingPoint float : Num (FloatingPoint Binary64)
float = 5.5 float = 5.5
float float
@ -2216,7 +2216,7 @@ mod solve_expr {
infer_eq_without_problem( infer_eq_without_problem(
indoc!( indoc!(
r#" r#"
x : Num.Num Num.Integer x : Num.Num (Num.Integer Num.Signed64)
x = x =
when 2 is when 2 is
3 -> 4 3 -> 4
@ -2428,7 +2428,7 @@ mod solve_expr {
r#" r#"
Foo a : { foo : a } Foo a : { foo : a }
v : Foo (Num.Num Num.Integer) v : Foo (Num.Num (Num.Integer Num.Signed64))
v = { foo: 42 } v = { foo: 42 }
v v
@ -2492,7 +2492,7 @@ mod solve_expr {
r#" r#"
Peano : [ S Peano, Z ] Peano : [ S Peano, Z ]
length : Peano -> Num.Num Num.Integer length : Peano -> Num.Num (Num.Integer Num.Signed64)
length = \peano -> length = \peano ->
when peano is when peano is
Z -> 0 Z -> 0
@ -2592,10 +2592,10 @@ mod solve_expr {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
r : { x : (Num.Num Num.Integer) } r : { x : (Num.Num (Num.Integer Signed64)) }
r = { x : 1 } r = { x : 1 }
s : { left : { x : Num.Num Num.FloatingPoint } } s : { left : { x : Num.Num (Num.FloatingPoint Num.Binary64) } }
s = { left: { x : 3.14 } } s = { left: { x : 3.14 } }
when 0 is when 0 is
@ -2757,7 +2757,7 @@ mod solve_expr {
infer_eq_without_problem( infer_eq_without_problem(
indoc!( indoc!(
r#" r#"
{ x, y } : { x : Str.Str, y : Num.Num Num.FloatingPoint } { x, y } : { x : Str.Str, y : Num.Num (Num.FloatingPoint Num.Binary64) }
{ x, y } = { x : "foo", y : 3.14 } { x, y } = { x : "foo", y : 3.14 }
x x
@ -2772,7 +2772,7 @@ mod solve_expr {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
Foo : { x : Str.Str, y : Num.Num Num.FloatingPoint } Foo : { x : Str.Str, y : Num.Num (Num.FloatingPoint Num.Binary64) }
{ x, y } : Foo { x, y } : Foo
{ x, y } = { x : "foo", y : 3.14 } { x, y } = { x : "foo", y : 3.14 }
@ -2830,7 +2830,7 @@ mod solve_expr {
infer_eq_without_problem( infer_eq_without_problem(
indoc!( indoc!(
r#" r#"
Foo : { x : Str.Str, y : Num.Num Num.FloatingPoint } Foo : { x : Str.Str, y : Num.Num (Num.FloatingPoint Num.Binary64) }
{ x, y } : Foo { x, y } : Foo
{ x, y } = { x : "foo", y : 3.14 } { x, y } = { x : "foo", y : 3.14 }

View file

@ -1118,7 +1118,7 @@ mod solve_uniq_expr {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
x : Num.Num Num.Integer x : Num.Num (Num.Integer Num.Signed64)
x = 4 x = 4
x x
@ -1368,7 +1368,7 @@ mod solve_uniq_expr {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
x : Num.Num Num.Integer x : I64
x = x =
when 2 is when 2 is
3 -> 4 3 -> 4
@ -1816,7 +1816,7 @@ mod solve_uniq_expr {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
{ x, y } : { x : Str.Str, y : Num.Num Num.FloatingPoint } { x, y } : { x : Str.Str, y : F64 }
{ x, y } = { x : "foo", y : 3.14 } { x, y } = { x : "foo", y : 3.14 }
x x
@ -2662,7 +2662,7 @@ mod solve_uniq_expr {
f f
"# "#
), ),
"Attr * (Attr a I64, Attr b I64 -> Attr c I64)", "Attr * (Attr b I64, Attr c I64 -> Attr d I64)",
); );
} }
@ -3159,4 +3159,55 @@ mod solve_uniq_expr {
"Attr a I64", "Attr a I64",
); );
} }
#[test]
fn list_set_out_of_bounds_num() {
infer_eq(
indoc!(
r#"
List.set [2] 1337 0
"#
),
"Attr * (List (Attr * (Num (Attr * *))))",
);
}
#[test]
fn list_set_out_of_bounds_int() {
infer_eq(
indoc!(
r#"
List.set [0x2] 1337 0
"#
),
"Attr * (List (Attr * I64))",
);
}
#[test]
fn list_set_out_of_bounds_float() {
infer_eq(
indoc!(
r#"
List.set [0.2] 1337 0
"#
),
"Attr * (List (Attr * F64))",
);
}
#[test]
#[ignore]
fn list_set_out_of_bounds_int_int() {
// the unification of an integer list with a new integer element is a problem
// same for floats, but it's fine with the unspecified Num
infer_eq(
indoc!(
r#"
List.set [0x2] 1337 0x1
"#
),
"Attr * (List (Attr a I64))",
);
}
} }

View file

@ -38,143 +38,213 @@ pub fn aliases() -> MutMap<Symbol, BuiltinAlias> {
}, },
); );
// Integer : [ @Integer ] // Integer range : [ @Integer range ]
add_alias( add_alias(
Symbol::NUM_INTEGER, Symbol::NUM_INTEGER,
BuiltinAlias { BuiltinAlias {
region: Region::zero(), region: Region::zero(),
vars: Vec::new(), vars: vec![Located::at(Region::zero(), "range".into())],
typ: integer_alias_content(), typ: integer_alias_content(flex(TVAR1)),
}, },
); );
// I128 Num Integer // Signed128 : [ @Signed128 ]
add_alias(
Symbol::NUM_SIGNED128,
BuiltinAlias {
region: Region::zero(),
vars: vec![],
typ: signed128_alias_content(),
},
);
// I128 : Num (Integer Signed128)
add_alias( add_alias(
Symbol::NUM_I128, Symbol::NUM_I128,
BuiltinAlias { BuiltinAlias {
region: Region::zero(), region: Region::zero(),
vars: Vec::new(), vars: Vec::new(),
typ: int_alias_content(), typ: int_alias_content(signed128_type()),
}, },
); );
// U128 : Num Integer // U128 : Num (Integer Unsigned128)
add_alias( add_alias(
Symbol::NUM_U128, Symbol::NUM_U128,
BuiltinAlias { BuiltinAlias {
region: Region::zero(), region: Region::zero(),
vars: Vec::new(), vars: Vec::new(),
typ: int_alias_content(), typ: int_alias_content(unsigned128_type()),
}, },
); );
// I64 Num Integer // Signed64 : [ @Signed64 ]
add_alias(
Symbol::NUM_SIGNED64,
BuiltinAlias {
region: Region::zero(),
vars: vec![],
typ: signed64_alias_content(),
},
);
// I64 : Num (Integer Signed64)
add_alias( add_alias(
Symbol::NUM_I64, Symbol::NUM_I64,
BuiltinAlias { BuiltinAlias {
region: Region::zero(), region: Region::zero(),
vars: Vec::new(), vars: Vec::new(),
typ: int_alias_content(), typ: int_alias_content(signed64_type()),
}, },
); );
// U64 : Num Integer // U64 : Num (Integer Unsigned64)
add_alias( add_alias(
Symbol::NUM_U64, Symbol::NUM_U64,
BuiltinAlias { BuiltinAlias {
region: Region::zero(), region: Region::zero(),
vars: Vec::new(), vars: Vec::new(),
typ: int_alias_content(), typ: int_alias_content(unsigned64_type()),
}, },
); );
// I32 Num Integer // Signed32 : [ @Signed32 ]
add_alias(
Symbol::NUM_SIGNED32,
BuiltinAlias {
region: Region::zero(),
vars: vec![],
typ: signed32_alias_content(),
},
);
// I32 : Num (Integer Signed32)
add_alias( add_alias(
Symbol::NUM_I32, Symbol::NUM_I32,
BuiltinAlias { BuiltinAlias {
region: Region::zero(), region: Region::zero(),
vars: Vec::new(), vars: Vec::new(),
typ: int_alias_content(), typ: int_alias_content(signed32_type()),
}, },
); );
// U32 : Num Integer // U32 : Num (Integer Unsigned32)
add_alias( add_alias(
Symbol::NUM_U32, Symbol::NUM_U32,
BuiltinAlias { BuiltinAlias {
region: Region::zero(), region: Region::zero(),
vars: Vec::new(), vars: Vec::new(),
typ: int_alias_content(), typ: int_alias_content(unsigned32_type()),
}, },
); );
// I16 Num Integer // Signed16 : [ @Signed16 ]
add_alias(
Symbol::NUM_SIGNED16,
BuiltinAlias {
region: Region::zero(),
vars: vec![],
typ: signed16_alias_content(),
},
);
// I16 : Num (Integer Signed16)
add_alias( add_alias(
Symbol::NUM_I16, Symbol::NUM_I16,
BuiltinAlias { BuiltinAlias {
region: Region::zero(), region: Region::zero(),
vars: Vec::new(), vars: Vec::new(),
typ: int_alias_content(), typ: int_alias_content(signed16_type()),
}, },
); );
// U16 : Num Integer // U16 : Num (Integer Unsigned16)
add_alias( add_alias(
Symbol::NUM_U16, Symbol::NUM_U16,
BuiltinAlias { BuiltinAlias {
region: Region::zero(), region: Region::zero(),
vars: Vec::new(), vars: Vec::new(),
typ: int_alias_content(), typ: int_alias_content(unsigned16_type()),
}, },
); );
// I8 Num Integer // Signed8 : [ @Signed8 ]
add_alias(
Symbol::NUM_SIGNED8,
BuiltinAlias {
region: Region::zero(),
vars: vec![],
typ: signed8_alias_content(),
},
);
// I8 : Num (Integer Signed8)
add_alias( add_alias(
Symbol::NUM_I8, Symbol::NUM_I8,
BuiltinAlias { BuiltinAlias {
region: Region::zero(), region: Region::zero(),
vars: Vec::new(), vars: Vec::new(),
typ: int_alias_content(), typ: int_alias_content(signed8_type()),
}, },
); );
// U8 : Num Integer // U8 : Num (Integer Unsigned8)
add_alias( add_alias(
Symbol::NUM_U8, Symbol::NUM_U8,
BuiltinAlias { BuiltinAlias {
region: Region::zero(), region: Region::zero(),
vars: Vec::new(), vars: Vec::new(),
typ: int_alias_content(), typ: int_alias_content(unsigned8_type()),
}, },
); );
// FloatingPoint : [ @FloatingPoint ] // Binary64 : [ @Binary64 ]
add_alias(
Symbol::NUM_BINARY64,
BuiltinAlias {
region: Region::zero(),
vars: vec![],
typ: binary64_alias_content(),
},
);
// Binary32 : [ @Binary32 ]
add_alias(
Symbol::NUM_BINARY32,
BuiltinAlias {
region: Region::zero(),
vars: vec![],
typ: binary32_alias_content(),
},
);
// FloatingPoint range : [ @FloatingPoint range ]
add_alias( add_alias(
Symbol::NUM_FLOATINGPOINT, Symbol::NUM_FLOATINGPOINT,
BuiltinAlias { BuiltinAlias {
region: Region::zero(), region: Region::zero(),
vars: Vec::new(), vars: vec![Located::at(Region::zero(), "range".into())],
typ: floatingpoint_alias_content(), typ: floatingpoint_alias_content(flex(TVAR1)),
}, },
); );
// F64 : Num FloatingPoint // F64 : Num (FloatingPoint Binary64)
add_alias( add_alias(
Symbol::NUM_F64, Symbol::NUM_F64,
BuiltinAlias { BuiltinAlias {
region: Region::zero(), region: Region::zero(),
vars: Vec::new(), vars: Vec::new(),
typ: float_alias_content(), typ: float_alias_content(binary64_type()),
}, },
); );
// F32 : Num FloatingPoint // F32 : Num (FloatingPoint Binary32)
add_alias( add_alias(
Symbol::NUM_F32, Symbol::NUM_F32,
BuiltinAlias { BuiltinAlias {
region: Region::zero(), region: Region::zero(),
vars: Vec::new(), vars: Vec::new(),
typ: float_alias_content(), typ: float_alias_content(binary32_type()),
}, },
); );
@ -226,57 +296,233 @@ fn num_alias_content(range: SolvedType) -> SolvedType {
// FLOATING POINT // FLOATING POINT
#[inline(always)] #[inline(always)]
pub fn floatingpoint_type() -> SolvedType { pub fn floatingpoint_type(range: SolvedType) -> SolvedType {
SolvedType::Alias( SolvedType::Alias(
Symbol::NUM_FLOATINGPOINT, Symbol::NUM_FLOATINGPOINT,
Vec::new(), vec![("range".into(), range.clone())],
Box::new(floatingpoint_alias_content()), Box::new(floatingpoint_alias_content(range)),
) )
} }
#[inline(always)] #[inline(always)]
fn floatingpoint_alias_content() -> SolvedType { fn floatingpoint_alias_content(range: SolvedType) -> SolvedType {
single_private_tag(Symbol::NUM_AT_FLOATINGPOINT, Vec::new()) single_private_tag(Symbol::NUM_AT_FLOATINGPOINT, vec![range])
} }
// FLOAT // FLOAT
#[inline(always)] #[inline(always)]
pub fn float_type() -> SolvedType { pub fn float_type() -> SolvedType {
SolvedType::Alias(Symbol::NUM_F64, Vec::new(), Box::new(float_alias_content())) SolvedType::Alias(
Symbol::NUM_F64,
Vec::new(),
Box::new(float_alias_content(binary64_type())),
)
} }
#[inline(always)] #[inline(always)]
fn float_alias_content() -> SolvedType { fn float_alias_content(typ: SolvedType) -> SolvedType {
num_type(floatingpoint_type()) num_type(floatingpoint_type(typ))
} }
// INT // INT
#[inline(always)] #[inline(always)]
pub fn int_type() -> SolvedType { pub fn int_type() -> SolvedType {
SolvedType::Alias(Symbol::NUM_I64, Vec::new(), Box::new(int_alias_content())) SolvedType::Alias(
Symbol::NUM_I64,
Vec::new(),
Box::new(int_alias_content(signed64_type())),
)
} }
#[inline(always)] #[inline(always)]
fn int_alias_content() -> SolvedType { fn int_alias_content(range: SolvedType) -> SolvedType {
num_type(integer_type()) num_type(integer_type(range))
} }
// INTEGER // INTEGER
#[inline(always)] #[inline(always)]
pub fn integer_type() -> SolvedType { pub fn integer_type(range: SolvedType) -> SolvedType {
SolvedType::Alias( SolvedType::Alias(
Symbol::NUM_INTEGER, Symbol::NUM_INTEGER,
Vec::new(), vec![("range".into(), range.clone())],
Box::new(integer_alias_content()), Box::new(integer_alias_content(range)),
) )
} }
#[inline(always)] #[inline(always)]
fn integer_alias_content() -> SolvedType { fn integer_alias_content(range: SolvedType) -> SolvedType {
single_private_tag(Symbol::NUM_AT_INTEGER, Vec::new()) single_private_tag(Symbol::NUM_AT_INTEGER, vec![range])
}
#[inline(always)]
pub fn binary64_type() -> SolvedType {
SolvedType::Alias(
Symbol::NUM_BINARY64,
vec![],
Box::new(binary64_alias_content()),
)
}
#[inline(always)]
pub fn binary64_alias_content() -> SolvedType {
single_private_tag(Symbol::NUM_AT_BINARY64, vec![])
}
#[inline(always)]
pub fn binary32_type() -> SolvedType {
SolvedType::Alias(
Symbol::NUM_BINARY32,
vec![],
Box::new(binary32_alias_content()),
)
}
#[inline(always)]
fn binary32_alias_content() -> SolvedType {
single_private_tag(Symbol::NUM_AT_BINARY32, vec![])
}
#[inline(always)]
pub fn signed128_type() -> SolvedType {
SolvedType::Alias(
Symbol::NUM_SIGNED128,
vec![],
Box::new(signed128_alias_content()),
)
}
#[inline(always)]
fn signed128_alias_content() -> SolvedType {
single_private_tag(Symbol::NUM_AT_SIGNED128, vec![])
}
#[inline(always)]
pub fn signed64_type() -> SolvedType {
SolvedType::Alias(
Symbol::NUM_SIGNED64,
vec![],
Box::new(signed64_alias_content()),
)
}
#[inline(always)]
fn signed64_alias_content() -> SolvedType {
single_private_tag(Symbol::NUM_AT_SIGNED64, vec![])
}
#[inline(always)]
pub fn signed32_type() -> SolvedType {
SolvedType::Alias(
Symbol::NUM_SIGNED32,
vec![],
Box::new(signed32_alias_content()),
)
}
#[inline(always)]
fn signed32_alias_content() -> SolvedType {
single_private_tag(Symbol::NUM_AT_SIGNED32, vec![])
}
#[inline(always)]
pub fn signed16_type() -> SolvedType {
SolvedType::Alias(
Symbol::NUM_SIGNED16,
vec![],
Box::new(signed16_alias_content()),
)
}
#[inline(always)]
fn signed16_alias_content() -> SolvedType {
single_private_tag(Symbol::NUM_AT_SIGNED16, vec![])
}
#[inline(always)]
pub fn signed8_type() -> SolvedType {
SolvedType::Alias(
Symbol::NUM_SIGNED8,
vec![],
Box::new(signed8_alias_content()),
)
}
#[inline(always)]
fn signed8_alias_content() -> SolvedType {
single_private_tag(Symbol::NUM_AT_SIGNED8, vec![])
}
#[inline(always)]
pub fn unsigned128_type() -> SolvedType {
SolvedType::Alias(
Symbol::NUM_UNSIGNED128,
vec![],
Box::new(unsigned128_alias_content()),
)
}
#[inline(always)]
fn unsigned128_alias_content() -> SolvedType {
single_private_tag(Symbol::NUM_AT_UNSIGNED128, vec![])
}
#[inline(always)]
pub fn unsigned64_type() -> SolvedType {
SolvedType::Alias(
Symbol::NUM_UNSIGNED64,
vec![],
Box::new(unsigned64_alias_content()),
)
}
#[inline(always)]
fn unsigned64_alias_content() -> SolvedType {
single_private_tag(Symbol::NUM_AT_UNSIGNED64, vec![])
}
#[inline(always)]
pub fn unsigned32_type() -> SolvedType {
SolvedType::Alias(
Symbol::NUM_UNSIGNED32,
vec![],
Box::new(unsigned32_alias_content()),
)
}
#[inline(always)]
fn unsigned32_alias_content() -> SolvedType {
single_private_tag(Symbol::NUM_AT_UNSIGNED32, vec![])
}
#[inline(always)]
pub fn unsigned16_type() -> SolvedType {
SolvedType::Alias(
Symbol::NUM_UNSIGNED16,
vec![],
Box::new(unsigned16_alias_content()),
)
}
#[inline(always)]
fn unsigned16_alias_content() -> SolvedType {
single_private_tag(Symbol::NUM_AT_UNSIGNED16, vec![])
}
#[inline(always)]
pub fn unsigned8_type() -> SolvedType {
SolvedType::Alias(
Symbol::NUM_UNSIGNED8,
vec![],
Box::new(unsigned8_alias_content()),
)
}
#[inline(always)]
fn unsigned8_alias_content() -> SolvedType {
single_private_tag(Symbol::NUM_AT_UNSIGNED8, vec![])
} }
#[inline(always)] #[inline(always)]
@ -351,7 +597,7 @@ pub fn dict_type(key: SolvedType, value: SolvedType) -> SolvedType {
SolvedType::Apply(Symbol::DICT_DICT, vec![key, value]) SolvedType::Apply(Symbol::DICT_DICT, vec![key, value])
} }
fn single_private_tag(symbol: Symbol, type_arguments: Vec<SolvedType>) -> SolvedType { pub fn single_private_tag(symbol: Symbol, type_arguments: Vec<SolvedType>) -> SolvedType {
SolvedType::TagUnion( SolvedType::TagUnion(
vec![(TagName::Private(symbol), type_arguments)], vec![(TagName::Private(symbol), type_arguments)],
Box::new(SolvedType::EmptyTagUnion), Box::new(SolvedType::EmptyTagUnion),

View file

@ -753,10 +753,10 @@ fn write_apply(
match &arg_content { match &arg_content {
Content::Structure(FlatType::Apply(symbol, nested_args)) => match *symbol { Content::Structure(FlatType::Apply(symbol, nested_args)) => match *symbol {
Symbol::NUM_INTEGER if nested_args.is_empty() => { Symbol::NUM_INTEGER if nested_args.len() == 1 => {
buf.push_str("I64"); buf.push_str("I64");
} }
Symbol::NUM_FLOATINGPOINT if nested_args.is_empty() => { Symbol::NUM_FLOATINGPOINT if nested_args.len() == 1 => {
buf.push_str("F64"); buf.push_str("F64");
} }
Symbol::ATTR_ATTR => match nested_args Symbol::ATTR_ATTR => match nested_args
@ -767,10 +767,10 @@ fn write_apply(
double_nested_symbol, double_nested_symbol,
double_nested_args, double_nested_args,
))) => match double_nested_symbol { ))) => match double_nested_symbol {
Symbol::NUM_INTEGER if double_nested_args.is_empty() => { Symbol::NUM_INTEGER if double_nested_args.len() == 1 => {
buf.push_str("I64"); buf.push_str("I64");
} }
Symbol::NUM_FLOATINGPOINT if double_nested_args.is_empty() => { Symbol::NUM_FLOATINGPOINT if double_nested_args.len() == 1 => {
buf.push_str("F64"); buf.push_str("F64");
} }
_ => default_case(subs, arg_content), _ => default_case(subs, arg_content),

View file

@ -198,7 +198,7 @@ fn unify_alias(
problems problems
} else { } else {
mismatch!() mismatch!("{}", symbol)
} }
} else { } else {
unify_pool(subs, pool, real_var, *other_real_var) unify_pool(subs, pool, real_var, *other_real_var)

View file

@ -8,7 +8,7 @@ use roc_can::expected::Expected;
use roc_can::expr::{canonicalize_expr, Expr, Output}; use roc_can::expr::{canonicalize_expr, Expr, Output};
use roc_can::operator; use roc_can::operator;
use roc_can::scope::Scope; use roc_can::scope::Scope;
use roc_collections::all::{ImMap, ImSet, MutMap, SendMap, SendSet}; use roc_collections::all::{ImMap, ImSet, MutMap, SendSet};
use roc_constrain::expr::constrain_expr; use roc_constrain::expr::constrain_expr;
use roc_constrain::module::{constrain_imported_values, Import}; use roc_constrain::module::{constrain_imported_values, Import};
use roc_module::ident::Ident; use roc_module::ident::Ident;
@ -37,7 +37,7 @@ pub fn infer_expr(
) -> (Content, Subs) { ) -> (Content, Subs) {
let env = solve::Env { let env = solve::Env {
aliases: MutMap::default(), aliases: MutMap::default(),
vars_by_symbol: SendMap::default(), vars_by_symbol: MutMap::default(),
}; };
let (solved, _) = solve::run(&env, problems, subs, constraint); let (solved, _) = solve::run(&env, problems, subs, constraint);

View file

@ -3,6 +3,6 @@ app "effect-example"
imports [base.Task] imports [base.Task]
provides [ main ] to base provides [ main ] to base
main : Task.Task {} F64 main : Task.Task {} []
main = main =
Task.after Task.getLine \lineThisThing -> Task.putLine lineThisThing Task.after Task.getLine \lineThisThing -> Task.putLine lineThisThing

View file

@ -12,5 +12,5 @@ platform folkertdev/foo
} }
mainForHost : Task.Task {} F64 as Fx mainForHost : Task.Task {} [] as Fx
mainForHost = main mainForHost = main