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

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
tar -xf zig-linux-x86_64-0.6.0+0088efc4b.tar.xz
ln -s "$PWD/zig-linux-x86_64-0.6.0+0088efc4b/zig" /usr/local/bin/zig
# test sccache
./ci/sccache -V
# copy sccache to prevent current working dir problems
cp ./ci/sccache /usr/local/bin/sccache

BIN
ci/sccache Executable file

Binary file not shown.

View file

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

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

View file

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

View file

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

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -198,7 +198,7 @@ fn unify_alias(
problems
} else {
mismatch!()
mismatch!("{}", symbol)
}
} else {
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::operator;
use roc_can::scope::Scope;
use roc_collections::all::{ImMap, ImSet, MutMap, SendMap, SendSet};
use roc_collections::all::{ImMap, ImSet, MutMap, SendSet};
use roc_constrain::expr::constrain_expr;
use roc_constrain::module::{constrain_imported_values, Import};
use roc_module::ident::Ident;
@ -37,7 +37,7 @@ pub fn infer_expr(
) -> (Content, Subs) {
let env = solve::Env {
aliases: MutMap::default(),
vars_by_symbol: SendMap::default(),
vars_by_symbol: MutMap::default(),
};
let (solved, _) = solve::run(&env, problems, subs, constraint);

View file

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

View file

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