mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 08:34:33 +00:00
Merge branch 'trunk' of github.com:rtfeldman/roc into docs-parse-code-blocks
This commit is contained in:
commit
1ff614b2f1
94 changed files with 6432 additions and 3889 deletions
|
@ -1,12 +1,10 @@
|
||||||
# Building the Roc compiler from source
|
# Building the Roc compiler from source
|
||||||
|
|
||||||
|
|
||||||
## Installing LLVM, Python, Zig, valgrind, libunwind, and libc++-dev
|
## Installing LLVM, Zig, valgrind, and Python 2.7
|
||||||
|
|
||||||
To build the compiler, you need these installed:
|
To build the compiler, you need these installed:
|
||||||
|
|
||||||
* `libunwind` (macOS should already have this one installed)
|
|
||||||
* `libc++-dev` and `libc++abi-dev`
|
|
||||||
* Python 2.7 (Windows only), `python-is-python3` (Ubuntu)
|
* Python 2.7 (Windows only), `python-is-python3` (Ubuntu)
|
||||||
* [Zig](https://ziglang.org/), see below for version
|
* [Zig](https://ziglang.org/), see below for version
|
||||||
* LLVM, see below for version
|
* LLVM, see below for version
|
||||||
|
@ -18,11 +16,6 @@ Alternatively, you can use `cargo test --no-fail-fast` or `cargo test -p specifi
|
||||||
|
|
||||||
For debugging LLVM IR, we use [DebugIR](https://github.com/vaivaswatha/debugir). This dependency is only required to build with the `--debug` flag, and for normal developtment you should be fine without it.
|
For debugging LLVM IR, we use [DebugIR](https://github.com/vaivaswatha/debugir). This dependency is only required to build with the `--debug` flag, and for normal developtment you should be fine without it.
|
||||||
|
|
||||||
### libunwind & libc++-dev
|
|
||||||
|
|
||||||
MacOS systems should already have `libunwind`, but other systems will need to install it (On Ubuntu, this can be done with `sudo apt-get install libunwind-dev`).
|
|
||||||
Some systems may already have `libc++-dev` on them, but if not, you may need to install it. (On Ubuntu, this can be done with `sudo apt-get install libc++-dev libc++abi-dev`.)
|
|
||||||
|
|
||||||
### libcxb libraries
|
### libcxb libraries
|
||||||
|
|
||||||
You may see an error like this during builds:
|
You may see an error like this during builds:
|
||||||
|
|
|
@ -9,7 +9,7 @@ use roc_mono::ir::ProcLayout;
|
||||||
use roc_mono::layout::{union_sorted_tags_help, Builtin, Layout, UnionLayout, UnionVariant};
|
use roc_mono::layout::{union_sorted_tags_help, Builtin, Layout, UnionLayout, UnionVariant};
|
||||||
use roc_parse::ast::{AssignedField, Expr, StrLiteral};
|
use roc_parse::ast::{AssignedField, Expr, StrLiteral};
|
||||||
use roc_region::all::{Located, Region};
|
use roc_region::all::{Located, Region};
|
||||||
use roc_types::subs::{Content, FlatType, RecordFields, Subs, Variable};
|
use roc_types::subs::{Content, FlatType, GetSubsSlice, RecordFields, Subs, UnionTags, Variable};
|
||||||
|
|
||||||
struct Env<'a, 'env> {
|
struct Env<'a, 'env> {
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
|
@ -162,19 +162,27 @@ fn jit_to_ast_help<'a>(
|
||||||
Content::Structure(FlatType::TagUnion(tags, _)) => {
|
Content::Structure(FlatType::TagUnion(tags, _)) => {
|
||||||
debug_assert_eq!(tags.len(), 1);
|
debug_assert_eq!(tags.len(), 1);
|
||||||
|
|
||||||
let (tag_name, payload_vars) = tags.iter().next().unwrap();
|
let (tag_name, payload_vars) = unpack_single_element_tag_union(env.subs, *tags);
|
||||||
|
|
||||||
Ok(single_tag_union_to_ast(
|
Ok(single_tag_union_to_ast(
|
||||||
env,
|
env,
|
||||||
ptr,
|
ptr,
|
||||||
field_layouts,
|
field_layouts,
|
||||||
tag_name.clone(),
|
tag_name,
|
||||||
payload_vars,
|
payload_vars,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
Content::Structure(FlatType::FunctionOrTagUnion(tag_name, _, _)) => Ok(
|
Content::Structure(FlatType::FunctionOrTagUnion(tag_name, _, _)) => {
|
||||||
single_tag_union_to_ast(env, ptr, field_layouts, *tag_name.clone(), &[]),
|
let tag_name = &env.subs[*tag_name];
|
||||||
),
|
|
||||||
|
Ok(single_tag_union_to_ast(
|
||||||
|
env,
|
||||||
|
ptr,
|
||||||
|
field_layouts,
|
||||||
|
tag_name,
|
||||||
|
&[],
|
||||||
|
))
|
||||||
|
}
|
||||||
Content::Structure(FlatType::Func(_, _, _)) => {
|
Content::Structure(FlatType::Func(_, _, _)) => {
|
||||||
// a function with a struct as the closure environment
|
// a function with a struct as the closure environment
|
||||||
Err(ToAstProblem::FunctionLayout)
|
Err(ToAstProblem::FunctionLayout)
|
||||||
|
@ -206,8 +214,13 @@ fn jit_to_ast_help<'a>(
|
||||||
Content::Structure(FlatType::TagUnion(tags, _)) => {
|
Content::Structure(FlatType::TagUnion(tags, _)) => {
|
||||||
debug_assert_eq!(union_layouts.len(), tags.len());
|
debug_assert_eq!(union_layouts.len(), tags.len());
|
||||||
|
|
||||||
let tags_vec: std::vec::Vec<(TagName, std::vec::Vec<Variable>)> =
|
let tags_vec: std::vec::Vec<(TagName, std::vec::Vec<Variable>)> = tags
|
||||||
tags.iter().map(|(a, b)| (a.clone(), b.clone())).collect();
|
.unsorted_iterator(env.subs, Variable::EMPTY_TAG_UNION)
|
||||||
|
.map(|(a, b)| (a.clone(), b.to_vec()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let tags_map: roc_collections::all::MutMap<_, _> =
|
||||||
|
tags_vec.iter().cloned().collect();
|
||||||
|
|
||||||
let union_variant = union_sorted_tags_help(env.arena, tags_vec, None, env.subs);
|
let union_variant = union_sorted_tags_help(env.arena, tags_vec, None, env.subs);
|
||||||
|
|
||||||
|
@ -247,7 +260,7 @@ fn jit_to_ast_help<'a>(
|
||||||
*(ptr.add(offset as usize) as *const i16) as i64
|
*(ptr.add(offset as usize) as *const i16) as i64
|
||||||
}
|
}
|
||||||
Builtin::Int64 => {
|
Builtin::Int64 => {
|
||||||
// used by non-recursive tag unions at the
|
// used by non-recursive unions at the
|
||||||
// moment, remove if that is no longer the case
|
// moment, remove if that is no longer the case
|
||||||
*(ptr.add(offset as usize) as *const i64) as i64
|
*(ptr.add(offset as usize) as *const i64) as i64
|
||||||
}
|
}
|
||||||
|
@ -262,7 +275,7 @@ fn jit_to_ast_help<'a>(
|
||||||
let loc_tag_expr =
|
let loc_tag_expr =
|
||||||
&*env.arena.alloc(Located::at_zero(tag_expr));
|
&*env.arena.alloc(Located::at_zero(tag_expr));
|
||||||
|
|
||||||
let variables = &tags[tag_name];
|
let variables = &tags_map[tag_name];
|
||||||
|
|
||||||
debug_assert_eq!(arg_layouts.len(), variables.len());
|
debug_assert_eq!(arg_layouts.len(), variables.len());
|
||||||
|
|
||||||
|
@ -295,7 +308,7 @@ fn jit_to_ast_help<'a>(
|
||||||
let loc_tag_expr =
|
let loc_tag_expr =
|
||||||
&*env.arena.alloc(Located::at_zero(tag_expr));
|
&*env.arena.alloc(Located::at_zero(tag_expr));
|
||||||
|
|
||||||
let variables = &tags[tag_name];
|
let variables = &tags_map[tag_name];
|
||||||
|
|
||||||
// because the arg_layouts include the tag ID, it is one longer
|
// because the arg_layouts include the tag ID, it is one longer
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
|
@ -339,8 +352,6 @@ fn jit_to_ast_help<'a>(
|
||||||
| Layout::RecursivePointer => {
|
| Layout::RecursivePointer => {
|
||||||
todo!("add support for rendering recursive tag unions in the REPL")
|
todo!("add support for rendering recursive tag unions in the REPL")
|
||||||
}
|
}
|
||||||
|
|
||||||
Layout::Closure(_, _, _) => Err(ToAstProblem::FunctionLayout),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -436,11 +447,12 @@ fn ptr_to_ast<'a>(
|
||||||
Content::Structure(FlatType::TagUnion(tags, _)) => {
|
Content::Structure(FlatType::TagUnion(tags, _)) => {
|
||||||
debug_assert_eq!(tags.len(), 1);
|
debug_assert_eq!(tags.len(), 1);
|
||||||
|
|
||||||
let (tag_name, payload_vars) = tags.iter().next().unwrap();
|
let (tag_name, payload_vars) = unpack_single_element_tag_union(env.subs, *tags);
|
||||||
single_tag_union_to_ast(env, ptr, field_layouts, tag_name.clone(), payload_vars)
|
single_tag_union_to_ast(env, ptr, field_layouts, tag_name, payload_vars)
|
||||||
}
|
}
|
||||||
Content::Structure(FlatType::FunctionOrTagUnion(tag_name, _, _)) => {
|
Content::Structure(FlatType::FunctionOrTagUnion(tag_name, _, _)) => {
|
||||||
single_tag_union_to_ast(env, ptr, field_layouts, *tag_name.clone(), &[])
|
let tag_name = &env.subs[*tag_name];
|
||||||
|
single_tag_union_to_ast(env, ptr, field_layouts, tag_name, &[])
|
||||||
}
|
}
|
||||||
Content::Structure(FlatType::EmptyRecord) => {
|
Content::Structure(FlatType::EmptyRecord) => {
|
||||||
struct_to_ast(env, ptr, &[], RecordFields::empty())
|
struct_to_ast(env, ptr, &[], RecordFields::empty())
|
||||||
|
@ -472,7 +484,8 @@ fn list_to_ast<'a>(
|
||||||
Content::Structure(FlatType::Apply(Symbol::LIST_LIST, vars)) => {
|
Content::Structure(FlatType::Apply(Symbol::LIST_LIST, vars)) => {
|
||||||
debug_assert_eq!(vars.len(), 1);
|
debug_assert_eq!(vars.len(), 1);
|
||||||
|
|
||||||
let elem_var = *vars.first().unwrap();
|
let elem_var_index = vars.into_iter().next().unwrap();
|
||||||
|
let elem_var = env.subs[elem_var_index];
|
||||||
|
|
||||||
env.subs.get_content_without_compacting(elem_var)
|
env.subs.get_content_without_compacting(elem_var)
|
||||||
}
|
}
|
||||||
|
@ -511,14 +524,14 @@ fn single_tag_union_to_ast<'a>(
|
||||||
env: &Env<'a, '_>,
|
env: &Env<'a, '_>,
|
||||||
ptr: *const u8,
|
ptr: *const u8,
|
||||||
field_layouts: &'a [Layout<'a>],
|
field_layouts: &'a [Layout<'a>],
|
||||||
tag_name: TagName,
|
tag_name: &TagName,
|
||||||
payload_vars: &[Variable],
|
payload_vars: &[Variable],
|
||||||
) -> Expr<'a> {
|
) -> Expr<'a> {
|
||||||
debug_assert_eq!(field_layouts.len(), payload_vars.len());
|
debug_assert_eq!(field_layouts.len(), payload_vars.len());
|
||||||
|
|
||||||
let arena = env.arena;
|
let arena = env.arena;
|
||||||
|
|
||||||
let tag_expr = tag_name_to_expr(env, &tag_name);
|
let tag_expr = tag_name_to_expr(env, tag_name);
|
||||||
|
|
||||||
let loc_tag_expr = &*arena.alloc(Located::at_zero(tag_expr));
|
let loc_tag_expr = &*arena.alloc(Located::at_zero(tag_expr));
|
||||||
|
|
||||||
|
@ -639,6 +652,36 @@ fn struct_to_ast<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn unpack_single_element_tag_union(subs: &Subs, tags: UnionTags) -> (&TagName, &[Variable]) {
|
||||||
|
let (tag_name_index, payload_vars_index) = tags.iter_all().next().unwrap();
|
||||||
|
|
||||||
|
let tag_name = &subs[tag_name_index];
|
||||||
|
let subs_slice = subs[payload_vars_index].as_subs_slice();
|
||||||
|
let payload_vars = subs.get_subs_slice(*subs_slice);
|
||||||
|
|
||||||
|
(tag_name, payload_vars)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unpack_two_element_tag_union(
|
||||||
|
subs: &Subs,
|
||||||
|
tags: UnionTags,
|
||||||
|
) -> (&TagName, &[Variable], &TagName, &[Variable]) {
|
||||||
|
let mut it = tags.iter_all();
|
||||||
|
let (tag_name_index, payload_vars_index) = it.next().unwrap();
|
||||||
|
|
||||||
|
let tag_name1 = &subs[tag_name_index];
|
||||||
|
let subs_slice = subs[payload_vars_index].as_subs_slice();
|
||||||
|
let payload_vars1 = subs.get_subs_slice(*subs_slice);
|
||||||
|
|
||||||
|
let (tag_name_index, payload_vars_index) = it.next().unwrap();
|
||||||
|
|
||||||
|
let tag_name2 = &subs[tag_name_index];
|
||||||
|
let subs_slice = subs[payload_vars_index].as_subs_slice();
|
||||||
|
let payload_vars2 = subs.get_subs_slice(*subs_slice);
|
||||||
|
|
||||||
|
(tag_name1, payload_vars1, tag_name2, payload_vars2)
|
||||||
|
}
|
||||||
|
|
||||||
fn bool_to_ast<'a>(env: &Env<'a, '_>, value: bool, content: &Content) -> Expr<'a> {
|
fn bool_to_ast<'a>(env: &Env<'a, '_>, value: bool, content: &Content) -> Expr<'a> {
|
||||||
use Content::*;
|
use Content::*;
|
||||||
|
|
||||||
|
@ -685,7 +728,7 @@ fn bool_to_ast<'a>(env: &Env<'a, '_>, value: bool, content: &Content) -> Expr<'a
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FlatType::TagUnion(tags, _) if tags.len() == 1 => {
|
FlatType::TagUnion(tags, _) if tags.len() == 1 => {
|
||||||
let (tag_name, payload_vars) = tags.iter().next().unwrap();
|
let (tag_name, payload_vars) = unpack_single_element_tag_union(env.subs, *tags);
|
||||||
|
|
||||||
let loc_tag_expr = {
|
let loc_tag_expr = {
|
||||||
let tag_name = &tag_name.as_ident_str(env.interns, env.home);
|
let tag_name = &tag_name.as_ident_str(env.interns, env.home);
|
||||||
|
@ -720,9 +763,8 @@ fn bool_to_ast<'a>(env: &Env<'a, '_>, value: bool, content: &Content) -> Expr<'a
|
||||||
Expr::Apply(loc_tag_expr, payload, CalledVia::Space)
|
Expr::Apply(loc_tag_expr, payload, CalledVia::Space)
|
||||||
}
|
}
|
||||||
FlatType::TagUnion(tags, _) if tags.len() == 2 => {
|
FlatType::TagUnion(tags, _) if tags.len() == 2 => {
|
||||||
let mut tags_iter = tags.iter();
|
let (tag_name_1, payload_vars_1, tag_name_2, payload_vars_2) =
|
||||||
let (tag_name_1, payload_vars_1) = tags_iter.next().unwrap();
|
unpack_two_element_tag_union(env.subs, *tags);
|
||||||
let (tag_name_2, payload_vars_2) = tags_iter.next().unwrap();
|
|
||||||
|
|
||||||
debug_assert!(payload_vars_1.is_empty());
|
debug_assert!(payload_vars_1.is_empty());
|
||||||
debug_assert!(payload_vars_2.is_empty());
|
debug_assert!(payload_vars_2.is_empty());
|
||||||
|
@ -801,7 +843,7 @@ fn byte_to_ast<'a>(env: &Env<'a, '_>, value: u8, content: &Content) -> Expr<'a>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FlatType::TagUnion(tags, _) if tags.len() == 1 => {
|
FlatType::TagUnion(tags, _) if tags.len() == 1 => {
|
||||||
let (tag_name, payload_vars) = tags.iter().next().unwrap();
|
let (tag_name, payload_vars) = unpack_single_element_tag_union(env.subs, *tags);
|
||||||
|
|
||||||
let loc_tag_expr = {
|
let loc_tag_expr = {
|
||||||
let tag_name = &tag_name.as_ident_str(env.interns, env.home);
|
let tag_name = &tag_name.as_ident_str(env.interns, env.home);
|
||||||
|
@ -839,8 +881,10 @@ fn byte_to_ast<'a>(env: &Env<'a, '_>, value: u8, content: &Content) -> Expr<'a>
|
||||||
// anything with fewer tags is not a byte
|
// anything with fewer tags is not a byte
|
||||||
debug_assert!(tags.len() > 2);
|
debug_assert!(tags.len() > 2);
|
||||||
|
|
||||||
let tags_vec: std::vec::Vec<(TagName, std::vec::Vec<Variable>)> =
|
let tags_vec: std::vec::Vec<(TagName, std::vec::Vec<Variable>)> = tags
|
||||||
tags.iter().map(|(a, b)| (a.clone(), b.clone())).collect();
|
.unsorted_iterator(env.subs, Variable::EMPTY_TAG_UNION)
|
||||||
|
.map(|(a, b)| (a.clone(), b.to_vec()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
let union_variant = union_sorted_tags_help(env.arena, tags_vec, None, env.subs);
|
let union_variant = union_sorted_tags_help(env.arena, tags_vec, None, env.subs);
|
||||||
|
|
||||||
|
@ -923,7 +967,7 @@ fn num_to_ast<'a>(env: &Env<'a, '_>, num_expr: Expr<'a>, content: &Content) -> E
|
||||||
// This was a single-tag union that got unwrapped at runtime.
|
// This was a single-tag union that got unwrapped at runtime.
|
||||||
debug_assert_eq!(tags.len(), 1);
|
debug_assert_eq!(tags.len(), 1);
|
||||||
|
|
||||||
let (tag_name, payload_vars) = tags.iter().next().unwrap();
|
let (tag_name, payload_vars) = unpack_single_element_tag_union(env.subs, *tags);
|
||||||
|
|
||||||
// If this tag union represents a number, skip right to
|
// If this tag union represents a number, skip right to
|
||||||
// returning tis as an Expr::Num
|
// returning tis as an Expr::Num
|
||||||
|
|
|
@ -135,10 +135,6 @@ pub fn gen_and_eval<'a>(
|
||||||
&context, "",
|
&context, "",
|
||||||
));
|
));
|
||||||
|
|
||||||
// Add roc_alloc, roc_realloc, and roc_dealloc, since the repl has no
|
|
||||||
// platform to provide them.
|
|
||||||
add_default_roc_externs(&context, module, &builder, ptr_bytes);
|
|
||||||
|
|
||||||
// mark our zig-defined builtins as internal
|
// mark our zig-defined builtins as internal
|
||||||
for function in FunctionIterator::from_module(module) {
|
for function in FunctionIterator::from_module(module) {
|
||||||
let name = function.get_name().to_str().unwrap();
|
let name = function.get_name().to_str().unwrap();
|
||||||
|
@ -183,11 +179,15 @@ pub fn gen_and_eval<'a>(
|
||||||
interns,
|
interns,
|
||||||
module,
|
module,
|
||||||
ptr_bytes,
|
ptr_bytes,
|
||||||
leak: false,
|
is_gen_test: false,
|
||||||
// important! we don't want any procedures to get the C calling convention
|
// important! we don't want any procedures to get the C calling convention
|
||||||
exposed_to_host: MutSet::default(),
|
exposed_to_host: MutSet::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Add roc_alloc, roc_realloc, and roc_dealloc, since the repl has no
|
||||||
|
// platform to provide them.
|
||||||
|
add_default_roc_externs(&env);
|
||||||
|
|
||||||
let (main_fn_name, main_fn) = roc_gen_llvm::llvm::build::build_procedures_return_main(
|
let (main_fn_name, main_fn) = roc_gen_llvm::llvm::build::build_procedures_return_main(
|
||||||
&env,
|
&env,
|
||||||
opt_level,
|
opt_level,
|
||||||
|
|
|
@ -40,6 +40,13 @@ export fn roc_dealloc(c_ptr: *c_void, alignment: u32) callconv(.C) void {
|
||||||
free(@alignCast(16, @ptrCast([*]u8, c_ptr)));
|
free(@alignCast(16, @ptrCast([*]u8, c_ptr)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export fn roc_panic(c_ptr: *c_void, tag_id: u32) callconv(.C) void {
|
||||||
|
const stderr = std.io.getStdErr().writer();
|
||||||
|
const msg = @ptrCast([*:0]const u8, c_ptr);
|
||||||
|
stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg}) catch unreachable;
|
||||||
|
std.process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
const RocCallResult = extern struct { flag: usize, content: RocStr };
|
const RocCallResult = extern struct { flag: usize, content: RocStr };
|
||||||
|
|
||||||
const Unit = extern struct {};
|
const Unit = extern struct {};
|
||||||
|
|
|
@ -40,6 +40,13 @@ export fn roc_dealloc(c_ptr: *c_void, alignment: u32) callconv(.C) void {
|
||||||
free(@alignCast(16, @ptrCast([*]u8, c_ptr)));
|
free(@alignCast(16, @ptrCast([*]u8, c_ptr)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export fn roc_panic(c_ptr: *c_void, tag_id: u32) callconv(.C) void {
|
||||||
|
const stderr = std.io.getStdErr().writer();
|
||||||
|
const msg = @ptrCast([*:0]const u8, c_ptr);
|
||||||
|
stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg}) catch unreachable;
|
||||||
|
std.process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
const RocCallResult = extern struct { flag: usize, content: RocStr };
|
const RocCallResult = extern struct { flag: usize, content: RocStr };
|
||||||
|
|
||||||
const Unit = extern struct {};
|
const Unit = extern struct {};
|
||||||
|
|
|
@ -421,9 +421,6 @@ fn link_linux(
|
||||||
"-lrt",
|
"-lrt",
|
||||||
"-lutil",
|
"-lutil",
|
||||||
"-lc_nonshared",
|
"-lc_nonshared",
|
||||||
"-lc++",
|
|
||||||
"-lc++abi",
|
|
||||||
"-lunwind",
|
|
||||||
libgcc_path.to_str().unwrap(),
|
libgcc_path.to_str().unwrap(),
|
||||||
// Output
|
// Output
|
||||||
"-o",
|
"-o",
|
||||||
|
@ -488,9 +485,6 @@ fn link_macos(
|
||||||
// "-lrt", // TODO shouldn't we need this?
|
// "-lrt", // TODO shouldn't we need this?
|
||||||
// "-lc_nonshared", // TODO shouldn't we need this?
|
// "-lc_nonshared", // TODO shouldn't we need this?
|
||||||
// "-lgcc", // TODO will eventually need compiler_rt from gcc or something - see https://github.com/rtfeldman/roc/pull/554#discussion_r496370840
|
// "-lgcc", // TODO will eventually need compiler_rt from gcc or something - see https://github.com/rtfeldman/roc/pull/554#discussion_r496370840
|
||||||
"-lc++",
|
|
||||||
// "-lc++abi",
|
|
||||||
// "-lunwind", // TODO will eventually need this, see https://github.com/rtfeldman/roc/pull/554#discussion_r496370840
|
|
||||||
// "-framework", // Uncomment this line & the following ro run the `rand` crate in examples/cli
|
// "-framework", // Uncomment this line & the following ro run the `rand` crate in examples/cli
|
||||||
// "Security",
|
// "Security",
|
||||||
// Output
|
// Output
|
||||||
|
|
|
@ -16,6 +16,10 @@ pub struct CodeGenTiming {
|
||||||
pub emit_o_file: Duration,
|
pub emit_o_file: Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: If modules besides this one start needing to know which version of
|
||||||
|
// llvm we're using, consider moving me somewhere else.
|
||||||
|
const LLVM_VERSION: &str = "12";
|
||||||
|
|
||||||
// TODO how should imported modules factor into this? What if those use builtins too?
|
// TODO how should imported modules factor into this? What if those use builtins too?
|
||||||
// TODO this should probably use more helper functions
|
// TODO this should probably use more helper functions
|
||||||
// TODO make this polymorphic in the llvm functions so it can be reused for another backend.
|
// TODO make this polymorphic in the llvm functions so it can be reused for another backend.
|
||||||
|
@ -140,7 +144,9 @@ pub fn gen_from_mono_module(
|
||||||
interns: loaded.interns,
|
interns: loaded.interns,
|
||||||
module,
|
module,
|
||||||
ptr_bytes,
|
ptr_bytes,
|
||||||
leak: false,
|
// in gen_tests, the compiler provides roc_panic
|
||||||
|
// and sets up the setjump/longjump exception handling
|
||||||
|
is_gen_test: false,
|
||||||
exposed_to_host: loaded.exposed_to_host.keys().copied().collect(),
|
exposed_to_host: loaded.exposed_to_host.keys().copied().collect(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -154,6 +160,9 @@ pub fn gen_from_mono_module(
|
||||||
|
|
||||||
env.dibuilder.finalize();
|
env.dibuilder.finalize();
|
||||||
|
|
||||||
|
// we don't use the debug info, and it causes weird errors.
|
||||||
|
module.strip_debug_info();
|
||||||
|
|
||||||
// Uncomment this to see the module's optimized LLVM instruction output:
|
// Uncomment this to see the module's optimized LLVM instruction output:
|
||||||
// env.module.print_to_stderr();
|
// env.module.print_to_stderr();
|
||||||
|
|
||||||
|
@ -195,7 +204,6 @@ pub fn gen_from_mono_module(
|
||||||
|
|
||||||
// run the debugir https://github.com/vaivaswatha/debugir tool
|
// run the debugir https://github.com/vaivaswatha/debugir tool
|
||||||
match Command::new("debugir")
|
match Command::new("debugir")
|
||||||
.env_clear()
|
|
||||||
.args(&["-instnamer", app_ll_file.to_str().unwrap()])
|
.args(&["-instnamer", app_ll_file.to_str().unwrap()])
|
||||||
.output()
|
.output()
|
||||||
{
|
{
|
||||||
|
@ -213,7 +221,6 @@ pub fn gen_from_mono_module(
|
||||||
|
|
||||||
// assemble the .ll into a .bc
|
// assemble the .ll into a .bc
|
||||||
let _ = Command::new("llvm-as")
|
let _ = Command::new("llvm-as")
|
||||||
.env_clear()
|
|
||||||
.args(&[
|
.args(&[
|
||||||
app_ll_dbg_file.to_str().unwrap(),
|
app_ll_dbg_file.to_str().unwrap(),
|
||||||
"-o",
|
"-o",
|
||||||
|
@ -222,18 +229,26 @@ pub fn gen_from_mono_module(
|
||||||
.output()
|
.output()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// write the .o file. Note that this builds the .o for the local machine,
|
let llc_args = &[
|
||||||
// and ignores the `target_machine` entirely.
|
|
||||||
let _ = Command::new("llc-12")
|
|
||||||
.env_clear()
|
|
||||||
.args(&[
|
|
||||||
"-filetype=obj",
|
"-filetype=obj",
|
||||||
app_bc_file.to_str().unwrap(),
|
app_bc_file.to_str().unwrap(),
|
||||||
"-o",
|
"-o",
|
||||||
app_o_file.to_str().unwrap(),
|
app_o_file.to_str().unwrap(),
|
||||||
])
|
];
|
||||||
|
|
||||||
|
// write the .o file. Note that this builds the .o for the local machine,
|
||||||
|
// and ignores the `target_machine` entirely.
|
||||||
|
//
|
||||||
|
// different systems name this executable differently, so we shotgun for
|
||||||
|
// the most common ones and then give up.
|
||||||
|
let _: Result<std::process::Output, std::io::Error> =
|
||||||
|
Command::new(format!("llc-{}", LLVM_VERSION))
|
||||||
|
.args(llc_args)
|
||||||
.output()
|
.output()
|
||||||
.unwrap();
|
.or_else(|_| Command::new("llc").args(llc_args).output())
|
||||||
|
.map_err(|_| {
|
||||||
|
panic!("We couldn't find llc-{} on your machine!", LLVM_VERSION);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
// Emit the .o file
|
// Emit the .o file
|
||||||
|
|
||||||
|
|
|
@ -733,6 +733,30 @@ pub fn listAppend(list: RocList, alignment: u32, element: Opaque, element_width:
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn listPrepend(list: RocList, alignment: u32, element: Opaque, element_width: usize) callconv(.C) RocList {
|
||||||
|
const old_length = list.len();
|
||||||
|
var output = list.reallocate(alignment, old_length + 1, element_width);
|
||||||
|
|
||||||
|
// can't use one memcpy here because source and target overlap
|
||||||
|
if (output.bytes) |target| {
|
||||||
|
var i: usize = old_length;
|
||||||
|
|
||||||
|
while (i > 0) {
|
||||||
|
i -= 1;
|
||||||
|
|
||||||
|
// move the ith element to the (i + 1)th position
|
||||||
|
@memcpy(target + (i + 1) * element_width, target + i * element_width, element_width);
|
||||||
|
}
|
||||||
|
|
||||||
|
// finally copy in the new first element
|
||||||
|
if (element) |source| {
|
||||||
|
@memcpy(target, source, element_width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn listSwap(
|
pub fn listSwap(
|
||||||
list: RocList,
|
list: RocList,
|
||||||
alignment: u32,
|
alignment: u32,
|
||||||
|
|
|
@ -33,6 +33,7 @@ comptime {
|
||||||
exportListFn(list.listContains, "contains");
|
exportListFn(list.listContains, "contains");
|
||||||
exportListFn(list.listRepeat, "repeat");
|
exportListFn(list.listRepeat, "repeat");
|
||||||
exportListFn(list.listAppend, "append");
|
exportListFn(list.listAppend, "append");
|
||||||
|
exportListFn(list.listPrepend, "prepend");
|
||||||
exportListFn(list.listSingle, "single");
|
exportListFn(list.listSingle, "single");
|
||||||
exportListFn(list.listJoin, "join");
|
exportListFn(list.listJoin, "join");
|
||||||
exportListFn(list.listRange, "range");
|
exportListFn(list.listRange, "range");
|
||||||
|
@ -78,6 +79,9 @@ comptime {
|
||||||
exportNumFn(num.powInt, "pow_int");
|
exportNumFn(num.powInt, "pow_int");
|
||||||
exportNumFn(num.acos, "acos");
|
exportNumFn(num.acos, "acos");
|
||||||
exportNumFn(num.asin, "asin");
|
exportNumFn(num.asin, "asin");
|
||||||
|
exportNumFn(num.bytesToU16C, "bytes_to_u16");
|
||||||
|
exportNumFn(num.bytesToU32C, "bytes_to_u32");
|
||||||
|
exportNumFn(num.round, "round");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Str Module
|
// Str Module
|
||||||
|
@ -98,6 +102,15 @@ comptime {
|
||||||
exportStrFn(str.strEqual, "equal");
|
exportStrFn(str.strEqual, "equal");
|
||||||
exportStrFn(str.strToUtf8C, "to_utf8");
|
exportStrFn(str.strToUtf8C, "to_utf8");
|
||||||
exportStrFn(str.fromUtf8C, "from_utf8");
|
exportStrFn(str.fromUtf8C, "from_utf8");
|
||||||
|
exportStrFn(str.fromUtf8RangeC, "from_utf8_range");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utils
|
||||||
|
const utils = @import("utils.zig");
|
||||||
|
comptime {
|
||||||
|
exportUtilsFn(utils.test_panic, "test_panic");
|
||||||
|
|
||||||
|
@export(utils.panic, .{ .name = "roc_builtins.utils." ++ "panic", .linkage = .Weak });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Export helpers - Must be run inside a comptime
|
// Export helpers - Must be run inside a comptime
|
||||||
|
@ -120,6 +133,10 @@ fn exportDecFn(comptime func: anytype, comptime func_name: []const u8) void {
|
||||||
exportBuiltinFn(func, "dec." ++ func_name);
|
exportBuiltinFn(func, "dec." ++ func_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn exportUtilsFn(comptime func: anytype, comptime func_name: []const u8) void {
|
||||||
|
exportBuiltinFn(func, "utils." ++ func_name);
|
||||||
|
}
|
||||||
|
|
||||||
// Custom panic function, as builtin Zig version errors during LLVM verification
|
// Custom panic function, as builtin Zig version errors during LLVM verification
|
||||||
pub fn panic(message: []const u8, stacktrace: ?*std.builtin.StackTrace) noreturn {
|
pub fn panic(message: []const u8, stacktrace: ?*std.builtin.StackTrace) noreturn {
|
||||||
std.debug.print("{s}: {?}", .{ message, stacktrace });
|
std.debug.print("{s}: {?}", .{ message, stacktrace });
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const always_inline = std.builtin.CallOptions.Modifier.always_inline;
|
const always_inline = std.builtin.CallOptions.Modifier.always_inline;
|
||||||
const math = std.math;
|
const math = std.math;
|
||||||
|
const RocList = @import("list.zig").RocList;
|
||||||
|
|
||||||
pub fn atan(num: f64) callconv(.C) f64 {
|
pub fn atan(num: f64) callconv(.C) f64 {
|
||||||
return @call(.{ .modifier = always_inline }, math.atan, .{num});
|
return @call(.{ .modifier = always_inline }, math.atan, .{num});
|
||||||
|
@ -21,3 +22,25 @@ pub fn acos(num: f64) callconv(.C) f64 {
|
||||||
pub fn asin(num: f64) callconv(.C) f64 {
|
pub fn asin(num: f64) callconv(.C) f64 {
|
||||||
return @call(.{ .modifier = always_inline }, math.asin, .{num});
|
return @call(.{ .modifier = always_inline }, math.asin, .{num});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn bytesToU16C(arg: RocList, position: usize) callconv(.C) u16 {
|
||||||
|
return @call(.{ .modifier = always_inline }, bytesToU16, .{ arg, position });
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bytesToU16(arg: RocList, position: usize) u16 {
|
||||||
|
const bytes = @ptrCast([*]const u8, arg.bytes);
|
||||||
|
return @bitCast(u16, [_]u8{ bytes[position], bytes[position + 1] });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bytesToU32C(arg: RocList, position: usize) callconv(.C) u32 {
|
||||||
|
return @call(.{ .modifier = always_inline }, bytesToU32, .{ arg, position });
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bytesToU32(arg: RocList, position: usize) u32 {
|
||||||
|
const bytes = @ptrCast([*]const u8, arg.bytes);
|
||||||
|
return @bitCast(u32, [_]u8{ bytes[position], bytes[position + 1], bytes[position + 2], bytes[position + 3] });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn round(num: f64) callconv(.C) i64 {
|
||||||
|
return @floatToInt(i32, (@round(num)));
|
||||||
|
}
|
||||||
|
|
|
@ -1156,6 +1156,11 @@ const FromUtf8Result = extern struct {
|
||||||
problem_code: Utf8ByteProblem,
|
problem_code: Utf8ByteProblem,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const CountAndStart = extern struct {
|
||||||
|
count: usize,
|
||||||
|
start: usize,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn fromUtf8C(arg: RocList, output: *FromUtf8Result) callconv(.C) void {
|
pub fn fromUtf8C(arg: RocList, output: *FromUtf8Result) callconv(.C) void {
|
||||||
output.* = @call(.{ .modifier = always_inline }, fromUtf8, .{arg});
|
output.* = @call(.{ .modifier = always_inline }, fromUtf8, .{arg});
|
||||||
}
|
}
|
||||||
|
@ -1192,6 +1197,24 @@ fn fromUtf8(arg: RocList) FromUtf8Result {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn fromUtf8RangeC(arg: RocList, countAndStart: CountAndStart, output: *FromUtf8Result) callconv(.C) void {
|
||||||
|
output.* = @call(.{ .modifier = always_inline }, fromUtf8Range, .{ arg, countAndStart });
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fromUtf8Range(arg: RocList, countAndStart: CountAndStart) FromUtf8Result {
|
||||||
|
const bytes = @ptrCast([*]const u8, arg.bytes)[countAndStart.start..countAndStart.count];
|
||||||
|
|
||||||
|
if (unicode.utf8ValidateSlice(bytes)) {
|
||||||
|
// the output will be correct. Now we need to clone the input
|
||||||
|
const string = RocStr.init(@ptrCast([*]const u8, bytes), countAndStart.count);
|
||||||
|
|
||||||
|
return FromUtf8Result{ .is_ok = true, .string = string, .byte_index = 0, .problem_code = Utf8ByteProblem.InvalidStartByte };
|
||||||
|
} else {
|
||||||
|
const temp = errorToProblem(@ptrCast([*]u8, arg.bytes), arg.length);
|
||||||
|
return FromUtf8Result{ .is_ok = false, .string = RocStr.empty(), .byte_index = temp.index, .problem_code = temp.problem };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn errorToProblem(bytes: [*]u8, length: usize) struct { index: usize, problem: Utf8ByteProblem } {
|
fn errorToProblem(bytes: [*]u8, length: usize) struct { index: usize, problem: Utf8ByteProblem } {
|
||||||
var index: usize = 0;
|
var index: usize = 0;
|
||||||
|
|
||||||
|
|
|
@ -15,12 +15,16 @@ extern fn roc_realloc(c_ptr: *c_void, new_size: usize, old_size: usize, alignmen
|
||||||
// This should never be passed a null pointer.
|
// This should never be passed a null pointer.
|
||||||
extern fn roc_dealloc(c_ptr: *c_void, alignment: u32) callconv(.C) void;
|
extern fn roc_dealloc(c_ptr: *c_void, alignment: u32) callconv(.C) void;
|
||||||
|
|
||||||
|
// Signals to the host that the program has paniced
|
||||||
|
extern fn roc_panic(c_ptr: *c_void, tag_id: u32) callconv(.C) void;
|
||||||
|
|
||||||
comptime {
|
comptime {
|
||||||
// During tetsts, use the testing allocators to satisfy these functions.
|
// During tetsts, use the testing allocators to satisfy these functions.
|
||||||
if (std.builtin.is_test) {
|
if (std.builtin.is_test) {
|
||||||
@export(testing_roc_alloc, .{ .name = "roc_alloc", .linkage = .Strong });
|
@export(testing_roc_alloc, .{ .name = "roc_alloc", .linkage = .Strong });
|
||||||
@export(testing_roc_realloc, .{ .name = "roc_realloc", .linkage = .Strong });
|
@export(testing_roc_realloc, .{ .name = "roc_realloc", .linkage = .Strong });
|
||||||
@export(testing_roc_dealloc, .{ .name = "roc_dealloc", .linkage = .Strong });
|
@export(testing_roc_dealloc, .{ .name = "roc_dealloc", .linkage = .Strong });
|
||||||
|
@export(testing_roc_panic, .{ .name = "roc_panic", .linkage = .Strong });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,6 +45,10 @@ fn testing_roc_dealloc(c_ptr: *c_void, _: u32) callconv(.C) void {
|
||||||
std.testing.allocator.destroy(ptr);
|
std.testing.allocator.destroy(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn testing_roc_panic(c_ptr: *c_void, _: u32) callconv(.C) void {
|
||||||
|
@panic("Roc paniced");
|
||||||
|
}
|
||||||
|
|
||||||
pub fn alloc(size: usize, alignment: u32) [*]u8 {
|
pub fn alloc(size: usize, alignment: u32) [*]u8 {
|
||||||
return @ptrCast([*]u8, @call(.{ .modifier = always_inline }, roc_alloc, .{ size, alignment }));
|
return @ptrCast([*]u8, @call(.{ .modifier = always_inline }, roc_alloc, .{ size, alignment }));
|
||||||
}
|
}
|
||||||
|
@ -53,6 +61,22 @@ pub fn dealloc(c_ptr: [*]u8, alignment: u32) void {
|
||||||
return @call(.{ .modifier = always_inline }, roc_dealloc, .{ c_ptr, alignment });
|
return @call(.{ .modifier = always_inline }, roc_dealloc, .{ c_ptr, alignment });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// must export this explicitly because right now it is not used from zig code
|
||||||
|
pub fn panic(c_ptr: *c_void, alignment: u32) callconv(.C) void {
|
||||||
|
return @call(.{ .modifier = always_inline }, roc_panic, .{ c_ptr, alignment });
|
||||||
|
}
|
||||||
|
|
||||||
|
// indirection because otherwise zig creats an alias to the panic function which our LLVM code
|
||||||
|
// does not know how to deal with
|
||||||
|
pub fn test_panic(c_ptr: *c_void, alignment: u32) callconv(.C) void {
|
||||||
|
const cstr = @ptrCast([*:0]u8, c_ptr);
|
||||||
|
|
||||||
|
// const stderr = std.io.getStdErr().writer();
|
||||||
|
// stderr.print("Roc panicked: {s}!\n", .{cstr}) catch unreachable;
|
||||||
|
|
||||||
|
std.c.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
pub const Inc = fn (?[*]u8) callconv(.C) void;
|
pub const Inc = fn (?[*]u8) callconv(.C) void;
|
||||||
pub const IncN = fn (?[*]u8, u64) callconv(.C) void;
|
pub const IncN = fn (?[*]u8, u64) callconv(.C) void;
|
||||||
pub const Dec = fn (?[*]u8) callconv(.C) void;
|
pub const Dec = fn (?[*]u8) callconv(.C) void;
|
||||||
|
|
|
@ -8,6 +8,9 @@ pub const NUM_ACOS: &str = "roc_builtins.num.acos";
|
||||||
pub const NUM_ATAN: &str = "roc_builtins.num.atan";
|
pub const NUM_ATAN: &str = "roc_builtins.num.atan";
|
||||||
pub const NUM_IS_FINITE: &str = "roc_builtins.num.is_finite";
|
pub const NUM_IS_FINITE: &str = "roc_builtins.num.is_finite";
|
||||||
pub const NUM_POW_INT: &str = "roc_builtins.num.pow_int";
|
pub const NUM_POW_INT: &str = "roc_builtins.num.pow_int";
|
||||||
|
pub const NUM_BYTES_TO_U16: &str = "roc_builtins.num.bytes_to_u16";
|
||||||
|
pub const NUM_BYTES_TO_U32: &str = "roc_builtins.num.bytes_to_u32";
|
||||||
|
pub const NUM_ROUND: &str = "roc_builtins.num.round";
|
||||||
|
|
||||||
pub const STR_INIT: &str = "roc_builtins.str.init";
|
pub const STR_INIT: &str = "roc_builtins.str.init";
|
||||||
pub const STR_COUNT_SEGMENTS: &str = "roc_builtins.str.count_segments";
|
pub const STR_COUNT_SEGMENTS: &str = "roc_builtins.str.count_segments";
|
||||||
|
@ -24,6 +27,7 @@ pub const STR_FROM_FLOAT: &str = "roc_builtins.str.from_float";
|
||||||
pub const STR_EQUAL: &str = "roc_builtins.str.equal";
|
pub const STR_EQUAL: &str = "roc_builtins.str.equal";
|
||||||
pub const STR_TO_UTF8: &str = "roc_builtins.str.to_utf8";
|
pub const STR_TO_UTF8: &str = "roc_builtins.str.to_utf8";
|
||||||
pub const STR_FROM_UTF8: &str = "roc_builtins.str.from_utf8";
|
pub const STR_FROM_UTF8: &str = "roc_builtins.str.from_utf8";
|
||||||
|
pub const STR_FROM_UTF8_RANGE: &str = "roc_builtins.str.from_utf8_range";
|
||||||
|
|
||||||
pub const DICT_HASH: &str = "roc_builtins.dict.hash";
|
pub const DICT_HASH: &str = "roc_builtins.dict.hash";
|
||||||
pub const DICT_HASH_STR: &str = "roc_builtins.dict.hash_str";
|
pub const DICT_HASH_STR: &str = "roc_builtins.dict.hash_str";
|
||||||
|
@ -56,6 +60,7 @@ pub const LIST_WALK_BACKWARDS: &str = "roc_builtins.list.walk_backwards";
|
||||||
pub const LIST_CONTAINS: &str = "roc_builtins.list.contains";
|
pub const LIST_CONTAINS: &str = "roc_builtins.list.contains";
|
||||||
pub const LIST_REPEAT: &str = "roc_builtins.list.repeat";
|
pub const LIST_REPEAT: &str = "roc_builtins.list.repeat";
|
||||||
pub const LIST_APPEND: &str = "roc_builtins.list.append";
|
pub const LIST_APPEND: &str = "roc_builtins.list.append";
|
||||||
|
pub const LIST_PREPEND: &str = "roc_builtins.list.prepend";
|
||||||
pub const LIST_DROP: &str = "roc_builtins.list.drop";
|
pub const LIST_DROP: &str = "roc_builtins.list.drop";
|
||||||
pub const LIST_SWAP: &str = "roc_builtins.list.swap";
|
pub const LIST_SWAP: &str = "roc_builtins.list.swap";
|
||||||
pub const LIST_SINGLE: &str = "roc_builtins.list.single";
|
pub const LIST_SINGLE: &str = "roc_builtins.list.single";
|
||||||
|
@ -75,3 +80,5 @@ pub const DEC_ADD_WITH_OVERFLOW: &str = "roc_builtins.dec.add_with_overflow";
|
||||||
pub const DEC_SUB_WITH_OVERFLOW: &str = "roc_builtins.dec.sub_with_overflow";
|
pub const DEC_SUB_WITH_OVERFLOW: &str = "roc_builtins.dec.sub_with_overflow";
|
||||||
pub const DEC_MUL_WITH_OVERFLOW: &str = "roc_builtins.dec.mul_with_overflow";
|
pub const DEC_MUL_WITH_OVERFLOW: &str = "roc_builtins.dec.mul_with_overflow";
|
||||||
pub const DEC_DIV: &str = "roc_builtins.dec.div";
|
pub const DEC_DIV: &str = "roc_builtins.dec.div";
|
||||||
|
|
||||||
|
pub const UTILS_TEST_PANIC: &str = "roc_builtins.utils.test_panic";
|
||||||
|
|
|
@ -4,11 +4,12 @@ use roc_module::symbol::Symbol;
|
||||||
use roc_region::all::Region;
|
use roc_region::all::Region;
|
||||||
use roc_types::builtin_aliases::{
|
use roc_types::builtin_aliases::{
|
||||||
bool_type, dict_type, float_type, i128_type, int_type, list_type, nat_type, num_type,
|
bool_type, dict_type, float_type, i128_type, int_type, list_type, nat_type, num_type,
|
||||||
ordering_type, result_type, set_type, str_type, str_utf8_byte_problem_type, u32_type, u64_type,
|
ordering_type, result_type, set_type, str_type, str_utf8_byte_problem_type, u16_type, u32_type,
|
||||||
u8_type,
|
u64_type, u8_type,
|
||||||
};
|
};
|
||||||
use roc_types::solved_types::SolvedType;
|
use roc_types::solved_types::SolvedType;
|
||||||
use roc_types::subs::VarId;
|
use roc_types::subs::VarId;
|
||||||
|
use roc_types::types::RecordField;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
/// Example:
|
/// Example:
|
||||||
|
@ -500,6 +501,32 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
Box::new(float_type(flex(TVAR1))),
|
Box::new(float_type(flex(TVAR1))),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// bytesToU16 : List U8, Nat -> Result U16 [ OutOfBounds ]
|
||||||
|
{
|
||||||
|
let position_out_of_bounds = SolvedType::TagUnion(
|
||||||
|
vec![(TagName::Global("OutOfBounds".into()), vec![])],
|
||||||
|
Box::new(SolvedType::Wildcard),
|
||||||
|
);
|
||||||
|
add_top_level_function_type!(
|
||||||
|
Symbol::NUM_BYTES_TO_U16,
|
||||||
|
vec![list_type(u8_type()), nat_type()],
|
||||||
|
Box::new(result_type(u16_type(), position_out_of_bounds)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// bytesToU32 : List U8, Nat -> Result U32 [ OutOfBounds ]
|
||||||
|
{
|
||||||
|
let position_out_of_bounds = SolvedType::TagUnion(
|
||||||
|
vec![(TagName::Global("OutOfBounds".into()), vec![])],
|
||||||
|
Box::new(SolvedType::Wildcard),
|
||||||
|
);
|
||||||
|
add_top_level_function_type!(
|
||||||
|
Symbol::NUM_BYTES_TO_U32,
|
||||||
|
vec![list_type(u8_type()), nat_type()],
|
||||||
|
Box::new(result_type(u32_type(), position_out_of_bounds)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Bool module
|
// Bool module
|
||||||
|
|
||||||
// and : Bool, Bool -> Bool
|
// and : Bool, Bool -> Bool
|
||||||
|
@ -592,10 +619,10 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
);
|
);
|
||||||
|
|
||||||
// fromUtf8 : List U8 -> Result Str [ BadUtf8 Utf8Problem ]*
|
// fromUtf8 : List U8 -> Result Str [ BadUtf8 Utf8Problem ]*
|
||||||
|
{
|
||||||
let bad_utf8 = SolvedType::TagUnion(
|
let bad_utf8 = SolvedType::TagUnion(
|
||||||
vec![(
|
vec![(
|
||||||
TagName::Global("BadUtf8".into()),
|
TagName::Global("BadUtf8".into()),
|
||||||
// vec![str_utf8_problem_type()],
|
|
||||||
vec![str_utf8_byte_problem_type(), nat_type()],
|
vec![str_utf8_byte_problem_type(), nat_type()],
|
||||||
)],
|
)],
|
||||||
Box::new(SolvedType::Wildcard),
|
Box::new(SolvedType::Wildcard),
|
||||||
|
@ -606,6 +633,36 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
vec![list_type(u8_type())],
|
vec![list_type(u8_type())],
|
||||||
Box::new(result_type(str_type(), bad_utf8)),
|
Box::new(result_type(str_type(), bad_utf8)),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// fromUtf8Range : List U8 -> Result Str [ BadUtf8 Utf8Problem, OutOfBounds ]*
|
||||||
|
{
|
||||||
|
let bad_utf8 = SolvedType::TagUnion(
|
||||||
|
vec![
|
||||||
|
(
|
||||||
|
TagName::Global("BadUtf8".into()),
|
||||||
|
vec![str_utf8_byte_problem_type(), nat_type()],
|
||||||
|
),
|
||||||
|
(TagName::Global("OutOfBounds".into()), vec![]),
|
||||||
|
],
|
||||||
|
Box::new(SolvedType::Wildcard),
|
||||||
|
);
|
||||||
|
|
||||||
|
add_top_level_function_type!(
|
||||||
|
Symbol::STR_FROM_UTF8_RANGE,
|
||||||
|
vec![
|
||||||
|
list_type(u8_type()),
|
||||||
|
SolvedType::Record {
|
||||||
|
fields: vec![
|
||||||
|
("start".into(), RecordField::Required(nat_type())),
|
||||||
|
("count".into(), RecordField::Required(nat_type())),
|
||||||
|
],
|
||||||
|
ext: Box::new(SolvedType::EmptyRecord),
|
||||||
|
}
|
||||||
|
],
|
||||||
|
Box::new(result_type(str_type(), bad_utf8)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// toUtf8 : Str -> List U8
|
// toUtf8 : Str -> List U8
|
||||||
add_top_level_function_type!(
|
add_top_level_function_type!(
|
||||||
|
|
|
@ -6,7 +6,7 @@ use roc_module::symbol::Symbol;
|
||||||
use roc_parse::ast::{AssignedField, Tag, TypeAnnotation};
|
use roc_parse::ast::{AssignedField, Tag, TypeAnnotation};
|
||||||
use roc_region::all::{Located, Region};
|
use roc_region::all::{Located, Region};
|
||||||
use roc_types::subs::{VarStore, Variable};
|
use roc_types::subs::{VarStore, Variable};
|
||||||
use roc_types::types::{Alias, Problem, RecordField, Type};
|
use roc_types::types::{Alias, LambdaSet, Problem, RecordField, Type};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct Annotation {
|
pub struct Annotation {
|
||||||
|
@ -227,14 +227,27 @@ fn can_annotation_help(
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure hidden variables are freshly instantiated
|
// make sure hidden variables are freshly instantiated
|
||||||
for var in alias.lambda_set_variables.iter() {
|
let mut lambda_set_variables =
|
||||||
substitutions.insert(var.into_inner(), Type::Variable(var_store.fresh()));
|
Vec::with_capacity(alias.lambda_set_variables.len());
|
||||||
|
for typ in alias.lambda_set_variables.iter() {
|
||||||
|
if let Type::Variable(var) = typ.0 {
|
||||||
|
let fresh = var_store.fresh();
|
||||||
|
substitutions.insert(var, Type::Variable(fresh));
|
||||||
|
lambda_set_variables.push(LambdaSet(Type::Variable(fresh)));
|
||||||
|
} else {
|
||||||
|
unreachable!("at this point there should be only vars in there");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// instantiate variables
|
// instantiate variables
|
||||||
actual.substitute(&substitutions);
|
actual.substitute(&substitutions);
|
||||||
|
|
||||||
Type::Alias(symbol, vars, Box::new(actual))
|
Type::Alias {
|
||||||
|
symbol,
|
||||||
|
type_arguments: vars,
|
||||||
|
lambda_set_variables,
|
||||||
|
actual: Box::new(actual),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let mut args = Vec::new();
|
let mut args = Vec::new();
|
||||||
|
@ -373,12 +386,18 @@ fn can_annotation_help(
|
||||||
introduced_variables.insert_host_exposed_alias(symbol, actual_var);
|
introduced_variables.insert_host_exposed_alias(symbol, actual_var);
|
||||||
Type::HostExposedAlias {
|
Type::HostExposedAlias {
|
||||||
name: symbol,
|
name: symbol,
|
||||||
arguments: vars,
|
type_arguments: vars,
|
||||||
|
lambda_set_variables: alias.lambda_set_variables.clone(),
|
||||||
actual: Box::new(alias.typ.clone()),
|
actual: Box::new(alias.typ.clone()),
|
||||||
actual_var,
|
actual_var,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Type::Alias(symbol, vars, Box::new(alias.typ.clone()))
|
Type::Alias {
|
||||||
|
symbol,
|
||||||
|
type_arguments: vars,
|
||||||
|
lambda_set_variables: alias.lambda_set_variables.clone(),
|
||||||
|
actual: Box::new(alias.typ.clone()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
|
|
@ -63,6 +63,7 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
|
||||||
STR_COUNT_GRAPHEMES => str_count_graphemes,
|
STR_COUNT_GRAPHEMES => str_count_graphemes,
|
||||||
STR_FROM_INT => str_from_int,
|
STR_FROM_INT => str_from_int,
|
||||||
STR_FROM_UTF8 => str_from_utf8,
|
STR_FROM_UTF8 => str_from_utf8,
|
||||||
|
STR_FROM_UTF8_RANGE => str_from_utf8_range,
|
||||||
STR_TO_UTF8 => str_to_utf8,
|
STR_TO_UTF8 => str_to_utf8,
|
||||||
STR_FROM_FLOAT=> str_from_float,
|
STR_FROM_FLOAT=> str_from_float,
|
||||||
LIST_LEN => list_len,
|
LIST_LEN => list_len,
|
||||||
|
@ -160,6 +161,8 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
|
||||||
NUM_ATAN => num_atan,
|
NUM_ATAN => num_atan,
|
||||||
NUM_ACOS => num_acos,
|
NUM_ACOS => num_acos,
|
||||||
NUM_ASIN => num_asin,
|
NUM_ASIN => num_asin,
|
||||||
|
NUM_BYTES_TO_U16 => num_bytes_to_u16,
|
||||||
|
NUM_BYTES_TO_U32 => num_bytes_to_u32,
|
||||||
NUM_MAX_INT => num_max_int,
|
NUM_MAX_INT => num_max_int,
|
||||||
NUM_MIN_INT => num_min_int,
|
NUM_MIN_INT => num_min_int,
|
||||||
NUM_BITWISE_AND => num_bitwise_and,
|
NUM_BITWISE_AND => num_bitwise_and,
|
||||||
|
@ -1087,6 +1090,16 @@ fn num_asin(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Num.bytesToU16 : List U8, Nat -> Result U16 [ OutOfBounds ]
|
||||||
|
fn num_bytes_to_u16(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
|
num_bytes_to(symbol, var_store, 1, LowLevel::NumBytesToU16)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Num.bytesToU32 : List U8, Nat -> Result U32 [ OutOfBounds ]
|
||||||
|
fn num_bytes_to_u32(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
|
num_bytes_to(symbol, var_store, 3, LowLevel::NumBytesToU32)
|
||||||
|
}
|
||||||
|
|
||||||
/// Num.bitwiseAnd : Int a, Int a -> Int a
|
/// Num.bitwiseAnd : Int a, Int a -> Int a
|
||||||
fn num_bitwise_and(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
fn num_bitwise_and(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
num_binop(symbol, var_store, LowLevel::NumBitwiseAnd)
|
num_binop(symbol, var_store, LowLevel::NumBitwiseAnd)
|
||||||
|
@ -1352,7 +1365,7 @@ fn str_from_int(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Str.fromUtf8 : List U8 -> Result Str [ BadUtf8 Utf8Problem ]*
|
/// Str.fromUtf8 : List U8 -> Result Str [ BadUtf8 { byteIndex : Nat, problem : Utf8Problem } } ]*
|
||||||
fn str_from_utf8(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
fn str_from_utf8(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
let bytes_var = var_store.fresh();
|
let bytes_var = var_store.fresh();
|
||||||
let bool_var = var_store.fresh();
|
let bool_var = var_store.fresh();
|
||||||
|
@ -1455,6 +1468,179 @@ fn str_from_utf8(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
ret_var,
|
ret_var,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
/// Str.fromUtf8Range : List U8, { start : Nat, count : Nat } -> Result Str [ BadUtf8 { byteIndex : Nat, problem : Utf8Problem } } ]*
|
||||||
|
fn str_from_utf8_range(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
|
let bytes_var = var_store.fresh();
|
||||||
|
let bool_var = var_store.fresh();
|
||||||
|
let arg_record_var = var_store.fresh();
|
||||||
|
let ll_record_var = var_store.fresh();
|
||||||
|
let ret_var = var_store.fresh();
|
||||||
|
|
||||||
|
// let arg_3 = RunLowLevel FromUtf8Range arg_1 arg_2
|
||||||
|
//
|
||||||
|
// arg_3 :
|
||||||
|
// { a : Bool -- isOk
|
||||||
|
// , b : String -- result_str
|
||||||
|
// , c : Nat -- problem_byte_index
|
||||||
|
// , d : I8 -- problem_code
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if arg_3.a then
|
||||||
|
// Ok arg_3.str
|
||||||
|
// else
|
||||||
|
// Err (BadUtf8 { byteIndex: arg_3.byteIndex, problem : arg_3.problem })
|
||||||
|
|
||||||
|
let def = crate::def::Def {
|
||||||
|
loc_pattern: no_region(Pattern::Identifier(Symbol::ARG_3)),
|
||||||
|
loc_expr: no_region(RunLowLevel {
|
||||||
|
op: LowLevel::StrFromUtf8Range,
|
||||||
|
args: vec![
|
||||||
|
(bytes_var, Var(Symbol::ARG_1)),
|
||||||
|
(arg_record_var, Var(Symbol::ARG_2)),
|
||||||
|
],
|
||||||
|
ret_var: ll_record_var,
|
||||||
|
}),
|
||||||
|
expr_var: ll_record_var,
|
||||||
|
pattern_vars: SendMap::default(),
|
||||||
|
annotation: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let cont = If {
|
||||||
|
branch_var: ret_var,
|
||||||
|
cond_var: bool_var,
|
||||||
|
branches: vec![(
|
||||||
|
// if-condition
|
||||||
|
no_region(
|
||||||
|
// arg_2.c -> Bool
|
||||||
|
Access {
|
||||||
|
record_var: ll_record_var,
|
||||||
|
ext_var: var_store.fresh(),
|
||||||
|
field: "c_isOk".into(),
|
||||||
|
field_var: var_store.fresh(),
|
||||||
|
loc_expr: Box::new(no_region(Var(Symbol::ARG_3))),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// all is good
|
||||||
|
no_region(tag(
|
||||||
|
"Ok",
|
||||||
|
// arg_2.a -> Str
|
||||||
|
vec![Access {
|
||||||
|
record_var: ll_record_var,
|
||||||
|
ext_var: var_store.fresh(),
|
||||||
|
field: "b_str".into(),
|
||||||
|
field_var: var_store.fresh(),
|
||||||
|
loc_expr: Box::new(no_region(Var(Symbol::ARG_3))),
|
||||||
|
}],
|
||||||
|
var_store,
|
||||||
|
)),
|
||||||
|
)],
|
||||||
|
final_else: Box::new(
|
||||||
|
// bad!!
|
||||||
|
no_region(tag(
|
||||||
|
"Err",
|
||||||
|
vec![tag(
|
||||||
|
"BadUtf8",
|
||||||
|
vec![
|
||||||
|
Access {
|
||||||
|
record_var: ll_record_var,
|
||||||
|
ext_var: var_store.fresh(),
|
||||||
|
field: "d_problem".into(),
|
||||||
|
field_var: var_store.fresh(),
|
||||||
|
loc_expr: Box::new(no_region(Var(Symbol::ARG_3))),
|
||||||
|
},
|
||||||
|
Access {
|
||||||
|
record_var: ll_record_var,
|
||||||
|
ext_var: var_store.fresh(),
|
||||||
|
field: "a_byteIndex".into(),
|
||||||
|
field_var: var_store.fresh(),
|
||||||
|
loc_expr: Box::new(no_region(Var(Symbol::ARG_3))),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
var_store,
|
||||||
|
)],
|
||||||
|
var_store,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
let roc_result = LetNonRec(Box::new(def), Box::new(no_region(cont)), ret_var);
|
||||||
|
|
||||||
|
// Only do the business with the let if we're in bounds!
|
||||||
|
|
||||||
|
let bounds_var = var_store.fresh();
|
||||||
|
let bounds_bool = var_store.fresh();
|
||||||
|
let add_var = var_store.fresh();
|
||||||
|
|
||||||
|
let body = If {
|
||||||
|
cond_var: bounds_bool,
|
||||||
|
branch_var: ret_var,
|
||||||
|
branches: vec![(
|
||||||
|
no_region(RunLowLevel {
|
||||||
|
op: LowLevel::NumLte,
|
||||||
|
args: vec![
|
||||||
|
(
|
||||||
|
bounds_var,
|
||||||
|
RunLowLevel {
|
||||||
|
op: LowLevel::NumAdd,
|
||||||
|
args: vec![
|
||||||
|
(
|
||||||
|
add_var,
|
||||||
|
Access {
|
||||||
|
record_var: arg_record_var,
|
||||||
|
ext_var: var_store.fresh(),
|
||||||
|
field: "start".into(),
|
||||||
|
field_var: var_store.fresh(),
|
||||||
|
loc_expr: Box::new(no_region(Var(Symbol::ARG_2))),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
add_var,
|
||||||
|
Access {
|
||||||
|
record_var: arg_record_var,
|
||||||
|
ext_var: var_store.fresh(),
|
||||||
|
field: "count".into(),
|
||||||
|
field_var: var_store.fresh(),
|
||||||
|
loc_expr: Box::new(no_region(Var(Symbol::ARG_2))),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
ret_var: add_var,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
bounds_var,
|
||||||
|
RunLowLevel {
|
||||||
|
op: LowLevel::ListLen,
|
||||||
|
args: vec![(bytes_var, Var(Symbol::ARG_1))],
|
||||||
|
ret_var: bounds_var,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
ret_var: bounds_bool,
|
||||||
|
}),
|
||||||
|
no_region(roc_result),
|
||||||
|
)],
|
||||||
|
final_else: Box::new(
|
||||||
|
// else-branch
|
||||||
|
no_region(
|
||||||
|
// Err
|
||||||
|
tag(
|
||||||
|
"Err",
|
||||||
|
vec![tag("OutOfBounds", Vec::new(), var_store)],
|
||||||
|
var_store,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
defn(
|
||||||
|
symbol,
|
||||||
|
vec![(bytes_var, Symbol::ARG_1), (arg_record_var, Symbol::ARG_2)],
|
||||||
|
var_store,
|
||||||
|
body,
|
||||||
|
ret_var,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// Str.toUtf8 : Str -> List U8
|
/// Str.toUtf8 : Str -> List U8
|
||||||
fn str_to_utf8(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
fn str_to_utf8(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
|
@ -3185,6 +3371,97 @@ fn defn(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn num_bytes_to(symbol: Symbol, var_store: &mut VarStore, offset: i64, low_level: LowLevel) -> Def {
|
||||||
|
let len_var = var_store.fresh();
|
||||||
|
let list_var = var_store.fresh();
|
||||||
|
let elem_var = var_store.fresh();
|
||||||
|
|
||||||
|
let ret_var = var_store.fresh();
|
||||||
|
let bool_var = var_store.fresh();
|
||||||
|
let add_var = var_store.fresh();
|
||||||
|
let cast_var = var_store.fresh();
|
||||||
|
|
||||||
|
// Perform a bounds check. If it passes, run LowLevel::low_level
|
||||||
|
let body = If {
|
||||||
|
cond_var: bool_var,
|
||||||
|
branch_var: var_store.fresh(),
|
||||||
|
branches: vec![(
|
||||||
|
// if-condition
|
||||||
|
no_region(
|
||||||
|
// index + offset < List.len list
|
||||||
|
RunLowLevel {
|
||||||
|
op: LowLevel::NumLt,
|
||||||
|
args: vec![
|
||||||
|
(
|
||||||
|
len_var,
|
||||||
|
RunLowLevel {
|
||||||
|
op: LowLevel::NumAdd,
|
||||||
|
args: vec![
|
||||||
|
(add_var, Var(Symbol::ARG_2)),
|
||||||
|
(
|
||||||
|
add_var,
|
||||||
|
RunLowLevel {
|
||||||
|
ret_var: cast_var,
|
||||||
|
args: vec![(cast_var, Num(var_store.fresh(), offset))],
|
||||||
|
op: LowLevel::NumIntCast,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
ret_var: add_var,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
len_var,
|
||||||
|
RunLowLevel {
|
||||||
|
op: LowLevel::ListLen,
|
||||||
|
args: vec![(list_var, Var(Symbol::ARG_1))],
|
||||||
|
ret_var: len_var,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
ret_var: bool_var,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// then-branch
|
||||||
|
no_region(
|
||||||
|
// Ok
|
||||||
|
tag(
|
||||||
|
"Ok",
|
||||||
|
vec![RunLowLevel {
|
||||||
|
op: low_level,
|
||||||
|
args: vec![
|
||||||
|
(list_var, Var(Symbol::ARG_1)),
|
||||||
|
(len_var, Var(Symbol::ARG_2)),
|
||||||
|
],
|
||||||
|
ret_var: elem_var,
|
||||||
|
}],
|
||||||
|
var_store,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)],
|
||||||
|
final_else: Box::new(
|
||||||
|
// else-branch
|
||||||
|
no_region(
|
||||||
|
// Err
|
||||||
|
tag(
|
||||||
|
"Err",
|
||||||
|
vec![tag("OutOfBounds", Vec::new(), var_store)],
|
||||||
|
var_store,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
defn(
|
||||||
|
symbol,
|
||||||
|
vec![(list_var, Symbol::ARG_1), (len_var, Symbol::ARG_2)],
|
||||||
|
var_store,
|
||||||
|
body,
|
||||||
|
ret_var,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn defn_help(
|
fn defn_help(
|
||||||
fn_name: Symbol,
|
fn_name: Symbol,
|
||||||
|
|
|
@ -60,6 +60,22 @@ impl Constraint {
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn contains_save_the_environment(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Constraint::Eq(_, _, _, _) => false,
|
||||||
|
Constraint::Store(_, _, _, _) => false,
|
||||||
|
Constraint::Lookup(_, _, _) => false,
|
||||||
|
Constraint::Pattern(_, _, _, _) => false,
|
||||||
|
Constraint::True => false,
|
||||||
|
Constraint::SaveTheEnvironment => true,
|
||||||
|
Constraint::Let(boxed) => {
|
||||||
|
boxed.ret_constraint.contains_save_the_environment()
|
||||||
|
|| boxed.defs_constraint.contains_save_the_environment()
|
||||||
|
}
|
||||||
|
Constraint::And(cs) => cs.iter().any(|c| c.contains_save_the_environment()),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subtract(declared: &Declared, detail: &VariableDetail, accum: &mut VariableDetail) {
|
fn subtract(declared: &Declared, detail: &VariableDetail, accum: &mut VariableDetail) {
|
||||||
|
@ -71,12 +87,12 @@ fn subtract(declared: &Declared, detail: &VariableDetail, accum: &mut VariableDe
|
||||||
|
|
||||||
// lambda set variables are always flex
|
// lambda set variables are always flex
|
||||||
for var in &detail.lambda_set_variables {
|
for var in &detail.lambda_set_variables {
|
||||||
if declared.rigid_vars.contains(&var.into_inner()) {
|
if declared.rigid_vars.contains(var) {
|
||||||
panic!("lambda set variable {:?} is declared as rigid", var);
|
panic!("lambda set variable {:?} is declared as rigid", var);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !declared.flex_vars.contains(&var.into_inner()) {
|
if !declared.flex_vars.contains(var) {
|
||||||
accum.lambda_set_variables.insert(*var);
|
accum.lambda_set_variables.push(*var);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1632,7 +1632,7 @@ fn make_tag_union_recursive<'a>(
|
||||||
typ.substitute_alias(symbol, &Type::Variable(rec_var));
|
typ.substitute_alias(symbol, &Type::Variable(rec_var));
|
||||||
}
|
}
|
||||||
Type::RecursiveTagUnion(_, _, _) => {}
|
Type::RecursiveTagUnion(_, _, _) => {}
|
||||||
Type::Alias(_, _, actual) => make_tag_union_recursive(
|
Type::Alias { actual, .. } => make_tag_union_recursive(
|
||||||
env,
|
env,
|
||||||
symbol,
|
symbol,
|
||||||
region,
|
region,
|
||||||
|
|
|
@ -47,7 +47,7 @@ impl Scope {
|
||||||
let alias = Alias {
|
let alias = Alias {
|
||||||
region,
|
region,
|
||||||
typ,
|
typ,
|
||||||
lambda_set_variables: MutSet::default(),
|
lambda_set_variables: Vec::new(),
|
||||||
recursion_variables: MutSet::default(),
|
recursion_variables: MutSet::default(),
|
||||||
type_variables: variables,
|
type_variables: variables,
|
||||||
};
|
};
|
||||||
|
@ -198,6 +198,11 @@ impl Scope {
|
||||||
true
|
true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let lambda_set_variables: Vec<_> = lambda_set_variables
|
||||||
|
.into_iter()
|
||||||
|
.map(|v| roc_types::types::LambdaSet(Type::Variable(v)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
let alias = Alias {
|
let alias = Alias {
|
||||||
region,
|
region,
|
||||||
type_variables: vars,
|
type_variables: vars,
|
||||||
|
|
|
@ -2,7 +2,7 @@ use roc_can::constraint::Constraint::{self, *};
|
||||||
use roc_can::constraint::LetConstraint;
|
use roc_can::constraint::LetConstraint;
|
||||||
use roc_can::expected::Expected::{self, *};
|
use roc_can::expected::Expected::{self, *};
|
||||||
use roc_collections::all::SendMap;
|
use roc_collections::all::SendMap;
|
||||||
use roc_module::ident::TagName;
|
use roc_module::ident::{Lowercase, TagName};
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_region::all::Region;
|
use roc_region::all::Region;
|
||||||
use roc_types::subs::Variable;
|
use roc_types::subs::Variable;
|
||||||
|
@ -89,9 +89,23 @@ pub fn str_type() -> Type {
|
||||||
builtin_type(Symbol::STR_STR, Vec::new())
|
builtin_type(Symbol::STR_STR, Vec::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn builtin_alias(
|
||||||
|
symbol: Symbol,
|
||||||
|
type_arguments: Vec<(Lowercase, Type)>,
|
||||||
|
actual: Box<Type>,
|
||||||
|
) -> Type {
|
||||||
|
Type::Alias {
|
||||||
|
symbol,
|
||||||
|
type_arguments,
|
||||||
|
actual,
|
||||||
|
lambda_set_variables: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn num_float(range: Type) -> Type {
|
pub fn num_float(range: Type) -> Type {
|
||||||
Type::Alias(
|
builtin_alias(
|
||||||
Symbol::NUM_FLOAT,
|
Symbol::NUM_FLOAT,
|
||||||
vec![("range".into(), range.clone())],
|
vec![("range".into(), range.clone())],
|
||||||
Box::new(num_num(num_floatingpoint(range))),
|
Box::new(num_num(num_floatingpoint(range))),
|
||||||
|
@ -108,7 +122,7 @@ pub fn num_floatingpoint(range: Type) -> Type {
|
||||||
Box::new(Type::EmptyTagUnion),
|
Box::new(Type::EmptyTagUnion),
|
||||||
);
|
);
|
||||||
|
|
||||||
Type::Alias(
|
builtin_alias(
|
||||||
Symbol::NUM_FLOATINGPOINT,
|
Symbol::NUM_FLOATINGPOINT,
|
||||||
vec![("range".into(), range)],
|
vec![("range".into(), range)],
|
||||||
Box::new(alias_content),
|
Box::new(alias_content),
|
||||||
|
@ -122,12 +136,12 @@ pub fn num_binary64() -> Type {
|
||||||
Box::new(Type::EmptyTagUnion),
|
Box::new(Type::EmptyTagUnion),
|
||||||
);
|
);
|
||||||
|
|
||||||
Type::Alias(Symbol::NUM_BINARY64, vec![], Box::new(alias_content))
|
builtin_alias(Symbol::NUM_BINARY64, vec![], Box::new(alias_content))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn num_int(range: Type) -> Type {
|
pub fn num_int(range: Type) -> Type {
|
||||||
Type::Alias(
|
builtin_alias(
|
||||||
Symbol::NUM_INT,
|
Symbol::NUM_INT,
|
||||||
vec![("range".into(), range.clone())],
|
vec![("range".into(), range.clone())],
|
||||||
Box::new(num_num(num_integer(range))),
|
Box::new(num_num(num_integer(range))),
|
||||||
|
@ -141,7 +155,7 @@ pub fn num_signed64() -> Type {
|
||||||
Box::new(Type::EmptyTagUnion),
|
Box::new(Type::EmptyTagUnion),
|
||||||
);
|
);
|
||||||
|
|
||||||
Type::Alias(Symbol::NUM_SIGNED64, vec![], Box::new(alias_content))
|
builtin_alias(Symbol::NUM_SIGNED64, vec![], Box::new(alias_content))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -154,7 +168,7 @@ pub fn num_integer(range: Type) -> Type {
|
||||||
Box::new(Type::EmptyTagUnion),
|
Box::new(Type::EmptyTagUnion),
|
||||||
);
|
);
|
||||||
|
|
||||||
Type::Alias(
|
builtin_alias(
|
||||||
Symbol::NUM_INTEGER,
|
Symbol::NUM_INTEGER,
|
||||||
vec![("range".into(), range)],
|
vec![("range".into(), range)],
|
||||||
Box::new(alias_content),
|
Box::new(alias_content),
|
||||||
|
@ -168,7 +182,7 @@ pub fn num_num(typ: Type) -> Type {
|
||||||
Box::new(Type::EmptyTagUnion),
|
Box::new(Type::EmptyTagUnion),
|
||||||
);
|
);
|
||||||
|
|
||||||
Type::Alias(
|
builtin_alias(
|
||||||
Symbol::NUM_NUM,
|
Symbol::NUM_NUM,
|
||||||
vec![("range".into(), typ)],
|
vec![("range".into(), typ)],
|
||||||
Box::new(alias_content),
|
Box::new(alias_content),
|
||||||
|
|
|
@ -1121,7 +1121,7 @@ pub fn constrain_decls(home: ModuleId, decls: &[Declaration]) -> Constraint {
|
||||||
}
|
}
|
||||||
|
|
||||||
// this assert make the "root" of the constraint wasn't dropped
|
// this assert make the "root" of the constraint wasn't dropped
|
||||||
debug_assert!(format!("{:?}", &constraint).contains("SaveTheEnvironment"));
|
debug_assert!(constraint.contains_save_the_environment());
|
||||||
|
|
||||||
constraint
|
constraint
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ pub fn constrain_imported_values(
|
||||||
|
|
||||||
// an imported symbol can be either an alias or a value
|
// an imported symbol can be either an alias or a value
|
||||||
match import.solved_type {
|
match import.solved_type {
|
||||||
SolvedType::Alias(symbol, _, _) if symbol == loc_symbol.value => {
|
SolvedType::Alias(symbol, _, _, _) if symbol == loc_symbol.value => {
|
||||||
// do nothing, in the future the alias definitions should not be in the list of imported values
|
// do nothing, in the future the alias definitions should not be in the list of imported values
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
|
|
@ -100,19 +100,6 @@ where
|
||||||
self.free_symbols(stmt);
|
self.free_symbols(stmt);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Stmt::Invoke {
|
|
||||||
symbol,
|
|
||||||
layout,
|
|
||||||
call,
|
|
||||||
pass,
|
|
||||||
fail: _,
|
|
||||||
exception_id: _,
|
|
||||||
} => {
|
|
||||||
// for now, treat invoke as a normal call
|
|
||||||
self.build_expr(symbol, &Expr::Call(call.clone()), layout)?;
|
|
||||||
self.free_symbols(stmt);
|
|
||||||
self.build_stmt(pass)
|
|
||||||
}
|
|
||||||
Stmt::Switch {
|
Stmt::Switch {
|
||||||
cond_symbol,
|
cond_symbol,
|
||||||
cond_layout,
|
cond_layout,
|
||||||
|
@ -201,6 +188,12 @@ where
|
||||||
Symbol::NUM_SUB => {
|
Symbol::NUM_SUB => {
|
||||||
self.build_run_low_level(sym, &LowLevel::NumSub, arguments, layout)
|
self.build_run_low_level(sym, &LowLevel::NumSub, arguments, layout)
|
||||||
}
|
}
|
||||||
|
Symbol::NUM_ROUND => self.build_run_low_level(
|
||||||
|
sym,
|
||||||
|
&LowLevel::NumRound,
|
||||||
|
arguments,
|
||||||
|
layout,
|
||||||
|
),
|
||||||
Symbol::BOOL_EQ => {
|
Symbol::BOOL_EQ => {
|
||||||
self.build_run_low_level(sym, &LowLevel::Eq, arguments, layout)
|
self.build_run_low_level(sym, &LowLevel::Eq, arguments, layout)
|
||||||
}
|
}
|
||||||
|
@ -300,7 +293,13 @@ where
|
||||||
// Should we panic?
|
// Should we panic?
|
||||||
x => Err(format!("wrong layout, {:?}, for LowLevel::Eq", x)),
|
x => Err(format!("wrong layout, {:?}, for LowLevel::Eq", x)),
|
||||||
},
|
},
|
||||||
|
LowLevel::NumRound => self.build_fn_call(
|
||||||
|
sym,
|
||||||
|
bitcode::NUM_ROUND.to_string(),
|
||||||
|
args,
|
||||||
|
&[Layout::Builtin(Builtin::Float64)],
|
||||||
|
layout,
|
||||||
|
),
|
||||||
x => Err(format!("low level, {:?}. is not yet implemented", x)),
|
x => Err(format!("low level, {:?}. is not yet implemented", x)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -487,20 +486,6 @@ where
|
||||||
self.scan_ast(following);
|
self.scan_ast(following);
|
||||||
}
|
}
|
||||||
|
|
||||||
Stmt::Invoke {
|
|
||||||
symbol,
|
|
||||||
layout: _,
|
|
||||||
call,
|
|
||||||
pass,
|
|
||||||
fail: _,
|
|
||||||
exception_id: _,
|
|
||||||
} => {
|
|
||||||
// for now, treat invoke as a normal call
|
|
||||||
self.set_last_seen(*symbol, stmt);
|
|
||||||
self.scan_ast_call(call, stmt);
|
|
||||||
self.scan_ast(pass);
|
|
||||||
}
|
|
||||||
|
|
||||||
Stmt::Switch {
|
Stmt::Switch {
|
||||||
cond_symbol,
|
cond_symbol,
|
||||||
branches,
|
branches,
|
||||||
|
@ -516,7 +501,6 @@ where
|
||||||
Stmt::Ret(sym) => {
|
Stmt::Ret(sym) => {
|
||||||
self.set_last_seen(*sym, stmt);
|
self.set_last_seen(*sym, stmt);
|
||||||
}
|
}
|
||||||
Stmt::Resume(_exception_id) => {}
|
|
||||||
Stmt::Refcounting(modify, following) => {
|
Stmt::Refcounting(modify, following) => {
|
||||||
let sym = modify.get_symbol();
|
let sym = modify.get_symbol();
|
||||||
|
|
||||||
|
|
|
@ -179,6 +179,12 @@ fn build_object<'a, B: Backend<'a>>(
|
||||||
"roc_dealloc".into(),
|
"roc_dealloc".into(),
|
||||||
"free".into(),
|
"free".into(),
|
||||||
)?;
|
)?;
|
||||||
|
generate_wrapper(
|
||||||
|
&mut backend,
|
||||||
|
&mut output,
|
||||||
|
"roc_panic".into(),
|
||||||
|
"roc_builtins.utils.test_panic".into(),
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup layout_ids for procedure calls.
|
// Setup layout_ids for procedure calls.
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -73,19 +73,22 @@ pub fn helper<'a>(
|
||||||
procedures.insert(key, proc);
|
procedures.insert(key, proc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// You can comment and uncomment this block out to get more useful information
|
||||||
println!("=========== Procedures ==========");
|
// while you're working on the dev backend!
|
||||||
println!("{:?}", procedures);
|
{
|
||||||
println!("=================================\n");
|
// println!("=========== Procedures ==========");
|
||||||
|
// println!("{:?}", procedures);
|
||||||
|
// println!("=================================\n");
|
||||||
|
|
||||||
println!("=========== Interns ==========");
|
// println!("=========== Interns ==========");
|
||||||
println!("{:?}", interns);
|
// println!("{:?}", interns);
|
||||||
println!("=================================\n");
|
// println!("=================================\n");
|
||||||
|
|
||||||
|
// println!("=========== Exposed ==========");
|
||||||
|
// println!("{:?}", exposed_to_host);
|
||||||
|
// println!("=================================\n");
|
||||||
|
}
|
||||||
|
|
||||||
println!("=========== Exposed ==========");
|
|
||||||
println!("{:?}", exposed_to_host);
|
|
||||||
println!("=================================\n");
|
|
||||||
*/
|
|
||||||
debug_assert_eq!(exposed_to_host.len(), 1);
|
debug_assert_eq!(exposed_to_host.len(), 1);
|
||||||
let main_fn_symbol = loaded.entry_point.symbol;
|
let main_fn_symbol = loaded.entry_point.symbol;
|
||||||
let main_fn_layout = loaded.entry_point.layout;
|
let main_fn_layout = loaded.entry_point.layout;
|
||||||
|
|
|
@ -271,13 +271,11 @@ fn build_transform_caller_help<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
|
|
||||||
match closure_data_layout {
|
match closure_data_layout {
|
||||||
Layout::Closure(_, lambda_set, _) => {
|
Layout::Struct(&[]) => {
|
||||||
if let Layout::Struct(&[]) = lambda_set.runtime_representation() {
|
// nothing to add
|
||||||
// do nothing
|
}
|
||||||
} else {
|
other => {
|
||||||
let closure_type =
|
let closure_type = basic_type_from_layout(env, &other).ptr_type(AddressSpace::Generic);
|
||||||
basic_type_from_layout(env, &lambda_set.runtime_representation())
|
|
||||||
.ptr_type(AddressSpace::Generic);
|
|
||||||
|
|
||||||
let closure_cast = env
|
let closure_cast = env
|
||||||
.builder
|
.builder
|
||||||
|
@ -289,33 +287,6 @@ fn build_transform_caller_help<'a, 'ctx, 'env>(
|
||||||
arguments_cast.push(closure_data);
|
arguments_cast.push(closure_data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Layout::Struct([Layout::Closure(_, lambda_set, _)]) => {
|
|
||||||
// a case required for Set.walk; may be able to remove when we can define builtins in
|
|
||||||
// terms of other builtins in the right way (using their function symbols instead of
|
|
||||||
// hacking with lowlevel ops).
|
|
||||||
let closure_type = basic_type_from_layout(
|
|
||||||
env,
|
|
||||||
&Layout::Struct(&[lambda_set.runtime_representation()]),
|
|
||||||
)
|
|
||||||
.ptr_type(AddressSpace::Generic);
|
|
||||||
|
|
||||||
let closure_cast = env
|
|
||||||
.builder
|
|
||||||
.build_bitcast(closure_ptr, closure_type, "load_opaque")
|
|
||||||
.into_pointer_value();
|
|
||||||
|
|
||||||
let closure_data = env.builder.build_load(closure_cast, "load_opaque");
|
|
||||||
|
|
||||||
arguments_cast.push(closure_data);
|
|
||||||
}
|
|
||||||
Layout::Struct([]) => {
|
|
||||||
// do nothing, should try to remove this case later
|
|
||||||
}
|
|
||||||
Layout::Struct(_) => {
|
|
||||||
// do nothing, should try to remove this case later
|
|
||||||
}
|
|
||||||
other => unreachable!("layout is not valid for a closure: {:?}", other),
|
|
||||||
}
|
|
||||||
|
|
||||||
let call = {
|
let call = {
|
||||||
env.builder
|
env.builder
|
||||||
|
@ -625,13 +596,13 @@ pub fn build_compare_wrapper<'a, 'ctx, 'env>(
|
||||||
let default = [value1, value2];
|
let default = [value1, value2];
|
||||||
|
|
||||||
let arguments_cast = match closure_data_layout {
|
let arguments_cast = match closure_data_layout {
|
||||||
Layout::Closure(_, lambda_set, _) => {
|
Layout::Struct(&[]) => {
|
||||||
if let Layout::Struct(&[]) = lambda_set.runtime_representation() {
|
// nothing to add
|
||||||
&default
|
&default
|
||||||
} else {
|
}
|
||||||
|
other => {
|
||||||
let closure_type =
|
let closure_type =
|
||||||
basic_type_from_layout(env, &lambda_set.runtime_representation())
|
basic_type_from_layout(env, &other).ptr_type(AddressSpace::Generic);
|
||||||
.ptr_type(AddressSpace::Generic);
|
|
||||||
|
|
||||||
let closure_cast = env
|
let closure_cast = env
|
||||||
.builder
|
.builder
|
||||||
|
@ -642,9 +613,6 @@ pub fn build_compare_wrapper<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
env.arena.alloc([value1, value2, closure_data]) as &[_]
|
env.arena.alloc([value1, value2, closure_data]) as &[_]
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Layout::Struct([]) => &default,
|
|
||||||
other => unreachable!("layout is not valid for a closure: {:?}", other),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let call = env.builder.build_call(
|
let call = env.builder.build_call(
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -88,10 +88,6 @@ fn build_hash_layout<'a, 'ctx, 'env>(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
Layout::Closure(_, _, _) => {
|
|
||||||
unreachable!("the type system will guarantee these are never hashed")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -121,69 +121,6 @@ pub fn list_repeat<'a, 'ctx, 'env>(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// List.prepend : List elem, elem -> List elem
|
|
||||||
pub fn list_prepend<'a, 'ctx, 'env>(
|
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
|
||||||
original_wrapper: StructValue<'ctx>,
|
|
||||||
elem: BasicValueEnum<'ctx>,
|
|
||||||
elem_layout: &Layout<'a>,
|
|
||||||
) -> BasicValueEnum<'ctx> {
|
|
||||||
let builder = env.builder;
|
|
||||||
|
|
||||||
// Load the usize length from the wrapper.
|
|
||||||
let len = list_len(builder, original_wrapper);
|
|
||||||
let elem_type = basic_type_from_layout(env, elem_layout);
|
|
||||||
let ptr_type = elem_type.ptr_type(AddressSpace::Generic);
|
|
||||||
let list_ptr = load_list_ptr(builder, original_wrapper, ptr_type);
|
|
||||||
|
|
||||||
// The output list length, which is the old list length + 1
|
|
||||||
let new_list_len = env.builder.build_int_add(
|
|
||||||
env.ptr_int().const_int(1_u64, false),
|
|
||||||
len,
|
|
||||||
"new_list_length",
|
|
||||||
);
|
|
||||||
|
|
||||||
// Allocate space for the new array that we'll copy into.
|
|
||||||
let clone_ptr = allocate_list(env, elem_layout, new_list_len);
|
|
||||||
|
|
||||||
builder.build_store(clone_ptr, elem);
|
|
||||||
|
|
||||||
let index_1_ptr = unsafe {
|
|
||||||
builder.build_in_bounds_gep(
|
|
||||||
clone_ptr,
|
|
||||||
&[env.ptr_int().const_int(1_u64, false)],
|
|
||||||
"load_index",
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Calculate the number of bytes we'll need to allocate.
|
|
||||||
let elem_bytes = env
|
|
||||||
.ptr_int()
|
|
||||||
.const_int(elem_layout.stack_size(env.ptr_bytes) as u64, false);
|
|
||||||
|
|
||||||
// This is the size of the list coming in, before we have added an element
|
|
||||||
// to the beginning.
|
|
||||||
let list_size = env
|
|
||||||
.builder
|
|
||||||
.build_int_mul(elem_bytes, len, "mul_old_len_by_elem_bytes");
|
|
||||||
|
|
||||||
let ptr_bytes = env.ptr_bytes;
|
|
||||||
|
|
||||||
if elem_layout.safe_to_memcpy() {
|
|
||||||
// Copy the bytes from the original array into the new
|
|
||||||
// one we just allocated
|
|
||||||
//
|
|
||||||
// TODO how do we decide when to do the small memcpy vs the normal one?
|
|
||||||
builder
|
|
||||||
.build_memcpy(index_1_ptr, ptr_bytes, list_ptr, ptr_bytes, list_size)
|
|
||||||
.unwrap();
|
|
||||||
} else {
|
|
||||||
panic!("TODO Cranelift currently only knows how to clone list elements that are Copy.");
|
|
||||||
}
|
|
||||||
|
|
||||||
store_list(env, clone_ptr, new_list_len)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// List.join : List (List elem) -> List elem
|
/// List.join : List (List elem) -> List elem
|
||||||
pub fn list_join<'a, 'ctx, 'env>(
|
pub fn list_join<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
@ -299,6 +236,25 @@ pub fn list_append<'a, 'ctx, 'env>(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// List.prepend : List elem, elem -> List elem
|
||||||
|
pub fn list_prepend<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
original_wrapper: StructValue<'ctx>,
|
||||||
|
element: BasicValueEnum<'ctx>,
|
||||||
|
element_layout: &Layout<'a>,
|
||||||
|
) -> BasicValueEnum<'ctx> {
|
||||||
|
call_bitcode_fn_returns_list(
|
||||||
|
env,
|
||||||
|
&[
|
||||||
|
pass_list_as_i128(env, original_wrapper.into()),
|
||||||
|
env.alignment_intvalue(element_layout),
|
||||||
|
pass_element_as_opaque(env, element),
|
||||||
|
layout_width(env, element_layout),
|
||||||
|
],
|
||||||
|
bitcode::LIST_PREPEND,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// List.swap : List elem, Nat, Nat -> List elem
|
/// List.swap : List elem, Nat, Nat -> List elem
|
||||||
pub fn list_swap<'a, 'ctx, 'env>(
|
pub fn list_swap<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
|
|
@ -250,6 +250,62 @@ pub fn str_to_utf8<'a, 'ctx, 'env>(
|
||||||
call_bitcode_fn_returns_list(env, &[string], bitcode::STR_TO_UTF8)
|
call_bitcode_fn_returns_list(env, &[string], bitcode::STR_TO_UTF8)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Str.fromUtf8 : List U8, { count : Nat, start : Nat } -> { a : Bool, b : Str, c : Nat, d : I8 }
|
||||||
|
pub fn str_from_utf8_range<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
_parent: FunctionValue<'ctx>,
|
||||||
|
list_wrapper: StructValue<'ctx>,
|
||||||
|
count_and_start: StructValue<'ctx>,
|
||||||
|
) -> BasicValueEnum<'ctx> {
|
||||||
|
let builder = env.builder;
|
||||||
|
let ctx = env.context;
|
||||||
|
|
||||||
|
let result_type = env.module.get_struct_type("str.FromUtf8Result").unwrap();
|
||||||
|
let result_ptr = builder.build_alloca(result_type, "alloca_utf8_validate_bytes_result");
|
||||||
|
|
||||||
|
call_void_bitcode_fn(
|
||||||
|
env,
|
||||||
|
&[
|
||||||
|
complex_bitcast(
|
||||||
|
env.builder,
|
||||||
|
list_wrapper.into(),
|
||||||
|
env.context.i128_type().into(),
|
||||||
|
"to_i128",
|
||||||
|
),
|
||||||
|
// TODO: This won't work for 32 bit targets!
|
||||||
|
complex_bitcast(
|
||||||
|
env.builder,
|
||||||
|
count_and_start.into(),
|
||||||
|
env.context.i128_type().into(),
|
||||||
|
"to_i128",
|
||||||
|
),
|
||||||
|
result_ptr.into(),
|
||||||
|
],
|
||||||
|
bitcode::STR_FROM_UTF8_RANGE,
|
||||||
|
);
|
||||||
|
|
||||||
|
let record_type = env.context.struct_type(
|
||||||
|
&[
|
||||||
|
env.ptr_int().into(),
|
||||||
|
super::convert::zig_str_type(env).into(),
|
||||||
|
env.context.bool_type().into(),
|
||||||
|
ctx.i8_type().into(),
|
||||||
|
],
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
let result_ptr_cast = env
|
||||||
|
.builder
|
||||||
|
.build_bitcast(
|
||||||
|
result_ptr,
|
||||||
|
record_type.ptr_type(AddressSpace::Generic),
|
||||||
|
"to_unnamed",
|
||||||
|
)
|
||||||
|
.into_pointer_value();
|
||||||
|
|
||||||
|
builder.build_load(result_ptr_cast, "load_utf8_validate_bytes_result")
|
||||||
|
}
|
||||||
|
|
||||||
/// Str.fromUtf8 : List U8 -> { a : Bool, b : Str, c : Nat, d : I8 }
|
/// Str.fromUtf8 : List U8 -> { a : Bool, b : Str, c : Nat, d : I8 }
|
||||||
pub fn str_from_utf8<'a, 'ctx, 'env>(
|
pub fn str_from_utf8<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
|
|
@ -198,10 +198,6 @@ fn build_eq<'a, 'ctx, 'env>(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
Layout::Closure(_, _, _) => {
|
|
||||||
unreachable!("the type system will guarantee these are never compared")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -340,10 +336,6 @@ fn build_neq<'a, 'ctx, 'env>(
|
||||||
Layout::RecursivePointer => {
|
Layout::RecursivePointer => {
|
||||||
unreachable!("recursion pointers should never be compared directly")
|
unreachable!("recursion pointers should never be compared directly")
|
||||||
}
|
}
|
||||||
|
|
||||||
Layout::Closure(_, _, _) => {
|
|
||||||
unreachable!("the type system will guarantee these are never compared")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,10 +26,6 @@ pub fn basic_type_from_layout<'a, 'ctx, 'env>(
|
||||||
use Layout::*;
|
use Layout::*;
|
||||||
|
|
||||||
match layout {
|
match layout {
|
||||||
Closure(_args, closure_layout, _ret_layout) => {
|
|
||||||
let closure_data_layout = closure_layout.runtime_representation();
|
|
||||||
basic_type_from_layout(env, &closure_data_layout)
|
|
||||||
}
|
|
||||||
Struct(sorted_fields) => basic_type_from_record(env, sorted_fields),
|
Struct(sorted_fields) => basic_type_from_record(env, sorted_fields),
|
||||||
Union(union_layout) => {
|
Union(union_layout) => {
|
||||||
use UnionLayout::*;
|
use UnionLayout::*;
|
||||||
|
|
|
@ -1,19 +1,18 @@
|
||||||
|
use crate::llvm::build::Env;
|
||||||
use crate::llvm::build::{add_func, C_CALL_CONV};
|
use crate::llvm::build::{add_func, C_CALL_CONV};
|
||||||
use crate::llvm::convert::ptr_int;
|
use crate::llvm::convert::ptr_int;
|
||||||
use inkwell::builder::Builder;
|
use inkwell::module::Linkage;
|
||||||
use inkwell::context::Context;
|
|
||||||
use inkwell::module::{Linkage, Module};
|
|
||||||
use inkwell::values::BasicValue;
|
use inkwell::values::BasicValue;
|
||||||
use inkwell::AddressSpace;
|
use inkwell::AddressSpace;
|
||||||
|
|
||||||
/// Define functions for roc_alloc, roc_realloc, and roc_dealloc
|
/// Define functions for roc_alloc, roc_realloc, and roc_dealloc
|
||||||
/// which use libc implementations (malloc, realloc, and free)
|
/// which use libc implementations (malloc, realloc, and free)
|
||||||
pub fn add_default_roc_externs<'ctx>(
|
pub fn add_default_roc_externs(env: &Env<'_, '_, '_>) {
|
||||||
ctx: &'ctx Context,
|
let ctx = env.context;
|
||||||
module: &Module<'ctx>,
|
let module = env.module;
|
||||||
builder: &Builder<'ctx>,
|
let builder = env.builder;
|
||||||
ptr_bytes: u32,
|
let ptr_bytes = env.ptr_bytes;
|
||||||
) {
|
|
||||||
let usize_type = ptr_int(ctx, ptr_bytes);
|
let usize_type = ptr_int(ctx, ptr_bytes);
|
||||||
let i8_ptr_type = ctx.i8_type().ptr_type(AddressSpace::Generic);
|
let i8_ptr_type = ctx.i8_type().ptr_type(AddressSpace::Generic);
|
||||||
|
|
||||||
|
@ -139,4 +138,69 @@ pub fn add_default_roc_externs<'ctx>(
|
||||||
crate::llvm::build::verify_fn(fn_val);
|
crate::llvm::build::verify_fn(fn_val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
add_sjlj_roc_panic(env)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_sjlj_roc_panic(env: &Env<'_, '_, '_>) {
|
||||||
|
let ctx = env.context;
|
||||||
|
let module = env.module;
|
||||||
|
let builder = env.builder;
|
||||||
|
|
||||||
|
// roc_panic
|
||||||
|
{
|
||||||
|
use crate::llvm::build::LLVM_LONGJMP;
|
||||||
|
|
||||||
|
// The type of this function (but not the implementation) should have
|
||||||
|
// already been defined by the builtins, which rely on it.
|
||||||
|
let fn_val = module.get_function("roc_panic").unwrap();
|
||||||
|
let mut params = fn_val.get_param_iter();
|
||||||
|
let ptr_arg = params.next().unwrap();
|
||||||
|
|
||||||
|
// in debug mode, this is assumed to be NullTerminatedString
|
||||||
|
let _tag_id_arg = params.next().unwrap();
|
||||||
|
|
||||||
|
debug_assert!(params.next().is_none());
|
||||||
|
|
||||||
|
let subprogram = env.new_subprogram("roc_panic");
|
||||||
|
fn_val.set_subprogram(subprogram);
|
||||||
|
|
||||||
|
env.dibuilder.finalize();
|
||||||
|
|
||||||
|
// Add a basic block for the entry point
|
||||||
|
let entry = ctx.append_basic_block(fn_val, "entry");
|
||||||
|
|
||||||
|
builder.position_at_end(entry);
|
||||||
|
|
||||||
|
let buffer = crate::llvm::build::get_sjlj_buffer(env);
|
||||||
|
|
||||||
|
// write our error message pointer
|
||||||
|
let index = env.ptr_int().const_int(3 * env.ptr_bytes as u64, false);
|
||||||
|
let message_buffer_raw =
|
||||||
|
unsafe { builder.build_gep(buffer, &[index], "raw_msg_buffer_ptr") };
|
||||||
|
let message_buffer = builder.build_bitcast(
|
||||||
|
message_buffer_raw,
|
||||||
|
env.context
|
||||||
|
.i8_type()
|
||||||
|
.ptr_type(AddressSpace::Generic)
|
||||||
|
.ptr_type(AddressSpace::Generic),
|
||||||
|
"to **u8",
|
||||||
|
);
|
||||||
|
|
||||||
|
env.builder
|
||||||
|
.build_store(message_buffer.into_pointer_value(), ptr_arg);
|
||||||
|
|
||||||
|
let tag = env.context.i32_type().const_int(1, false);
|
||||||
|
if true {
|
||||||
|
let _call = env.build_intrinsic_call(LLVM_LONGJMP, &[buffer.into()]);
|
||||||
|
} else {
|
||||||
|
let _call = env.build_intrinsic_call(LLVM_LONGJMP, &[buffer.into(), tag.into()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.build_unreachable();
|
||||||
|
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
crate::llvm::build::verify_fn(fn_val);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -278,7 +278,7 @@ impl<'ctx> PointerToRefcount<'ctx> {
|
||||||
// build then block
|
// build then block
|
||||||
{
|
{
|
||||||
builder.position_at_end(then_block);
|
builder.position_at_end(then_block);
|
||||||
if !env.leak {
|
if !env.is_gen_test {
|
||||||
let ptr = builder.build_pointer_cast(
|
let ptr = builder.build_pointer_cast(
|
||||||
refcount_ptr.value,
|
refcount_ptr.value,
|
||||||
ctx.i8_type().ptr_type(AddressSpace::Generic),
|
ctx.i8_type().ptr_type(AddressSpace::Generic),
|
||||||
|
@ -659,23 +659,6 @@ fn modify_refcount_layout_build_function<'a, 'ctx, 'env>(
|
||||||
Some(function)
|
Some(function)
|
||||||
}
|
}
|
||||||
|
|
||||||
Closure(_, lambda_set, _) => {
|
|
||||||
if lambda_set.contains_refcounted() {
|
|
||||||
let function = modify_refcount_layout_build_function(
|
|
||||||
env,
|
|
||||||
parent,
|
|
||||||
layout_ids,
|
|
||||||
mode,
|
|
||||||
when_recursive,
|
|
||||||
&lambda_set.runtime_representation(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Some(function)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Struct(layouts) => {
|
Struct(layouts) => {
|
||||||
let function = modify_refcount_struct(env, layout_ids, layouts, mode, when_recursive);
|
let function = modify_refcount_struct(env, layout_ids, layouts, mode, when_recursive);
|
||||||
|
|
||||||
|
|
|
@ -165,13 +165,14 @@ fn generate_entry_doc<'a>(
|
||||||
(new_acc, Some(comments_or_new_lines))
|
(new_acc, Some(comments_or_new_lines))
|
||||||
}
|
}
|
||||||
|
|
||||||
Def::Annotation(loc_pattern, _loc_ann) => match loc_pattern.value {
|
Def::Annotation(loc_pattern, loc_ann) => match loc_pattern.value {
|
||||||
Pattern::Identifier(identifier) => {
|
Pattern::Identifier(identifier) => {
|
||||||
// Check if the definition is exposed
|
// Check if the definition is exposed
|
||||||
if ident_ids.get_id(&identifier.into()).is_some() {
|
if ident_ids.get_id(&identifier.into()).is_some() {
|
||||||
|
let name = identifier.to_string();
|
||||||
let doc_def = DocDef {
|
let doc_def = DocDef {
|
||||||
name: identifier.to_string(),
|
name,
|
||||||
type_annotation: NoTypeAnn,
|
type_annotation: type_to_docs(false, loc_ann.value),
|
||||||
type_vars: Vec::new(),
|
type_vars: Vec::new(),
|
||||||
docs: before_comments_or_new_lines.and_then(comments_or_new_lines_to_docs),
|
docs: before_comments_or_new_lines.and_then(comments_or_new_lines_to_docs),
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use roc_can::annotation::IntroducedVariables;
|
||||||
use roc_can::def::{Declaration, Def};
|
use roc_can::def::{Declaration, Def};
|
||||||
use roc_can::env::Env;
|
use roc_can::env::Env;
|
||||||
use roc_can::expr::{Expr, Recursive};
|
use roc_can::expr::{Expr, Recursive};
|
||||||
|
@ -160,25 +161,22 @@ fn build_effect_always(
|
||||||
(function_var, closure)
|
(function_var, closure)
|
||||||
};
|
};
|
||||||
|
|
||||||
use roc_can::annotation::IntroducedVariables;
|
|
||||||
|
|
||||||
let mut introduced_variables = IntroducedVariables::default();
|
let mut introduced_variables = IntroducedVariables::default();
|
||||||
|
|
||||||
let signature = {
|
let signature = {
|
||||||
// Effect.always : a -> Effect a
|
// Effect.always : a -> Effect a
|
||||||
let var_a = var_store.fresh();
|
let var_a = var_store.fresh();
|
||||||
|
|
||||||
introduced_variables.insert_named("a".into(), var_a);
|
introduced_variables.insert_named("a".into(), var_a);
|
||||||
|
|
||||||
let effect_a = {
|
let effect_a = build_effect_alias(
|
||||||
let actual = build_effect_actual(effect_tag_name, Type::Variable(var_a), var_store);
|
|
||||||
|
|
||||||
Type::Alias(
|
|
||||||
effect_symbol,
|
effect_symbol,
|
||||||
vec![("a".into(), Type::Variable(var_a))],
|
effect_tag_name,
|
||||||
Box::new(actual),
|
"a",
|
||||||
)
|
var_a,
|
||||||
};
|
Type::Variable(var_a),
|
||||||
|
var_store,
|
||||||
|
&mut introduced_variables,
|
||||||
|
);
|
||||||
|
|
||||||
let closure_var = var_store.fresh();
|
let closure_var = var_store.fresh();
|
||||||
introduced_variables.insert_wildcard(closure_var);
|
introduced_variables.insert_wildcard(closure_var);
|
||||||
|
@ -353,8 +351,6 @@ fn build_effect_map(
|
||||||
loc_body: Box::new(Located::at_zero(body)),
|
loc_body: Box::new(Located::at_zero(body)),
|
||||||
};
|
};
|
||||||
|
|
||||||
use roc_can::annotation::IntroducedVariables;
|
|
||||||
|
|
||||||
let mut introduced_variables = IntroducedVariables::default();
|
let mut introduced_variables = IntroducedVariables::default();
|
||||||
|
|
||||||
let signature = {
|
let signature = {
|
||||||
|
@ -365,26 +361,25 @@ fn build_effect_map(
|
||||||
introduced_variables.insert_named("a".into(), var_a);
|
introduced_variables.insert_named("a".into(), var_a);
|
||||||
introduced_variables.insert_named("b".into(), var_b);
|
introduced_variables.insert_named("b".into(), var_b);
|
||||||
|
|
||||||
let effect_a = {
|
let effect_a = build_effect_alias(
|
||||||
let actual =
|
|
||||||
build_effect_actual(effect_tag_name.clone(), Type::Variable(var_a), var_store);
|
|
||||||
|
|
||||||
Type::Alias(
|
|
||||||
effect_symbol,
|
effect_symbol,
|
||||||
vec![("a".into(), Type::Variable(var_a))],
|
effect_tag_name.clone(),
|
||||||
Box::new(actual),
|
"a",
|
||||||
)
|
var_a,
|
||||||
};
|
Type::Variable(var_a),
|
||||||
|
var_store,
|
||||||
|
&mut introduced_variables,
|
||||||
|
);
|
||||||
|
|
||||||
let effect_b = {
|
let effect_b = build_effect_alias(
|
||||||
let actual = build_effect_actual(effect_tag_name, Type::Variable(var_b), var_store);
|
|
||||||
|
|
||||||
Type::Alias(
|
|
||||||
effect_symbol,
|
effect_symbol,
|
||||||
vec![("b".into(), Type::Variable(var_b))],
|
effect_tag_name,
|
||||||
Box::new(actual),
|
"b",
|
||||||
)
|
var_b,
|
||||||
};
|
Type::Variable(var_b),
|
||||||
|
var_store,
|
||||||
|
&mut introduced_variables,
|
||||||
|
);
|
||||||
|
|
||||||
let closure_var = var_store.fresh();
|
let closure_var = var_store.fresh();
|
||||||
introduced_variables.insert_wildcard(closure_var);
|
introduced_variables.insert_wildcard(closure_var);
|
||||||
|
@ -526,8 +521,6 @@ fn build_effect_after(
|
||||||
loc_body: Box::new(Located::at_zero(to_effect_call)),
|
loc_body: Box::new(Located::at_zero(to_effect_call)),
|
||||||
};
|
};
|
||||||
|
|
||||||
use roc_can::annotation::IntroducedVariables;
|
|
||||||
|
|
||||||
let mut introduced_variables = IntroducedVariables::default();
|
let mut introduced_variables = IntroducedVariables::default();
|
||||||
|
|
||||||
let signature = {
|
let signature = {
|
||||||
|
@ -537,26 +530,25 @@ fn build_effect_after(
|
||||||
introduced_variables.insert_named("a".into(), var_a);
|
introduced_variables.insert_named("a".into(), var_a);
|
||||||
introduced_variables.insert_named("b".into(), var_b);
|
introduced_variables.insert_named("b".into(), var_b);
|
||||||
|
|
||||||
let effect_a = {
|
let effect_a = build_effect_alias(
|
||||||
let actual =
|
|
||||||
build_effect_actual(effect_tag_name.clone(), Type::Variable(var_a), var_store);
|
|
||||||
|
|
||||||
Type::Alias(
|
|
||||||
effect_symbol,
|
effect_symbol,
|
||||||
vec![("a".into(), Type::Variable(var_a))],
|
effect_tag_name.clone(),
|
||||||
Box::new(actual),
|
"a",
|
||||||
)
|
var_a,
|
||||||
};
|
Type::Variable(var_a),
|
||||||
|
var_store,
|
||||||
|
&mut introduced_variables,
|
||||||
|
);
|
||||||
|
|
||||||
let effect_b = {
|
let effect_b = build_effect_alias(
|
||||||
let actual = build_effect_actual(effect_tag_name, Type::Variable(var_b), var_store);
|
|
||||||
|
|
||||||
Type::Alias(
|
|
||||||
effect_symbol,
|
effect_symbol,
|
||||||
vec![("b".into(), Type::Variable(var_b))],
|
effect_tag_name,
|
||||||
Box::new(actual),
|
"b",
|
||||||
)
|
var_b,
|
||||||
};
|
Type::Variable(var_b),
|
||||||
|
var_store,
|
||||||
|
&mut introduced_variables,
|
||||||
|
);
|
||||||
|
|
||||||
let closure_var = var_store.fresh();
|
let closure_var = var_store.fresh();
|
||||||
introduced_variables.insert_wildcard(closure_var);
|
introduced_variables.insert_wildcard(closure_var);
|
||||||
|
@ -763,6 +755,40 @@ pub fn build_host_exposed_def(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn build_effect_alias(
|
||||||
|
effect_symbol: Symbol,
|
||||||
|
effect_tag_name: TagName,
|
||||||
|
a_name: &str,
|
||||||
|
a_var: Variable,
|
||||||
|
a_type: Type,
|
||||||
|
var_store: &mut VarStore,
|
||||||
|
introduced_variables: &mut IntroducedVariables,
|
||||||
|
) -> Type {
|
||||||
|
let closure_var = var_store.fresh();
|
||||||
|
introduced_variables.insert_wildcard(closure_var);
|
||||||
|
|
||||||
|
let actual = {
|
||||||
|
Type::TagUnion(
|
||||||
|
vec![(
|
||||||
|
effect_tag_name,
|
||||||
|
vec![Type::Function(
|
||||||
|
vec![Type::EmptyRec],
|
||||||
|
Box::new(Type::Variable(closure_var)),
|
||||||
|
Box::new(a_type),
|
||||||
|
)],
|
||||||
|
)],
|
||||||
|
Box::new(Type::EmptyTagUnion),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
Type::Alias {
|
||||||
|
symbol: effect_symbol,
|
||||||
|
type_arguments: vec![(a_name.into(), Type::Variable(a_var))],
|
||||||
|
lambda_set_variables: vec![roc_types::types::LambdaSet(Type::Variable(closure_var))],
|
||||||
|
actual: Box::new(actual),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn build_effect_actual(
|
pub fn build_effect_actual(
|
||||||
effect_tag_name: TagName,
|
effect_tag_name: TagName,
|
||||||
a_type: Type,
|
a_type: Type,
|
||||||
|
|
|
@ -4060,7 +4060,7 @@ fn add_def_to_module<'a>(
|
||||||
pattern_vars.push(*var);
|
pattern_vars.push(*var);
|
||||||
}
|
}
|
||||||
|
|
||||||
let layout = match layout_cache.from_var(
|
let layout = match layout_cache.raw_from_var(
|
||||||
mono_env.arena,
|
mono_env.arena,
|
||||||
annotation,
|
annotation,
|
||||||
mono_env.subs,
|
mono_env.subs,
|
||||||
|
@ -4085,7 +4085,7 @@ fn add_def_to_module<'a>(
|
||||||
|
|
||||||
procs.insert_exposed(
|
procs.insert_exposed(
|
||||||
symbol,
|
symbol,
|
||||||
ProcLayout::from_layout(mono_env.arena, layout),
|
ProcLayout::from_raw(mono_env.arena, layout),
|
||||||
mono_env.arena,
|
mono_env.arena,
|
||||||
mono_env.subs,
|
mono_env.subs,
|
||||||
def.annotation,
|
def.annotation,
|
||||||
|
|
|
@ -13,6 +13,7 @@ pub enum LowLevel {
|
||||||
StrCountGraphemes,
|
StrCountGraphemes,
|
||||||
StrFromInt,
|
StrFromInt,
|
||||||
StrFromUtf8,
|
StrFromUtf8,
|
||||||
|
StrFromUtf8Range,
|
||||||
StrToUtf8,
|
StrToUtf8,
|
||||||
StrFromFloat,
|
StrFromFloat,
|
||||||
ListLen,
|
ListLen,
|
||||||
|
@ -86,6 +87,8 @@ pub enum LowLevel {
|
||||||
NumAtan,
|
NumAtan,
|
||||||
NumAcos,
|
NumAcos,
|
||||||
NumAsin,
|
NumAsin,
|
||||||
|
NumBytesToU16,
|
||||||
|
NumBytesToU32,
|
||||||
NumBitwiseAnd,
|
NumBitwiseAnd,
|
||||||
NumBitwiseXor,
|
NumBitwiseXor,
|
||||||
NumBitwiseOr,
|
NumBitwiseOr,
|
||||||
|
@ -110,21 +113,20 @@ impl LowLevel {
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
StrConcat | StrJoinWith | StrIsEmpty | StrStartsWith | StrStartsWithCodePt
|
StrConcat | StrJoinWith | StrIsEmpty | StrStartsWith | StrStartsWithCodePt
|
||||||
| StrEndsWith | StrSplit | StrCountGraphemes | StrFromInt | StrFromUtf8 | StrToUtf8
|
| StrEndsWith | StrSplit | StrCountGraphemes | StrFromInt | StrFromUtf8
|
||||||
| StrFromFloat | ListLen | ListGetUnsafe | ListSet | ListDrop | ListSingle
|
| StrFromUtf8Range | StrToUtf8 | StrFromFloat | ListLen | ListGetUnsafe | ListSet
|
||||||
| ListRepeat | ListReverse | ListConcat | ListContains | ListAppend | ListPrepend
|
| ListDrop | ListSingle | ListRepeat | ListReverse | ListConcat | ListContains
|
||||||
| ListJoin | ListRange | ListSwap | DictSize | DictEmpty | DictInsert | DictRemove
|
| ListAppend | ListPrepend | ListJoin | ListRange | ListSwap | DictSize | DictEmpty
|
||||||
| DictContains | DictGetUnsafe | DictKeys | DictValues | DictUnion
|
| DictInsert | DictRemove | DictContains | DictGetUnsafe | DictKeys | DictValues
|
||||||
| DictIntersection | DictDifference | SetFromList | NumAdd | NumAddWrap
|
| DictUnion | DictIntersection | DictDifference | SetFromList | NumAdd | NumAddWrap
|
||||||
| NumAddChecked | NumSub | NumSubWrap | NumSubChecked | NumMul | NumMulWrap
|
| NumAddChecked | NumSub | NumSubWrap | NumSubChecked | NumMul | NumMulWrap
|
||||||
| NumMulChecked | NumGt | NumGte | NumLt | NumLte | NumCompare | NumDivUnchecked
|
| NumMulChecked | NumGt | NumGte | NumLt | NumLte | NumCompare | NumDivUnchecked
|
||||||
| NumRemUnchecked | NumIsMultipleOf | NumAbs | NumNeg | NumSin | NumCos
|
| NumRemUnchecked | NumIsMultipleOf | NumAbs | NumNeg | NumSin | NumCos
|
||||||
| NumSqrtUnchecked | NumLogUnchecked | NumRound | NumToFloat | NumPow | NumCeiling
|
| NumSqrtUnchecked | NumLogUnchecked | NumRound | NumToFloat | NumPow | NumCeiling
|
||||||
| NumPowInt | NumFloor | NumIsFinite | NumAtan | NumAcos | NumAsin | NumBitwiseAnd
|
| NumPowInt | NumFloor | NumIsFinite | NumAtan | NumAcos | NumAsin | NumBitwiseAnd
|
||||||
| NumBitwiseXor | NumBitwiseOr | NumShiftLeftBy | NumShiftRightBy
|
| NumBitwiseXor | NumBitwiseOr | NumShiftLeftBy | NumShiftRightBy | NumBytesToU16
|
||||||
| NumShiftRightZfBy | NumIntCast | Eq | NotEq | And | Or | Not | Hash | ExpectTrue => {
|
| NumBytesToU32 | NumShiftRightZfBy | NumIntCast | Eq | NotEq | And | Or | Not
|
||||||
false
|
| Hash | ExpectTrue => false,
|
||||||
}
|
|
||||||
|
|
||||||
ListMap | ListMap2 | ListMap3 | ListMapWithIndex | ListKeepIf | ListWalk
|
ListMap | ListMap2 | ListMap3 | ListMapWithIndex | ListKeepIf | ListWalk
|
||||||
| ListWalkUntil | ListWalkBackwards | ListKeepOks | ListKeepErrs | ListSortWith
|
| ListWalkUntil | ListWalkBackwards | ListKeepOks | ListKeepErrs | ListSortWith
|
||||||
|
|
|
@ -891,7 +891,9 @@ define_builtins! {
|
||||||
100 NUM_AT_DECIMAL: "@Decimal"
|
100 NUM_AT_DECIMAL: "@Decimal"
|
||||||
101 NUM_DECIMAL: "Decimal" imported
|
101 NUM_DECIMAL: "Decimal" imported
|
||||||
102 NUM_DEC: "Dec" imported // the Num.Dectype alias
|
102 NUM_DEC: "Dec" imported // the Num.Dectype alias
|
||||||
|
103 NUM_BYTES_TO_U16: "bytesToU16"
|
||||||
|
104 NUM_BYTES_TO_U32: "bytesToU32"
|
||||||
|
105 NUM_CAST_TO_NAT: "#castToNat"
|
||||||
}
|
}
|
||||||
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
|
||||||
|
@ -921,6 +923,7 @@ define_builtins! {
|
||||||
15 STR_TO_UTF8: "toUtf8"
|
15 STR_TO_UTF8: "toUtf8"
|
||||||
16 STR_STARTS_WITH_CODE_PT: "startsWithCodePt"
|
16 STR_STARTS_WITH_CODE_PT: "startsWithCodePt"
|
||||||
17 STR_ALIAS_ANALYSIS_STATIC: "#aliasAnalysisStatic" // string with the static lifetime
|
17 STR_ALIAS_ANALYSIS_STATIC: "#aliasAnalysisStatic" // string with the static lifetime
|
||||||
|
18 STR_FROM_UTF8_RANGE: "fromUtf8Range"
|
||||||
}
|
}
|
||||||
4 LIST: "List" => {
|
4 LIST: "List" => {
|
||||||
0 LIST_LIST: "List" imported // the List.List type alias
|
0 LIST_LIST: "List" imported // the List.List type alias
|
||||||
|
|
|
@ -64,24 +64,10 @@ where
|
||||||
let mut hasher = DefaultHasher::new();
|
let mut hasher = DefaultHasher::new();
|
||||||
|
|
||||||
for layout in argument_layouts {
|
for layout in argument_layouts {
|
||||||
match layout {
|
|
||||||
Layout::Closure(_, lambda_set, _) => {
|
|
||||||
lambda_set.runtime_representation().hash(&mut hasher);
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
layout.hash(&mut hasher);
|
layout.hash(&mut hasher);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match return_layout {
|
|
||||||
Layout::Closure(_, lambda_set, _) => {
|
|
||||||
lambda_set.runtime_representation().hash(&mut hasher);
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
return_layout.hash(&mut hasher);
|
return_layout.hash(&mut hasher);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hasher.finish()
|
hasher.finish()
|
||||||
};
|
};
|
||||||
|
@ -303,30 +289,6 @@ fn stmt_spec<'a>(
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
Invoke {
|
|
||||||
symbol,
|
|
||||||
call,
|
|
||||||
layout: call_layout,
|
|
||||||
pass,
|
|
||||||
fail,
|
|
||||||
exception_id: _,
|
|
||||||
} => {
|
|
||||||
// a call that might throw an exception
|
|
||||||
|
|
||||||
let value_id = call_spec(builder, env, block, call_layout, call)?;
|
|
||||||
|
|
||||||
let pass_block = builder.add_block();
|
|
||||||
env.symbols.insert(*symbol, value_id);
|
|
||||||
let pass_value_id = stmt_spec(builder, env, pass_block, layout, pass)?;
|
|
||||||
env.symbols.remove(symbol);
|
|
||||||
let pass_block_expr = BlockExpr(pass_block, pass_value_id);
|
|
||||||
|
|
||||||
let fail_block = builder.add_block();
|
|
||||||
let fail_value_id = stmt_spec(builder, env, fail_block, layout, fail)?;
|
|
||||||
let fail_block_expr = BlockExpr(fail_block, fail_value_id);
|
|
||||||
|
|
||||||
builder.add_choice(block, &[pass_block_expr, fail_block_expr])
|
|
||||||
}
|
|
||||||
Switch {
|
Switch {
|
||||||
cond_symbol: _,
|
cond_symbol: _,
|
||||||
cond_layout: _,
|
cond_layout: _,
|
||||||
|
@ -434,7 +396,7 @@ fn stmt_spec<'a>(
|
||||||
let jpid = env.join_points[id];
|
let jpid = env.join_points[id];
|
||||||
builder.add_jump(block, jpid, argument, ret_type_id)
|
builder.add_jump(block, jpid, argument, ret_type_id)
|
||||||
}
|
}
|
||||||
Resume(_) | RuntimeError(_) => {
|
RuntimeError(_) => {
|
||||||
let type_id = layout_spec(builder, layout)?;
|
let type_id = layout_spec(builder, layout)?;
|
||||||
|
|
||||||
builder.add_terminate(block, type_id)
|
builder.add_terminate(block, type_id)
|
||||||
|
@ -1258,11 +1220,6 @@ fn layout_spec_help(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Closure(_, lambda_set, _) => layout_spec_help(
|
|
||||||
builder,
|
|
||||||
&lambda_set.runtime_representation(),
|
|
||||||
when_recursive,
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,10 +10,7 @@ pub const OWNED: bool = false;
|
||||||
pub const BORROWED: bool = true;
|
pub const BORROWED: bool = true;
|
||||||
|
|
||||||
fn should_borrow_layout(layout: &Layout) -> bool {
|
fn should_borrow_layout(layout: &Layout) -> bool {
|
||||||
match layout {
|
layout.is_refcounted()
|
||||||
Layout::Closure(_, _, _) => false,
|
|
||||||
_ => layout.is_refcounted(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn infer_borrow<'a>(
|
pub fn infer_borrow<'a>(
|
||||||
|
@ -49,7 +46,14 @@ pub fn infer_borrow<'a>(
|
||||||
// component (in top-sorted order, from primitives (std-lib) to main)
|
// component (in top-sorted order, from primitives (std-lib) to main)
|
||||||
|
|
||||||
let successor_map = &make_successor_mapping(arena, procs);
|
let successor_map = &make_successor_mapping(arena, procs);
|
||||||
let successors = move |key: &Symbol| successor_map[key].iter().copied();
|
let successors = move |key: &Symbol| {
|
||||||
|
let slice = match successor_map.get(key) {
|
||||||
|
None => &[] as &[_],
|
||||||
|
Some(s) => s.as_slice(),
|
||||||
|
};
|
||||||
|
|
||||||
|
slice.iter().copied()
|
||||||
|
};
|
||||||
|
|
||||||
let mut symbols = Vec::with_capacity_in(procs.len(), arena);
|
let mut symbols = Vec::with_capacity_in(procs.len(), arena);
|
||||||
symbols.extend(procs.keys().map(|x| x.0));
|
symbols.extend(procs.keys().map(|x| x.0));
|
||||||
|
@ -220,7 +224,10 @@ impl<'a> DeclarationToIndex<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unreachable!("symbol/layout combo must be in DeclarationToIndex")
|
unreachable!(
|
||||||
|
"symbol/layout {:?} {:?} combo must be in DeclarationToIndex",
|
||||||
|
needle_symbol, needle_layout
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -362,10 +369,6 @@ impl<'a> ParamMap<'a> {
|
||||||
Let(_, _, _, cont) => {
|
Let(_, _, _, cont) => {
|
||||||
stack.push(cont);
|
stack.push(cont);
|
||||||
}
|
}
|
||||||
Invoke { pass, fail, .. } => {
|
|
||||||
stack.push(pass);
|
|
||||||
stack.push(fail);
|
|
||||||
}
|
|
||||||
Switch {
|
Switch {
|
||||||
branches,
|
branches,
|
||||||
default_branch,
|
default_branch,
|
||||||
|
@ -376,7 +379,7 @@ impl<'a> ParamMap<'a> {
|
||||||
}
|
}
|
||||||
Refcounting(_, _) => unreachable!("these have not been introduced yet"),
|
Refcounting(_, _) => unreachable!("these have not been introduced yet"),
|
||||||
|
|
||||||
Ret(_) | Resume(_) | Jump(_, _) | RuntimeError(_) => {
|
Ret(_) | Jump(_, _) | RuntimeError(_) => {
|
||||||
// these are terminal, do nothing
|
// these are terminal, do nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -881,23 +884,6 @@ impl<'a> BorrowInfState<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Invoke {
|
|
||||||
symbol,
|
|
||||||
call,
|
|
||||||
layout: _,
|
|
||||||
pass,
|
|
||||||
fail,
|
|
||||||
exception_id: _,
|
|
||||||
} => {
|
|
||||||
self.collect_stmt(param_map, pass);
|
|
||||||
self.collect_stmt(param_map, fail);
|
|
||||||
|
|
||||||
self.collect_call(param_map, *symbol, call);
|
|
||||||
|
|
||||||
// TODO how to preserve the tail call of an invoke?
|
|
||||||
// self.preserve_tail_call(*x, v, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
Jump(j, ys) => {
|
Jump(j, ys) => {
|
||||||
let ps = param_map.get_join_point(*j);
|
let ps = param_map.get_join_point(*j);
|
||||||
|
|
||||||
|
@ -919,7 +905,7 @@ impl<'a> BorrowInfState<'a> {
|
||||||
}
|
}
|
||||||
Refcounting(_, _) => unreachable!("these have not been introduced yet"),
|
Refcounting(_, _) => unreachable!("these have not been introduced yet"),
|
||||||
|
|
||||||
Ret(_) | RuntimeError(_) | Resume(_) => {
|
Ret(_) | RuntimeError(_) => {
|
||||||
// these are terminal, do nothing
|
// these are terminal, do nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1012,9 +998,12 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
||||||
NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumLogUnchecked | NumRound
|
NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumLogUnchecked | NumRound
|
||||||
| NumCeiling | NumFloor | NumToFloat | Not | NumIsFinite | NumAtan | NumAcos | NumAsin
|
| NumCeiling | NumFloor | NumToFloat | Not | NumIsFinite | NumAtan | NumAcos | NumAsin
|
||||||
| NumIntCast => arena.alloc_slice_copy(&[irrelevant]),
|
| NumIntCast => arena.alloc_slice_copy(&[irrelevant]),
|
||||||
|
NumBytesToU16 => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
||||||
|
NumBytesToU32 => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
||||||
StrStartsWith | StrEndsWith => arena.alloc_slice_copy(&[owned, borrowed]),
|
StrStartsWith | StrEndsWith => arena.alloc_slice_copy(&[owned, borrowed]),
|
||||||
StrStartsWithCodePt => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
StrStartsWithCodePt => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
||||||
StrFromUtf8 => arena.alloc_slice_copy(&[owned]),
|
StrFromUtf8 => arena.alloc_slice_copy(&[owned]),
|
||||||
|
StrFromUtf8Range => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
||||||
StrToUtf8 => arena.alloc_slice_copy(&[owned]),
|
StrToUtf8 => arena.alloc_slice_copy(&[owned]),
|
||||||
StrFromInt | StrFromFloat => arena.alloc_slice_copy(&[irrelevant]),
|
StrFromInt | StrFromFloat => arena.alloc_slice_copy(&[irrelevant]),
|
||||||
Hash => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
Hash => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
||||||
|
@ -1096,13 +1085,6 @@ fn call_info_stmt<'a>(arena: &'a Bump, stmt: &Stmt<'a>, info: &mut CallInfo<'a>)
|
||||||
}
|
}
|
||||||
stack.push(cont);
|
stack.push(cont);
|
||||||
}
|
}
|
||||||
Invoke {
|
|
||||||
call, pass, fail, ..
|
|
||||||
} => {
|
|
||||||
call_info_call(call, info);
|
|
||||||
stack.push(pass);
|
|
||||||
stack.push(fail);
|
|
||||||
}
|
|
||||||
Switch {
|
Switch {
|
||||||
branches,
|
branches,
|
||||||
default_branch,
|
default_branch,
|
||||||
|
@ -1113,7 +1095,7 @@ fn call_info_stmt<'a>(arena: &'a Bump, stmt: &Stmt<'a>, info: &mut CallInfo<'a>)
|
||||||
}
|
}
|
||||||
Refcounting(_, _) => unreachable!("these have not been introduced yet"),
|
Refcounting(_, _) => unreachable!("these have not been introduced yet"),
|
||||||
|
|
||||||
Ret(_) | Resume(_) | Jump(_, _) | RuntimeError(_) => {
|
Ret(_) | Jump(_, _) | RuntimeError(_) => {
|
||||||
// these are terminal, do nothing
|
// these are terminal, do nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,18 +160,10 @@ impl<'a, 'i> Env<'a, 'i> {
|
||||||
fn try_insert_struct_info(&mut self, symbol: Symbol, layout: &Layout<'a>) {
|
fn try_insert_struct_info(&mut self, symbol: Symbol, layout: &Layout<'a>) {
|
||||||
use Layout::*;
|
use Layout::*;
|
||||||
|
|
||||||
match layout {
|
if let Struct(fields) = layout {
|
||||||
Struct(fields) => {
|
|
||||||
self.constructor_map.insert(symbol, 0);
|
self.constructor_map.insert(symbol, 0);
|
||||||
self.layout_map.insert(symbol, Layout::Struct(fields));
|
self.layout_map.insert(symbol, Layout::Struct(fields));
|
||||||
}
|
}
|
||||||
Closure(_, lambda_set, _) => {
|
|
||||||
self.constructor_map.insert(symbol, 0);
|
|
||||||
self.layout_map
|
|
||||||
.insert(symbol, lambda_set.runtime_representation());
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_struct_info(&mut self, symbol: Symbol, fields: &'a [Layout<'a>]) {
|
fn insert_struct_info(&mut self, symbol: Symbol, fields: &'a [Layout<'a>]) {
|
||||||
|
@ -244,10 +236,6 @@ fn layout_for_constructor<'a>(
|
||||||
debug_assert_eq!(constructor, 0);
|
debug_assert_eq!(constructor, 0);
|
||||||
HasFields(fields)
|
HasFields(fields)
|
||||||
}
|
}
|
||||||
Closure(_arguments, _lambda_set, _result) => {
|
|
||||||
// HasFields(fields)
|
|
||||||
ConstructorLayout::Unknown
|
|
||||||
}
|
|
||||||
other => unreachable!("weird layout {:?}", other),
|
other => unreachable!("weird layout {:?}", other),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -368,21 +356,11 @@ pub fn expand_and_cancel_proc<'a>(
|
||||||
let mut introduced = Vec::new_in(env.arena);
|
let mut introduced = Vec::new_in(env.arena);
|
||||||
|
|
||||||
for (layout, symbol) in arguments {
|
for (layout, symbol) in arguments {
|
||||||
match layout {
|
if let Layout::Struct(fields) = layout {
|
||||||
Layout::Struct(fields) => {
|
|
||||||
env.insert_struct_info(*symbol, fields);
|
env.insert_struct_info(*symbol, fields);
|
||||||
|
|
||||||
introduced.push(*symbol);
|
introduced.push(*symbol);
|
||||||
}
|
}
|
||||||
Layout::Closure(_arguments, _lambda_set, _result) => {
|
|
||||||
// TODO can this be improved again?
|
|
||||||
// let fpointer = Layout::FunctionPointer(arguments, result);
|
|
||||||
// let fields = env.arena.alloc([fpointer, *closure_layout.layout]);
|
|
||||||
// env.insert_struct_info(*symbol, fields);
|
|
||||||
// introduced.push(*symbol);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = expand_and_cancel(env, stmt);
|
let result = expand_and_cancel(env, stmt);
|
||||||
|
@ -585,29 +563,6 @@ fn expand_and_cancel<'a>(env: &mut Env<'a, '_>, stmt: &'a Stmt<'a>) -> &'a Stmt<
|
||||||
expand_and_cancel(env, cont)
|
expand_and_cancel(env, cont)
|
||||||
}
|
}
|
||||||
|
|
||||||
Invoke {
|
|
||||||
symbol,
|
|
||||||
call,
|
|
||||||
layout,
|
|
||||||
pass,
|
|
||||||
fail,
|
|
||||||
exception_id,
|
|
||||||
} => {
|
|
||||||
let pass = expand_and_cancel(env, pass);
|
|
||||||
let fail = expand_and_cancel(env, fail);
|
|
||||||
|
|
||||||
let stmt = Invoke {
|
|
||||||
symbol: *symbol,
|
|
||||||
call: call.clone(),
|
|
||||||
layout: *layout,
|
|
||||||
pass,
|
|
||||||
fail,
|
|
||||||
exception_id: *exception_id,
|
|
||||||
};
|
|
||||||
|
|
||||||
env.arena.alloc(stmt)
|
|
||||||
}
|
|
||||||
|
|
||||||
Join {
|
Join {
|
||||||
id,
|
id,
|
||||||
parameters,
|
parameters,
|
||||||
|
@ -627,7 +582,7 @@ fn expand_and_cancel<'a>(env: &mut Env<'a, '_>, stmt: &'a Stmt<'a>) -> &'a Stmt<
|
||||||
env.arena.alloc(stmt)
|
env.arena.alloc(stmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
Resume(_) | Ret(_) | Jump(_, _) | RuntimeError(_) => stmt,
|
Ret(_) | Jump(_, _) | RuntimeError(_) => stmt,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -32,26 +32,10 @@ pub fn occurring_variables(stmt: &Stmt<'_>) -> (MutSet<Symbol>, MutSet<Symbol>)
|
||||||
stack.push(cont);
|
stack.push(cont);
|
||||||
}
|
}
|
||||||
|
|
||||||
Invoke {
|
|
||||||
symbol,
|
|
||||||
call,
|
|
||||||
pass,
|
|
||||||
fail,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
occurring_variables_call(call, &mut result);
|
|
||||||
result.insert(*symbol);
|
|
||||||
bound_variables.insert(*symbol);
|
|
||||||
stack.push(pass);
|
|
||||||
stack.push(fail);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ret(symbol) => {
|
Ret(symbol) => {
|
||||||
result.insert(*symbol);
|
result.insert(*symbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
Resume(_) => {}
|
|
||||||
|
|
||||||
Refcounting(modify, cont) => {
|
Refcounting(modify, cont) => {
|
||||||
let symbol = modify.get_symbol();
|
let symbol = modify.get_symbol();
|
||||||
result.insert(symbol);
|
result.insert(symbol);
|
||||||
|
@ -238,11 +222,6 @@ fn consume_expr(m: &VarMap, e: &Expr<'_>) -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn consume_call(_: &VarMap, _: &crate::ir::Call<'_>) -> bool {
|
|
||||||
// variables bound by a call (or invoke) must always be consumed
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Context<'a> {
|
impl<'a> Context<'a> {
|
||||||
pub fn new(arena: &'a Bump, param_map: &'a ParamMap<'a>) -> Self {
|
pub fn new(arena: &'a Bump, param_map: &'a ParamMap<'a>) -> Self {
|
||||||
let mut vars = MutMap::default();
|
let mut vars = MutMap::default();
|
||||||
|
@ -271,10 +250,20 @@ impl<'a> Context<'a> {
|
||||||
fn get_var_info(&self, symbol: Symbol) -> VarInfo {
|
fn get_var_info(&self, symbol: Symbol) -> VarInfo {
|
||||||
match self.vars.get(&symbol) {
|
match self.vars.get(&symbol) {
|
||||||
Some(info) => *info,
|
Some(info) => *info,
|
||||||
None => panic!(
|
None => {
|
||||||
"Symbol {:?} {} has no info in {:?}",
|
eprintln!(
|
||||||
symbol, symbol, self.vars
|
"Symbol {:?} {} has no info in self.vars",
|
||||||
),
|
symbol,
|
||||||
|
symbol, // self.vars
|
||||||
|
);
|
||||||
|
|
||||||
|
VarInfo {
|
||||||
|
persistent: true,
|
||||||
|
reference: false,
|
||||||
|
consume: false,
|
||||||
|
reset: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -804,22 +793,6 @@ impl<'a> Context<'a> {
|
||||||
(new_b, live_vars)
|
(new_b, live_vars)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_var_info_invoke(
|
|
||||||
&self,
|
|
||||||
symbol: Symbol,
|
|
||||||
layout: &Layout<'a>,
|
|
||||||
call: &crate::ir::Call<'a>,
|
|
||||||
) -> Self {
|
|
||||||
// is this value a constant?
|
|
||||||
// TODO do function pointers also fall into this category?
|
|
||||||
let persistent = call.arguments.is_empty();
|
|
||||||
|
|
||||||
// must this value be consumed?
|
|
||||||
let consume = consume_call(&self.vars, call);
|
|
||||||
|
|
||||||
self.update_var_info_help(symbol, layout, persistent, consume, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_var_info(&self, symbol: Symbol, layout: &Layout<'a>, expr: &Expr<'a>) -> Self {
|
fn update_var_info(&self, symbol: Symbol, layout: &Layout<'a>, expr: &Expr<'a>) -> Self {
|
||||||
// is this value a constant?
|
// is this value a constant?
|
||||||
// TODO do function pointers also fall into this category?
|
// TODO do function pointers also fall into this category?
|
||||||
|
@ -955,82 +928,6 @@ impl<'a> Context<'a> {
|
||||||
ctx.visit_variable_declaration(*symbol, expr.clone(), *layout, b, &b_live_vars)
|
ctx.visit_variable_declaration(*symbol, expr.clone(), *layout, b, &b_live_vars)
|
||||||
}
|
}
|
||||||
|
|
||||||
Invoke {
|
|
||||||
symbol,
|
|
||||||
call,
|
|
||||||
pass,
|
|
||||||
fail,
|
|
||||||
layout,
|
|
||||||
exception_id,
|
|
||||||
} => {
|
|
||||||
// live vars of the whole expression
|
|
||||||
let invoke_live_vars = collect_stmt(stmt, &self.jp_live_vars, MutSet::default());
|
|
||||||
|
|
||||||
let fail = {
|
|
||||||
// TODO should we use ctor info like Lean?
|
|
||||||
let ctx = self.clone();
|
|
||||||
let (b, alt_live_vars) = ctx.visit_stmt(fail);
|
|
||||||
ctx.add_dec_for_alt(&invoke_live_vars, &alt_live_vars, b)
|
|
||||||
};
|
|
||||||
|
|
||||||
let pass = {
|
|
||||||
// TODO should we use ctor info like Lean?
|
|
||||||
let ctx = self.clone();
|
|
||||||
let ctx = ctx.update_var_info_invoke(*symbol, layout, call);
|
|
||||||
let (b, alt_live_vars) = ctx.visit_stmt(pass);
|
|
||||||
ctx.add_dec_for_alt(&invoke_live_vars, &alt_live_vars, b)
|
|
||||||
};
|
|
||||||
|
|
||||||
let invoke = Invoke {
|
|
||||||
symbol: *symbol,
|
|
||||||
call: call.clone(),
|
|
||||||
pass,
|
|
||||||
fail,
|
|
||||||
layout: *layout,
|
|
||||||
exception_id: *exception_id,
|
|
||||||
};
|
|
||||||
|
|
||||||
let cont = self.arena.alloc(invoke);
|
|
||||||
|
|
||||||
use crate::ir::CallType;
|
|
||||||
let stmt = match &call.call_type {
|
|
||||||
CallType::LowLevel { op, .. } => {
|
|
||||||
let ps = crate::borrow::lowlevel_borrow_signature(self.arena, *op);
|
|
||||||
self.add_dec_after_lowlevel(call.arguments, ps, cont, &invoke_live_vars)
|
|
||||||
}
|
|
||||||
|
|
||||||
CallType::HigherOrderLowLevel { .. } => {
|
|
||||||
todo!("copy the code for normal calls over to here");
|
|
||||||
}
|
|
||||||
|
|
||||||
CallType::Foreign { .. } => {
|
|
||||||
let ps = crate::borrow::foreign_borrow_signature(
|
|
||||||
self.arena,
|
|
||||||
call.arguments.len(),
|
|
||||||
);
|
|
||||||
|
|
||||||
self.add_dec_after_lowlevel(call.arguments, ps, cont, &invoke_live_vars)
|
|
||||||
}
|
|
||||||
|
|
||||||
CallType::ByName {
|
|
||||||
name,
|
|
||||||
ret_layout,
|
|
||||||
arg_layouts,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
let top_level = ProcLayout::new(self.arena, arg_layouts, *ret_layout);
|
|
||||||
|
|
||||||
// get the borrow signature
|
|
||||||
let ps = self
|
|
||||||
.param_map
|
|
||||||
.get_symbol(*name, top_level)
|
|
||||||
.expect("function is defined");
|
|
||||||
self.add_dec_after_application(call.arguments, ps, cont, &invoke_live_vars)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
(stmt, invoke_live_vars)
|
|
||||||
}
|
|
||||||
Join {
|
Join {
|
||||||
id: j,
|
id: j,
|
||||||
parameters: _,
|
parameters: _,
|
||||||
|
@ -1076,8 +973,6 @@ impl<'a> Context<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Resume(_) => (stmt, MutSet::default()),
|
|
||||||
|
|
||||||
Jump(j, xs) => {
|
Jump(j, xs) => {
|
||||||
let empty = MutSet::default();
|
let empty = MutSet::default();
|
||||||
let j_live_vars = match self.jp_live_vars.get(j) {
|
let j_live_vars = match self.jp_live_vars.get(j) {
|
||||||
|
@ -1166,25 +1061,7 @@ pub fn collect_stmt(
|
||||||
|
|
||||||
vars
|
vars
|
||||||
}
|
}
|
||||||
Invoke {
|
|
||||||
symbol,
|
|
||||||
call,
|
|
||||||
pass,
|
|
||||||
fail,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
vars = collect_stmt(pass, jp_live_vars, vars);
|
|
||||||
vars = collect_stmt(fail, jp_live_vars, vars);
|
|
||||||
|
|
||||||
vars.remove(symbol);
|
|
||||||
|
|
||||||
let mut result = MutSet::default();
|
|
||||||
occurring_variables_call(call, &mut result);
|
|
||||||
|
|
||||||
vars.extend(result);
|
|
||||||
|
|
||||||
vars
|
|
||||||
}
|
|
||||||
Ret(symbol) => {
|
Ret(symbol) => {
|
||||||
vars.insert(*symbol);
|
vars.insert(*symbol);
|
||||||
vars
|
vars
|
||||||
|
@ -1242,8 +1119,6 @@ pub fn collect_stmt(
|
||||||
vars
|
vars
|
||||||
}
|
}
|
||||||
|
|
||||||
Resume(_) => vars,
|
|
||||||
|
|
||||||
RuntimeError(_) => vars,
|
RuntimeError(_) => vars,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ use roc_module::symbol::{IdentIds, ModuleId, Symbol};
|
||||||
use roc_problem::can::RuntimeError;
|
use roc_problem::can::RuntimeError;
|
||||||
use roc_region::all::{Located, Region};
|
use roc_region::all::{Located, Region};
|
||||||
use roc_types::solved_types::SolvedType;
|
use roc_types::solved_types::SolvedType;
|
||||||
use roc_types::subs::{Content, FlatType, Subs, SubsSlice, Variable};
|
use roc_types::subs::{Content, FlatType, Subs, Variable, VariableSubsSlice};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder};
|
use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder};
|
||||||
|
|
||||||
|
@ -546,11 +546,11 @@ impl<'a> Procs<'a> {
|
||||||
// anonymous functions cannot reference themselves, therefore cannot be tail-recursive
|
// anonymous functions cannot reference themselves, therefore cannot be tail-recursive
|
||||||
let is_self_recursive = false;
|
let is_self_recursive = false;
|
||||||
|
|
||||||
let layout = layout_cache
|
let raw_layout = layout_cache
|
||||||
.from_var(env.arena, annotation, env.subs)
|
.raw_from_var(env.arena, annotation, env.subs)
|
||||||
.unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err));
|
.unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err));
|
||||||
|
|
||||||
let top_level = ProcLayout::from_layout(env.arena, layout);
|
let top_level = ProcLayout::from_raw(env.arena, raw_layout);
|
||||||
|
|
||||||
match patterns_to_when(env, layout_cache, loc_args, ret_var, loc_body) {
|
match patterns_to_when(env, layout_cache, loc_args, ret_var, loc_body) {
|
||||||
Ok((_, pattern_symbols, body)) => {
|
Ok((_, pattern_symbols, body)) => {
|
||||||
|
@ -617,7 +617,11 @@ impl<'a> Procs<'a> {
|
||||||
Ok((proc, layout)) => {
|
Ok((proc, layout)) => {
|
||||||
let top_level = ProcLayout::from_raw(env.arena, layout);
|
let top_level = ProcLayout::from_raw(env.arena, layout);
|
||||||
|
|
||||||
debug_assert_eq!(outside_layout, top_level);
|
debug_assert_eq!(
|
||||||
|
outside_layout, top_level,
|
||||||
|
"different raw layouts for {:?}",
|
||||||
|
proc.name
|
||||||
|
);
|
||||||
|
|
||||||
if self.module_thunks.contains(&proc.name) {
|
if self.module_thunks.contains(&proc.name) {
|
||||||
debug_assert!(top_level.arguments.is_empty());
|
debug_assert!(top_level.arguments.is_empty());
|
||||||
|
@ -690,10 +694,8 @@ impl<'a> Procs<'a> {
|
||||||
layout: ProcLayout<'a>,
|
layout: ProcLayout<'a>,
|
||||||
layout_cache: &mut LayoutCache<'a>,
|
layout_cache: &mut LayoutCache<'a>,
|
||||||
) {
|
) {
|
||||||
let tuple = (name, layout);
|
|
||||||
|
|
||||||
// If we've already specialized this one, no further work is needed.
|
// If we've already specialized this one, no further work is needed.
|
||||||
if self.specialized.contains_key(&tuple) {
|
if self.specialized.contains_key(&(name, layout)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -703,15 +705,12 @@ impl<'a> Procs<'a> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We're done with that tuple, so move layout back out to avoid cloning it.
|
|
||||||
let (name, layout) = tuple;
|
|
||||||
|
|
||||||
let pending = PendingSpecialization::from_var(env.arena, env.subs, fn_var);
|
|
||||||
|
|
||||||
// This should only be called when pending_specializations is Some.
|
// This should only be called when pending_specializations is Some.
|
||||||
// Otherwise, it's being called in the wrong pass!
|
// Otherwise, it's being called in the wrong pass!
|
||||||
match &mut self.pending_specializations {
|
match &mut self.pending_specializations {
|
||||||
Some(pending_specializations) => {
|
Some(pending_specializations) => {
|
||||||
|
let pending = PendingSpecialization::from_var(env.arena, env.subs, fn_var);
|
||||||
|
|
||||||
// register the pending specialization, so this gets code genned later
|
// register the pending specialization, so this gets code genned later
|
||||||
if self.module_thunks.contains(&name) {
|
if self.module_thunks.contains(&name) {
|
||||||
debug_assert!(layout.arguments.is_empty());
|
debug_assert!(layout.arguments.is_empty());
|
||||||
|
@ -732,7 +731,26 @@ impl<'a> Procs<'a> {
|
||||||
// (We had a bug around this before this system existed!)
|
// (We had a bug around this before this system existed!)
|
||||||
self.specialized.insert((symbol, layout), InProgress);
|
self.specialized.insert((symbol, layout), InProgress);
|
||||||
|
|
||||||
match specialize(env, self, symbol, layout_cache, pending, partial_proc) {
|
// See https://github.com/rtfeldman/roc/issues/1600
|
||||||
|
//
|
||||||
|
// The annotation variable is the generic/lifted/top-level annotation.
|
||||||
|
// It is connected to the variables of the function's body
|
||||||
|
//
|
||||||
|
// fn_var is the variable representing the type that we actually need for the
|
||||||
|
// function right here.
|
||||||
|
//
|
||||||
|
// For some reason, it matters that we unify with the original variable. Extracting
|
||||||
|
// that variable into a SolvedType and then introducing it again severs some
|
||||||
|
// connection that turns out to be important
|
||||||
|
match specialize_variable(
|
||||||
|
env,
|
||||||
|
self,
|
||||||
|
symbol,
|
||||||
|
layout_cache,
|
||||||
|
fn_var,
|
||||||
|
Default::default(),
|
||||||
|
partial_proc,
|
||||||
|
) {
|
||||||
Ok((proc, _ignore_layout)) => {
|
Ok((proc, _ignore_layout)) => {
|
||||||
// the `layout` is a function pointer, while `_ignore_layout` can be a
|
// the `layout` is a function pointer, while `_ignore_layout` can be a
|
||||||
// closure. We only specialize functions, storing this value with a closure
|
// closure. We only specialize functions, storing this value with a closure
|
||||||
|
@ -881,17 +899,6 @@ pub type Stores<'a> = &'a [(Symbol, Layout<'a>, Expr<'a>)];
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum Stmt<'a> {
|
pub enum Stmt<'a> {
|
||||||
Let(Symbol, Expr<'a>, Layout<'a>, &'a Stmt<'a>),
|
Let(Symbol, Expr<'a>, Layout<'a>, &'a Stmt<'a>),
|
||||||
Invoke {
|
|
||||||
symbol: Symbol,
|
|
||||||
call: Call<'a>,
|
|
||||||
layout: Layout<'a>,
|
|
||||||
pass: &'a Stmt<'a>,
|
|
||||||
fail: &'a Stmt<'a>,
|
|
||||||
exception_id: ExceptionId,
|
|
||||||
},
|
|
||||||
/// after cleanup, rethrow the exception object (stored in the exception id)
|
|
||||||
/// so it bubbles up
|
|
||||||
Resume(ExceptionId),
|
|
||||||
Switch {
|
Switch {
|
||||||
/// This *must* stand for an integer, because Switch potentially compiles to a jump table.
|
/// This *must* stand for an integer, because Switch potentially compiles to a jump table.
|
||||||
cond_symbol: Symbol,
|
cond_symbol: Symbol,
|
||||||
|
@ -1375,45 +1382,11 @@ impl<'a> Stmt<'a> {
|
||||||
.append(alloc.hardline())
|
.append(alloc.hardline())
|
||||||
.append(cont.to_doc(alloc)),
|
.append(cont.to_doc(alloc)),
|
||||||
|
|
||||||
Invoke {
|
|
||||||
symbol,
|
|
||||||
call,
|
|
||||||
pass,
|
|
||||||
fail: Stmt::Resume(_),
|
|
||||||
..
|
|
||||||
} => alloc
|
|
||||||
.text("let ")
|
|
||||||
.append(symbol_to_doc(alloc, *symbol))
|
|
||||||
.append(" = ")
|
|
||||||
.append(call.to_doc(alloc))
|
|
||||||
.append(";")
|
|
||||||
.append(alloc.hardline())
|
|
||||||
.append(pass.to_doc(alloc)),
|
|
||||||
|
|
||||||
Invoke {
|
|
||||||
symbol,
|
|
||||||
call,
|
|
||||||
pass,
|
|
||||||
fail,
|
|
||||||
..
|
|
||||||
} => alloc
|
|
||||||
.text("invoke ")
|
|
||||||
.append(symbol_to_doc(alloc, *symbol))
|
|
||||||
.append(" = ")
|
|
||||||
.append(call.to_doc(alloc))
|
|
||||||
.append(" catch")
|
|
||||||
.append(alloc.hardline())
|
|
||||||
.append(fail.to_doc(alloc).indent(4))
|
|
||||||
.append(alloc.hardline())
|
|
||||||
.append(pass.to_doc(alloc)),
|
|
||||||
|
|
||||||
Ret(symbol) => alloc
|
Ret(symbol) => alloc
|
||||||
.text("ret ")
|
.text("ret ")
|
||||||
.append(symbol_to_doc(alloc, *symbol))
|
.append(symbol_to_doc(alloc, *symbol))
|
||||||
.append(";"),
|
.append(";"),
|
||||||
|
|
||||||
Resume(_) => alloc.text("unreachable;"),
|
|
||||||
|
|
||||||
Switch {
|
Switch {
|
||||||
cond_symbol,
|
cond_symbol,
|
||||||
branches,
|
branches,
|
||||||
|
@ -2028,7 +2001,9 @@ fn specialize_external<'a>(
|
||||||
|
|
||||||
aliases.insert(*symbol, (name, top_level, layout));
|
aliases.insert(*symbol, (name, top_level, layout));
|
||||||
}
|
}
|
||||||
RawFunctionLayout::ZeroArgumentThunk(_) => unreachable!("so far"),
|
RawFunctionLayout::ZeroArgumentThunk(_) => {
|
||||||
|
unreachable!("so far");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2096,7 +2071,7 @@ fn specialize_external<'a>(
|
||||||
|
|
||||||
match closure_layout.layout_for_member(proc_name) {
|
match closure_layout.layout_for_member(proc_name) {
|
||||||
ClosureRepresentation::Union {
|
ClosureRepresentation::Union {
|
||||||
tag_layout: field_layouts,
|
alphabetic_order_fields: field_layouts,
|
||||||
union_layout,
|
union_layout,
|
||||||
tag_id,
|
tag_id,
|
||||||
..
|
..
|
||||||
|
@ -2104,7 +2079,23 @@ fn specialize_external<'a>(
|
||||||
debug_assert!(matches!(union_layout, UnionLayout::NonRecursive(_)));
|
debug_assert!(matches!(union_layout, UnionLayout::NonRecursive(_)));
|
||||||
debug_assert_eq!(field_layouts.len(), captured.len());
|
debug_assert_eq!(field_layouts.len(), captured.len());
|
||||||
|
|
||||||
for (index, (symbol, _variable)) in captured.iter().enumerate() {
|
// captured variables are in symbol-alphabetic order, but now we want
|
||||||
|
// them ordered by their alignment requirements
|
||||||
|
let mut combined = Vec::from_iter_in(
|
||||||
|
captured.iter().map(|(x, _)| x).zip(field_layouts.iter()),
|
||||||
|
env.arena,
|
||||||
|
);
|
||||||
|
|
||||||
|
let ptr_bytes = env.ptr_bytes;
|
||||||
|
|
||||||
|
combined.sort_by(|(_, layout1), (_, layout2)| {
|
||||||
|
let size1 = layout1.alignment_bytes(ptr_bytes);
|
||||||
|
let size2 = layout2.alignment_bytes(ptr_bytes);
|
||||||
|
|
||||||
|
size2.cmp(&size1)
|
||||||
|
});
|
||||||
|
|
||||||
|
for (index, (symbol, layout)) in combined.iter().enumerate() {
|
||||||
let expr = Expr::UnionAtIndex {
|
let expr = Expr::UnionAtIndex {
|
||||||
tag_id,
|
tag_id,
|
||||||
structure: Symbol::ARG_CLOSURE,
|
structure: Symbol::ARG_CLOSURE,
|
||||||
|
@ -2112,18 +2103,31 @@ fn specialize_external<'a>(
|
||||||
union_layout,
|
union_layout,
|
||||||
};
|
};
|
||||||
|
|
||||||
let layout = field_layouts[index];
|
|
||||||
|
|
||||||
specialized_body = Stmt::Let(
|
specialized_body = Stmt::Let(
|
||||||
*symbol,
|
**symbol,
|
||||||
expr,
|
expr,
|
||||||
layout,
|
**layout,
|
||||||
env.arena.alloc(specialized_body),
|
env.arena.alloc(specialized_body),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ClosureRepresentation::Other(layout) => match layout {
|
ClosureRepresentation::AlphabeticOrderStruct(field_layouts) => {
|
||||||
Layout::Struct(field_layouts) => {
|
// captured variables are in symbol-alphabetic order, but now we want
|
||||||
|
// them ordered by their alignment requirements
|
||||||
|
let mut combined = Vec::from_iter_in(
|
||||||
|
captured.iter().map(|(x, _)| x).zip(field_layouts.iter()),
|
||||||
|
env.arena,
|
||||||
|
);
|
||||||
|
|
||||||
|
let ptr_bytes = env.ptr_bytes;
|
||||||
|
|
||||||
|
combined.sort_by(|(_, layout1), (_, layout2)| {
|
||||||
|
let size1 = layout1.alignment_bytes(ptr_bytes);
|
||||||
|
let size2 = layout2.alignment_bytes(ptr_bytes);
|
||||||
|
|
||||||
|
size2.cmp(&size1)
|
||||||
|
});
|
||||||
|
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
captured.len(),
|
captured.len(),
|
||||||
field_layouts.len(),
|
field_layouts.len(),
|
||||||
|
@ -2133,19 +2137,17 @@ fn specialize_external<'a>(
|
||||||
&field_layouts
|
&field_layouts
|
||||||
);
|
);
|
||||||
|
|
||||||
for (index, (symbol, _variable)) in captured.iter().enumerate() {
|
for (index, (symbol, layout)) in combined.iter().enumerate() {
|
||||||
let expr = Expr::StructAtIndex {
|
let expr = Expr::StructAtIndex {
|
||||||
index: index as _,
|
index: index as _,
|
||||||
field_layouts,
|
field_layouts,
|
||||||
structure: Symbol::ARG_CLOSURE,
|
structure: Symbol::ARG_CLOSURE,
|
||||||
};
|
};
|
||||||
|
|
||||||
let layout = field_layouts[index];
|
|
||||||
|
|
||||||
specialized_body = Stmt::Let(
|
specialized_body = Stmt::Let(
|
||||||
*symbol,
|
**symbol,
|
||||||
expr,
|
expr,
|
||||||
layout,
|
**layout,
|
||||||
env.arena.alloc(specialized_body),
|
env.arena.alloc(specialized_body),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2158,6 +2160,8 @@ fn specialize_external<'a>(
|
||||||
// Symbol::ARG_CLOSURE,
|
// Symbol::ARG_CLOSURE,
|
||||||
// );
|
// );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ClosureRepresentation::Other(layout) => match layout {
|
||||||
Layout::Builtin(Builtin::Int1) => {
|
Layout::Builtin(Builtin::Int1) => {
|
||||||
// just ignore this value
|
// just ignore this value
|
||||||
// IDEA don't pass this value in the future
|
// IDEA don't pass this value in the future
|
||||||
|
@ -2458,28 +2462,72 @@ fn specialize_solved_type<'a>(
|
||||||
host_exposed_aliases: BumpMap<Symbol, SolvedType>,
|
host_exposed_aliases: BumpMap<Symbol, SolvedType>,
|
||||||
partial_proc: PartialProc<'a>,
|
partial_proc: PartialProc<'a>,
|
||||||
) -> Result<SpecializeSuccess<'a>, SpecializeFailure<'a>> {
|
) -> Result<SpecializeSuccess<'a>, SpecializeFailure<'a>> {
|
||||||
|
specialize_variable_help(
|
||||||
|
env,
|
||||||
|
procs,
|
||||||
|
proc_name,
|
||||||
|
layout_cache,
|
||||||
|
|env| introduce_solved_type_to_subs(env, &solved_type),
|
||||||
|
host_exposed_aliases,
|
||||||
|
partial_proc,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn specialize_variable<'a>(
|
||||||
|
env: &mut Env<'a, '_>,
|
||||||
|
procs: &mut Procs<'a>,
|
||||||
|
proc_name: Symbol,
|
||||||
|
layout_cache: &mut LayoutCache<'a>,
|
||||||
|
fn_var: Variable,
|
||||||
|
host_exposed_aliases: BumpMap<Symbol, SolvedType>,
|
||||||
|
partial_proc: PartialProc<'a>,
|
||||||
|
) -> Result<SpecializeSuccess<'a>, SpecializeFailure<'a>> {
|
||||||
|
specialize_variable_help(
|
||||||
|
env,
|
||||||
|
procs,
|
||||||
|
proc_name,
|
||||||
|
layout_cache,
|
||||||
|
|_| fn_var,
|
||||||
|
host_exposed_aliases,
|
||||||
|
partial_proc,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn specialize_variable_help<'a, F>(
|
||||||
|
env: &mut Env<'a, '_>,
|
||||||
|
procs: &mut Procs<'a>,
|
||||||
|
proc_name: Symbol,
|
||||||
|
layout_cache: &mut LayoutCache<'a>,
|
||||||
|
fn_var_thunk: F,
|
||||||
|
host_exposed_aliases: BumpMap<Symbol, SolvedType>,
|
||||||
|
partial_proc: PartialProc<'a>,
|
||||||
|
) -> Result<SpecializeSuccess<'a>, SpecializeFailure<'a>>
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Env<'a, '_>) -> Variable,
|
||||||
|
{
|
||||||
// add the specializations that other modules require of us
|
// add the specializations that other modules require of us
|
||||||
use roc_solve::solve::instantiate_rigids;
|
use roc_solve::solve::instantiate_rigids;
|
||||||
|
|
||||||
let snapshot = env.subs.snapshot();
|
let snapshot = env.subs.snapshot();
|
||||||
let cache_snapshot = layout_cache.snapshot();
|
let cache_snapshot = layout_cache.snapshot();
|
||||||
|
|
||||||
let fn_var = introduce_solved_type_to_subs(env, &solved_type);
|
// important: evaluate after the snapshot has been created!
|
||||||
|
let fn_var = fn_var_thunk(env);
|
||||||
|
|
||||||
// for debugging only
|
// for debugging only
|
||||||
let attempted_layout = layout_cache
|
let raw = layout_cache
|
||||||
.from_var(env.arena, fn_var, env.subs)
|
.raw_from_var(env.arena, fn_var, env.subs)
|
||||||
.unwrap_or_else(|err| panic!("TODO handle invalid function {:?}", err));
|
.unwrap_or_else(|err| panic!("TODO handle invalid function {:?}", err));
|
||||||
|
|
||||||
let raw = match attempted_layout {
|
let raw = if procs.module_thunks.contains(&proc_name) {
|
||||||
Layout::Closure(a, lambda_set, c) => {
|
match raw {
|
||||||
if procs.module_thunks.contains(&proc_name) {
|
RawFunctionLayout::Function(_, lambda_set, _) => {
|
||||||
RawFunctionLayout::ZeroArgumentThunk(lambda_set.runtime_representation())
|
RawFunctionLayout::ZeroArgumentThunk(lambda_set.runtime_representation())
|
||||||
|
}
|
||||||
|
_ => raw,
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
RawFunctionLayout::Function(a, lambda_set, c)
|
raw
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => RawFunctionLayout::ZeroArgumentThunk(attempted_layout),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// make sure rigid variables in the annotation are converted to flex variables
|
// make sure rigid variables in the annotation are converted to flex variables
|
||||||
|
@ -2506,12 +2554,12 @@ fn specialize_solved_type<'a>(
|
||||||
match specialized {
|
match specialized {
|
||||||
Ok(proc) => {
|
Ok(proc) => {
|
||||||
// when successful, the layout after unification should be the layout before unification
|
// when successful, the layout after unification should be the layout before unification
|
||||||
debug_assert_eq!(
|
// debug_assert_eq!(
|
||||||
attempted_layout,
|
// attempted_layout,
|
||||||
layout_cache
|
// layout_cache
|
||||||
.from_var(env.arena, fn_var, env.subs)
|
// .from_var(env.arena, fn_var, env.subs)
|
||||||
.unwrap_or_else(|err| panic!("TODO handle invalid function {:?}", err))
|
// .unwrap_or_else(|err| panic!("TODO handle invalid function {:?}", err))
|
||||||
);
|
// );
|
||||||
|
|
||||||
env.subs.rollback_to(snapshot);
|
env.subs.rollback_to(snapshot);
|
||||||
layout_cache.rollback_to(cache_snapshot);
|
layout_cache.rollback_to(cache_snapshot);
|
||||||
|
@ -2541,39 +2589,20 @@ impl<'a> ProcLayout<'a> {
|
||||||
let mut arguments = Vec::with_capacity_in(old_arguments.len(), arena);
|
let mut arguments = Vec::with_capacity_in(old_arguments.len(), arena);
|
||||||
|
|
||||||
for old in old_arguments {
|
for old in old_arguments {
|
||||||
match old {
|
let other = old;
|
||||||
Layout::Closure(_, lambda_set, _) => {
|
arguments.push(*other);
|
||||||
let repr = lambda_set.runtime_representation();
|
|
||||||
arguments.push(repr)
|
|
||||||
}
|
|
||||||
other => arguments.push(*other),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_result = match result {
|
let other = result;
|
||||||
Layout::Closure(_, lambda_set, _) => lambda_set.runtime_representation(),
|
let new_result = other;
|
||||||
other => other,
|
|
||||||
};
|
|
||||||
|
|
||||||
ProcLayout {
|
ProcLayout {
|
||||||
arguments: arguments.into_bump_slice(),
|
arguments: arguments.into_bump_slice(),
|
||||||
result: new_result,
|
result: new_result,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn from_layout(arena: &'a Bump, layout: Layout<'a>) -> Self {
|
|
||||||
match layout {
|
|
||||||
Layout::Closure(arguments, lambda_set, result) => {
|
|
||||||
let arguments = lambda_set.extend_argument_list(arena, arguments);
|
|
||||||
ProcLayout::new(arena, arguments, *result)
|
|
||||||
}
|
|
||||||
_ => ProcLayout {
|
|
||||||
arguments: &[],
|
|
||||||
result: layout,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_raw(arena: &'a Bump, raw: RawFunctionLayout<'a>) -> Self {
|
pub fn from_raw(arena: &'a Bump, raw: RawFunctionLayout<'a>) -> Self {
|
||||||
match raw {
|
match raw {
|
||||||
RawFunctionLayout::Function(arguments, lambda_set, result) => {
|
RawFunctionLayout::Function(arguments, lambda_set, result) => {
|
||||||
let arguments = lambda_set.extend_argument_list(arena, arguments);
|
let arguments = lambda_set.extend_argument_list(arena, arguments);
|
||||||
|
@ -2666,11 +2695,13 @@ macro_rules! match_on_closure_argument {
|
||||||
let arg_layouts = top_level.arguments;
|
let arg_layouts = top_level.arguments;
|
||||||
let ret_layout = top_level.result;
|
let ret_layout = top_level.result;
|
||||||
|
|
||||||
|
|
||||||
match closure_data_layout {
|
match closure_data_layout {
|
||||||
RawFunctionLayout::Function(_, lambda_set, _) => {
|
RawFunctionLayout::Function(_, lambda_set, _) => {
|
||||||
lowlevel_match_on_lambda_set(
|
lowlevel_match_on_lambda_set(
|
||||||
$env,
|
$env,
|
||||||
lambda_set,
|
lambda_set,
|
||||||
|
$op,
|
||||||
$closure_data_symbol,
|
$closure_data_symbol,
|
||||||
|top_level_function, closure_data, closure_env_layout, specialization_id| self::Call {
|
|top_level_function, closure_data, closure_env_layout, specialization_id| self::Call {
|
||||||
call_type: CallType::HigherOrderLowLevel {
|
call_type: CallType::HigherOrderLowLevel {
|
||||||
|
@ -2750,7 +2781,10 @@ pub fn with_hole<'a>(
|
||||||
hole,
|
hole,
|
||||||
),
|
),
|
||||||
|
|
||||||
Num(var, num) => match num_argument_to_int_or_float(env.subs, env.ptr_bytes, var, false) {
|
Num(var, num) => {
|
||||||
|
// first figure out what kind of number this is
|
||||||
|
|
||||||
|
match num_argument_to_int_or_float(env.subs, env.ptr_bytes, var, false) {
|
||||||
IntOrFloat::SignedIntType(precision) => Stmt::Let(
|
IntOrFloat::SignedIntType(precision) => Stmt::Let(
|
||||||
assigned,
|
assigned,
|
||||||
Expr::Literal(Literal::Int(num.into())),
|
Expr::Literal(Literal::Int(num.into())),
|
||||||
|
@ -2775,7 +2809,8 @@ pub fn with_hole<'a>(
|
||||||
Layout::Builtin(Builtin::Decimal),
|
Layout::Builtin(Builtin::Decimal),
|
||||||
hole,
|
hole,
|
||||||
),
|
),
|
||||||
},
|
}
|
||||||
|
}
|
||||||
LetNonRec(def, cont, _) => {
|
LetNonRec(def, cont, _) => {
|
||||||
if let roc_can::pattern::Pattern::Identifier(symbol) = &def.loc_pattern.value {
|
if let roc_can::pattern::Pattern::Identifier(symbol) = &def.loc_pattern.value {
|
||||||
if let Closure {
|
if let Closure {
|
||||||
|
@ -4054,10 +4089,27 @@ fn construct_closure_data<'a>(
|
||||||
match lambda_set.layout_for_member(name) {
|
match lambda_set.layout_for_member(name) {
|
||||||
ClosureRepresentation::Union {
|
ClosureRepresentation::Union {
|
||||||
tag_id,
|
tag_id,
|
||||||
tag_layout: _,
|
alphabetic_order_fields: field_layouts,
|
||||||
tag_name,
|
tag_name,
|
||||||
union_layout,
|
union_layout,
|
||||||
} => {
|
} => {
|
||||||
|
// captured variables are in symbol-alphabetic order, but now we want
|
||||||
|
// them ordered by their alignment requirements
|
||||||
|
let mut combined =
|
||||||
|
Vec::from_iter_in(symbols.iter().zip(field_layouts.iter()), env.arena);
|
||||||
|
|
||||||
|
let ptr_bytes = env.ptr_bytes;
|
||||||
|
|
||||||
|
combined.sort_by(|(_, layout1), (_, layout2)| {
|
||||||
|
let size1 = layout1.alignment_bytes(ptr_bytes);
|
||||||
|
let size2 = layout2.alignment_bytes(ptr_bytes);
|
||||||
|
|
||||||
|
size2.cmp(&size1)
|
||||||
|
});
|
||||||
|
|
||||||
|
let symbols =
|
||||||
|
Vec::from_iter_in(combined.iter().map(|(a, _)| **a), env.arena).into_bump_slice();
|
||||||
|
|
||||||
let expr = Expr::Tag {
|
let expr = Expr::Tag {
|
||||||
tag_id,
|
tag_id,
|
||||||
tag_layout: union_layout,
|
tag_layout: union_layout,
|
||||||
|
@ -4072,9 +4124,33 @@ fn construct_closure_data<'a>(
|
||||||
env.arena.alloc(hole),
|
env.arena.alloc(hole),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
ClosureRepresentation::Other(Layout::Struct(field_layouts)) => {
|
ClosureRepresentation::AlphabeticOrderStruct(field_layouts) => {
|
||||||
debug_assert_eq!(field_layouts.len(), symbols.len());
|
debug_assert_eq!(field_layouts.len(), symbols.len());
|
||||||
|
|
||||||
|
// captured variables are in symbol-alphabetic order, but now we want
|
||||||
|
// them ordered by their alignment requirements
|
||||||
|
let mut combined =
|
||||||
|
Vec::from_iter_in(symbols.iter().zip(field_layouts.iter()), env.arena);
|
||||||
|
|
||||||
|
let ptr_bytes = env.ptr_bytes;
|
||||||
|
|
||||||
|
combined.sort_by(|(_, layout1), (_, layout2)| {
|
||||||
|
let size1 = layout1.alignment_bytes(ptr_bytes);
|
||||||
|
let size2 = layout2.alignment_bytes(ptr_bytes);
|
||||||
|
|
||||||
|
size2.cmp(&size1)
|
||||||
|
});
|
||||||
|
|
||||||
|
let symbols =
|
||||||
|
Vec::from_iter_in(combined.iter().map(|(a, _)| **a), env.arena).into_bump_slice();
|
||||||
|
let field_layouts =
|
||||||
|
Vec::from_iter_in(combined.iter().map(|(_, b)| **b), env.arena).into_bump_slice();
|
||||||
|
|
||||||
|
debug_assert_eq!(
|
||||||
|
Layout::Struct(field_layouts),
|
||||||
|
lambda_set.runtime_representation()
|
||||||
|
);
|
||||||
|
|
||||||
let expr = Expr::Struct(symbols);
|
let expr = Expr::Struct(symbols);
|
||||||
|
|
||||||
Stmt::Let(assigned, expr, lambda_set.runtime_representation(), hole)
|
Stmt::Let(assigned, expr, lambda_set.runtime_representation(), hole)
|
||||||
|
@ -4346,7 +4422,7 @@ fn convert_tag_union<'a>(
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn tag_union_to_function<'a>(
|
fn tag_union_to_function<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
argument_variables: SubsSlice<Variable>,
|
argument_variables: VariableSubsSlice,
|
||||||
return_variable: Variable,
|
return_variable: Variable,
|
||||||
tag_name: TagName,
|
tag_name: TagName,
|
||||||
proc_symbol: Symbol,
|
proc_symbol: Symbol,
|
||||||
|
@ -4554,15 +4630,12 @@ pub fn from_can<'a>(
|
||||||
arguments,
|
arguments,
|
||||||
};
|
};
|
||||||
|
|
||||||
let exception_id = ExceptionId(env.unique_symbol());
|
let rest = Stmt::Let(
|
||||||
let rest = Stmt::Invoke {
|
env.unique_symbol(),
|
||||||
symbol: env.unique_symbol(),
|
Expr::Call(call),
|
||||||
call,
|
bool_layout,
|
||||||
layout: bool_layout,
|
env.arena.alloc(rest),
|
||||||
pass: env.arena.alloc(rest),
|
);
|
||||||
fail: env.arena.alloc(Stmt::Resume(exception_id)),
|
|
||||||
exception_id,
|
|
||||||
};
|
|
||||||
|
|
||||||
with_hole(
|
with_hole(
|
||||||
env,
|
env,
|
||||||
|
@ -4674,8 +4747,15 @@ pub fn from_can<'a>(
|
||||||
CapturedSymbols::None
|
CapturedSymbols::None
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
debug_assert!(captured_symbols.is_empty());
|
// just allow this. see https://github.com/rtfeldman/roc/issues/1585
|
||||||
|
if captured_symbols.is_empty() {
|
||||||
CapturedSymbols::None
|
CapturedSymbols::None
|
||||||
|
} else {
|
||||||
|
let mut temp =
|
||||||
|
Vec::from_iter_in(captured_symbols, env.arena);
|
||||||
|
temp.sort();
|
||||||
|
CapturedSymbols::Captured(temp.into_bump_slice())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -5141,35 +5221,6 @@ fn substitute_in_stmt_help<'a>(
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Invoke {
|
|
||||||
symbol,
|
|
||||||
call,
|
|
||||||
layout,
|
|
||||||
pass,
|
|
||||||
fail,
|
|
||||||
exception_id,
|
|
||||||
} => {
|
|
||||||
let opt_call = substitute_in_call(arena, call, subs);
|
|
||||||
let opt_pass = substitute_in_stmt_help(arena, pass, subs);
|
|
||||||
let opt_fail = substitute_in_stmt_help(arena, fail, subs);
|
|
||||||
|
|
||||||
if opt_pass.is_some() || opt_fail.is_some() | opt_call.is_some() {
|
|
||||||
let pass = opt_pass.unwrap_or(pass);
|
|
||||||
let fail = opt_fail.unwrap_or_else(|| *fail);
|
|
||||||
let call = opt_call.unwrap_or_else(|| call.clone());
|
|
||||||
|
|
||||||
Some(arena.alloc(Invoke {
|
|
||||||
symbol: *symbol,
|
|
||||||
call,
|
|
||||||
layout: *layout,
|
|
||||||
pass,
|
|
||||||
fail,
|
|
||||||
exception_id: *exception_id,
|
|
||||||
}))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Join {
|
Join {
|
||||||
id,
|
id,
|
||||||
parameters,
|
parameters,
|
||||||
|
@ -5284,8 +5335,6 @@ fn substitute_in_stmt_help<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Resume(_) => None,
|
|
||||||
|
|
||||||
RuntimeError(_) => None,
|
RuntimeError(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5947,12 +5996,21 @@ fn reuse_function_symbol<'a>(
|
||||||
None => {
|
None => {
|
||||||
match arg_var {
|
match arg_var {
|
||||||
Some(arg_var) if env.is_imported_symbol(original) => {
|
Some(arg_var) if env.is_imported_symbol(original) => {
|
||||||
let layout = layout_cache
|
let raw = layout_cache
|
||||||
.from_var(env.arena, arg_var, env.subs)
|
.raw_from_var(env.arena, arg_var, env.subs)
|
||||||
.expect("creating layout does not fail");
|
.expect("creating layout does not fail");
|
||||||
|
|
||||||
if procs.imported_module_thunks.contains(&original) {
|
if procs.imported_module_thunks.contains(&original) {
|
||||||
let top_level = ProcLayout::new(env.arena, &[], layout);
|
let layout = match raw {
|
||||||
|
RawFunctionLayout::ZeroArgumentThunk(layout) => layout,
|
||||||
|
RawFunctionLayout::Function(_, lambda_set, _) => {
|
||||||
|
lambda_set.runtime_representation()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let raw = RawFunctionLayout::ZeroArgumentThunk(layout);
|
||||||
|
let top_level = ProcLayout::from_raw(env.arena, raw);
|
||||||
|
|
||||||
procs.insert_passed_by_name(
|
procs.insert_passed_by_name(
|
||||||
env,
|
env,
|
||||||
arg_var,
|
arg_var,
|
||||||
|
@ -5963,7 +6021,7 @@ fn reuse_function_symbol<'a>(
|
||||||
|
|
||||||
force_thunk(env, original, layout, symbol, env.arena.alloc(result))
|
force_thunk(env, original, layout, symbol, env.arena.alloc(result))
|
||||||
} else {
|
} else {
|
||||||
let top_level = ProcLayout::from_layout(env.arena, layout);
|
let top_level = ProcLayout::from_raw(env.arena, raw);
|
||||||
procs.insert_passed_by_name(
|
procs.insert_passed_by_name(
|
||||||
env,
|
env,
|
||||||
arg_var,
|
arg_var,
|
||||||
|
@ -6006,7 +6064,7 @@ fn reuse_function_symbol<'a>(
|
||||||
let captured = partial_proc.captured_symbols.clone();
|
let captured = partial_proc.captured_symbols.clone();
|
||||||
|
|
||||||
match res_layout {
|
match res_layout {
|
||||||
RawFunctionLayout::Function(argument_layouts, lambda_set, ret_layout) => {
|
RawFunctionLayout::Function(_, lambda_set, _) => {
|
||||||
// define the function pointer
|
// define the function pointer
|
||||||
let function_ptr_layout = ProcLayout::from_raw(env.arena, res_layout);
|
let function_ptr_layout = ProcLayout::from_raw(env.arena, res_layout);
|
||||||
|
|
||||||
|
@ -6040,7 +6098,11 @@ fn reuse_function_symbol<'a>(
|
||||||
)
|
)
|
||||||
} else if procs.module_thunks.contains(&original) {
|
} else if procs.module_thunks.contains(&original) {
|
||||||
// this is a 0-argument thunk
|
// this is a 0-argument thunk
|
||||||
let layout = Layout::Closure(argument_layouts, lambda_set, ret_layout);
|
|
||||||
|
// TODO suspicious
|
||||||
|
// let layout = Layout::Closure(argument_layouts, lambda_set, ret_layout);
|
||||||
|
// panic!("suspicious");
|
||||||
|
let layout = lambda_set.runtime_representation();
|
||||||
let top_level = ProcLayout::new(env.arena, &[], layout);
|
let top_level = ProcLayout::new(env.arena, &[], layout);
|
||||||
procs.insert_passed_by_name(
|
procs.insert_passed_by_name(
|
||||||
env,
|
env,
|
||||||
|
@ -6151,69 +6213,46 @@ fn add_needed_external<'a>(
|
||||||
existing.insert(name, solved_type);
|
existing.insert(name, solved_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn can_throw_exception(call: &Call) -> bool {
|
|
||||||
match call.call_type {
|
|
||||||
CallType::ByName { name, .. } => matches!(
|
|
||||||
name,
|
|
||||||
Symbol::NUM_ADD
|
|
||||||
| Symbol::NUM_SUB
|
|
||||||
| Symbol::NUM_MUL
|
|
||||||
| Symbol::NUM_DIV_FLOAT
|
|
||||||
| Symbol::NUM_ABS
|
|
||||||
| Symbol::NUM_NEG
|
|
||||||
),
|
|
||||||
|
|
||||||
CallType::Foreign { .. } => {
|
|
||||||
// calling foreign functions is very unsafe
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
CallType::LowLevel { .. } => {
|
|
||||||
// lowlevel operations themselves don't throw
|
|
||||||
// TODO except for on allocation?
|
|
||||||
false
|
|
||||||
}
|
|
||||||
CallType::HigherOrderLowLevel { .. } => {
|
|
||||||
// TODO throwing is based on whether the HOF can throw
|
|
||||||
// or if there is (potentially) allocation in the lowlevel
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Symbol that links an Invoke with a Rethrow
|
|
||||||
/// we'll assign the exception object to this symbol
|
|
||||||
/// so we can later rethrow the exception
|
|
||||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
|
||||||
pub struct ExceptionId(Symbol);
|
|
||||||
|
|
||||||
impl ExceptionId {
|
|
||||||
pub fn into_inner(self) -> Symbol {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_call<'a>(
|
fn build_call<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
_env: &mut Env<'a, '_>,
|
||||||
call: Call<'a>,
|
call: Call<'a>,
|
||||||
assigned: Symbol,
|
assigned: Symbol,
|
||||||
return_layout: Layout<'a>,
|
return_layout: Layout<'a>,
|
||||||
hole: &'a Stmt<'a>,
|
hole: &'a Stmt<'a>,
|
||||||
) -> Stmt<'a> {
|
) -> Stmt<'a> {
|
||||||
if can_throw_exception(&call) {
|
|
||||||
let id = ExceptionId(env.unique_symbol());
|
|
||||||
let fail = env.arena.alloc(Stmt::Resume(id));
|
|
||||||
Stmt::Invoke {
|
|
||||||
symbol: assigned,
|
|
||||||
call,
|
|
||||||
layout: return_layout,
|
|
||||||
fail,
|
|
||||||
pass: hole,
|
|
||||||
exception_id: id,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Stmt::Let(assigned, Expr::Call(call), return_layout, hole)
|
Stmt::Let(assigned, Expr::Call(call), return_layout, hole)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// See https://github.com/rtfeldman/roc/issues/1549
|
||||||
|
///
|
||||||
|
/// What happened is that a function has a type error, but the arguments are not processed.
|
||||||
|
/// That means specializations were missing. Normally that is not a problem, but because
|
||||||
|
/// of our closure strategy, internal functions can "leak". That's what happened here.
|
||||||
|
///
|
||||||
|
/// The solution is to evaluate the arguments as normal, and only when calling the function give an error
|
||||||
|
fn evaluate_arguments_then_runtime_error<'a>(
|
||||||
|
env: &mut Env<'a, '_>,
|
||||||
|
procs: &mut Procs<'a>,
|
||||||
|
layout_cache: &mut LayoutCache<'a>,
|
||||||
|
msg: String,
|
||||||
|
loc_args: std::vec::Vec<(Variable, Located<roc_can::expr::Expr>)>,
|
||||||
|
) -> Stmt<'a> {
|
||||||
|
let arena = env.arena;
|
||||||
|
|
||||||
|
// eventually we will throw this runtime error
|
||||||
|
let result = Stmt::RuntimeError(env.arena.alloc(msg));
|
||||||
|
|
||||||
|
// but, we also still evaluate and specialize the arguments to give better error messages
|
||||||
|
let arg_symbols = Vec::from_iter_in(
|
||||||
|
loc_args
|
||||||
|
.iter()
|
||||||
|
.map(|(_, arg_expr)| possible_reuse_symbol(env, procs, &arg_expr.value)),
|
||||||
|
arena,
|
||||||
|
)
|
||||||
|
.into_bump_slice();
|
||||||
|
|
||||||
|
let iter = loc_args.into_iter().rev().zip(arg_symbols.iter().rev());
|
||||||
|
assign_to_symbols(env, procs, layout_cache, iter, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
@ -6234,14 +6273,16 @@ fn call_by_name<'a>(
|
||||||
"Hit an unresolved type variable {:?} when creating a layout for {:?} (var {:?})",
|
"Hit an unresolved type variable {:?} when creating a layout for {:?} (var {:?})",
|
||||||
var, proc_name, fn_var
|
var, proc_name, fn_var
|
||||||
);
|
);
|
||||||
Stmt::RuntimeError(env.arena.alloc(msg))
|
|
||||||
|
evaluate_arguments_then_runtime_error(env, procs, layout_cache, msg, loc_args)
|
||||||
}
|
}
|
||||||
Err(LayoutProblem::Erroneous) => {
|
Err(LayoutProblem::Erroneous) => {
|
||||||
let msg = format!(
|
let msg = format!(
|
||||||
"Hit an erroneous type when creating a layout for {:?}",
|
"Hit an erroneous type when creating a layout for {:?}",
|
||||||
proc_name
|
proc_name
|
||||||
);
|
);
|
||||||
Stmt::RuntimeError(env.arena.alloc(msg))
|
|
||||||
|
evaluate_arguments_then_runtime_error(env, procs, layout_cache, msg, loc_args)
|
||||||
}
|
}
|
||||||
Ok(RawFunctionLayout::Function(arg_layouts, lambda_set, ret_layout)) => {
|
Ok(RawFunctionLayout::Function(arg_layouts, lambda_set, ret_layout)) => {
|
||||||
if procs.module_thunks.contains(&proc_name) {
|
if procs.module_thunks.contains(&proc_name) {
|
||||||
|
@ -6646,8 +6687,12 @@ fn call_by_name_module_thunk<'a>(
|
||||||
|
|
||||||
match specialize(env, procs, proc_name, layout_cache, pending, partial_proc)
|
match specialize(env, procs, proc_name, layout_cache, pending, partial_proc)
|
||||||
{
|
{
|
||||||
Ok((proc, layout)) => {
|
Ok((proc, raw_layout)) => {
|
||||||
debug_assert!(layout.is_zero_argument_thunk());
|
debug_assert!(
|
||||||
|
raw_layout.is_zero_argument_thunk(),
|
||||||
|
"but actually {:?}",
|
||||||
|
raw_layout
|
||||||
|
);
|
||||||
|
|
||||||
let was_present =
|
let was_present =
|
||||||
procs.specialized.remove(&(proc_name, top_level_layout));
|
procs.specialized.remove(&(proc_name, top_level_layout));
|
||||||
|
@ -7503,7 +7548,8 @@ pub fn num_argument_to_int_or_float(
|
||||||
debug_assert!(args.len() == 1);
|
debug_assert!(args.len() == 1);
|
||||||
|
|
||||||
// Recurse on the second argument
|
// Recurse on the second argument
|
||||||
num_argument_to_int_or_float(subs, ptr_bytes, args[0].1, false)
|
let var = subs[args.variables().into_iter().next().unwrap()];
|
||||||
|
num_argument_to_int_or_float(subs, ptr_bytes, var, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
Content::Alias(Symbol::NUM_I128, _, _)
|
Content::Alias(Symbol::NUM_I128, _, _)
|
||||||
|
@ -7561,7 +7607,8 @@ pub fn num_argument_to_int_or_float(
|
||||||
debug_assert!(args.len() == 1);
|
debug_assert!(args.len() == 1);
|
||||||
|
|
||||||
// Recurse on the second argument
|
// Recurse on the second argument
|
||||||
num_argument_to_int_or_float(subs, ptr_bytes, args[0].1, true)
|
let var = subs[args.variables().into_iter().next().unwrap()];
|
||||||
|
num_argument_to_int_or_float(subs, ptr_bytes, var, true)
|
||||||
}
|
}
|
||||||
Content::Alias(Symbol::NUM_FLOAT, _, _) // We default FloatingPoint to F64
|
Content::Alias(Symbol::NUM_FLOAT, _, _) // We default FloatingPoint to F64
|
||||||
| Content::Alias(Symbol::NUM_F64, _, _)
|
| Content::Alias(Symbol::NUM_F64, _, _)
|
||||||
|
@ -7606,6 +7653,7 @@ pub fn num_argument_to_int_or_float(
|
||||||
fn lowlevel_match_on_lambda_set<'a, ToLowLevelCall>(
|
fn lowlevel_match_on_lambda_set<'a, ToLowLevelCall>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
lambda_set: LambdaSet<'a>,
|
lambda_set: LambdaSet<'a>,
|
||||||
|
op: LowLevel,
|
||||||
closure_data_symbol: Symbol,
|
closure_data_symbol: Symbol,
|
||||||
to_lowlevel_call: ToLowLevelCall,
|
to_lowlevel_call: ToLowLevelCall,
|
||||||
return_layout: Layout<'a>,
|
return_layout: Layout<'a>,
|
||||||
|
@ -7645,12 +7693,11 @@ where
|
||||||
env.arena.alloc(result),
|
env.arena.alloc(result),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Layout::Struct(_) => {
|
Layout::Struct(_) => match lambda_set.set.get(0) {
|
||||||
let function_symbol = lambda_set.set[0].0;
|
Some((function_symbol, _)) => {
|
||||||
|
|
||||||
let call_spec_id = env.next_call_specialization_id();
|
let call_spec_id = env.next_call_specialization_id();
|
||||||
let call = to_lowlevel_call(
|
let call = to_lowlevel_call(
|
||||||
function_symbol,
|
*function_symbol,
|
||||||
closure_data_symbol,
|
closure_data_symbol,
|
||||||
lambda_set.is_represented(),
|
lambda_set.is_represented(),
|
||||||
call_spec_id,
|
call_spec_id,
|
||||||
|
@ -7658,6 +7705,17 @@ where
|
||||||
|
|
||||||
build_call(env, call, assigned, return_layout, env.arena.alloc(hole))
|
build_call(env, call, assigned, return_layout, env.arena.alloc(hole))
|
||||||
}
|
}
|
||||||
|
None => {
|
||||||
|
eprintln!(
|
||||||
|
"a function passed to `{:?}` LowLevel call has an empty lambda set!
|
||||||
|
The most likely reason is that some symbol you use is not in scope.
|
||||||
|
",
|
||||||
|
op
|
||||||
|
);
|
||||||
|
|
||||||
|
hole.clone()
|
||||||
|
}
|
||||||
|
},
|
||||||
Layout::Builtin(Builtin::Int1) => {
|
Layout::Builtin(Builtin::Int1) => {
|
||||||
let closure_tag_id_symbol = closure_data_symbol;
|
let closure_tag_id_symbol = closure_data_symbol;
|
||||||
|
|
||||||
|
@ -7869,6 +7927,12 @@ fn union_lambda_set_to_switch<'a>(
|
||||||
assigned: Symbol,
|
assigned: Symbol,
|
||||||
hole: &'a Stmt<'a>,
|
hole: &'a Stmt<'a>,
|
||||||
) -> Stmt<'a> {
|
) -> Stmt<'a> {
|
||||||
|
// NOTE this can happen if there is a type error somewhere. Since the lambda set is empty,
|
||||||
|
// there is really nothing we can do here, so we just proceed with the hole itself and
|
||||||
|
// hope that the type error is communicated in a clear way elsewhere.
|
||||||
|
if lambda_set.is_empty() {
|
||||||
|
return hole.clone();
|
||||||
|
}
|
||||||
debug_assert!(!lambda_set.is_empty());
|
debug_assert!(!lambda_set.is_empty());
|
||||||
|
|
||||||
let join_point_id = JoinPointId(env.unique_symbol());
|
let join_point_id = JoinPointId(env.unique_symbol());
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -135,28 +135,6 @@ fn function_s<'a, 'i>(
|
||||||
|
|
||||||
arena.alloc(new_join)
|
arena.alloc(new_join)
|
||||||
}
|
}
|
||||||
Invoke {
|
|
||||||
symbol,
|
|
||||||
call,
|
|
||||||
layout,
|
|
||||||
pass,
|
|
||||||
fail,
|
|
||||||
exception_id,
|
|
||||||
} => {
|
|
||||||
let new_pass = function_s(env, w, c, pass);
|
|
||||||
let new_fail = function_s(env, w, c, fail);
|
|
||||||
|
|
||||||
let new_invoke = Invoke {
|
|
||||||
symbol: *symbol,
|
|
||||||
call: call.clone(),
|
|
||||||
layout: *layout,
|
|
||||||
pass: new_pass,
|
|
||||||
fail: new_fail,
|
|
||||||
exception_id: *exception_id,
|
|
||||||
};
|
|
||||||
|
|
||||||
arena.alloc(new_invoke)
|
|
||||||
}
|
|
||||||
Switch {
|
Switch {
|
||||||
cond_symbol,
|
cond_symbol,
|
||||||
cond_layout,
|
cond_layout,
|
||||||
|
@ -195,7 +173,7 @@ fn function_s<'a, 'i>(
|
||||||
arena.alloc(new_refcounting)
|
arena.alloc(new_refcounting)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Resume(_) | Ret(_) | Jump(_, _) | RuntimeError(_) => stmt,
|
Ret(_) | Jump(_, _) | RuntimeError(_) => stmt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -318,37 +296,6 @@ fn function_d_main<'a, 'i>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Invoke {
|
|
||||||
symbol,
|
|
||||||
call,
|
|
||||||
layout,
|
|
||||||
pass,
|
|
||||||
fail,
|
|
||||||
exception_id,
|
|
||||||
} => {
|
|
||||||
if has_live_var(&env.jp_live_vars, stmt, x) {
|
|
||||||
let new_pass = {
|
|
||||||
let temp = function_d_main(env, x, c, pass);
|
|
||||||
function_d_finalize(env, x, c, temp)
|
|
||||||
};
|
|
||||||
let new_fail = {
|
|
||||||
let temp = function_d_main(env, x, c, fail);
|
|
||||||
function_d_finalize(env, x, c, temp)
|
|
||||||
};
|
|
||||||
let new_switch = Invoke {
|
|
||||||
symbol: *symbol,
|
|
||||||
call: call.clone(),
|
|
||||||
layout: *layout,
|
|
||||||
pass: new_pass,
|
|
||||||
fail: new_fail,
|
|
||||||
exception_id: *exception_id,
|
|
||||||
};
|
|
||||||
|
|
||||||
(arena.alloc(new_switch), true)
|
|
||||||
} else {
|
|
||||||
(stmt, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Switch {
|
Switch {
|
||||||
cond_symbol,
|
cond_symbol,
|
||||||
cond_layout,
|
cond_layout,
|
||||||
|
@ -433,9 +380,7 @@ fn function_d_main<'a, 'i>(
|
||||||
|
|
||||||
(arena.alloc(new_join), found)
|
(arena.alloc(new_join), found)
|
||||||
}
|
}
|
||||||
Ret(_) | Resume(_) | Jump(_, _) | RuntimeError(_) => {
|
Ret(_) | Jump(_, _) | RuntimeError(_) => (stmt, has_live_var(&env.jp_live_vars, stmt, x)),
|
||||||
(stmt, has_live_var(&env.jp_live_vars, stmt, x))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -550,36 +495,13 @@ fn function_r<'a, 'i>(env: &mut Env<'a, 'i>, stmt: &'a Stmt<'a>) -> &'a Stmt<'a>
|
||||||
|
|
||||||
arena.alloc(Let(*symbol, expr.clone(), *layout, b))
|
arena.alloc(Let(*symbol, expr.clone(), *layout, b))
|
||||||
}
|
}
|
||||||
Invoke {
|
|
||||||
symbol,
|
|
||||||
call,
|
|
||||||
layout,
|
|
||||||
pass,
|
|
||||||
fail,
|
|
||||||
exception_id,
|
|
||||||
} => {
|
|
||||||
let branch_info = BranchInfo::None;
|
|
||||||
let new_pass = function_r_branch_body(env, &branch_info, pass);
|
|
||||||
let new_fail = function_r_branch_body(env, &branch_info, fail);
|
|
||||||
|
|
||||||
let invoke = Invoke {
|
|
||||||
symbol: *symbol,
|
|
||||||
call: call.clone(),
|
|
||||||
layout: *layout,
|
|
||||||
pass: new_pass,
|
|
||||||
fail: new_fail,
|
|
||||||
exception_id: *exception_id,
|
|
||||||
};
|
|
||||||
|
|
||||||
arena.alloc(invoke)
|
|
||||||
}
|
|
||||||
Refcounting(modify_rc, continuation) => {
|
Refcounting(modify_rc, continuation) => {
|
||||||
let b = function_r(env, continuation);
|
let b = function_r(env, continuation);
|
||||||
|
|
||||||
arena.alloc(Refcounting(*modify_rc, b))
|
arena.alloc(Refcounting(*modify_rc, b))
|
||||||
}
|
}
|
||||||
|
|
||||||
Resume(_) | Ret(_) | Jump(_, _) | RuntimeError(_) => {
|
Ret(_) | Jump(_, _) | RuntimeError(_) => {
|
||||||
// terminals
|
// terminals
|
||||||
stmt
|
stmt
|
||||||
}
|
}
|
||||||
|
@ -594,19 +516,6 @@ fn has_live_var<'a>(jp_live_vars: &JPLiveVarMap, stmt: &'a Stmt<'a>, needle: Sym
|
||||||
debug_assert_ne!(*s, needle);
|
debug_assert_ne!(*s, needle);
|
||||||
has_live_var_expr(e, needle) || has_live_var(jp_live_vars, c, needle)
|
has_live_var_expr(e, needle) || has_live_var(jp_live_vars, c, needle)
|
||||||
}
|
}
|
||||||
Invoke {
|
|
||||||
symbol,
|
|
||||||
call,
|
|
||||||
pass,
|
|
||||||
fail,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
debug_assert_ne!(*symbol, needle);
|
|
||||||
|
|
||||||
has_live_var_call(call, needle)
|
|
||||||
|| has_live_var(jp_live_vars, pass, needle)
|
|
||||||
|| has_live_var(jp_live_vars, fail, needle)
|
|
||||||
}
|
|
||||||
Switch { cond_symbol, .. } if *cond_symbol == needle => true,
|
Switch { cond_symbol, .. } if *cond_symbol == needle => true,
|
||||||
Switch {
|
Switch {
|
||||||
branches,
|
branches,
|
||||||
|
@ -647,7 +556,7 @@ fn has_live_var<'a>(jp_live_vars: &JPLiveVarMap, stmt: &'a Stmt<'a>, needle: Sym
|
||||||
Jump(id, arguments) => {
|
Jump(id, arguments) => {
|
||||||
arguments.iter().any(|s| *s == needle) || jp_live_vars[id].contains(&needle)
|
arguments.iter().any(|s| *s == needle) || jp_live_vars[id].contains(&needle)
|
||||||
}
|
}
|
||||||
Resume(_) | RuntimeError(_) => false,
|
RuntimeError(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -92,28 +92,6 @@ fn insert_jumps<'a>(
|
||||||
Some(arena.alloc(jump))
|
Some(arena.alloc(jump))
|
||||||
}
|
}
|
||||||
|
|
||||||
Invoke {
|
|
||||||
symbol,
|
|
||||||
call:
|
|
||||||
crate::ir::Call {
|
|
||||||
call_type: CallType::ByName { name: fsym, .. },
|
|
||||||
arguments,
|
|
||||||
..
|
|
||||||
},
|
|
||||||
fail,
|
|
||||||
pass: Stmt::Ret(rsym),
|
|
||||||
exception_id,
|
|
||||||
..
|
|
||||||
} if needle == *fsym && symbol == rsym => {
|
|
||||||
debug_assert_eq!(fail, &&Stmt::Resume(*exception_id));
|
|
||||||
|
|
||||||
// replace the call and return with a jump
|
|
||||||
|
|
||||||
let jump = Stmt::Jump(goal_id, arguments);
|
|
||||||
|
|
||||||
Some(arena.alloc(jump))
|
|
||||||
}
|
|
||||||
|
|
||||||
Let(symbol, expr, layout, cont) => {
|
Let(symbol, expr, layout, cont) => {
|
||||||
let opt_cont = insert_jumps(arena, cont, goal_id, needle);
|
let opt_cont = insert_jumps(arena, cont, goal_id, needle);
|
||||||
|
|
||||||
|
@ -126,36 +104,6 @@ fn insert_jumps<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Invoke {
|
|
||||||
symbol,
|
|
||||||
call,
|
|
||||||
fail,
|
|
||||||
pass,
|
|
||||||
layout,
|
|
||||||
exception_id,
|
|
||||||
} => {
|
|
||||||
let opt_pass = insert_jumps(arena, pass, goal_id, needle);
|
|
||||||
let opt_fail = insert_jumps(arena, fail, goal_id, needle);
|
|
||||||
|
|
||||||
if opt_pass.is_some() || opt_fail.is_some() {
|
|
||||||
let pass = opt_pass.unwrap_or(pass);
|
|
||||||
let fail = opt_fail.unwrap_or(fail);
|
|
||||||
|
|
||||||
let stmt = Invoke {
|
|
||||||
symbol: *symbol,
|
|
||||||
call: call.clone(),
|
|
||||||
layout: *layout,
|
|
||||||
pass,
|
|
||||||
fail,
|
|
||||||
exception_id: *exception_id,
|
|
||||||
};
|
|
||||||
|
|
||||||
Some(arena.alloc(stmt))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Join {
|
Join {
|
||||||
id,
|
id,
|
||||||
parameters,
|
parameters,
|
||||||
|
@ -241,7 +189,6 @@ fn insert_jumps<'a>(
|
||||||
None => None,
|
None => None,
|
||||||
},
|
},
|
||||||
|
|
||||||
Resume(_) => None,
|
|
||||||
Ret(_) => None,
|
Ret(_) => None,
|
||||||
Jump(_, _) => None,
|
Jump(_, _) => None,
|
||||||
RuntimeError(_) => None,
|
RuntimeError(_) => None,
|
||||||
|
|
|
@ -59,8 +59,16 @@ pub fn make_solved_types(
|
||||||
args.push((name.clone(), SolvedType::new(solved_subs, *var)));
|
args.push((name.clone(), SolvedType::new(solved_subs, *var)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut lambda_set_variables = Vec::with_capacity(alias.lambda_set_variables.len());
|
||||||
|
for set in alias.lambda_set_variables.iter() {
|
||||||
|
lambda_set_variables.push(roc_types::solved_types::SolvedLambdaSet(
|
||||||
|
SolvedType::from_type(solved_subs, &set.0),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let solved_type = SolvedType::from_type(solved_subs, &alias.typ);
|
let solved_type = SolvedType::from_type(solved_subs, &alias.typ);
|
||||||
let solved_alias = SolvedType::Alias(*symbol, args, Box::new(solved_type));
|
let solved_alias =
|
||||||
|
SolvedType::Alias(*symbol, args, lambda_set_variables, Box::new(solved_type));
|
||||||
|
|
||||||
solved_types.insert(*symbol, solved_alias);
|
solved_types.insert(*symbol, solved_alias);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
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::{default_hasher, MutMap};
|
use roc_collections::all::MutMap;
|
||||||
|
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};
|
||||||
use roc_types::solved_types::Solved;
|
use roc_types::solved_types::Solved;
|
||||||
use roc_types::subs::{
|
use roc_types::subs::{
|
||||||
Content, Descriptor, FlatType, Mark, OptVariable, Rank, RecordFields, Subs, SubsSlice, Variable,
|
AliasVariables, Content, Descriptor, FlatType, Mark, OptVariable, Rank, RecordFields, Subs,
|
||||||
|
SubsIndex, SubsSlice, UnionTags, Variable, VariableSubsSlice,
|
||||||
};
|
};
|
||||||
use roc_types::types::Type::{self, *};
|
use roc_types::types::Type::{self, *};
|
||||||
use roc_types::types::{Alias, Category, ErrorType, PatternCategory};
|
use roc_types::types::{gather_fields_unsorted_iter, Alias, Category, ErrorType, PatternCategory};
|
||||||
use roc_unify::unify::unify;
|
use roc_unify::unify::unify;
|
||||||
use roc_unify::unify::Unified::*;
|
use roc_unify::unify::Unified::*;
|
||||||
|
|
||||||
|
@ -643,12 +645,14 @@ fn type_to_variable(
|
||||||
match typ {
|
match typ {
|
||||||
Variable(var) => *var,
|
Variable(var) => *var,
|
||||||
Apply(symbol, args) => {
|
Apply(symbol, args) => {
|
||||||
let mut arg_vars = Vec::with_capacity(args.len());
|
let mut new_arg_vars = Vec::with_capacity(args.len());
|
||||||
|
|
||||||
for arg in args {
|
for arg in args {
|
||||||
arg_vars.push(type_to_variable(subs, rank, pools, cached, arg))
|
let var = type_to_variable(subs, rank, pools, cached, arg);
|
||||||
|
new_arg_vars.push(var);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let arg_vars = VariableSubsSlice::insert_into_subs(subs, new_arg_vars);
|
||||||
let flat_type = FlatType::Apply(*symbol, arg_vars);
|
let flat_type = FlatType::Apply(*symbol, arg_vars);
|
||||||
let content = Content::Structure(flat_type);
|
let content = Content::Structure(flat_type);
|
||||||
|
|
||||||
|
@ -666,11 +670,7 @@ fn type_to_variable(
|
||||||
new_arg_vars.push(var);
|
new_arg_vars.push(var);
|
||||||
}
|
}
|
||||||
|
|
||||||
let start = subs.variables.len() as u32;
|
let arg_vars = VariableSubsSlice::insert_into_subs(subs, new_arg_vars);
|
||||||
let length = arg_vars.len() as u16;
|
|
||||||
let arg_vars = SubsSlice::new(start, length);
|
|
||||||
|
|
||||||
subs.variables.extend(new_arg_vars);
|
|
||||||
|
|
||||||
let ret_var = type_to_variable(subs, rank, pools, cached, ret_type);
|
let ret_var = type_to_variable(subs, rank, pools, cached, ret_type);
|
||||||
let closure_var = type_to_variable(subs, rank, pools, cached, closure_type);
|
let closure_var = type_to_variable(subs, rank, pools, cached, closure_type);
|
||||||
|
@ -679,60 +679,36 @@ fn type_to_variable(
|
||||||
register(subs, rank, pools, content)
|
register(subs, rank, pools, content)
|
||||||
}
|
}
|
||||||
Record(fields, ext) => {
|
Record(fields, ext) => {
|
||||||
let mut field_vars = MutMap::with_capacity_and_hasher(fields.len(), default_hasher());
|
let mut field_vars = Vec::with_capacity(fields.len());
|
||||||
|
|
||||||
for (field, field_type) in fields {
|
for (field, field_type) in fields {
|
||||||
let field_var =
|
let field_var =
|
||||||
field_type.map(|typ| type_to_variable(subs, rank, pools, cached, typ));
|
field_type.map(|typ| type_to_variable(subs, rank, pools, cached, typ));
|
||||||
|
|
||||||
field_vars.insert(field.clone(), field_var);
|
field_vars.push((field.clone(), field_var));
|
||||||
}
|
}
|
||||||
|
|
||||||
let temp_ext_var = type_to_variable(subs, rank, pools, cached, ext);
|
let temp_ext_var = type_to_variable(subs, rank, pools, cached, ext);
|
||||||
let new_ext_var = match roc_types::pretty_print::chase_ext_record(
|
|
||||||
subs,
|
|
||||||
temp_ext_var,
|
|
||||||
&mut field_vars,
|
|
||||||
) {
|
|
||||||
Ok(()) => Variable::EMPTY_RECORD,
|
|
||||||
Err((new, _)) => new,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut all_fields: Vec<_> = field_vars.into_iter().collect();
|
let (it, new_ext_var) =
|
||||||
all_fields.sort_unstable_by(RecordFields::compare);
|
gather_fields_unsorted_iter(subs, RecordFields::empty(), temp_ext_var);
|
||||||
|
|
||||||
let record_fields = RecordFields::insert_into_subs(subs, all_fields);
|
let it = it
|
||||||
|
.into_iter()
|
||||||
|
.map(|(field, field_type)| (field.clone(), field_type));
|
||||||
|
|
||||||
|
field_vars.extend(it);
|
||||||
|
field_vars.sort_unstable_by(RecordFields::compare);
|
||||||
|
|
||||||
|
let record_fields = RecordFields::insert_into_subs(subs, field_vars);
|
||||||
|
|
||||||
let content = Content::Structure(FlatType::Record(record_fields, new_ext_var));
|
let content = Content::Structure(FlatType::Record(record_fields, new_ext_var));
|
||||||
|
|
||||||
register(subs, rank, pools, content)
|
register(subs, rank, pools, content)
|
||||||
}
|
}
|
||||||
TagUnion(tags, ext) => {
|
TagUnion(tags, ext) => {
|
||||||
let mut tag_vars = MutMap::with_capacity_and_hasher(tags.len(), default_hasher());
|
let (union_tags, ext) = type_to_union_tags(subs, rank, pools, cached, tags, ext);
|
||||||
|
let content = Content::Structure(FlatType::TagUnion(union_tags, ext));
|
||||||
for (tag, tag_argument_types) in tags {
|
|
||||||
let mut tag_argument_vars = Vec::with_capacity(tag_argument_types.len());
|
|
||||||
|
|
||||||
for arg_type in tag_argument_types {
|
|
||||||
tag_argument_vars.push(type_to_variable(subs, rank, pools, cached, arg_type));
|
|
||||||
}
|
|
||||||
|
|
||||||
tag_vars.insert(tag.clone(), tag_argument_vars);
|
|
||||||
}
|
|
||||||
|
|
||||||
let temp_ext_var = type_to_variable(subs, rank, pools, cached, ext);
|
|
||||||
let mut ext_tag_vec = Vec::new();
|
|
||||||
let new_ext_var = match roc_types::pretty_print::chase_ext_tag_union(
|
|
||||||
subs,
|
|
||||||
temp_ext_var,
|
|
||||||
&mut ext_tag_vec,
|
|
||||||
) {
|
|
||||||
Ok(()) => Variable::EMPTY_TAG_UNION,
|
|
||||||
Err((new, _)) => new,
|
|
||||||
};
|
|
||||||
tag_vars.extend(ext_tag_vec.into_iter());
|
|
||||||
|
|
||||||
let content = Content::Structure(FlatType::TagUnion(tag_vars, new_ext_var));
|
|
||||||
|
|
||||||
register(subs, rank, pools, content)
|
register(subs, rank, pools, content)
|
||||||
}
|
}
|
||||||
|
@ -749,41 +725,19 @@ fn type_to_variable(
|
||||||
};
|
};
|
||||||
debug_assert!(ext_tag_vec.is_empty());
|
debug_assert!(ext_tag_vec.is_empty());
|
||||||
|
|
||||||
let content = Content::Structure(FlatType::FunctionOrTagUnion(
|
let start = subs.tag_names.len() as u32;
|
||||||
Box::new(tag_name.clone()),
|
subs.tag_names.push(tag_name.clone());
|
||||||
*symbol,
|
let slice = SubsIndex::new(start);
|
||||||
new_ext_var,
|
|
||||||
));
|
let content =
|
||||||
|
Content::Structure(FlatType::FunctionOrTagUnion(slice, *symbol, new_ext_var));
|
||||||
|
|
||||||
register(subs, rank, pools, content)
|
register(subs, rank, pools, content)
|
||||||
}
|
}
|
||||||
RecursiveTagUnion(rec_var, tags, ext) => {
|
RecursiveTagUnion(rec_var, tags, ext) => {
|
||||||
let mut tag_vars = MutMap::with_capacity_and_hasher(tags.len(), default_hasher());
|
let (union_tags, ext) = type_to_union_tags(subs, rank, pools, cached, tags, ext);
|
||||||
|
|
||||||
for (tag, tag_argument_types) in tags {
|
|
||||||
let mut tag_argument_vars = Vec::with_capacity(tag_argument_types.len());
|
|
||||||
|
|
||||||
for arg_type in tag_argument_types {
|
|
||||||
tag_argument_vars.push(type_to_variable(subs, rank, pools, cached, arg_type));
|
|
||||||
}
|
|
||||||
|
|
||||||
tag_vars.insert(tag.clone(), tag_argument_vars);
|
|
||||||
}
|
|
||||||
|
|
||||||
let temp_ext_var = type_to_variable(subs, rank, pools, cached, ext);
|
|
||||||
let mut ext_tag_vec = Vec::new();
|
|
||||||
let new_ext_var = match roc_types::pretty_print::chase_ext_tag_union(
|
|
||||||
subs,
|
|
||||||
temp_ext_var,
|
|
||||||
&mut ext_tag_vec,
|
|
||||||
) {
|
|
||||||
Ok(()) => Variable::EMPTY_TAG_UNION,
|
|
||||||
Err((new, _)) => new,
|
|
||||||
};
|
|
||||||
tag_vars.extend(ext_tag_vec.into_iter());
|
|
||||||
|
|
||||||
let content =
|
let content =
|
||||||
Content::Structure(FlatType::RecursiveTagUnion(*rec_var, tag_vars, new_ext_var));
|
Content::Structure(FlatType::RecursiveTagUnion(*rec_var, union_tags, ext));
|
||||||
|
|
||||||
let tag_union_var = register(subs, rank, pools, content);
|
let tag_union_var = register(subs, rank, pools, content);
|
||||||
|
|
||||||
|
@ -797,8 +751,35 @@ fn type_to_variable(
|
||||||
|
|
||||||
tag_union_var
|
tag_union_var
|
||||||
}
|
}
|
||||||
Alias(Symbol::BOOL_BOOL, _, _) => Variable::BOOL,
|
|
||||||
Alias(symbol, args, alias_type) => {
|
Type::Alias {
|
||||||
|
symbol,
|
||||||
|
type_arguments: args,
|
||||||
|
actual: alias_type,
|
||||||
|
lambda_set_variables,
|
||||||
|
} => {
|
||||||
|
// the rank of these variables is NONE (encoded as 0 in practice)
|
||||||
|
// using them for other ranks causes issues
|
||||||
|
if rank.is_none() {
|
||||||
|
// TODO replace by arithmetic?
|
||||||
|
match *symbol {
|
||||||
|
Symbol::NUM_I128 => return Variable::I128,
|
||||||
|
Symbol::NUM_I64 => return Variable::I64,
|
||||||
|
Symbol::NUM_I32 => return Variable::I32,
|
||||||
|
Symbol::NUM_I16 => return Variable::I16,
|
||||||
|
Symbol::NUM_I8 => return Variable::I8,
|
||||||
|
|
||||||
|
Symbol::NUM_U128 => return Variable::U128,
|
||||||
|
Symbol::NUM_U64 => return Variable::U64,
|
||||||
|
Symbol::NUM_U32 => return Variable::U32,
|
||||||
|
Symbol::NUM_U16 => return Variable::U16,
|
||||||
|
Symbol::NUM_U8 => return Variable::U8,
|
||||||
|
|
||||||
|
// Symbol::NUM_NAT => return Variable::NAT,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut arg_vars = Vec::with_capacity(args.len());
|
let mut arg_vars = Vec::with_capacity(args.len());
|
||||||
|
|
||||||
for (arg, arg_type) in args {
|
for (arg, arg_type) in args {
|
||||||
|
@ -807,6 +788,13 @@ fn type_to_variable(
|
||||||
arg_vars.push((arg.clone(), arg_var));
|
arg_vars.push((arg.clone(), arg_var));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let lambda_set_variables: Vec<_> = lambda_set_variables
|
||||||
|
.iter()
|
||||||
|
.map(|ls| type_to_variable(subs, rank, pools, cached, &ls.0))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let arg_vars = AliasVariables::insert_into_subs(subs, arg_vars, lambda_set_variables);
|
||||||
|
|
||||||
let alias_var = type_to_variable(subs, rank, pools, cached, alias_type);
|
let alias_var = type_to_variable(subs, rank, pools, cached, alias_type);
|
||||||
let content = Content::Alias(*symbol, arg_vars, alias_var);
|
let content = Content::Alias(*symbol, arg_vars, alias_var);
|
||||||
|
|
||||||
|
@ -814,9 +802,10 @@ fn type_to_variable(
|
||||||
}
|
}
|
||||||
HostExposedAlias {
|
HostExposedAlias {
|
||||||
name: symbol,
|
name: symbol,
|
||||||
arguments: args,
|
type_arguments: args,
|
||||||
actual: alias_type,
|
actual: alias_type,
|
||||||
actual_var,
|
actual_var,
|
||||||
|
lambda_set_variables,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let mut arg_vars = Vec::with_capacity(args.len());
|
let mut arg_vars = Vec::with_capacity(args.len());
|
||||||
|
@ -827,6 +816,13 @@ fn type_to_variable(
|
||||||
arg_vars.push((arg.clone(), arg_var));
|
arg_vars.push((arg.clone(), arg_var));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let lambda_set_variables: Vec<_> = lambda_set_variables
|
||||||
|
.iter()
|
||||||
|
.map(|ls| type_to_variable(subs, rank, pools, cached, &ls.0))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let arg_vars = AliasVariables::insert_into_subs(subs, arg_vars, lambda_set_variables);
|
||||||
|
|
||||||
let alias_var = type_to_variable(subs, rank, pools, cached, alias_type);
|
let alias_var = type_to_variable(subs, rank, pools, cached, alias_type);
|
||||||
|
|
||||||
// unify the actual_var with the result var
|
// unify the actual_var with the result var
|
||||||
|
@ -857,6 +853,59 @@ fn type_to_variable(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn type_to_union_tags(
|
||||||
|
subs: &mut Subs,
|
||||||
|
rank: Rank,
|
||||||
|
pools: &mut Pools,
|
||||||
|
cached: &mut MutMap<Symbol, Variable>,
|
||||||
|
tags: &[(TagName, Vec<Type>)],
|
||||||
|
ext: &Type,
|
||||||
|
) -> (UnionTags, Variable) {
|
||||||
|
let mut tag_vars = Vec::with_capacity(tags.len());
|
||||||
|
|
||||||
|
let mut tag_argument_vars = Vec::new();
|
||||||
|
for (tag, tag_argument_types) in tags {
|
||||||
|
for arg_type in tag_argument_types {
|
||||||
|
let new_var = type_to_variable(subs, rank, pools, cached, arg_type);
|
||||||
|
tag_argument_vars.push(new_var);
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_slice = VariableSubsSlice::insert_into_subs(subs, tag_argument_vars.drain(..));
|
||||||
|
|
||||||
|
tag_vars.push((tag.clone(), new_slice));
|
||||||
|
}
|
||||||
|
|
||||||
|
let temp_ext_var = type_to_variable(subs, rank, pools, cached, ext);
|
||||||
|
|
||||||
|
let ext = {
|
||||||
|
let (it, ext) =
|
||||||
|
roc_types::types::gather_tags_unsorted_iter(subs, UnionTags::default(), temp_ext_var);
|
||||||
|
|
||||||
|
tag_vars.extend(it.map(|(n, v)| (n.clone(), v)));
|
||||||
|
tag_vars.sort_unstable_by(|(a, _), (b, _)| a.cmp(b));
|
||||||
|
|
||||||
|
// deduplicate, keeping the right-most occurrence of a tag name
|
||||||
|
let mut i = 0;
|
||||||
|
|
||||||
|
while i < tag_vars.len() {
|
||||||
|
match (tag_vars.get(i), tag_vars.get(i + 1)) {
|
||||||
|
(Some((t1, _)), Some((t2, _))) => {
|
||||||
|
if t1 == t2 {
|
||||||
|
tag_vars.remove(i);
|
||||||
|
} else {
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ext
|
||||||
|
};
|
||||||
|
|
||||||
|
(UnionTags::insert_slices_into_subs(subs, tag_vars), ext)
|
||||||
|
}
|
||||||
|
|
||||||
fn check_for_infinite_type(
|
fn check_for_infinite_type(
|
||||||
subs: &mut Subs,
|
subs: &mut Subs,
|
||||||
problems: &mut Vec<TypeError>,
|
problems: &mut Vec<TypeError>,
|
||||||
|
@ -865,7 +914,7 @@ fn check_for_infinite_type(
|
||||||
) {
|
) {
|
||||||
let var = loc_var.value;
|
let var = loc_var.value;
|
||||||
|
|
||||||
while let Some((recursive, _chain)) = subs.occurs(var) {
|
while let Err((recursive, _chain)) = subs.occurs(var) {
|
||||||
let description = subs.get(recursive);
|
let description = subs.get(recursive);
|
||||||
let content = description.content;
|
let content = description.content;
|
||||||
|
|
||||||
|
@ -882,19 +931,24 @@ fn check_for_infinite_type(
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut new_tags = MutMap::default();
|
let mut new_tags = Vec::with_capacity(tags.len());
|
||||||
|
|
||||||
for (label, args) in &tags {
|
for (name_index, slice_index) in tags.iter_all() {
|
||||||
let new_args: Vec<_> = args
|
let slice = subs[slice_index];
|
||||||
.iter()
|
|
||||||
.map(|var| subs.explicit_substitute(recursive, rec_var, *var))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
new_tags.insert(label.clone(), new_args);
|
let mut new_vars = Vec::new();
|
||||||
|
for var_index in slice {
|
||||||
|
let var = subs[var_index];
|
||||||
|
new_vars.push(subs.explicit_substitute(recursive, rec_var, var));
|
||||||
|
}
|
||||||
|
|
||||||
|
new_tags.push((subs[name_index].clone(), new_vars));
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_ext_var = subs.explicit_substitute(recursive, rec_var, ext_var);
|
let new_ext_var = subs.explicit_substitute(recursive, rec_var, ext_var);
|
||||||
|
|
||||||
|
let new_tags = UnionTags::insert_into_subs(subs, new_tags);
|
||||||
|
|
||||||
let flat_type = FlatType::RecursiveTagUnion(rec_var, new_tags, new_ext_var);
|
let flat_type = FlatType::RecursiveTagUnion(rec_var, new_tags, new_ext_var);
|
||||||
|
|
||||||
subs.set_content(recursive, Content::Structure(flat_type));
|
subs.set_content(recursive, Content::Structure(flat_type));
|
||||||
|
@ -1049,9 +1103,9 @@ fn adjust_rank_content(
|
||||||
Apply(_, args) => {
|
Apply(_, args) => {
|
||||||
let mut rank = Rank::toplevel();
|
let mut rank = Rank::toplevel();
|
||||||
|
|
||||||
for var in args {
|
for var_index in args.into_iter() {
|
||||||
rank =
|
let var = subs[var_index];
|
||||||
rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, *var));
|
rank = rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, var));
|
||||||
}
|
}
|
||||||
|
|
||||||
rank
|
rank
|
||||||
|
@ -1104,9 +1158,13 @@ fn adjust_rank_content(
|
||||||
TagUnion(tags, ext_var) => {
|
TagUnion(tags, ext_var) => {
|
||||||
let mut rank = adjust_rank(subs, young_mark, visit_mark, group_rank, *ext_var);
|
let mut rank = adjust_rank(subs, young_mark, visit_mark, group_rank, *ext_var);
|
||||||
|
|
||||||
for var in tags.values().flatten() {
|
for (_, index) in tags.iter_all() {
|
||||||
rank =
|
let slice = subs[index];
|
||||||
rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, *var));
|
for var_index in slice {
|
||||||
|
let var = subs[var_index];
|
||||||
|
rank = rank
|
||||||
|
.max(adjust_rank(subs, young_mark, visit_mark, group_rank, var));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rank
|
rank
|
||||||
|
@ -1119,17 +1177,31 @@ fn adjust_rank_content(
|
||||||
RecursiveTagUnion(rec_var, tags, ext_var) => {
|
RecursiveTagUnion(rec_var, tags, ext_var) => {
|
||||||
let mut rank = adjust_rank(subs, young_mark, visit_mark, group_rank, *ext_var);
|
let mut rank = adjust_rank(subs, young_mark, visit_mark, group_rank, *ext_var);
|
||||||
|
|
||||||
for var in tags.values().flatten() {
|
for (_, index) in tags.iter_all() {
|
||||||
rank =
|
let slice = subs[index];
|
||||||
rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, *var));
|
for var_index in slice {
|
||||||
|
let var = subs[var_index];
|
||||||
|
rank = rank
|
||||||
|
.max(adjust_rank(subs, young_mark, visit_mark, group_rank, var));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// THEORY: the recursion var has the same rank as the tag union itself
|
// THEORY: the recursion var has the same rank as the tag union itself
|
||||||
// all types it uses are also in the tags already, so it cannot influence the
|
// all types it uses are also in the tags already, so it cannot influence the
|
||||||
// rank
|
// rank
|
||||||
|
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
let rec_var_rank =
|
||||||
|
adjust_rank(subs, young_mark, visit_mark, group_rank, *rec_var);
|
||||||
|
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
rank >= adjust_rank(subs, young_mark, visit_mark, group_rank, *rec_var)
|
rank >= rec_var_rank,
|
||||||
|
"rank was {:?} but recursion var {:?} has higher rank {:?}",
|
||||||
|
rank,
|
||||||
|
rec_var,
|
||||||
|
rec_var_rank
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
rank
|
rank
|
||||||
}
|
}
|
||||||
|
@ -1141,8 +1213,9 @@ fn adjust_rank_content(
|
||||||
Alias(_, args, real_var) => {
|
Alias(_, args, real_var) => {
|
||||||
let mut rank = Rank::toplevel();
|
let mut rank = Rank::toplevel();
|
||||||
|
|
||||||
for (_, var) in args {
|
for var_index in args.variables() {
|
||||||
rank = rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, *var));
|
let var = subs[var_index];
|
||||||
|
rank = rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, var));
|
||||||
}
|
}
|
||||||
|
|
||||||
// from elm-compiler: THEORY: anything in the real_var would be Rank::toplevel()
|
// from elm-compiler: THEORY: anything in the real_var would be Rank::toplevel()
|
||||||
|
@ -1225,7 +1298,8 @@ fn instantiate_rigids_help(
|
||||||
Structure(flat_type) => {
|
Structure(flat_type) => {
|
||||||
match flat_type {
|
match flat_type {
|
||||||
Apply(_, args) => {
|
Apply(_, args) => {
|
||||||
for var in args.into_iter() {
|
for var_index in args.into_iter() {
|
||||||
|
let var = subs[var_index];
|
||||||
instantiate_rigids_help(subs, max_rank, pools, var);
|
instantiate_rigids_help(subs, max_rank, pools, var);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1252,8 +1326,10 @@ fn instantiate_rigids_help(
|
||||||
}
|
}
|
||||||
|
|
||||||
TagUnion(tags, ext_var) => {
|
TagUnion(tags, ext_var) => {
|
||||||
for (_, vars) in tags {
|
for (_, index) in tags.iter_all() {
|
||||||
for var in vars.into_iter() {
|
let slice = subs[index];
|
||||||
|
for var_index in slice {
|
||||||
|
let var = subs[var_index];
|
||||||
instantiate_rigids_help(subs, max_rank, pools, var);
|
instantiate_rigids_help(subs, max_rank, pools, var);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1268,8 +1344,10 @@ fn instantiate_rigids_help(
|
||||||
RecursiveTagUnion(rec_var, tags, ext_var) => {
|
RecursiveTagUnion(rec_var, tags, ext_var) => {
|
||||||
instantiate_rigids_help(subs, max_rank, pools, rec_var);
|
instantiate_rigids_help(subs, max_rank, pools, rec_var);
|
||||||
|
|
||||||
for (_, vars) in tags {
|
for (_, index) in tags.iter_all() {
|
||||||
for var in vars.into_iter() {
|
let slice = subs[index];
|
||||||
|
for var_index in slice {
|
||||||
|
let var = subs[var_index];
|
||||||
instantiate_rigids_help(subs, max_rank, pools, var);
|
instantiate_rigids_help(subs, max_rank, pools, var);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1290,8 +1368,9 @@ fn instantiate_rigids_help(
|
||||||
subs.set(copy, make_descriptor(FlexVar(Some(name))));
|
subs.set(copy, make_descriptor(FlexVar(Some(name))));
|
||||||
}
|
}
|
||||||
|
|
||||||
Alias(_, args, real_type_var) => {
|
Alias(_symbol, args, real_type_var) => {
|
||||||
for (_, var) in args.into_iter() {
|
for var_index in args.variables().into_iter() {
|
||||||
|
let var = subs[var_index];
|
||||||
instantiate_rigids_help(subs, max_rank, pools, var);
|
instantiate_rigids_help(subs, max_rank, pools, var);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1360,12 +1439,17 @@ fn deep_copy_var_help(
|
||||||
Structure(flat_type) => {
|
Structure(flat_type) => {
|
||||||
let new_flat_type = match flat_type {
|
let new_flat_type = match flat_type {
|
||||||
Apply(symbol, args) => {
|
Apply(symbol, args) => {
|
||||||
let args = args
|
let mut new_arg_vars = Vec::with_capacity(args.len());
|
||||||
.into_iter()
|
|
||||||
.map(|var| deep_copy_var_help(subs, max_rank, pools, var))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Apply(symbol, args)
|
for index in args.into_iter() {
|
||||||
|
let var = subs[index];
|
||||||
|
let copy_var = deep_copy_var_help(subs, max_rank, pools, var);
|
||||||
|
new_arg_vars.push(copy_var);
|
||||||
|
}
|
||||||
|
|
||||||
|
let arg_vars = VariableSubsSlice::insert_into_subs(subs, new_arg_vars);
|
||||||
|
|
||||||
|
Apply(symbol, arg_vars)
|
||||||
}
|
}
|
||||||
|
|
||||||
Func(arg_vars, closure_var, ret_var) => {
|
Func(arg_vars, closure_var, ret_var) => {
|
||||||
|
@ -1380,11 +1464,7 @@ fn deep_copy_var_help(
|
||||||
new_arg_vars.push(copy_var);
|
new_arg_vars.push(copy_var);
|
||||||
}
|
}
|
||||||
|
|
||||||
let start = subs.variables.len() as u32;
|
let arg_vars = VariableSubsSlice::insert_into_subs(subs, new_arg_vars);
|
||||||
let length = arg_vars.len() as u16;
|
|
||||||
let arg_vars = SubsSlice::new(start, length);
|
|
||||||
|
|
||||||
subs.variables.extend(new_arg_vars);
|
|
||||||
|
|
||||||
Func(arg_vars, new_closure_var, new_ret_var)
|
Func(arg_vars, new_closure_var, new_ret_var)
|
||||||
}
|
}
|
||||||
|
@ -1433,17 +1513,35 @@ fn deep_copy_var_help(
|
||||||
}
|
}
|
||||||
|
|
||||||
TagUnion(tags, ext_var) => {
|
TagUnion(tags, ext_var) => {
|
||||||
let mut new_tags = MutMap::default();
|
let mut new_variable_slices = Vec::with_capacity(tags.len());
|
||||||
|
|
||||||
for (tag, vars) in tags {
|
let mut new_variables = Vec::new();
|
||||||
let new_vars: Vec<Variable> = vars
|
for index in tags.variables() {
|
||||||
.into_iter()
|
let slice = subs[index];
|
||||||
.map(|var| deep_copy_var_help(subs, max_rank, pools, var))
|
for var_index in slice {
|
||||||
.collect();
|
let var = subs[var_index];
|
||||||
new_tags.insert(tag, new_vars);
|
let new_var = deep_copy_var_help(subs, max_rank, pools, var);
|
||||||
|
new_variables.push(new_var);
|
||||||
}
|
}
|
||||||
|
|
||||||
TagUnion(new_tags, deep_copy_var_help(subs, max_rank, pools, ext_var))
|
let new_slice =
|
||||||
|
VariableSubsSlice::insert_into_subs(subs, new_variables.drain(..));
|
||||||
|
|
||||||
|
new_variable_slices.push(new_slice);
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_variables = {
|
||||||
|
let start = subs.variable_slices.len() as u32;
|
||||||
|
let length = new_variable_slices.len() as u16;
|
||||||
|
subs.variable_slices.extend(new_variable_slices);
|
||||||
|
|
||||||
|
SubsSlice::new(start, length)
|
||||||
|
};
|
||||||
|
|
||||||
|
let union_tags = UnionTags::from_slices(tags.tag_names(), new_variables);
|
||||||
|
|
||||||
|
let new_ext = deep_copy_var_help(subs, max_rank, pools, ext_var);
|
||||||
|
TagUnion(union_tags, new_ext)
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionOrTagUnion(tag_name, symbol, ext_var) => FunctionOrTagUnion(
|
FunctionOrTagUnion(tag_name, symbol, ext_var) => FunctionOrTagUnion(
|
||||||
|
@ -1453,23 +1551,37 @@ fn deep_copy_var_help(
|
||||||
),
|
),
|
||||||
|
|
||||||
RecursiveTagUnion(rec_var, tags, ext_var) => {
|
RecursiveTagUnion(rec_var, tags, ext_var) => {
|
||||||
let mut new_tags = MutMap::default();
|
let mut new_variable_slices = Vec::with_capacity(tags.len());
|
||||||
|
|
||||||
let new_rec_var = deep_copy_var_help(subs, max_rank, pools, rec_var);
|
let mut new_variables = Vec::new();
|
||||||
|
for index in tags.variables() {
|
||||||
for (tag, vars) in tags {
|
let slice = subs[index];
|
||||||
let new_vars: Vec<Variable> = vars
|
for var_index in slice {
|
||||||
.into_iter()
|
let var = subs[var_index];
|
||||||
.map(|var| deep_copy_var_help(subs, max_rank, pools, var))
|
let new_var = deep_copy_var_help(subs, max_rank, pools, var);
|
||||||
.collect();
|
new_variables.push(new_var);
|
||||||
new_tags.insert(tag, new_vars);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RecursiveTagUnion(
|
let new_slice =
|
||||||
new_rec_var,
|
VariableSubsSlice::insert_into_subs(subs, new_variables.drain(..));
|
||||||
new_tags,
|
|
||||||
deep_copy_var_help(subs, max_rank, pools, ext_var),
|
new_variable_slices.push(new_slice);
|
||||||
)
|
}
|
||||||
|
|
||||||
|
let new_variables = {
|
||||||
|
let start = subs.variable_slices.len() as u32;
|
||||||
|
let length = new_variable_slices.len() as u16;
|
||||||
|
subs.variable_slices.extend(new_variable_slices);
|
||||||
|
|
||||||
|
SubsSlice::new(start, length)
|
||||||
|
};
|
||||||
|
|
||||||
|
let union_tags = UnionTags::from_slices(tags.tag_names(), new_variables);
|
||||||
|
|
||||||
|
let new_ext = deep_copy_var_help(subs, max_rank, pools, ext_var);
|
||||||
|
let new_rec_var = deep_copy_var_help(subs, max_rank, pools, rec_var);
|
||||||
|
|
||||||
|
RecursiveTagUnion(new_rec_var, union_tags, new_ext)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1503,13 +1615,20 @@ fn deep_copy_var_help(
|
||||||
copy
|
copy
|
||||||
}
|
}
|
||||||
|
|
||||||
Alias(symbol, args, real_type_var) => {
|
Alias(symbol, mut args, real_type_var) => {
|
||||||
let new_args = args
|
let mut new_vars = Vec::with_capacity(args.variables().len());
|
||||||
.into_iter()
|
|
||||||
.map(|(name, var)| (name, deep_copy_var_help(subs, max_rank, pools, var)))
|
for var_index in args.variables() {
|
||||||
.collect();
|
let var = subs[var_index];
|
||||||
|
let new_var = deep_copy_var_help(subs, max_rank, pools, var);
|
||||||
|
|
||||||
|
new_vars.push(new_var);
|
||||||
|
}
|
||||||
|
|
||||||
|
args.replace_variables(subs, new_vars);
|
||||||
|
|
||||||
let new_real_type_var = deep_copy_var_help(subs, max_rank, pools, real_type_var);
|
let new_real_type_var = deep_copy_var_help(subs, max_rank, pools, real_type_var);
|
||||||
let new_content = Alias(symbol, new_args, new_real_type_var);
|
let new_content = Alias(symbol, args, new_real_type_var);
|
||||||
|
|
||||||
subs.set(copy, make_descriptor(new_content));
|
subs.set(copy, make_descriptor(new_content));
|
||||||
|
|
||||||
|
|
|
@ -3903,6 +3903,7 @@ mod solve_expr {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[ignore]
|
||||||
fn rbtree_full_remove_min() {
|
fn rbtree_full_remove_min() {
|
||||||
infer_eq_without_problem(
|
infer_eq_without_problem(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
|
|
@ -27,6 +27,25 @@ pub unsafe fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) {
|
||||||
libc::free(c_ptr)
|
libc::free(c_ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
|
||||||
|
use roc_gen_llvm::llvm::build::PanicTagId;
|
||||||
|
|
||||||
|
use libc::c_char;
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
use std::ffi::CStr;
|
||||||
|
|
||||||
|
match PanicTagId::try_from(tag_id) {
|
||||||
|
Ok(PanicTagId::NullTerminatedString) => {
|
||||||
|
let slice = CStr::from_ptr(c_ptr as *const c_char);
|
||||||
|
let string = slice.to_str().unwrap();
|
||||||
|
eprintln!("Roc hit a panic: {}", string);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
Err(_) => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn roc_list_construction() {
|
fn roc_list_construction() {
|
||||||
let list = RocList::from_slice(&[1i64; 23]);
|
let list = RocList::from_slice(&[1i64; 23]);
|
||||||
|
@ -276,6 +295,20 @@ fn list_prepend() {
|
||||||
RocList::from_slice(&[6, 4]),
|
RocList::from_slice(&[6, 4]),
|
||||||
RocList<i64>
|
RocList<i64>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
init : List Str
|
||||||
|
init =
|
||||||
|
["foo"]
|
||||||
|
|
||||||
|
List.prepend init "bar"
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
RocList::from_slice(&[RocStr::from_slice(b"bar"), RocStr::from_slice(b"foo"),]),
|
||||||
|
RocList<RocStr>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -481,8 +481,12 @@ mod gen_num {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn f64_round_old() {
|
fn f64_round() {
|
||||||
assert_evals_to!("Num.round 3.6", 4, i64);
|
assert_evals_to!("Num.round 3.6", 4, i64);
|
||||||
|
assert_evals_to!("Num.round 3.4", 3, i64);
|
||||||
|
assert_evals_to!("Num.round 2.5", 3, i64);
|
||||||
|
assert_evals_to!("Num.round -2.3", -2, i64);
|
||||||
|
assert_evals_to!("Num.round -2.5", -3, i64);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1620,4 +1624,158 @@ mod gen_num {
|
||||||
// overflow
|
// overflow
|
||||||
assert_evals_to!("Num.isMultipleOf -9223372036854775808 -1", true, bool);
|
assert_evals_to!("Num.isMultipleOf -9223372036854775808 -1", true, bool);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bytes_to_u16_clearly_out_of_bounds() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
bytes = Str.toUtf8 "hello"
|
||||||
|
when Num.bytesToU16 bytes 234 is
|
||||||
|
Ok v -> v
|
||||||
|
Err OutOfBounds -> 1
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
1,
|
||||||
|
u16
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bytes_to_u16_subtly_out_of_bounds() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
bytes = Str.toUtf8 "hello"
|
||||||
|
when Num.bytesToU16 bytes 4 is
|
||||||
|
Ok v -> v
|
||||||
|
Err OutOfBounds -> 1
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
1,
|
||||||
|
u16
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bytes_to_u32_clearly_out_of_bounds() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
bytes = Str.toUtf8 "hello"
|
||||||
|
when Num.bytesToU32 bytes 234 is
|
||||||
|
Ok v -> v
|
||||||
|
Err OutOfBounds -> 1
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
1,
|
||||||
|
u32
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bytes_to_u32_subtly_out_of_bounds() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
bytes = Str.toUtf8 "hello"
|
||||||
|
when Num.bytesToU32 bytes 2 is
|
||||||
|
Ok v -> v
|
||||||
|
Err OutOfBounds -> 1
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
1,
|
||||||
|
u32
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bytes_to_u16_max_u8s() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
when Num.bytesToU16 [255, 255] 0 is
|
||||||
|
Ok v -> v
|
||||||
|
Err OutOfBounds -> 1
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
65535,
|
||||||
|
u16
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bytes_to_u16_min_u8s() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
when Num.bytesToU16 [0, 0] 0 is
|
||||||
|
Ok v -> v
|
||||||
|
Err OutOfBounds -> 1
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
0,
|
||||||
|
u16
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bytes_to_u16_random_u8s() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
when Num.bytesToU16 [164, 215] 0 is
|
||||||
|
Ok v -> v
|
||||||
|
Err OutOfBounds -> 1
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
55_204,
|
||||||
|
u16
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bytes_to_u32_min_u8s() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
when Num.bytesToU32 [0, 0, 0, 0] 0 is
|
||||||
|
Ok v -> v
|
||||||
|
Err OutOfBounds -> 1
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
0,
|
||||||
|
u32
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bytes_to_u32_max_u8s() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
when Num.bytesToU32 [255, 255, 255, 255] 0 is
|
||||||
|
Ok v -> v
|
||||||
|
Err OutOfBounds -> 1
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
4_294_967_295,
|
||||||
|
u32
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bytes_to_u32_random_u8s() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
when Num.bytesToU32 [252, 124, 128, 121] 0 is
|
||||||
|
Ok v -> v
|
||||||
|
Err OutOfBounds -> 1
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
2_038_463_740,
|
||||||
|
u32
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1997,6 +1997,7 @@ fn case_or_pattern() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[ignore]
|
||||||
fn rosetree_basic() {
|
fn rosetree_basic() {
|
||||||
assert_non_opt_evals_to!(
|
assert_non_opt_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -2392,7 +2393,6 @@ fn call_invalid_layout() {
|
||||||
3,
|
3,
|
||||||
i64,
|
i64,
|
||||||
|x| x,
|
|x| x,
|
||||||
false,
|
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2529,3 +2529,234 @@ fn pattern_match_unit_tag() {
|
||||||
i64
|
i64
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mirror_llvm_alignment_padding() {
|
||||||
|
// see https://github.com/rtfeldman/roc/issues/1569
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [ main ] to "./platform"
|
||||||
|
|
||||||
|
main : Str
|
||||||
|
main =
|
||||||
|
p1 = {name : "test1", test: 1 == 1 }
|
||||||
|
|
||||||
|
List.map [p1, p1 ] (\{ test } -> if test then "pass" else "fail")
|
||||||
|
|> Str.joinWith "\n"
|
||||||
|
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
RocStr::from_slice(b"pass\npass"),
|
||||||
|
RocStr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lambda_set_bool() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [ main ] to "./platform"
|
||||||
|
|
||||||
|
p1 = (\u -> u == 97)
|
||||||
|
p2 = (\u -> u == 98)
|
||||||
|
|
||||||
|
main : I64
|
||||||
|
main =
|
||||||
|
oneOfResult = List.map [p1, p2] (\p -> p 42)
|
||||||
|
|
||||||
|
when oneOfResult is
|
||||||
|
_ -> 32
|
||||||
|
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
32,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lambda_set_byte() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [ main ] to "./platform"
|
||||||
|
|
||||||
|
p1 = (\u -> u == 97)
|
||||||
|
p2 = (\u -> u == 98)
|
||||||
|
p3 = (\u -> u == 99)
|
||||||
|
|
||||||
|
main : I64
|
||||||
|
main =
|
||||||
|
oneOfResult = List.map [p1, p2, p3] (\p -> p 42)
|
||||||
|
|
||||||
|
when oneOfResult is
|
||||||
|
_ -> 32
|
||||||
|
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
32,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lambda_set_struct_byte() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [ main ] to "./platform"
|
||||||
|
|
||||||
|
|
||||||
|
main : I64
|
||||||
|
main =
|
||||||
|
r : [ Red, Green, Blue ]
|
||||||
|
r = Red
|
||||||
|
|
||||||
|
p1 = (\u -> r == u)
|
||||||
|
oneOfResult = List.map [p1, p1] (\p -> p Green)
|
||||||
|
|
||||||
|
when oneOfResult is
|
||||||
|
_ -> 32
|
||||||
|
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
32,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lambda_set_enum_byte_byte() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [ main ] to "./platform"
|
||||||
|
|
||||||
|
|
||||||
|
main : I64
|
||||||
|
main =
|
||||||
|
r : [ Red, Green, Blue ]
|
||||||
|
r = Red
|
||||||
|
|
||||||
|
g : [ Red, Green, Blue ]
|
||||||
|
g = Green
|
||||||
|
|
||||||
|
p1 = (\u -> r == u)
|
||||||
|
p2 = (\u -> g == u)
|
||||||
|
oneOfResult = List.map [p1, p2] (\p -> p Green)
|
||||||
|
|
||||||
|
when oneOfResult is
|
||||||
|
_ -> 32
|
||||||
|
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
32,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_walk_until() {
|
||||||
|
// see https://github.com/rtfeldman/roc/issues/1576
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [ main ] to "./platform"
|
||||||
|
|
||||||
|
|
||||||
|
satisfyA : {} -> List {}
|
||||||
|
satisfyA = \_ -> []
|
||||||
|
|
||||||
|
oneOfResult =
|
||||||
|
List.walkUntil [ satisfyA ] (\_, _ -> Stop []) []
|
||||||
|
|
||||||
|
main =
|
||||||
|
when oneOfResult is
|
||||||
|
_ -> 32
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
32,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn int_literal_not_specialized_with_annotation() {
|
||||||
|
// see https://github.com/rtfeldman/roc/issues/1600
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [ main ] to "./platform"
|
||||||
|
|
||||||
|
main =
|
||||||
|
satisfy : (U8 -> Str) -> Str
|
||||||
|
satisfy = \_ -> "foo"
|
||||||
|
|
||||||
|
myEq : a, a -> Str
|
||||||
|
myEq = \_, _ -> "bar"
|
||||||
|
|
||||||
|
p1 : Num * -> Str
|
||||||
|
p1 = (\u -> myEq u 64)
|
||||||
|
|
||||||
|
when satisfy p1 is
|
||||||
|
_ -> 32
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
32,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn int_literal_not_specialized_no_annotation() {
|
||||||
|
// see https://github.com/rtfeldman/roc/issues/1600
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [ main ] to "./platform"
|
||||||
|
|
||||||
|
main =
|
||||||
|
satisfy : (U8 -> Str) -> Str
|
||||||
|
satisfy = \_ -> "foo"
|
||||||
|
|
||||||
|
myEq : a, a -> Str
|
||||||
|
myEq = \_, _ -> "bar"
|
||||||
|
|
||||||
|
p1 = (\u -> myEq u 64)
|
||||||
|
|
||||||
|
when satisfy p1 is
|
||||||
|
_ -> 32
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
32,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unresolved_tvar_when_capture_is_unused() {
|
||||||
|
// see https://github.com/rtfeldman/roc/issues/1585
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [ main ] to "./platform"
|
||||||
|
|
||||||
|
main : I64
|
||||||
|
main =
|
||||||
|
r : Bool
|
||||||
|
r = False
|
||||||
|
|
||||||
|
p1 = (\_ -> r == (1 == 1))
|
||||||
|
oneOfResult = List.map [p1] (\p -> p Green)
|
||||||
|
|
||||||
|
when oneOfResult is
|
||||||
|
_ -> 32
|
||||||
|
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
32,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -830,3 +830,118 @@ fn str_to_utf8() {
|
||||||
&[u8]
|
&[u8]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn str_from_utf8_range() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
bytes = Str.toUtf8 "hello"
|
||||||
|
when Str.fromUtf8Range bytes { count: 5, start: 0 } is
|
||||||
|
Ok utf8String -> utf8String
|
||||||
|
_ -> ""
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
RocStr::from("hello"),
|
||||||
|
RocStr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn str_from_utf8_range_slice() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
bytes = Str.toUtf8 "hello"
|
||||||
|
when Str.fromUtf8Range bytes { count: 4, start: 1 } is
|
||||||
|
Ok utf8String -> utf8String
|
||||||
|
_ -> ""
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
RocStr::from("ello"),
|
||||||
|
RocStr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn str_from_utf8_range_slice_not_end() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
bytes = Str.toUtf8 "hello"
|
||||||
|
when Str.fromUtf8Range bytes { count: 3, start: 1 } is
|
||||||
|
Ok utf8String -> utf8String
|
||||||
|
_ -> ""
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
RocStr::from("ell"),
|
||||||
|
RocStr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn str_from_utf8_range_order_does_not_matter() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
bytes = Str.toUtf8 "hello"
|
||||||
|
when Str.fromUtf8Range bytes { start: 1, count: 3 } is
|
||||||
|
Ok utf8String -> utf8String
|
||||||
|
_ -> ""
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
RocStr::from("ell"),
|
||||||
|
RocStr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn str_from_utf8_range_out_of_bounds_start_value() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
bytes = Str.toUtf8 "hello"
|
||||||
|
when Str.fromUtf8Range bytes { start: 7, count: 3 } is
|
||||||
|
Ok _ -> ""
|
||||||
|
Err (BadUtf8 _ _) -> ""
|
||||||
|
Err OutOfBounds -> "out of bounds"
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
RocStr::from("out of bounds"),
|
||||||
|
RocStr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn str_from_utf8_range_count_too_high() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
bytes = Str.toUtf8 "hello"
|
||||||
|
when Str.fromUtf8Range bytes { start: 0, count: 6 } is
|
||||||
|
Ok _ -> ""
|
||||||
|
Err (BadUtf8 _ _) -> ""
|
||||||
|
Err OutOfBounds -> "out of bounds"
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
RocStr::from("out of bounds"),
|
||||||
|
RocStr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn str_from_utf8_range_count_too_high_for_start() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
bytes = Str.toUtf8 "hello"
|
||||||
|
when Str.fromUtf8Range bytes { start: 4, count: 3 } is
|
||||||
|
Ok _ -> ""
|
||||||
|
Err (BadUtf8 _ _) -> ""
|
||||||
|
Err OutOfBounds -> "out of bounds"
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
RocStr::from("out of bounds"),
|
||||||
|
RocStr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ pub fn helper<'a>(
|
||||||
arena: &'a bumpalo::Bump,
|
arena: &'a bumpalo::Bump,
|
||||||
src: &str,
|
src: &str,
|
||||||
stdlib: &'a roc_builtins::std::StdLib,
|
stdlib: &'a roc_builtins::std::StdLib,
|
||||||
leak: bool,
|
is_gen_test: bool,
|
||||||
ignore_problems: bool,
|
ignore_problems: bool,
|
||||||
context: &'a inkwell::context::Context,
|
context: &'a inkwell::context::Context,
|
||||||
) -> (&'static str, String, Library) {
|
) -> (&'static str, String, Library) {
|
||||||
|
@ -171,13 +171,6 @@ pub fn helper<'a>(
|
||||||
let builder = context.create_builder();
|
let builder = context.create_builder();
|
||||||
let module = roc_gen_llvm::llvm::build::module_from_builtins(context, "app");
|
let module = roc_gen_llvm::llvm::build::module_from_builtins(context, "app");
|
||||||
|
|
||||||
// Add roc_alloc, roc_realloc, and roc_dealloc, since the repl has no
|
|
||||||
// platform to provide them.
|
|
||||||
add_default_roc_externs(context, &module, &builder, ptr_bytes);
|
|
||||||
|
|
||||||
// strip Zig debug stuff
|
|
||||||
module.strip_debug_info();
|
|
||||||
|
|
||||||
let opt_level = if cfg!(debug_assertions) {
|
let opt_level = if cfg!(debug_assertions) {
|
||||||
OptLevel::Normal
|
OptLevel::Normal
|
||||||
} else {
|
} else {
|
||||||
|
@ -219,11 +212,18 @@ pub fn helper<'a>(
|
||||||
interns,
|
interns,
|
||||||
module,
|
module,
|
||||||
ptr_bytes,
|
ptr_bytes,
|
||||||
leak,
|
is_gen_test,
|
||||||
// important! we don't want any procedures to get the C calling convention
|
// important! we don't want any procedures to get the C calling convention
|
||||||
exposed_to_host: MutSet::default(),
|
exposed_to_host: MutSet::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// strip Zig debug stuff
|
||||||
|
module.strip_debug_info();
|
||||||
|
|
||||||
|
// Add roc_alloc, roc_realloc, and roc_dealloc, since the repl has no
|
||||||
|
// platform to provide them.
|
||||||
|
add_default_roc_externs(&env);
|
||||||
|
|
||||||
let (main_fn_name, main_fn) = roc_gen_llvm::llvm::build::build_procedures_return_main(
|
let (main_fn_name, main_fn) = roc_gen_llvm::llvm::build::build_procedures_return_main(
|
||||||
&env,
|
&env,
|
||||||
opt_level,
|
opt_level,
|
||||||
|
@ -233,6 +233,9 @@ pub fn helper<'a>(
|
||||||
|
|
||||||
env.dibuilder.finalize();
|
env.dibuilder.finalize();
|
||||||
|
|
||||||
|
// strip all debug info: we don't use it at the moment and causes weird validation issues
|
||||||
|
module.strip_debug_info();
|
||||||
|
|
||||||
// Uncomment this to see the module's un-optimized LLVM instruction output:
|
// Uncomment this to see the module's un-optimized LLVM instruction output:
|
||||||
// env.module.print_to_stderr();
|
// env.module.print_to_stderr();
|
||||||
|
|
||||||
|
@ -260,7 +263,7 @@ pub fn helper<'a>(
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! assert_llvm_evals_to {
|
macro_rules! assert_llvm_evals_to {
|
||||||
($src:expr, $expected:expr, $ty:ty, $transform:expr, $leak:expr, $ignore_problems:expr) => {
|
($src:expr, $expected:expr, $ty:ty, $transform:expr, $ignore_problems:expr) => {
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use inkwell::context::Context;
|
use inkwell::context::Context;
|
||||||
use roc_gen_llvm::run_jit_function;
|
use roc_gen_llvm::run_jit_function;
|
||||||
|
@ -271,11 +274,19 @@ macro_rules! assert_llvm_evals_to {
|
||||||
// NOTE the stdlib must be in the arena; just taking a reference will segfault
|
// NOTE the stdlib must be in the arena; just taking a reference will segfault
|
||||||
let stdlib = arena.alloc(roc_builtins::std::standard_stdlib());
|
let stdlib = arena.alloc(roc_builtins::std::standard_stdlib());
|
||||||
|
|
||||||
let (main_fn_name, errors, lib) =
|
let is_gen_test = true;
|
||||||
$crate::helpers::eval::helper(&arena, $src, stdlib, $leak, $ignore_problems, &context);
|
let (main_fn_name, errors, lib) = $crate::helpers::eval::helper(
|
||||||
|
&arena,
|
||||||
|
$src,
|
||||||
|
stdlib,
|
||||||
|
is_gen_test,
|
||||||
|
$ignore_problems,
|
||||||
|
&context,
|
||||||
|
);
|
||||||
|
|
||||||
let transform = |success| {
|
let transform = |success| {
|
||||||
let expected = $expected;
|
let expected = $expected;
|
||||||
|
#[allow(clippy::redundant_closure_call)]
|
||||||
let given = $transform(success);
|
let given = $transform(success);
|
||||||
assert_eq!(&given, &expected);
|
assert_eq!(&given, &expected);
|
||||||
};
|
};
|
||||||
|
@ -283,7 +294,7 @@ macro_rules! assert_llvm_evals_to {
|
||||||
};
|
};
|
||||||
|
|
||||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||||
assert_llvm_evals_to!($src, $expected, $ty, $transform, true, false);
|
assert_llvm_evals_to!($src, $expected, $ty, $transform, false);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -295,20 +306,7 @@ macro_rules! assert_evals_to {
|
||||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||||
// Same as above, except with an additional transformation argument.
|
// Same as above, except with an additional transformation argument.
|
||||||
{
|
{
|
||||||
assert_evals_to!($src, $expected, $ty, $transform, true);
|
assert_llvm_evals_to!($src, $expected, $ty, $transform, false);
|
||||||
}
|
|
||||||
};
|
|
||||||
($src:expr, $expected:expr, $ty:ty, $transform:expr, $leak:expr) => {
|
|
||||||
// Run un-optimized tests, and then optimized tests, in separate scopes.
|
|
||||||
// These each rebuild everything from scratch, starting with
|
|
||||||
// parsing the source, so that there's no chance their passing
|
|
||||||
// or failing depends on leftover state from the previous one.
|
|
||||||
{
|
|
||||||
assert_llvm_evals_to!($src, $expected, $ty, $transform, $leak, false);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// NOTE at the moment, the optimized tests do the same thing
|
|
||||||
// assert_opt_evals_to!($src, $expected, $ty, $transform, $leak);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -321,10 +319,10 @@ macro_rules! assert_non_opt_evals_to {
|
||||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||||
// Same as above, except with an additional transformation argument.
|
// Same as above, except with an additional transformation argument.
|
||||||
{
|
{
|
||||||
assert_llvm_evals_to!($src, $expected, $ty, $transform, true, false);
|
assert_llvm_evals_to!($src, $expected, $ty, $transform, false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
($src:expr, $expected:expr, $ty:ty, $transform:expr, $leak:expr) => {{
|
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {{
|
||||||
assert_llvm_evals_to!($src, $expected, $ty, $transform, $leak);
|
assert_llvm_evals_to!($src, $expected, $ty, $transform);
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
procedure Num.25 (#Attr.2, #Attr.3):
|
procedure Num.25 (#Attr.2, #Attr.3):
|
||||||
let Test.15 = lowlevel NumSub #Attr.2 #Attr.3;
|
let Test.14 = lowlevel NumSub #Attr.2 #Attr.3;
|
||||||
ret Test.15;
|
ret Test.14;
|
||||||
|
|
||||||
procedure Num.26 (#Attr.2, #Attr.3):
|
procedure Num.26 (#Attr.2, #Attr.3):
|
||||||
let Test.12 = lowlevel NumMul #Attr.2 #Attr.3;
|
let Test.12 = lowlevel NumMul #Attr.2 #Attr.3;
|
||||||
|
@ -8,13 +8,13 @@ procedure Num.26 (#Attr.2, #Attr.3):
|
||||||
|
|
||||||
procedure Test.1 (Test.2, Test.3):
|
procedure Test.1 (Test.2, Test.3):
|
||||||
joinpoint Test.7 Test.2 Test.3:
|
joinpoint Test.7 Test.2 Test.3:
|
||||||
let Test.17 = 0i64;
|
let Test.15 = 0i64;
|
||||||
let Test.18 = lowlevel Eq Test.17 Test.2;
|
let Test.16 = lowlevel Eq Test.15 Test.2;
|
||||||
if Test.18 then
|
if Test.16 then
|
||||||
ret Test.3;
|
ret Test.3;
|
||||||
else
|
else
|
||||||
let Test.14 = 1i64;
|
let Test.13 = 1i64;
|
||||||
let Test.10 = CallByName Num.25 Test.2 Test.14;
|
let Test.10 = CallByName Num.25 Test.2 Test.13;
|
||||||
let Test.11 = CallByName Num.26 Test.2 Test.3;
|
let Test.11 = CallByName Num.26 Test.2 Test.3;
|
||||||
jump Test.7 Test.10 Test.11;
|
jump Test.7 Test.10 Test.11;
|
||||||
in
|
in
|
||||||
|
|
|
@ -1,24 +1,20 @@
|
||||||
procedure List.7 (#Attr.2):
|
procedure List.7 (#Attr.2):
|
||||||
let Test.7 = lowlevel ListLen #Attr.2;
|
let Test.6 = lowlevel ListLen #Attr.2;
|
||||||
ret Test.7;
|
ret Test.6;
|
||||||
|
|
||||||
procedure Num.24 (#Attr.2, #Attr.3):
|
procedure Num.24 (#Attr.2, #Attr.3):
|
||||||
let Test.5 = lowlevel NumAdd #Attr.2 #Attr.3;
|
let Test.5 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
ret Test.5;
|
ret Test.5;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.14 = 1i64;
|
let Test.11 = 1i64;
|
||||||
let Test.15 = 2i64;
|
let Test.12 = 2i64;
|
||||||
let Test.1 = Array [Test.14, Test.15];
|
let Test.1 = Array [Test.11, Test.12];
|
||||||
let Test.11 = 5i64;
|
let Test.9 = 5i64;
|
||||||
let Test.12 = 4i64;
|
let Test.10 = 4i64;
|
||||||
invoke Test.8 = CallByName Num.24 Test.11 Test.12 catch
|
let Test.7 = CallByName Num.24 Test.9 Test.10;
|
||||||
dec Test.1;
|
let Test.8 = 3i64;
|
||||||
unreachable;
|
let Test.3 = CallByName Num.24 Test.7 Test.8;
|
||||||
let Test.9 = 3i64;
|
|
||||||
invoke Test.3 = CallByName Num.24 Test.8 Test.9 catch
|
|
||||||
dec Test.1;
|
|
||||||
unreachable;
|
|
||||||
let Test.4 = CallByName List.7 Test.1;
|
let Test.4 = CallByName List.7 Test.1;
|
||||||
dec Test.1;
|
dec Test.1;
|
||||||
let Test.2 = CallByName Num.24 Test.3 Test.4;
|
let Test.2 = CallByName Num.24 Test.3 Test.4;
|
||||||
|
|
|
@ -3,16 +3,16 @@ procedure Num.24 (#Attr.2, #Attr.3):
|
||||||
ret Test.6;
|
ret Test.6;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.12 = 41i64;
|
let Test.11 = 41i64;
|
||||||
let Test.1 = Just Test.12;
|
let Test.1 = Just Test.11;
|
||||||
let Test.9 = 0i64;
|
let Test.8 = 0i64;
|
||||||
let Test.10 = GetTagId Test.1;
|
let Test.9 = GetTagId Test.1;
|
||||||
let Test.11 = lowlevel Eq Test.9 Test.10;
|
let Test.10 = lowlevel Eq Test.8 Test.9;
|
||||||
if Test.11 then
|
if Test.10 then
|
||||||
let Test.3 = UnionAtIndex (Id 0) (Index 0) Test.1;
|
let Test.3 = UnionAtIndex (Id 0) (Index 0) Test.1;
|
||||||
let Test.5 = 1i64;
|
let Test.5 = 1i64;
|
||||||
let Test.4 = CallByName Num.24 Test.3 Test.5;
|
let Test.4 = CallByName Num.24 Test.3 Test.5;
|
||||||
ret Test.4;
|
ret Test.4;
|
||||||
else
|
else
|
||||||
let Test.8 = 1i64;
|
let Test.7 = 1i64;
|
||||||
ret Test.8;
|
ret Test.7;
|
||||||
|
|
|
@ -3,18 +3,18 @@ procedure Num.24 (#Attr.2, #Attr.3):
|
||||||
ret Test.10;
|
ret Test.10;
|
||||||
|
|
||||||
procedure Test.3 (Test.5):
|
procedure Test.3 (Test.5):
|
||||||
let Test.18 = 1i64;
|
let Test.16 = 1i64;
|
||||||
let Test.19 = GetTagId Test.5;
|
let Test.17 = GetTagId Test.5;
|
||||||
let Test.20 = lowlevel Eq Test.18 Test.19;
|
let Test.18 = lowlevel Eq Test.16 Test.17;
|
||||||
if Test.20 then
|
if Test.18 then
|
||||||
let Test.13 = 0i64;
|
let Test.12 = 0i64;
|
||||||
ret Test.13;
|
ret Test.12;
|
||||||
else
|
else
|
||||||
let Test.6 = UnionAtIndex (Id 0) (Index 1) Test.5;
|
let Test.6 = UnionAtIndex (Id 0) (Index 1) Test.5;
|
||||||
let Test.15 = 1i64;
|
let Test.14 = 1i64;
|
||||||
let Test.16 = CallByName Test.3 Test.6;
|
let Test.15 = CallByName Test.3 Test.6;
|
||||||
let Test.14 = CallByName Num.24 Test.15 Test.16;
|
let Test.13 = CallByName Num.24 Test.14 Test.15;
|
||||||
ret Test.14;
|
ret Test.13;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.2 = Nil ;
|
let Test.2 = Nil ;
|
||||||
|
|
|
@ -1,40 +1,40 @@
|
||||||
procedure List.4 (#Attr.2, #Attr.3, #Attr.4):
|
procedure List.4 (#Attr.2, #Attr.3, #Attr.4):
|
||||||
let Test.23 = lowlevel ListLen #Attr.2;
|
let Test.22 = lowlevel ListLen #Attr.2;
|
||||||
let Test.21 = lowlevel NumLt #Attr.3 Test.23;
|
let Test.20 = lowlevel NumLt #Attr.3 Test.22;
|
||||||
if Test.21 then
|
if Test.20 then
|
||||||
let Test.22 = lowlevel ListSet #Attr.2 #Attr.3 #Attr.4;
|
let Test.21 = lowlevel ListSet #Attr.2 #Attr.3 #Attr.4;
|
||||||
ret Test.22;
|
ret Test.21;
|
||||||
else
|
else
|
||||||
ret #Attr.2;
|
ret #Attr.2;
|
||||||
|
|
||||||
procedure List.7 (#Attr.2):
|
procedure List.7 (#Attr.2):
|
||||||
let Test.10 = lowlevel ListLen #Attr.2;
|
let Test.9 = lowlevel ListLen #Attr.2;
|
||||||
ret Test.10;
|
ret Test.9;
|
||||||
|
|
||||||
procedure Num.24 (#Attr.2, #Attr.3):
|
procedure Num.24 (#Attr.2, #Attr.3):
|
||||||
let Test.7 = lowlevel NumAdd #Attr.2 #Attr.3;
|
let Test.7 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
ret Test.7;
|
ret Test.7;
|
||||||
|
|
||||||
procedure Test.1 ():
|
procedure Test.1 ():
|
||||||
let Test.12 = 1i64;
|
let Test.11 = 1i64;
|
||||||
let Test.13 = 2i64;
|
let Test.12 = 2i64;
|
||||||
let Test.14 = 3i64;
|
let Test.13 = 3i64;
|
||||||
let Test.11 = Array [Test.12, Test.13, Test.14];
|
let Test.10 = Array [Test.11, Test.12, Test.13];
|
||||||
ret Test.11;
|
ret Test.10;
|
||||||
|
|
||||||
procedure Test.2 (Test.3):
|
procedure Test.2 (Test.3):
|
||||||
|
let Test.17 = 0i64;
|
||||||
let Test.18 = 0i64;
|
let Test.18 = 0i64;
|
||||||
let Test.19 = 0i64;
|
let Test.16 = CallByName List.4 Test.3 Test.17 Test.18;
|
||||||
let Test.17 = CallByName List.4 Test.3 Test.18 Test.19;
|
ret Test.16;
|
||||||
ret Test.17;
|
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.16 = CallByName Test.1;
|
let Test.15 = CallByName Test.1;
|
||||||
let Test.15 = CallByName Test.2 Test.16;
|
let Test.14 = CallByName Test.2 Test.15;
|
||||||
let Test.5 = CallByName List.7 Test.15;
|
let Test.5 = CallByName List.7 Test.14;
|
||||||
dec Test.15;
|
dec Test.14;
|
||||||
let Test.9 = CallByName Test.1;
|
let Test.8 = CallByName Test.1;
|
||||||
let Test.6 = CallByName List.7 Test.9;
|
let Test.6 = CallByName List.7 Test.8;
|
||||||
dec Test.9;
|
dec Test.8;
|
||||||
let Test.4 = CallByName Num.24 Test.5 Test.6;
|
let Test.4 = CallByName Num.24 Test.5 Test.6;
|
||||||
ret Test.4;
|
ret Test.4;
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
|
procedure List.7 (#Attr.2):
|
||||||
|
let Test.7 = lowlevel ListLen #Attr.2;
|
||||||
|
ret Test.7;
|
||||||
|
|
||||||
procedure List.7 (#Attr.2):
|
procedure List.7 (#Attr.2):
|
||||||
let Test.8 = lowlevel ListLen #Attr.2;
|
let Test.8 = lowlevel ListLen #Attr.2;
|
||||||
ret Test.8;
|
ret Test.8;
|
||||||
|
|
||||||
procedure List.7 (#Attr.2):
|
|
||||||
let Test.9 = lowlevel ListLen #Attr.2;
|
|
||||||
ret Test.9;
|
|
||||||
|
|
||||||
procedure Num.24 (#Attr.2, #Attr.3):
|
procedure Num.24 (#Attr.2, #Attr.3):
|
||||||
let Test.6 = lowlevel NumAdd #Attr.2 #Attr.3;
|
let Test.6 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
ret Test.6;
|
ret Test.6;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.11 = 1i64;
|
let Test.10 = 1i64;
|
||||||
let Test.12 = 2i64;
|
let Test.11 = 2i64;
|
||||||
let Test.13 = 3i64;
|
let Test.12 = 3i64;
|
||||||
let Test.1 = Array [Test.11, Test.12, Test.13];
|
let Test.1 = Array [Test.10, Test.11, Test.12];
|
||||||
let Test.10 = 1f64;
|
let Test.9 = 1f64;
|
||||||
let Test.2 = Array [Test.10];
|
let Test.2 = Array [Test.9];
|
||||||
let Test.4 = CallByName List.7 Test.1;
|
let Test.4 = CallByName List.7 Test.1;
|
||||||
dec Test.1;
|
dec Test.1;
|
||||||
let Test.5 = CallByName List.7 Test.2;
|
let Test.5 = CallByName List.7 Test.2;
|
||||||
|
|
|
@ -3,28 +3,28 @@ procedure Num.24 (#Attr.2, #Attr.3):
|
||||||
ret Test.8;
|
ret Test.8;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.21 = 41i64;
|
let Test.20 = 41i64;
|
||||||
let Test.20 = Just Test.21;
|
let Test.19 = Just Test.20;
|
||||||
let Test.2 = Just Test.20;
|
let Test.2 = Just Test.19;
|
||||||
joinpoint Test.17:
|
joinpoint Test.16:
|
||||||
let Test.10 = 1i64;
|
let Test.9 = 1i64;
|
||||||
ret Test.10;
|
ret Test.9;
|
||||||
in
|
in
|
||||||
let Test.15 = 0i64;
|
let Test.14 = 0i64;
|
||||||
let Test.16 = GetTagId Test.2;
|
let Test.15 = GetTagId Test.2;
|
||||||
let Test.19 = lowlevel Eq Test.15 Test.16;
|
let Test.18 = lowlevel Eq Test.14 Test.15;
|
||||||
if Test.19 then
|
|
||||||
let Test.12 = UnionAtIndex (Id 0) (Index 0) Test.2;
|
|
||||||
let Test.13 = 0i64;
|
|
||||||
let Test.14 = GetTagId Test.12;
|
|
||||||
let Test.18 = lowlevel Eq Test.13 Test.14;
|
|
||||||
if Test.18 then
|
if Test.18 then
|
||||||
let Test.11 = UnionAtIndex (Id 0) (Index 0) Test.2;
|
let Test.11 = UnionAtIndex (Id 0) (Index 0) Test.2;
|
||||||
let Test.5 = UnionAtIndex (Id 0) (Index 0) Test.11;
|
let Test.12 = 0i64;
|
||||||
|
let Test.13 = GetTagId Test.11;
|
||||||
|
let Test.17 = lowlevel Eq Test.12 Test.13;
|
||||||
|
if Test.17 then
|
||||||
|
let Test.10 = UnionAtIndex (Id 0) (Index 0) Test.2;
|
||||||
|
let Test.5 = UnionAtIndex (Id 0) (Index 0) Test.10;
|
||||||
let Test.7 = 1i64;
|
let Test.7 = 1i64;
|
||||||
let Test.6 = CallByName Num.24 Test.5 Test.7;
|
let Test.6 = CallByName Num.24 Test.5 Test.7;
|
||||||
ret Test.6;
|
ret Test.6;
|
||||||
else
|
else
|
||||||
jump Test.17;
|
jump Test.16;
|
||||||
else
|
else
|
||||||
jump Test.17;
|
jump Test.16;
|
||||||
|
|
|
@ -3,10 +3,10 @@ procedure Num.26 (#Attr.2, #Attr.3):
|
||||||
ret Test.17;
|
ret Test.17;
|
||||||
|
|
||||||
procedure Test.1 (Test.6):
|
procedure Test.1 (Test.6):
|
||||||
let Test.25 = StructAtIndex 1 Test.6;
|
let Test.22 = StructAtIndex 1 Test.6;
|
||||||
let Test.26 = false;
|
let Test.23 = false;
|
||||||
let Test.27 = lowlevel Eq Test.26 Test.25;
|
let Test.24 = lowlevel Eq Test.23 Test.22;
|
||||||
if Test.27 then
|
if Test.24 then
|
||||||
let Test.8 = StructAtIndex 0 Test.6;
|
let Test.8 = StructAtIndex 0 Test.6;
|
||||||
ret Test.8;
|
ret Test.8;
|
||||||
else
|
else
|
||||||
|
@ -14,9 +14,9 @@ procedure Test.1 (Test.6):
|
||||||
ret Test.10;
|
ret Test.10;
|
||||||
|
|
||||||
procedure Test.1 (Test.6):
|
procedure Test.1 (Test.6):
|
||||||
let Test.36 = false;
|
let Test.33 = false;
|
||||||
let Test.37 = lowlevel Eq Test.36 Test.6;
|
let Test.34 = lowlevel Eq Test.33 Test.6;
|
||||||
if Test.37 then
|
if Test.34 then
|
||||||
let Test.8 = 3i64;
|
let Test.8 = 3i64;
|
||||||
ret Test.8;
|
ret Test.8;
|
||||||
else
|
else
|
||||||
|
@ -24,19 +24,19 @@ procedure Test.1 (Test.6):
|
||||||
ret Test.10;
|
ret Test.10;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.40 = true;
|
let Test.37 = true;
|
||||||
let Test.5 = CallByName Test.1 Test.40;
|
let Test.5 = CallByName Test.1 Test.37;
|
||||||
let Test.38 = false;
|
let Test.35 = false;
|
||||||
let Test.3 = CallByName Test.1 Test.38;
|
let Test.3 = CallByName Test.1 Test.35;
|
||||||
let Test.31 = 11i64;
|
let Test.28 = 11i64;
|
||||||
let Test.32 = true;
|
let Test.29 = true;
|
||||||
let Test.30 = Struct {Test.31, Test.32};
|
let Test.27 = Struct {Test.28, Test.29};
|
||||||
let Test.4 = CallByName Test.1 Test.30;
|
let Test.4 = CallByName Test.1 Test.27;
|
||||||
let Test.28 = 7i64;
|
let Test.25 = 7i64;
|
||||||
let Test.29 = false;
|
let Test.26 = false;
|
||||||
let Test.22 = Struct {Test.28, Test.29};
|
let Test.19 = Struct {Test.25, Test.26};
|
||||||
let Test.2 = CallByName Test.1 Test.22;
|
let Test.2 = CallByName Test.1 Test.19;
|
||||||
let Test.19 = CallByName Num.26 Test.2 Test.3;
|
let Test.18 = CallByName Num.26 Test.2 Test.3;
|
||||||
let Test.16 = CallByName Num.26 Test.19 Test.4;
|
let Test.16 = CallByName Num.26 Test.18 Test.4;
|
||||||
let Test.15 = CallByName Num.26 Test.16 Test.5;
|
let Test.15 = CallByName Num.26 Test.16 Test.5;
|
||||||
ret Test.15;
|
ret Test.15;
|
||||||
|
|
|
@ -3,30 +3,28 @@ procedure Num.24 (#Attr.2, #Attr.3):
|
||||||
ret Test.19;
|
ret Test.19;
|
||||||
|
|
||||||
procedure Num.25 (#Attr.2, #Attr.3):
|
procedure Num.25 (#Attr.2, #Attr.3):
|
||||||
let Test.23 = lowlevel NumSub #Attr.2 #Attr.3;
|
let Test.22 = lowlevel NumSub #Attr.2 #Attr.3;
|
||||||
ret Test.23;
|
ret Test.22;
|
||||||
|
|
||||||
procedure Num.27 (#Attr.2, #Attr.3):
|
procedure Num.27 (#Attr.2, #Attr.3):
|
||||||
let Test.28 = lowlevel NumLt #Attr.2 #Attr.3;
|
let Test.26 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||||
ret Test.28;
|
ret Test.26;
|
||||||
|
|
||||||
procedure Test.1 (Test.2, Test.3, Test.4):
|
procedure Test.1 (Test.2, Test.3, Test.4):
|
||||||
joinpoint Test.12 Test.2 Test.3 Test.4:
|
joinpoint Test.12 Test.2 Test.3 Test.4:
|
||||||
let Test.14 = CallByName Num.27 Test.3 Test.4;
|
let Test.14 = CallByName Num.27 Test.3 Test.4;
|
||||||
if Test.14 then
|
if Test.14 then
|
||||||
dec Test.2;
|
dec Test.2;
|
||||||
let Test.27 = Array [];
|
let Test.25 = Array [];
|
||||||
let Test.26 = 0i64;
|
let Test.24 = 0i64;
|
||||||
let Test.25 = Struct {Test.26, Test.27};
|
let Test.23 = Struct {Test.24, Test.25};
|
||||||
let Test.5 = StructAtIndex 0 Test.25;
|
let Test.5 = StructAtIndex 0 Test.23;
|
||||||
let Test.6 = StructAtIndex 1 Test.25;
|
let Test.6 = StructAtIndex 1 Test.23;
|
||||||
let Test.22 = 1i64;
|
let Test.21 = 1i64;
|
||||||
let Test.21 = CallByName Num.25 Test.5 Test.22;
|
let Test.20 = CallByName Num.25 Test.5 Test.21;
|
||||||
let Test.16 = CallByName Test.1 Test.6 Test.3 Test.21;
|
let Test.16 = CallByName Test.1 Test.6 Test.3 Test.20;
|
||||||
let Test.18 = 1i64;
|
let Test.18 = 1i64;
|
||||||
invoke Test.17 = CallByName Num.24 Test.5 Test.18 catch
|
let Test.17 = CallByName Num.24 Test.5 Test.18;
|
||||||
dec Test.16;
|
|
||||||
unreachable;
|
|
||||||
jump Test.12 Test.16 Test.17 Test.4;
|
jump Test.12 Test.16 Test.17 Test.4;
|
||||||
else
|
else
|
||||||
ret Test.2;
|
ret Test.2;
|
||||||
|
|
|
@ -10,8 +10,8 @@ procedure Test.1 (Test.4):
|
||||||
ret Test.7;
|
ret Test.7;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.10 = 4i64;
|
let Test.9 = 4i64;
|
||||||
let Test.11 = 9i64;
|
let Test.10 = 9i64;
|
||||||
let Test.6 = Struct {Test.10, Test.11};
|
let Test.6 = Struct {Test.9, Test.10};
|
||||||
let Test.5 = CallByName Test.1 Test.6;
|
let Test.5 = CallByName Test.1 Test.6;
|
||||||
ret Test.5;
|
ret Test.5;
|
||||||
|
|
|
@ -9,6 +9,6 @@ procedure Test.1 (Test.4):
|
||||||
ret Test.7;
|
ret Test.7;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.10 = 9i64;
|
let Test.9 = 9i64;
|
||||||
let Test.5 = CallByName Test.1 Test.10;
|
let Test.5 = CallByName Test.1 Test.9;
|
||||||
ret Test.5;
|
ret Test.5;
|
||||||
|
|
|
@ -9,8 +9,8 @@ procedure Test.1 (Test.2):
|
||||||
ret Test.7;
|
ret Test.7;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.10 = 4i64;
|
let Test.9 = 4i64;
|
||||||
let Test.11 = 9i64;
|
let Test.10 = 9i64;
|
||||||
let Test.6 = Struct {Test.10, Test.11};
|
let Test.6 = Struct {Test.9, Test.10};
|
||||||
let Test.5 = CallByName Test.1 Test.6;
|
let Test.5 = CallByName Test.1 Test.6;
|
||||||
ret Test.5;
|
ret Test.5;
|
||||||
|
|
|
@ -8,6 +8,6 @@ procedure Test.1 (Test.2):
|
||||||
ret Test.7;
|
ret Test.7;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.10 = 9i64;
|
let Test.9 = 9i64;
|
||||||
let Test.5 = CallByName Test.1 Test.10;
|
let Test.5 = CallByName Test.1 Test.9;
|
||||||
ret Test.5;
|
ret Test.5;
|
||||||
|
|
|
@ -1,53 +1,43 @@
|
||||||
procedure Num.24 (#Attr.2, #Attr.3):
|
procedure Num.24 (#Attr.2, #Attr.3):
|
||||||
let Test.28 = lowlevel NumAdd #Attr.2 #Attr.3;
|
let Test.24 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
ret Test.28;
|
|
||||||
|
|
||||||
procedure Num.26 (#Attr.2, #Attr.3):
|
|
||||||
let Test.22 = lowlevel NumMul #Attr.2 #Attr.3;
|
|
||||||
ret Test.22;
|
|
||||||
|
|
||||||
procedure Test.1 ():
|
|
||||||
let Test.30 = 1i64;
|
|
||||||
ret Test.30;
|
|
||||||
|
|
||||||
procedure Test.2 ():
|
|
||||||
let Test.24 = 2i64;
|
|
||||||
ret Test.24;
|
ret Test.24;
|
||||||
|
|
||||||
procedure Test.3 (Test.6):
|
procedure Num.26 (#Attr.2, #Attr.3):
|
||||||
let Test.27 = CallByName Test.1;
|
let Test.19 = lowlevel NumMul #Attr.2 #Attr.3;
|
||||||
let Test.26 = CallByName Num.24 Test.6 Test.27;
|
ret Test.19;
|
||||||
ret Test.26;
|
|
||||||
|
|
||||||
procedure Test.4 (Test.7):
|
procedure Test.1 ():
|
||||||
let Test.21 = CallByName Test.2;
|
let Test.25 = 1i64;
|
||||||
let Test.20 = CallByName Num.26 Test.7 Test.21;
|
ret Test.25;
|
||||||
|
|
||||||
|
procedure Test.2 ():
|
||||||
|
let Test.20 = 2i64;
|
||||||
ret Test.20;
|
ret Test.20;
|
||||||
|
|
||||||
|
procedure Test.3 (Test.6):
|
||||||
|
let Test.23 = CallByName Test.1;
|
||||||
|
let Test.22 = CallByName Num.24 Test.6 Test.23;
|
||||||
|
ret Test.22;
|
||||||
|
|
||||||
|
procedure Test.4 (Test.7):
|
||||||
|
let Test.18 = CallByName Test.2;
|
||||||
|
let Test.17 = CallByName Num.26 Test.7 Test.18;
|
||||||
|
ret Test.17;
|
||||||
|
|
||||||
procedure Test.5 (Test.8, Test.9):
|
procedure Test.5 (Test.8, Test.9):
|
||||||
joinpoint Test.15 Test.14:
|
let Test.14 = CallByName Test.3 Test.9;
|
||||||
ret Test.14;
|
ret Test.14;
|
||||||
in
|
|
||||||
switch Test.8:
|
|
||||||
case 0:
|
|
||||||
let Test.16 = CallByName Test.3 Test.9;
|
|
||||||
jump Test.15 Test.16;
|
|
||||||
|
|
||||||
default:
|
|
||||||
let Test.17 = CallByName Test.4 Test.9;
|
|
||||||
jump Test.15 Test.17;
|
|
||||||
|
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
joinpoint Test.19 Test.12:
|
joinpoint Test.16 Test.12:
|
||||||
let Test.13 = 42i64;
|
let Test.13 = 42i64;
|
||||||
let Test.11 = CallByName Test.5 Test.12 Test.13;
|
let Test.11 = CallByName Test.5 Test.12 Test.13;
|
||||||
ret Test.11;
|
ret Test.11;
|
||||||
in
|
in
|
||||||
let Test.25 = true;
|
let Test.21 = true;
|
||||||
if Test.25 then
|
if Test.21 then
|
||||||
let Test.3 = Struct {};
|
let Test.3 = Struct {};
|
||||||
jump Test.19 Test.3;
|
jump Test.16 Test.3;
|
||||||
else
|
else
|
||||||
let Test.4 = Struct {};
|
let Test.4 = Struct {};
|
||||||
jump Test.19 Test.4;
|
jump Test.16 Test.4;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
procedure Num.24 (#Attr.2, #Attr.3):
|
procedure Num.24 (#Attr.2, #Attr.3):
|
||||||
let Test.29 = lowlevel NumAdd #Attr.2 #Attr.3;
|
let Test.28 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
ret Test.29;
|
ret Test.28;
|
||||||
|
|
||||||
procedure Num.26 (#Attr.2, #Attr.3):
|
procedure Num.26 (#Attr.2, #Attr.3):
|
||||||
let Test.25 = lowlevel NumMul #Attr.2 #Attr.3;
|
let Test.25 = lowlevel NumMul #Attr.2 #Attr.3;
|
||||||
|
@ -23,8 +23,8 @@ procedure Test.1 (Test.2, Test.3):
|
||||||
|
|
||||||
procedure Test.7 (Test.10, #Attr.12):
|
procedure Test.7 (Test.10, #Attr.12):
|
||||||
let Test.4 = UnionAtIndex (Id 0) (Index 0) #Attr.12;
|
let Test.4 = UnionAtIndex (Id 0) (Index 0) #Attr.12;
|
||||||
let Test.28 = CallByName Num.24 Test.10 Test.4;
|
let Test.27 = CallByName Num.24 Test.10 Test.4;
|
||||||
ret Test.28;
|
ret Test.27;
|
||||||
|
|
||||||
procedure Test.8 (Test.11, #Attr.12):
|
procedure Test.8 (Test.11, #Attr.12):
|
||||||
let Test.6 = UnionAtIndex (Id 1) (Index 1) #Attr.12;
|
let Test.6 = UnionAtIndex (Id 1) (Index 1) #Attr.12;
|
||||||
|
@ -44,8 +44,8 @@ procedure Test.0 ():
|
||||||
let Test.13 = CallByName Test.1 Test.14 Test.15;
|
let Test.13 = CallByName Test.1 Test.14 Test.15;
|
||||||
ret Test.13;
|
ret Test.13;
|
||||||
in
|
in
|
||||||
let Test.27 = true;
|
let Test.26 = true;
|
||||||
if Test.27 then
|
if Test.26 then
|
||||||
let Test.7 = ClosureTag(Test.7) Test.4;
|
let Test.7 = ClosureTag(Test.7) Test.4;
|
||||||
jump Test.22 Test.7;
|
jump Test.22 Test.7;
|
||||||
else
|
else
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
procedure Num.24 (#Attr.2, #Attr.3):
|
procedure Num.24 (#Attr.2, #Attr.3):
|
||||||
let Test.25 = lowlevel NumAdd #Attr.2 #Attr.3;
|
let Test.24 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
ret Test.25;
|
ret Test.24;
|
||||||
|
|
||||||
procedure Num.26 (#Attr.2, #Attr.3):
|
procedure Num.26 (#Attr.2, #Attr.3):
|
||||||
let Test.21 = lowlevel NumMul #Attr.2 #Attr.3;
|
let Test.21 = lowlevel NumMul #Attr.2 #Attr.3;
|
||||||
|
@ -8,8 +8,8 @@ procedure Num.26 (#Attr.2, #Attr.3):
|
||||||
|
|
||||||
procedure Test.6 (Test.8, #Attr.12):
|
procedure Test.6 (Test.8, #Attr.12):
|
||||||
let Test.4 = UnionAtIndex (Id 0) (Index 0) #Attr.12;
|
let Test.4 = UnionAtIndex (Id 0) (Index 0) #Attr.12;
|
||||||
let Test.24 = CallByName Num.24 Test.8 Test.4;
|
let Test.23 = CallByName Num.24 Test.8 Test.4;
|
||||||
ret Test.24;
|
ret Test.23;
|
||||||
|
|
||||||
procedure Test.7 (Test.9, #Attr.12):
|
procedure Test.7 (Test.9, #Attr.12):
|
||||||
let Test.5 = UnionAtIndex (Id 1) (Index 0) #Attr.12;
|
let Test.5 = UnionAtIndex (Id 1) (Index 0) #Attr.12;
|
||||||
|
@ -35,8 +35,8 @@ procedure Test.0 ():
|
||||||
jump Test.15 Test.17;
|
jump Test.15 Test.17;
|
||||||
|
|
||||||
in
|
in
|
||||||
let Test.23 = true;
|
let Test.22 = true;
|
||||||
if Test.23 then
|
if Test.22 then
|
||||||
let Test.6 = ClosureTag(Test.6) Test.4;
|
let Test.6 = ClosureTag(Test.6) Test.4;
|
||||||
jump Test.19 Test.6;
|
jump Test.19 Test.6;
|
||||||
else
|
else
|
||||||
|
|
|
@ -3,28 +3,28 @@ procedure Num.24 (#Attr.2, #Attr.3):
|
||||||
ret Test.8;
|
ret Test.8;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.21 = 41i64;
|
let Test.20 = 41i64;
|
||||||
let Test.20 = Just Test.21;
|
let Test.19 = Just Test.20;
|
||||||
let Test.2 = Just Test.20;
|
let Test.2 = Just Test.19;
|
||||||
joinpoint Test.17:
|
joinpoint Test.16:
|
||||||
let Test.10 = 1i64;
|
let Test.9 = 1i64;
|
||||||
ret Test.10;
|
ret Test.9;
|
||||||
in
|
in
|
||||||
let Test.15 = 0i64;
|
let Test.14 = 0i64;
|
||||||
let Test.16 = GetTagId Test.2;
|
let Test.15 = GetTagId Test.2;
|
||||||
let Test.19 = lowlevel Eq Test.15 Test.16;
|
let Test.18 = lowlevel Eq Test.14 Test.15;
|
||||||
if Test.19 then
|
|
||||||
let Test.12 = UnionAtIndex (Id 0) (Index 0) Test.2;
|
|
||||||
let Test.13 = 0i64;
|
|
||||||
let Test.14 = GetTagId Test.12;
|
|
||||||
let Test.18 = lowlevel Eq Test.13 Test.14;
|
|
||||||
if Test.18 then
|
if Test.18 then
|
||||||
let Test.11 = UnionAtIndex (Id 0) (Index 0) Test.2;
|
let Test.11 = UnionAtIndex (Id 0) (Index 0) Test.2;
|
||||||
let Test.5 = UnionAtIndex (Id 0) (Index 0) Test.11;
|
let Test.12 = 0i64;
|
||||||
|
let Test.13 = GetTagId Test.11;
|
||||||
|
let Test.17 = lowlevel Eq Test.12 Test.13;
|
||||||
|
if Test.17 then
|
||||||
|
let Test.10 = UnionAtIndex (Id 0) (Index 0) Test.2;
|
||||||
|
let Test.5 = UnionAtIndex (Id 0) (Index 0) Test.10;
|
||||||
let Test.7 = 1i64;
|
let Test.7 = 1i64;
|
||||||
let Test.6 = CallByName Num.24 Test.5 Test.7;
|
let Test.6 = CallByName Num.24 Test.5 Test.7;
|
||||||
ret Test.6;
|
ret Test.6;
|
||||||
else
|
else
|
||||||
jump Test.17;
|
jump Test.16;
|
||||||
else
|
else
|
||||||
jump Test.17;
|
jump Test.16;
|
||||||
|
|
|
@ -3,7 +3,7 @@ procedure Num.24 (#Attr.2, #Attr.3):
|
||||||
ret Test.5;
|
ret Test.5;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.7 = 2i64;
|
let Test.6 = 2i64;
|
||||||
let Test.4 = 3i64;
|
let Test.4 = 3i64;
|
||||||
let Test.3 = CallByName Num.24 Test.7 Test.4;
|
let Test.3 = CallByName Num.24 Test.6 Test.4;
|
||||||
ret Test.3;
|
ret Test.3;
|
||||||
|
|
|
@ -3,26 +3,26 @@ procedure Num.24 (#Attr.2, #Attr.3):
|
||||||
ret Test.7;
|
ret Test.7;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.17 = 3i64;
|
let Test.16 = 3i64;
|
||||||
let Test.16 = 2i64;
|
let Test.15 = 2i64;
|
||||||
let Test.4 = Struct {Test.16, Test.17};
|
let Test.4 = Struct {Test.15, Test.16};
|
||||||
joinpoint Test.13:
|
joinpoint Test.12:
|
||||||
let Test.2 = StructAtIndex 0 Test.4;
|
let Test.2 = StructAtIndex 0 Test.4;
|
||||||
let Test.3 = StructAtIndex 1 Test.4;
|
let Test.3 = StructAtIndex 1 Test.4;
|
||||||
let Test.6 = CallByName Num.24 Test.2 Test.3;
|
let Test.6 = CallByName Num.24 Test.2 Test.3;
|
||||||
ret Test.6;
|
ret Test.6;
|
||||||
in
|
in
|
||||||
let Test.11 = StructAtIndex 1 Test.4;
|
let Test.10 = StructAtIndex 1 Test.4;
|
||||||
let Test.12 = 3i64;
|
let Test.11 = 3i64;
|
||||||
let Test.15 = lowlevel Eq Test.12 Test.11;
|
let Test.14 = lowlevel Eq Test.11 Test.10;
|
||||||
if Test.15 then
|
|
||||||
let Test.9 = StructAtIndex 0 Test.4;
|
|
||||||
let Test.10 = 4i64;
|
|
||||||
let Test.14 = lowlevel Eq Test.10 Test.9;
|
|
||||||
if Test.14 then
|
if Test.14 then
|
||||||
|
let Test.8 = StructAtIndex 0 Test.4;
|
||||||
|
let Test.9 = 4i64;
|
||||||
|
let Test.13 = lowlevel Eq Test.9 Test.8;
|
||||||
|
if Test.13 then
|
||||||
let Test.5 = 9i64;
|
let Test.5 = 9i64;
|
||||||
ret Test.5;
|
ret Test.5;
|
||||||
else
|
else
|
||||||
jump Test.13;
|
jump Test.12;
|
||||||
else
|
else
|
||||||
jump Test.13;
|
jump Test.12;
|
||||||
|
|
|
@ -365,6 +365,7 @@ pub fn num_type(range: SolvedType) -> SolvedType {
|
||||||
SolvedType::Alias(
|
SolvedType::Alias(
|
||||||
Symbol::NUM_NUM,
|
Symbol::NUM_NUM,
|
||||||
vec![("range".into(), range.clone())],
|
vec![("range".into(), range.clone())],
|
||||||
|
vec![],
|
||||||
Box::new(num_alias_content(range)),
|
Box::new(num_alias_content(range)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -381,6 +382,7 @@ pub fn floatingpoint_type(range: SolvedType) -> SolvedType {
|
||||||
SolvedType::Alias(
|
SolvedType::Alias(
|
||||||
Symbol::NUM_FLOATINGPOINT,
|
Symbol::NUM_FLOATINGPOINT,
|
||||||
vec![("range".into(), range.clone())],
|
vec![("range".into(), range.clone())],
|
||||||
|
vec![],
|
||||||
Box::new(floatingpoint_alias_content(range)),
|
Box::new(floatingpoint_alias_content(range)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -397,6 +399,7 @@ pub fn float_type(range: SolvedType) -> SolvedType {
|
||||||
SolvedType::Alias(
|
SolvedType::Alias(
|
||||||
Symbol::NUM_FLOAT,
|
Symbol::NUM_FLOAT,
|
||||||
vec![("range".into(), range.clone())],
|
vec![("range".into(), range.clone())],
|
||||||
|
vec![],
|
||||||
Box::new(float_alias_content(range)),
|
Box::new(float_alias_content(range)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -410,7 +413,12 @@ fn float_alias_content(range: SolvedType) -> SolvedType {
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn nat_type() -> SolvedType {
|
pub fn nat_type() -> SolvedType {
|
||||||
SolvedType::Alias(Symbol::NUM_NAT, vec![], Box::new(nat_alias_content()))
|
SolvedType::Alias(
|
||||||
|
Symbol::NUM_NAT,
|
||||||
|
vec![],
|
||||||
|
vec![],
|
||||||
|
Box::new(nat_alias_content()),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -422,7 +430,12 @@ fn nat_alias_content() -> SolvedType {
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn u64_type() -> SolvedType {
|
pub fn u64_type() -> SolvedType {
|
||||||
SolvedType::Alias(Symbol::NUM_U64, vec![], Box::new(u64_alias_content()))
|
SolvedType::Alias(
|
||||||
|
Symbol::NUM_U64,
|
||||||
|
vec![],
|
||||||
|
vec![],
|
||||||
|
Box::new(u64_alias_content()),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -434,7 +447,12 @@ fn u64_alias_content() -> SolvedType {
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn u32_type() -> SolvedType {
|
pub fn u32_type() -> SolvedType {
|
||||||
SolvedType::Alias(Symbol::NUM_U32, vec![], Box::new(u32_alias_content()))
|
SolvedType::Alias(
|
||||||
|
Symbol::NUM_U32,
|
||||||
|
vec![],
|
||||||
|
vec![],
|
||||||
|
Box::new(u32_alias_content()),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -446,7 +464,12 @@ fn u32_alias_content() -> SolvedType {
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn i128_type() -> SolvedType {
|
pub fn i128_type() -> SolvedType {
|
||||||
SolvedType::Alias(Symbol::NUM_I128, vec![], Box::new(i128_alias_content()))
|
SolvedType::Alias(
|
||||||
|
Symbol::NUM_I128,
|
||||||
|
vec![],
|
||||||
|
vec![],
|
||||||
|
Box::new(i128_alias_content()),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -461,6 +484,7 @@ pub fn int_type(range: SolvedType) -> SolvedType {
|
||||||
SolvedType::Alias(
|
SolvedType::Alias(
|
||||||
Symbol::NUM_INT,
|
Symbol::NUM_INT,
|
||||||
vec![("range".into(), range.clone())],
|
vec![("range".into(), range.clone())],
|
||||||
|
vec![],
|
||||||
Box::new(int_alias_content(range)),
|
Box::new(int_alias_content(range)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -477,6 +501,7 @@ pub fn integer_type(range: SolvedType) -> SolvedType {
|
||||||
SolvedType::Alias(
|
SolvedType::Alias(
|
||||||
Symbol::NUM_INTEGER,
|
Symbol::NUM_INTEGER,
|
||||||
vec![("range".into(), range.clone())],
|
vec![("range".into(), range.clone())],
|
||||||
|
vec![],
|
||||||
Box::new(integer_alias_content(range)),
|
Box::new(integer_alias_content(range)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -491,15 +516,27 @@ pub fn u8_type() -> SolvedType {
|
||||||
SolvedType::Alias(
|
SolvedType::Alias(
|
||||||
Symbol::NUM_U8,
|
Symbol::NUM_U8,
|
||||||
vec![],
|
vec![],
|
||||||
|
vec![],
|
||||||
Box::new(int_alias_content(unsigned8_type())),
|
Box::new(int_alias_content(unsigned8_type())),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn u16_type() -> SolvedType {
|
||||||
|
SolvedType::Alias(
|
||||||
|
Symbol::NUM_U16,
|
||||||
|
vec![],
|
||||||
|
vec![],
|
||||||
|
Box::new(int_alias_content(unsigned16_type())),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn binary64_type() -> SolvedType {
|
pub fn binary64_type() -> SolvedType {
|
||||||
SolvedType::Alias(
|
SolvedType::Alias(
|
||||||
Symbol::NUM_BINARY64,
|
Symbol::NUM_BINARY64,
|
||||||
vec![],
|
vec![],
|
||||||
|
vec![],
|
||||||
Box::new(binary64_alias_content()),
|
Box::new(binary64_alias_content()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -514,6 +551,7 @@ pub fn binary32_type() -> SolvedType {
|
||||||
SolvedType::Alias(
|
SolvedType::Alias(
|
||||||
Symbol::NUM_BINARY32,
|
Symbol::NUM_BINARY32,
|
||||||
vec![],
|
vec![],
|
||||||
|
vec![],
|
||||||
Box::new(binary32_alias_content()),
|
Box::new(binary32_alias_content()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -528,6 +566,7 @@ pub fn natural_type() -> SolvedType {
|
||||||
SolvedType::Alias(
|
SolvedType::Alias(
|
||||||
Symbol::NUM_NATURAL,
|
Symbol::NUM_NATURAL,
|
||||||
vec![],
|
vec![],
|
||||||
|
vec![],
|
||||||
Box::new(natural_alias_content()),
|
Box::new(natural_alias_content()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -542,6 +581,7 @@ pub fn signed128_type() -> SolvedType {
|
||||||
SolvedType::Alias(
|
SolvedType::Alias(
|
||||||
Symbol::NUM_SIGNED128,
|
Symbol::NUM_SIGNED128,
|
||||||
vec![],
|
vec![],
|
||||||
|
vec![],
|
||||||
Box::new(signed128_alias_content()),
|
Box::new(signed128_alias_content()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -556,6 +596,7 @@ pub fn signed64_type() -> SolvedType {
|
||||||
SolvedType::Alias(
|
SolvedType::Alias(
|
||||||
Symbol::NUM_SIGNED64,
|
Symbol::NUM_SIGNED64,
|
||||||
vec![],
|
vec![],
|
||||||
|
vec![],
|
||||||
Box::new(signed64_alias_content()),
|
Box::new(signed64_alias_content()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -570,6 +611,7 @@ pub fn signed32_type() -> SolvedType {
|
||||||
SolvedType::Alias(
|
SolvedType::Alias(
|
||||||
Symbol::NUM_SIGNED32,
|
Symbol::NUM_SIGNED32,
|
||||||
vec![],
|
vec![],
|
||||||
|
vec![],
|
||||||
Box::new(signed32_alias_content()),
|
Box::new(signed32_alias_content()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -584,6 +626,7 @@ pub fn signed16_type() -> SolvedType {
|
||||||
SolvedType::Alias(
|
SolvedType::Alias(
|
||||||
Symbol::NUM_SIGNED16,
|
Symbol::NUM_SIGNED16,
|
||||||
vec![],
|
vec![],
|
||||||
|
vec![],
|
||||||
Box::new(signed16_alias_content()),
|
Box::new(signed16_alias_content()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -598,6 +641,7 @@ pub fn signed8_type() -> SolvedType {
|
||||||
SolvedType::Alias(
|
SolvedType::Alias(
|
||||||
Symbol::NUM_SIGNED8,
|
Symbol::NUM_SIGNED8,
|
||||||
vec![],
|
vec![],
|
||||||
|
vec![],
|
||||||
Box::new(signed8_alias_content()),
|
Box::new(signed8_alias_content()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -612,6 +656,7 @@ pub fn unsigned128_type() -> SolvedType {
|
||||||
SolvedType::Alias(
|
SolvedType::Alias(
|
||||||
Symbol::NUM_UNSIGNED128,
|
Symbol::NUM_UNSIGNED128,
|
||||||
vec![],
|
vec![],
|
||||||
|
vec![],
|
||||||
Box::new(unsigned128_alias_content()),
|
Box::new(unsigned128_alias_content()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -626,6 +671,7 @@ pub fn unsigned64_type() -> SolvedType {
|
||||||
SolvedType::Alias(
|
SolvedType::Alias(
|
||||||
Symbol::NUM_UNSIGNED64,
|
Symbol::NUM_UNSIGNED64,
|
||||||
vec![],
|
vec![],
|
||||||
|
vec![],
|
||||||
Box::new(unsigned64_alias_content()),
|
Box::new(unsigned64_alias_content()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -640,6 +686,7 @@ pub fn unsigned32_type() -> SolvedType {
|
||||||
SolvedType::Alias(
|
SolvedType::Alias(
|
||||||
Symbol::NUM_UNSIGNED32,
|
Symbol::NUM_UNSIGNED32,
|
||||||
vec![],
|
vec![],
|
||||||
|
vec![],
|
||||||
Box::new(unsigned32_alias_content()),
|
Box::new(unsigned32_alias_content()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -654,6 +701,7 @@ pub fn unsigned16_type() -> SolvedType {
|
||||||
SolvedType::Alias(
|
SolvedType::Alias(
|
||||||
Symbol::NUM_UNSIGNED16,
|
Symbol::NUM_UNSIGNED16,
|
||||||
vec![],
|
vec![],
|
||||||
|
vec![],
|
||||||
Box::new(unsigned16_alias_content()),
|
Box::new(unsigned16_alias_content()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -668,6 +716,7 @@ pub fn unsigned8_type() -> SolvedType {
|
||||||
SolvedType::Alias(
|
SolvedType::Alias(
|
||||||
Symbol::NUM_UNSIGNED8,
|
Symbol::NUM_UNSIGNED8,
|
||||||
vec![],
|
vec![],
|
||||||
|
vec![],
|
||||||
Box::new(unsigned8_alias_content()),
|
Box::new(unsigned8_alias_content()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -687,6 +736,7 @@ pub fn decimal_type() -> SolvedType {
|
||||||
SolvedType::Alias(
|
SolvedType::Alias(
|
||||||
Symbol::NUM_DECIMAL,
|
Symbol::NUM_DECIMAL,
|
||||||
vec![],
|
vec![],
|
||||||
|
vec![],
|
||||||
Box::new(decimal_alias_content()),
|
Box::new(decimal_alias_content()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -695,7 +745,8 @@ pub fn decimal_type() -> SolvedType {
|
||||||
pub fn bool_type() -> SolvedType {
|
pub fn bool_type() -> SolvedType {
|
||||||
SolvedType::Alias(
|
SolvedType::Alias(
|
||||||
Symbol::BOOL_BOOL,
|
Symbol::BOOL_BOOL,
|
||||||
Vec::new(),
|
vec![],
|
||||||
|
vec![],
|
||||||
Box::new(bool_alias_content()),
|
Box::new(bool_alias_content()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -728,6 +779,7 @@ pub fn result_type(a: SolvedType, e: SolvedType) -> SolvedType {
|
||||||
SolvedType::Alias(
|
SolvedType::Alias(
|
||||||
Symbol::RESULT_RESULT,
|
Symbol::RESULT_RESULT,
|
||||||
vec![("ok".into(), a.clone()), ("err".into(), e.clone())],
|
vec![("ok".into(), a.clone()), ("err".into(), e.clone())],
|
||||||
|
vec![],
|
||||||
Box::new(result_alias_content(a, e)),
|
Box::new(result_alias_content(a, e)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -757,7 +809,8 @@ pub fn str_type() -> SolvedType {
|
||||||
pub fn str_utf8_problem_type() -> SolvedType {
|
pub fn str_utf8_problem_type() -> SolvedType {
|
||||||
SolvedType::Alias(
|
SolvedType::Alias(
|
||||||
Symbol::STR_UT8_PROBLEM,
|
Symbol::STR_UT8_PROBLEM,
|
||||||
Vec::new(),
|
vec![],
|
||||||
|
vec![],
|
||||||
Box::new(str_utf8_problem_alias_content()),
|
Box::new(str_utf8_problem_alias_content()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -780,7 +833,8 @@ pub fn str_utf8_problem_alias_content() -> SolvedType {
|
||||||
pub fn str_utf8_byte_problem_type() -> SolvedType {
|
pub fn str_utf8_byte_problem_type() -> SolvedType {
|
||||||
SolvedType::Alias(
|
SolvedType::Alias(
|
||||||
Symbol::STR_UT8_BYTE_PROBLEM,
|
Symbol::STR_UT8_BYTE_PROBLEM,
|
||||||
Vec::new(),
|
vec![],
|
||||||
|
vec![],
|
||||||
Box::new(str_utf8_byte_problem_alias_content()),
|
Box::new(str_utf8_byte_problem_alias_content()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::subs::{Content, FlatType, GetSubsSlice, Subs, Variable};
|
use crate::subs::{Content, FlatType, GetSubsSlice, Subs, UnionTags, Variable};
|
||||||
use crate::types::{name_type_var, RecordField};
|
use crate::types::{name_type_var, RecordField};
|
||||||
use roc_collections::all::{MutMap, MutSet};
|
use roc_collections::all::{MutMap, MutSet};
|
||||||
use roc_module::ident::{Lowercase, TagName};
|
use roc_module::ident::{Lowercase, TagName};
|
||||||
|
@ -76,25 +76,34 @@ fn find_names_needed(
|
||||||
use crate::subs::Content::*;
|
use crate::subs::Content::*;
|
||||||
use crate::subs::FlatType::*;
|
use crate::subs::FlatType::*;
|
||||||
|
|
||||||
while let Some((recursive, _chain)) = subs.occurs(variable) {
|
while let Err((recursive, _chain)) = subs.occurs(variable) {
|
||||||
let rec_var = subs.fresh_unnamed_flex_var();
|
let rec_var = subs.fresh_unnamed_flex_var();
|
||||||
let content = subs.get_content_without_compacting(recursive);
|
let content = subs.get_content_without_compacting(recursive);
|
||||||
|
|
||||||
match content {
|
match content {
|
||||||
Content::Structure(FlatType::TagUnion(tags, ext_var)) => {
|
Content::Structure(FlatType::TagUnion(tags, ext_var)) => {
|
||||||
|
let ext_var = *ext_var;
|
||||||
|
|
||||||
let mut new_tags = MutMap::default();
|
let mut new_tags = MutMap::default();
|
||||||
|
|
||||||
for (label, args) in tags {
|
for (name_index, slice_index) in tags.iter_all() {
|
||||||
let new_args = args
|
let slice = subs[slice_index];
|
||||||
.clone()
|
|
||||||
.into_iter()
|
|
||||||
.map(|var| if var == recursive { rec_var } else { var })
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
new_tags.insert(label.clone(), new_args);
|
let mut new_vars = Vec::new();
|
||||||
|
for var_index in slice {
|
||||||
|
let var = subs[var_index];
|
||||||
|
new_vars.push(if var == recursive { rec_var } else { var });
|
||||||
}
|
}
|
||||||
|
|
||||||
let flat_type = FlatType::RecursiveTagUnion(rec_var, new_tags, *ext_var);
|
new_tags.insert(subs[name_index].clone(), new_vars);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut x: Vec<_> = new_tags.into_iter().collect();
|
||||||
|
x.sort();
|
||||||
|
|
||||||
|
let union_tags = UnionTags::insert_into_subs(subs, x);
|
||||||
|
|
||||||
|
let flat_type = FlatType::RecursiveTagUnion(rec_var, union_tags, ext_var);
|
||||||
subs.set_content(recursive, Content::Structure(flat_type));
|
subs.set_content(recursive, Content::Structure(flat_type));
|
||||||
}
|
}
|
||||||
_ => panic!(
|
_ => panic!(
|
||||||
|
@ -141,8 +150,9 @@ fn find_names_needed(
|
||||||
names_taken.insert(name.clone());
|
names_taken.insert(name.clone());
|
||||||
}
|
}
|
||||||
Structure(Apply(_, args)) => {
|
Structure(Apply(_, args)) => {
|
||||||
for var in args {
|
for index in args.into_iter() {
|
||||||
find_names_needed(*var, subs, roots, root_appearances, names_taken);
|
let var = subs[index];
|
||||||
|
find_names_needed(var, subs, roots, root_appearances, names_taken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Structure(Func(arg_vars, _closure_var, ret_var)) => {
|
Structure(Func(arg_vars, _closure_var, ret_var)) => {
|
||||||
|
@ -162,11 +172,12 @@ fn find_names_needed(
|
||||||
find_names_needed(*ext_var, subs, roots, root_appearances, names_taken);
|
find_names_needed(*ext_var, subs, roots, root_appearances, names_taken);
|
||||||
}
|
}
|
||||||
Structure(TagUnion(tags, ext_var)) => {
|
Structure(TagUnion(tags, ext_var)) => {
|
||||||
let mut sorted_tags: Vec<_> = tags.iter().collect();
|
for slice_index in tags.variables() {
|
||||||
sorted_tags.sort();
|
let slice = subs[slice_index];
|
||||||
|
for var_index in slice {
|
||||||
for var in sorted_tags.into_iter().map(|(_, v)| v).flatten() {
|
let var = subs[var_index];
|
||||||
find_names_needed(*var, subs, roots, root_appearances, names_taken);
|
find_names_needed(var, subs, roots, root_appearances, names_taken);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
find_names_needed(*ext_var, subs, roots, root_appearances, names_taken);
|
find_names_needed(*ext_var, subs, roots, root_appearances, names_taken);
|
||||||
|
@ -175,19 +186,22 @@ fn find_names_needed(
|
||||||
find_names_needed(*ext_var, subs, roots, root_appearances, names_taken);
|
find_names_needed(*ext_var, subs, roots, root_appearances, names_taken);
|
||||||
}
|
}
|
||||||
Structure(RecursiveTagUnion(rec_var, tags, ext_var)) => {
|
Structure(RecursiveTagUnion(rec_var, tags, ext_var)) => {
|
||||||
let mut sorted_tags: Vec<_> = tags.iter().collect();
|
for slice_index in tags.variables() {
|
||||||
sorted_tags.sort();
|
let slice = subs[slice_index];
|
||||||
|
for var_index in slice {
|
||||||
for var in sorted_tags.into_iter().map(|(_, v)| v).flatten() {
|
let var = subs[var_index];
|
||||||
find_names_needed(*var, subs, roots, root_appearances, names_taken);
|
find_names_needed(var, subs, roots, root_appearances, names_taken);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
find_names_needed(*ext_var, subs, roots, root_appearances, names_taken);
|
find_names_needed(*ext_var, subs, roots, root_appearances, names_taken);
|
||||||
find_names_needed(*rec_var, subs, roots, root_appearances, names_taken);
|
find_names_needed(*rec_var, subs, roots, root_appearances, names_taken);
|
||||||
}
|
}
|
||||||
Alias(_symbol, args, _actual) => {
|
Alias(_symbol, args, _actual) => {
|
||||||
for (_, var) in args {
|
// only find names for named parameters!
|
||||||
find_names_needed(*var, subs, roots, root_appearances, names_taken);
|
for var_index in args.variables().into_iter().take(args.len()) {
|
||||||
|
let var = subs[var_index];
|
||||||
|
find_names_needed(var, subs, roots, root_appearances, names_taken);
|
||||||
}
|
}
|
||||||
// TODO should we also look in the actual variable?
|
// TODO should we also look in the actual variable?
|
||||||
// find_names_needed(_actual, subs, roots, root_appearances, names_taken);
|
// find_names_needed(_actual, subs, roots, root_appearances, names_taken);
|
||||||
|
@ -294,10 +308,13 @@ fn write_content(env: &Env, content: &Content, subs: &Subs, buf: &mut String, pa
|
||||||
Symbol::NUM_NUM => {
|
Symbol::NUM_NUM => {
|
||||||
debug_assert_eq!(args.len(), 1);
|
debug_assert_eq!(args.len(), 1);
|
||||||
|
|
||||||
let (_, arg_var) = args
|
let arg_var_index = args
|
||||||
.get(0)
|
.variables()
|
||||||
|
.into_iter()
|
||||||
|
.next()
|
||||||
.expect("Num was not applied to a type argument!");
|
.expect("Num was not applied to a type argument!");
|
||||||
let content = subs.get_content_without_compacting(*arg_var);
|
let arg_var = subs[arg_var_index];
|
||||||
|
let content = subs.get_content_without_compacting(arg_var);
|
||||||
|
|
||||||
match &content {
|
match &content {
|
||||||
Alias(nested, _, _) => match *nested {
|
Alias(nested, _, _) => match *nested {
|
||||||
|
@ -320,11 +337,12 @@ fn write_content(env: &Env, content: &Content, subs: &Subs, buf: &mut String, pa
|
||||||
_ => write_parens!(write_parens, buf, {
|
_ => write_parens!(write_parens, buf, {
|
||||||
write_symbol(env, *symbol, buf);
|
write_symbol(env, *symbol, buf);
|
||||||
|
|
||||||
for (_, var) in args {
|
for var_index in args.variables() {
|
||||||
|
let var = subs[var_index];
|
||||||
buf.push(' ');
|
buf.push(' ');
|
||||||
write_content(
|
write_content(
|
||||||
env,
|
env,
|
||||||
subs.get_content_without_compacting(*var),
|
subs.get_content_without_compacting(var),
|
||||||
subs,
|
subs,
|
||||||
buf,
|
buf,
|
||||||
Parens::InTypeParam,
|
Parens::InTypeParam,
|
||||||
|
@ -345,13 +363,94 @@ fn write_content(env: &Env, content: &Content, subs: &Subs, buf: &mut String, pa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ExtContent<'a> {
|
||||||
|
Empty,
|
||||||
|
Content(Variable, &'a Content),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ExtContent<'a> {
|
||||||
|
fn from_var(subs: &'a Subs, ext: Variable) -> Self {
|
||||||
|
let content = subs.get_content_without_compacting(ext);
|
||||||
|
match content {
|
||||||
|
Content::Structure(FlatType::EmptyTagUnion) => ExtContent::Empty,
|
||||||
|
Content::Structure(FlatType::EmptyRecord) => ExtContent::Empty,
|
||||||
|
|
||||||
|
Content::FlexVar(_) | Content::RigidVar(_) => ExtContent::Content(ext, content),
|
||||||
|
|
||||||
|
other => unreachable!("something weird ended up in an ext var: {:?}", other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_ext_content<'a>(
|
||||||
|
env: &Env,
|
||||||
|
subs: &'a Subs,
|
||||||
|
buf: &mut String,
|
||||||
|
ext_content: ExtContent<'a>,
|
||||||
|
parens: Parens,
|
||||||
|
) {
|
||||||
|
if let ExtContent::Content(_, content) = ext_content {
|
||||||
|
// This is an open record or tag union, so print the variable
|
||||||
|
// right after the '}' or ']'
|
||||||
|
//
|
||||||
|
// e.g. the "*" at the end of `{ x: I64 }*`
|
||||||
|
// or the "r" at the end of `{ x: I64 }r`
|
||||||
|
write_content(env, content, subs, buf, parens)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_sorted_tags2<'a>(
|
||||||
|
env: &Env,
|
||||||
|
subs: &'a Subs,
|
||||||
|
buf: &mut String,
|
||||||
|
tags: &UnionTags,
|
||||||
|
ext_var: Variable,
|
||||||
|
) -> ExtContent<'a> {
|
||||||
|
// Sort the fields so they always end up in the same order.
|
||||||
|
let (it, new_ext_var) = tags.unsorted_iterator_and_ext(subs, ext_var);
|
||||||
|
let mut sorted_fields: Vec<_> = it.collect();
|
||||||
|
|
||||||
|
let interns = &env.interns;
|
||||||
|
let home = env.home;
|
||||||
|
|
||||||
|
sorted_fields.sort_by(|(a, _), (b, _)| {
|
||||||
|
a.as_ident_str(interns, home)
|
||||||
|
.cmp(&b.as_ident_str(interns, home))
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut any_written_yet = false;
|
||||||
|
|
||||||
|
for (label, vars) in sorted_fields {
|
||||||
|
if any_written_yet {
|
||||||
|
buf.push_str(", ");
|
||||||
|
} else {
|
||||||
|
any_written_yet = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.push_str(label.as_ident_str(interns, home).as_str());
|
||||||
|
|
||||||
|
for var in vars {
|
||||||
|
buf.push(' ');
|
||||||
|
write_content(
|
||||||
|
env,
|
||||||
|
subs.get_content_without_compacting(*var),
|
||||||
|
subs,
|
||||||
|
buf,
|
||||||
|
Parens::InTypeParam,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ExtContent::from_var(subs, new_ext_var)
|
||||||
|
}
|
||||||
|
|
||||||
fn write_sorted_tags<'a>(
|
fn write_sorted_tags<'a>(
|
||||||
env: &Env,
|
env: &Env,
|
||||||
subs: &'a Subs,
|
subs: &'a Subs,
|
||||||
buf: &mut String,
|
buf: &mut String,
|
||||||
tags: &MutMap<TagName, Vec<Variable>>,
|
tags: &MutMap<TagName, Vec<Variable>>,
|
||||||
ext_var: Variable,
|
ext_var: Variable,
|
||||||
) -> Result<(), (Variable, &'a Content)> {
|
) -> ExtContent<'a> {
|
||||||
// Sort the fields so they always end up in the same order.
|
// Sort the fields so they always end up in the same order.
|
||||||
let mut sorted_fields = Vec::with_capacity(tags.len());
|
let mut sorted_fields = Vec::with_capacity(tags.len());
|
||||||
|
|
||||||
|
@ -362,7 +461,7 @@ fn write_sorted_tags<'a>(
|
||||||
// If the `ext` contains tags, merge them into the list of tags.
|
// If the `ext` contains tags, merge them into the list of tags.
|
||||||
// this can occur when inferring mutually recursive tags
|
// this can occur when inferring mutually recursive tags
|
||||||
let mut from_ext = Default::default();
|
let mut from_ext = Default::default();
|
||||||
let ext_content = chase_ext_tag_union(subs, ext_var, &mut from_ext);
|
let _ext_content = chase_ext_tag_union(subs, ext_var, &mut from_ext);
|
||||||
|
|
||||||
for (tag_name, arguments) in from_ext.iter() {
|
for (tag_name, arguments) in from_ext.iter() {
|
||||||
sorted_fields.push((tag_name, arguments));
|
sorted_fields.push((tag_name, arguments));
|
||||||
|
@ -399,19 +498,31 @@ fn write_sorted_tags<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ext_content
|
ExtContent::from_var(subs, ext_var)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_flat_type(env: &Env, flat_type: &FlatType, subs: &Subs, buf: &mut String, parens: Parens) {
|
fn write_flat_type(env: &Env, flat_type: &FlatType, subs: &Subs, buf: &mut String, parens: Parens) {
|
||||||
use crate::subs::FlatType::*;
|
use crate::subs::FlatType::*;
|
||||||
|
|
||||||
match flat_type {
|
match flat_type {
|
||||||
Apply(symbol, args) => write_apply(env, *symbol, args, subs, buf, parens),
|
Apply(symbol, args) => write_apply(
|
||||||
|
env,
|
||||||
|
*symbol,
|
||||||
|
subs.get_subs_slice(*args.as_subs_slice()),
|
||||||
|
subs,
|
||||||
|
buf,
|
||||||
|
parens,
|
||||||
|
),
|
||||||
EmptyRecord => buf.push_str(EMPTY_RECORD),
|
EmptyRecord => buf.push_str(EMPTY_RECORD),
|
||||||
EmptyTagUnion => buf.push_str(EMPTY_TAG_UNION),
|
EmptyTagUnion => buf.push_str(EMPTY_TAG_UNION),
|
||||||
Func(args, _closure, ret) => {
|
Func(args, _closure, ret) => write_fn(
|
||||||
write_fn(env, subs.get_subs_slice(*args), *ret, subs, buf, parens)
|
env,
|
||||||
}
|
subs.get_subs_slice(*args.as_subs_slice()),
|
||||||
|
*ret,
|
||||||
|
subs,
|
||||||
|
buf,
|
||||||
|
parens,
|
||||||
|
),
|
||||||
Record(fields, ext_var) => {
|
Record(fields, ext_var) => {
|
||||||
use crate::types::{gather_fields, RecordStructure};
|
use crate::types::{gather_fields, RecordStructure};
|
||||||
|
|
||||||
|
@ -476,54 +587,33 @@ fn write_flat_type(env: &Env, flat_type: &FlatType, subs: &Subs, buf: &mut Strin
|
||||||
TagUnion(tags, ext_var) => {
|
TagUnion(tags, ext_var) => {
|
||||||
buf.push_str("[ ");
|
buf.push_str("[ ");
|
||||||
|
|
||||||
let ext_content = write_sorted_tags(env, subs, buf, tags, *ext_var);
|
let ext_content = write_sorted_tags2(env, subs, buf, tags, *ext_var);
|
||||||
|
|
||||||
buf.push_str(" ]");
|
buf.push_str(" ]");
|
||||||
|
|
||||||
if let Err((_, content)) = ext_content {
|
write_ext_content(env, subs, buf, ext_content, parens)
|
||||||
// This is an open tag union, so print the variable
|
|
||||||
// right after the ']'
|
|
||||||
//
|
|
||||||
// e.g. the "*" at the end of `{ x: I64 }*`
|
|
||||||
// or the "r" at the end of `{ x: I64 }r`
|
|
||||||
write_content(env, content, subs, buf, parens)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionOrTagUnion(tag_name, _, ext_var) => {
|
FunctionOrTagUnion(tag_name, _, ext_var) => {
|
||||||
buf.push_str("[ ");
|
buf.push_str("[ ");
|
||||||
|
|
||||||
let mut tags: MutMap<TagName, _> = MutMap::default();
|
let mut tags: MutMap<TagName, _> = MutMap::default();
|
||||||
tags.insert(*tag_name.clone(), vec![]);
|
tags.insert(subs[*tag_name].clone(), vec![]);
|
||||||
let ext_content = write_sorted_tags(env, subs, buf, &tags, *ext_var);
|
let ext_content = write_sorted_tags(env, subs, buf, &tags, *ext_var);
|
||||||
|
|
||||||
buf.push_str(" ]");
|
buf.push_str(" ]");
|
||||||
|
|
||||||
if let Err((_, content)) = ext_content {
|
write_ext_content(env, subs, buf, ext_content, parens)
|
||||||
// This is an open tag union, so print the variable
|
|
||||||
// right after the ']'
|
|
||||||
//
|
|
||||||
// e.g. the "*" at the end of `{ x: I64 }*`
|
|
||||||
// or the "r" at the end of `{ x: I64 }r`
|
|
||||||
write_content(env, content, subs, buf, parens)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RecursiveTagUnion(rec_var, tags, ext_var) => {
|
RecursiveTagUnion(rec_var, tags, ext_var) => {
|
||||||
buf.push_str("[ ");
|
buf.push_str("[ ");
|
||||||
|
|
||||||
let ext_content = write_sorted_tags(env, subs, buf, tags, *ext_var);
|
let ext_content = write_sorted_tags2(env, subs, buf, tags, *ext_var);
|
||||||
|
|
||||||
buf.push_str(" ]");
|
buf.push_str(" ]");
|
||||||
|
|
||||||
if let Err((_, content)) = ext_content {
|
write_ext_content(env, subs, buf, ext_content, parens);
|
||||||
// This is an open tag union, so print the variable
|
|
||||||
// right after the ']'
|
|
||||||
//
|
|
||||||
// e.g. the "*" at the end of `{ x: I64 }*`
|
|
||||||
// or the "r" at the end of `{ x: I64 }r`
|
|
||||||
write_content(env, content, subs, buf, parens)
|
|
||||||
}
|
|
||||||
|
|
||||||
buf.push_str(" as ");
|
buf.push_str(" as ");
|
||||||
write_content(
|
write_content(
|
||||||
|
@ -548,54 +638,41 @@ pub fn chase_ext_tag_union<'a>(
|
||||||
use FlatType::*;
|
use FlatType::*;
|
||||||
match subs.get_content_without_compacting(var) {
|
match subs.get_content_without_compacting(var) {
|
||||||
Content::Structure(EmptyTagUnion) => Ok(()),
|
Content::Structure(EmptyTagUnion) => Ok(()),
|
||||||
Content::Structure(TagUnion(tags, ext_var))
|
Content::Structure(TagUnion(tags, ext_var)) => {
|
||||||
| Content::Structure(RecursiveTagUnion(_, tags, ext_var)) => {
|
for (name_index, slice_index) in tags.iter_all() {
|
||||||
for (label, vars) in tags {
|
let subs_slice = subs[slice_index];
|
||||||
fields.push((label.clone(), vars.to_vec()));
|
let slice = subs.get_subs_slice(*subs_slice.as_subs_slice());
|
||||||
|
let tag_name = subs[name_index].clone();
|
||||||
|
|
||||||
|
fields.push((tag_name, slice.to_vec()));
|
||||||
|
}
|
||||||
|
|
||||||
|
chase_ext_tag_union(subs, *ext_var, fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
Content::Structure(RecursiveTagUnion(_, tags, ext_var)) => {
|
||||||
|
for (name_index, slice_index) in tags.iter_all() {
|
||||||
|
let subs_slice = subs[slice_index];
|
||||||
|
let slice = subs.get_subs_slice(*subs_slice.as_subs_slice());
|
||||||
|
let tag_name = subs[name_index].clone();
|
||||||
|
|
||||||
|
fields.push((tag_name, slice.to_vec()));
|
||||||
}
|
}
|
||||||
|
|
||||||
chase_ext_tag_union(subs, *ext_var, fields)
|
chase_ext_tag_union(subs, *ext_var, fields)
|
||||||
}
|
}
|
||||||
Content::Structure(FunctionOrTagUnion(tag_name, _, ext_var)) => {
|
Content::Structure(FunctionOrTagUnion(tag_name, _, ext_var)) => {
|
||||||
fields.push((*tag_name.clone(), vec![]));
|
fields.push((subs[*tag_name].clone(), vec![]));
|
||||||
|
|
||||||
chase_ext_tag_union(subs, *ext_var, fields)
|
chase_ext_tag_union(subs, *ext_var, fields)
|
||||||
}
|
}
|
||||||
|
|
||||||
Content::Alias(_, _, var) => chase_ext_tag_union(subs, *var, fields),
|
Content::Alias(_, _, var) => chase_ext_tag_union(subs, *var, fields),
|
||||||
|
|
||||||
content => Err((var, content)),
|
content => Err((var, content)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn chase_ext_record(
|
|
||||||
subs: &Subs,
|
|
||||||
var: Variable,
|
|
||||||
fields: &mut MutMap<Lowercase, RecordField<Variable>>,
|
|
||||||
) -> Result<(), (Variable, Content)> {
|
|
||||||
use crate::subs::Content::*;
|
|
||||||
use crate::subs::FlatType::*;
|
|
||||||
|
|
||||||
match subs.get_content_without_compacting(var) {
|
|
||||||
Structure(Record(sub_fields, sub_ext)) => {
|
|
||||||
for (i1, i2, i3) in sub_fields.iter_all() {
|
|
||||||
let label = &subs[i1];
|
|
||||||
let var = subs[i2];
|
|
||||||
let record_field = subs[i3].map(|_| var);
|
|
||||||
|
|
||||||
fields.insert(label.clone(), record_field);
|
|
||||||
}
|
|
||||||
|
|
||||||
chase_ext_record(subs, *sub_ext, fields)
|
|
||||||
}
|
|
||||||
|
|
||||||
Structure(EmptyRecord) => Ok(()),
|
|
||||||
|
|
||||||
Alias(_, _, var) => chase_ext_record(subs, *var, fields),
|
|
||||||
|
|
||||||
content => Err((var, content.clone())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_apply(
|
fn write_apply(
|
||||||
env: &Env,
|
env: &Env,
|
||||||
symbol: Symbol,
|
symbol: Symbol,
|
||||||
|
|
|
@ -119,18 +119,24 @@ fn hash_solved_type_help<H: Hasher>(
|
||||||
hash_solved_type_help(ext, flex_vars, state);
|
hash_solved_type_help(ext, flex_vars, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
Alias(name, arguments, actual) => {
|
Alias(name, arguments, solved_lambda_sets, actual) => {
|
||||||
name.hash(state);
|
name.hash(state);
|
||||||
for (name, x) in arguments {
|
for (name, x) in arguments {
|
||||||
name.hash(state);
|
name.hash(state);
|
||||||
hash_solved_type_help(x, flex_vars, state);
|
hash_solved_type_help(x, flex_vars, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for set in solved_lambda_sets {
|
||||||
|
hash_solved_type_help(&set.0, flex_vars, state);
|
||||||
|
}
|
||||||
|
|
||||||
hash_solved_type_help(actual, flex_vars, state);
|
hash_solved_type_help(actual, flex_vars, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
HostExposedAlias {
|
HostExposedAlias {
|
||||||
name,
|
name,
|
||||||
arguments,
|
arguments,
|
||||||
|
lambda_set_variables: solved_lambda_sets,
|
||||||
actual,
|
actual,
|
||||||
actual_var,
|
actual_var,
|
||||||
} => {
|
} => {
|
||||||
|
@ -139,6 +145,11 @@ fn hash_solved_type_help<H: Hasher>(
|
||||||
name.hash(state);
|
name.hash(state);
|
||||||
hash_solved_type_help(x, flex_vars, state);
|
hash_solved_type_help(x, flex_vars, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for set in solved_lambda_sets {
|
||||||
|
hash_solved_type_help(&set.0, flex_vars, state);
|
||||||
|
}
|
||||||
|
|
||||||
hash_solved_type_help(actual, flex_vars, state);
|
hash_solved_type_help(actual, flex_vars, state);
|
||||||
var_id_hash_help(*actual_var, flex_vars, state);
|
var_id_hash_help(*actual_var, flex_vars, state);
|
||||||
}
|
}
|
||||||
|
@ -156,6 +167,9 @@ fn var_id_hash_help<H: Hasher>(var_id: VarId, flex_vars: &mut Vec<VarId>, state:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct SolvedLambdaSet(pub SolvedType);
|
||||||
|
|
||||||
/// This is a fully solved type, with no Variables remaining in it.
|
/// This is a fully solved type, with no Variables remaining in it.
|
||||||
#[derive(Debug, Clone, Eq)]
|
#[derive(Debug, Clone, Eq)]
|
||||||
pub enum SolvedType {
|
pub enum SolvedType {
|
||||||
|
@ -183,11 +197,18 @@ pub enum SolvedType {
|
||||||
Erroneous(Problem),
|
Erroneous(Problem),
|
||||||
|
|
||||||
/// A type alias
|
/// A type alias
|
||||||
Alias(Symbol, Vec<(Lowercase, SolvedType)>, Box<SolvedType>),
|
/// TODO transmit lambda sets!
|
||||||
|
Alias(
|
||||||
|
Symbol,
|
||||||
|
Vec<(Lowercase, SolvedType)>,
|
||||||
|
Vec<SolvedLambdaSet>,
|
||||||
|
Box<SolvedType>,
|
||||||
|
),
|
||||||
|
|
||||||
HostExposedAlias {
|
HostExposedAlias {
|
||||||
name: Symbol,
|
name: Symbol,
|
||||||
arguments: Vec<(Lowercase, SolvedType)>,
|
arguments: Vec<(Lowercase, SolvedType)>,
|
||||||
|
lambda_set_variables: Vec<SolvedLambdaSet>,
|
||||||
actual_var: VarId,
|
actual_var: VarId,
|
||||||
actual: Box<SolvedType>,
|
actual: Box<SolvedType>,
|
||||||
},
|
},
|
||||||
|
@ -293,19 +314,37 @@ impl SolvedType {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Erroneous(problem) => SolvedType::Erroneous(problem.clone()),
|
Erroneous(problem) => SolvedType::Erroneous(problem.clone()),
|
||||||
Alias(symbol, args, box_type) => {
|
Alias {
|
||||||
|
symbol,
|
||||||
|
type_arguments,
|
||||||
|
lambda_set_variables,
|
||||||
|
actual: box_type,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
let solved_type = Self::from_type(solved_subs, box_type);
|
let solved_type = Self::from_type(solved_subs, box_type);
|
||||||
let mut solved_args = Vec::with_capacity(args.len());
|
let mut solved_args = Vec::with_capacity(type_arguments.len());
|
||||||
|
|
||||||
for (name, var) in args {
|
for (name, var) in type_arguments {
|
||||||
solved_args.push((name.clone(), Self::from_type(solved_subs, var)));
|
solved_args.push((name.clone(), Self::from_type(solved_subs, var)));
|
||||||
}
|
}
|
||||||
|
|
||||||
SolvedType::Alias(*symbol, solved_args, Box::new(solved_type))
|
let mut solved_lambda_sets = Vec::with_capacity(lambda_set_variables.len());
|
||||||
|
|
||||||
|
for var in lambda_set_variables {
|
||||||
|
solved_lambda_sets.push(SolvedLambdaSet(Self::from_type(solved_subs, &var.0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
SolvedType::Alias(
|
||||||
|
*symbol,
|
||||||
|
solved_args,
|
||||||
|
solved_lambda_sets,
|
||||||
|
Box::new(solved_type),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
HostExposedAlias {
|
HostExposedAlias {
|
||||||
name,
|
name,
|
||||||
arguments,
|
type_arguments: arguments,
|
||||||
|
lambda_set_variables,
|
||||||
actual_var,
|
actual_var,
|
||||||
actual,
|
actual,
|
||||||
} => {
|
} => {
|
||||||
|
@ -316,9 +355,15 @@ impl SolvedType {
|
||||||
solved_args.push((name.clone(), Self::from_type(solved_subs, var)));
|
solved_args.push((name.clone(), Self::from_type(solved_subs, var)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut solved_lambda_sets = Vec::with_capacity(lambda_set_variables.len());
|
||||||
|
for var in lambda_set_variables {
|
||||||
|
solved_lambda_sets.push(SolvedLambdaSet(Self::from_type(solved_subs, &var.0)));
|
||||||
|
}
|
||||||
|
|
||||||
SolvedType::HostExposedAlias {
|
SolvedType::HostExposedAlias {
|
||||||
name: *name,
|
name: *name,
|
||||||
arguments: solved_args,
|
arguments: solved_args,
|
||||||
|
lambda_set_variables: solved_lambda_sets,
|
||||||
actual_var: VarId::from_var(*actual_var, solved_subs.inner()),
|
actual_var: VarId::from_var(*actual_var, solved_subs.inner()),
|
||||||
actual: Box::new(solved_type),
|
actual: Box::new(solved_type),
|
||||||
}
|
}
|
||||||
|
@ -352,16 +397,29 @@ impl SolvedType {
|
||||||
Alias(symbol, args, actual_var) => {
|
Alias(symbol, args, actual_var) => {
|
||||||
let mut new_args = Vec::with_capacity(args.len());
|
let mut new_args = Vec::with_capacity(args.len());
|
||||||
|
|
||||||
for (arg_name, arg_var) in args {
|
for (name_index, var_index) in args.named_type_arguments() {
|
||||||
|
let arg_var = subs[var_index];
|
||||||
|
|
||||||
new_args.push((
|
new_args.push((
|
||||||
arg_name.clone(),
|
subs[name_index].clone(),
|
||||||
Self::from_var_help(subs, recursion_vars, *arg_var),
|
Self::from_var_help(subs, recursion_vars, arg_var),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut solved_lambda_sets = Vec::with_capacity(0);
|
||||||
|
for var_index in args.unnamed_type_arguments() {
|
||||||
|
let var = subs[var_index];
|
||||||
|
|
||||||
|
solved_lambda_sets.push(SolvedLambdaSet(Self::from_var_help(
|
||||||
|
subs,
|
||||||
|
recursion_vars,
|
||||||
|
var,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
let aliased_to = Self::from_var_help(subs, recursion_vars, *actual_var);
|
let aliased_to = Self::from_var_help(subs, recursion_vars, *actual_var);
|
||||||
|
|
||||||
SolvedType::Alias(*symbol, new_args, Box::new(aliased_to))
|
SolvedType::Alias(*symbol, new_args, solved_lambda_sets, Box::new(aliased_to))
|
||||||
}
|
}
|
||||||
Error => SolvedType::Error,
|
Error => SolvedType::Error,
|
||||||
}
|
}
|
||||||
|
@ -378,8 +436,8 @@ impl SolvedType {
|
||||||
Apply(symbol, args) => {
|
Apply(symbol, args) => {
|
||||||
let mut new_args = Vec::with_capacity(args.len());
|
let mut new_args = Vec::with_capacity(args.len());
|
||||||
|
|
||||||
for var in args.iter().copied() {
|
for var in subs.get_subs_slice(*args.as_subs_slice()) {
|
||||||
new_args.push(Self::from_var_help(subs, recursion_vars, var));
|
new_args.push(Self::from_var_help(subs, recursion_vars, *var));
|
||||||
}
|
}
|
||||||
|
|
||||||
SolvedType::Apply(*symbol, new_args)
|
SolvedType::Apply(*symbol, new_args)
|
||||||
|
@ -387,7 +445,7 @@ impl SolvedType {
|
||||||
Func(args, closure, ret) => {
|
Func(args, closure, ret) => {
|
||||||
let mut new_args = Vec::with_capacity(args.len());
|
let mut new_args = Vec::with_capacity(args.len());
|
||||||
|
|
||||||
for var in subs.get_subs_slice(*args) {
|
for var in subs.get_subs_slice(*args.as_subs_slice()) {
|
||||||
new_args.push(Self::from_var_help(subs, recursion_vars, *var));
|
new_args.push(Self::from_var_help(subs, recursion_vars, *var));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -420,13 +478,16 @@ impl SolvedType {
|
||||||
TagUnion(tags, ext_var) => {
|
TagUnion(tags, ext_var) => {
|
||||||
let mut new_tags = Vec::with_capacity(tags.len());
|
let mut new_tags = Vec::with_capacity(tags.len());
|
||||||
|
|
||||||
for (tag_name, args) in tags {
|
for (name_index, slice_index) in tags.iter_all() {
|
||||||
let mut new_args = Vec::with_capacity(args.len());
|
let slice = subs[slice_index];
|
||||||
|
|
||||||
for var in args {
|
let mut new_args = Vec::with_capacity(slice.len());
|
||||||
new_args.push(Self::from_var_help(subs, recursion_vars, *var));
|
|
||||||
|
for var_index in slice {
|
||||||
|
let var = subs[var_index];
|
||||||
|
new_args.push(Self::from_var_help(subs, recursion_vars, var));
|
||||||
}
|
}
|
||||||
|
let tag_name = subs[name_index].clone();
|
||||||
new_tags.push((tag_name.clone(), new_args));
|
new_tags.push((tag_name.clone(), new_args));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -437,20 +498,23 @@ impl SolvedType {
|
||||||
FunctionOrTagUnion(tag_name, symbol, ext_var) => {
|
FunctionOrTagUnion(tag_name, symbol, ext_var) => {
|
||||||
let ext = Self::from_var_help(subs, recursion_vars, *ext_var);
|
let ext = Self::from_var_help(subs, recursion_vars, *ext_var);
|
||||||
|
|
||||||
SolvedType::FunctionOrTagUnion(*tag_name.clone(), *symbol, Box::new(ext))
|
SolvedType::FunctionOrTagUnion(subs[*tag_name].clone(), *symbol, Box::new(ext))
|
||||||
}
|
}
|
||||||
RecursiveTagUnion(rec_var, tags, ext_var) => {
|
RecursiveTagUnion(rec_var, tags, ext_var) => {
|
||||||
recursion_vars.insert(subs, *rec_var);
|
recursion_vars.insert(subs, *rec_var);
|
||||||
|
|
||||||
let mut new_tags = Vec::with_capacity(tags.len());
|
let mut new_tags = Vec::with_capacity(tags.len());
|
||||||
|
|
||||||
for (tag_name, args) in tags {
|
for (name_index, slice_index) in tags.iter_all() {
|
||||||
let mut new_args = Vec::with_capacity(args.len());
|
let slice = subs[slice_index];
|
||||||
|
|
||||||
for var in args {
|
let mut new_args = Vec::with_capacity(slice.len());
|
||||||
new_args.push(Self::from_var_help(subs, recursion_vars, *var));
|
|
||||||
|
for var_index in slice {
|
||||||
|
let var = subs[var_index];
|
||||||
|
new_args.push(Self::from_var_help(subs, recursion_vars, var));
|
||||||
}
|
}
|
||||||
|
let tag_name = subs[name_index].clone();
|
||||||
new_tags.push((tag_name.clone(), new_args));
|
new_tags.push((tag_name.clone(), new_args));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -607,20 +671,35 @@ pub fn to_type(
|
||||||
Box::new(to_type(ext, free_vars, var_store)),
|
Box::new(to_type(ext, free_vars, var_store)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Alias(symbol, solved_type_variables, solved_actual) => {
|
Alias(symbol, solved_type_variables, solved_lambda_sets, solved_actual) => {
|
||||||
let mut type_variables = Vec::with_capacity(solved_type_variables.len());
|
let mut type_variables = Vec::with_capacity(solved_type_variables.len());
|
||||||
|
|
||||||
for (lowercase, solved_arg) in solved_type_variables {
|
for (lowercase, solved_arg) in solved_type_variables {
|
||||||
type_variables.push((lowercase.clone(), to_type(solved_arg, free_vars, var_store)));
|
type_variables.push((lowercase.clone(), to_type(solved_arg, free_vars, var_store)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut lambda_set_variables = Vec::with_capacity(solved_lambda_sets.len());
|
||||||
|
for solved_set in solved_lambda_sets {
|
||||||
|
lambda_set_variables.push(crate::types::LambdaSet(to_type(
|
||||||
|
&solved_set.0,
|
||||||
|
free_vars,
|
||||||
|
var_store,
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
let actual = to_type(solved_actual, free_vars, var_store);
|
let actual = to_type(solved_actual, free_vars, var_store);
|
||||||
|
|
||||||
Type::Alias(*symbol, type_variables, Box::new(actual))
|
Type::Alias {
|
||||||
|
symbol: *symbol,
|
||||||
|
type_arguments: type_variables,
|
||||||
|
lambda_set_variables,
|
||||||
|
actual: Box::new(actual),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
HostExposedAlias {
|
HostExposedAlias {
|
||||||
name,
|
name,
|
||||||
arguments: solved_type_variables,
|
arguments: solved_type_variables,
|
||||||
|
lambda_set_variables: solved_lambda_sets,
|
||||||
actual_var,
|
actual_var,
|
||||||
actual: solved_actual,
|
actual: solved_actual,
|
||||||
} => {
|
} => {
|
||||||
|
@ -630,11 +709,21 @@ pub fn to_type(
|
||||||
type_variables.push((lowercase.clone(), to_type(solved_arg, free_vars, var_store)));
|
type_variables.push((lowercase.clone(), to_type(solved_arg, free_vars, var_store)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut lambda_set_variables = Vec::with_capacity(solved_lambda_sets.len());
|
||||||
|
for solved_set in solved_lambda_sets {
|
||||||
|
lambda_set_variables.push(crate::types::LambdaSet(to_type(
|
||||||
|
&solved_set.0,
|
||||||
|
free_vars,
|
||||||
|
var_store,
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
let actual = to_type(solved_actual, free_vars, var_store);
|
let actual = to_type(solved_actual, free_vars, var_store);
|
||||||
|
|
||||||
Type::HostExposedAlias {
|
Type::HostExposedAlias {
|
||||||
name: *name,
|
name: *name,
|
||||||
arguments: type_variables,
|
type_arguments: type_variables,
|
||||||
|
lambda_set_variables,
|
||||||
actual_var: var_id_to_flex_var(*actual_var, free_vars, var_store),
|
actual_var: var_id_to_flex_var(*actual_var, free_vars, var_store),
|
||||||
actual: Box::new(actual),
|
actual: Box::new(actual),
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,7 @@
|
||||||
use crate::pretty_print::Parens;
|
use crate::pretty_print::Parens;
|
||||||
use crate::subs::{LambdaSet, RecordFields, Subs, VarStore, Variable};
|
use crate::subs::{
|
||||||
|
GetSubsSlice, RecordFields, Subs, UnionTags, VarStore, Variable, VariableSubsSlice,
|
||||||
|
};
|
||||||
use roc_collections::all::{ImMap, ImSet, Index, MutSet, SendMap};
|
use roc_collections::all::{ImMap, ImSet, Index, MutSet, SendMap};
|
||||||
use roc_module::ident::{ForeignSymbol, Ident, Lowercase, TagName};
|
use roc_module::ident::{ForeignSymbol, Ident, Lowercase, TagName};
|
||||||
use roc_module::low_level::LowLevel;
|
use roc_module::low_level::LowLevel;
|
||||||
|
@ -11,6 +13,11 @@ pub const TYPE_NUM: &str = "Num";
|
||||||
pub const TYPE_INTEGER: &str = "Integer";
|
pub const TYPE_INTEGER: &str = "Integer";
|
||||||
pub const TYPE_FLOATINGPOINT: &str = "FloatingPoint";
|
pub const TYPE_FLOATINGPOINT: &str = "FloatingPoint";
|
||||||
|
|
||||||
|
const GREEK_LETTERS: &[char] = &[
|
||||||
|
'α', 'ν', 'β', 'ξ', 'γ', 'ο', 'δ', 'π', 'ε', 'ρ', 'ζ', 'σ', 'η', 'τ', 'θ', 'υ', 'ι', 'φ', 'κ',
|
||||||
|
'χ', 'λ', 'ψ', 'μ', 'ω', 'ς',
|
||||||
|
];
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Intuitively
|
/// Intuitively
|
||||||
///
|
///
|
||||||
|
@ -132,6 +139,26 @@ impl RecordField<Type> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
|
pub struct LambdaSet(pub Type);
|
||||||
|
|
||||||
|
impl LambdaSet {
|
||||||
|
fn substitute(&mut self, substitutions: &ImMap<Variable, Type>) {
|
||||||
|
self.0.substitute(substitutions);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn instantiate_aliases(
|
||||||
|
&mut self,
|
||||||
|
region: Region,
|
||||||
|
aliases: &ImMap<Symbol, Alias>,
|
||||||
|
var_store: &mut VarStore,
|
||||||
|
introduced: &mut ImSet<Variable>,
|
||||||
|
) {
|
||||||
|
self.0
|
||||||
|
.instantiate_aliases(region, aliases, var_store, introduced)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone)]
|
#[derive(PartialEq, Eq, Clone)]
|
||||||
pub enum Type {
|
pub enum Type {
|
||||||
EmptyRec,
|
EmptyRec,
|
||||||
|
@ -141,10 +168,16 @@ pub enum Type {
|
||||||
Record(SendMap<Lowercase, RecordField<Type>>, Box<Type>),
|
Record(SendMap<Lowercase, RecordField<Type>>, Box<Type>),
|
||||||
TagUnion(Vec<(TagName, Vec<Type>)>, Box<Type>),
|
TagUnion(Vec<(TagName, Vec<Type>)>, Box<Type>),
|
||||||
FunctionOrTagUnion(TagName, Symbol, Box<Type>),
|
FunctionOrTagUnion(TagName, Symbol, Box<Type>),
|
||||||
Alias(Symbol, Vec<(Lowercase, Type)>, Box<Type>),
|
Alias {
|
||||||
|
symbol: Symbol,
|
||||||
|
type_arguments: Vec<(Lowercase, Type)>,
|
||||||
|
lambda_set_variables: Vec<LambdaSet>,
|
||||||
|
actual: Box<Type>,
|
||||||
|
},
|
||||||
HostExposedAlias {
|
HostExposedAlias {
|
||||||
name: Symbol,
|
name: Symbol,
|
||||||
arguments: Vec<(Lowercase, Type)>,
|
type_arguments: Vec<(Lowercase, Type)>,
|
||||||
|
lambda_set_variables: Vec<LambdaSet>,
|
||||||
actual_var: Variable,
|
actual_var: Variable,
|
||||||
actual: Box<Type>,
|
actual: Box<Type>,
|
||||||
},
|
},
|
||||||
|
@ -197,20 +230,36 @@ impl fmt::Debug for Type {
|
||||||
|
|
||||||
write!(f, ")")
|
write!(f, ")")
|
||||||
}
|
}
|
||||||
Type::Alias(symbol, args, _actual) => {
|
Type::Alias {
|
||||||
write!(f, "Alias {:?}", symbol)?;
|
symbol,
|
||||||
|
type_arguments,
|
||||||
|
lambda_set_variables,
|
||||||
|
actual: _actual,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
write!(f, "(Alias {:?}", symbol)?;
|
||||||
|
|
||||||
for (_, arg) in args {
|
for (_, arg) in type_arguments {
|
||||||
write!(f, " {:?}", arg)?;
|
write!(f, " {:?}", arg)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (lambda_set, greek_letter) in
|
||||||
|
lambda_set_variables.iter().zip(GREEK_LETTERS.iter())
|
||||||
|
{
|
||||||
|
write!(f, " {}@{:?}", greek_letter, lambda_set.0)?;
|
||||||
|
}
|
||||||
|
|
||||||
// Sometimes it's useful to see the expansion of the alias
|
// Sometimes it's useful to see the expansion of the alias
|
||||||
// write!(f, "[ but actually {:?} ]", _actual)?;
|
write!(f, "[ but actually {:?} ]", _actual)?;
|
||||||
|
|
||||||
|
write!(f, ")")?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Type::HostExposedAlias {
|
Type::HostExposedAlias {
|
||||||
name, arguments, ..
|
name,
|
||||||
|
type_arguments: arguments,
|
||||||
|
..
|
||||||
} => {
|
} => {
|
||||||
write!(f, "HostExposedAlias {:?}", name)?;
|
write!(f, "HostExposedAlias {:?}", name)?;
|
||||||
|
|
||||||
|
@ -441,14 +490,24 @@ impl Type {
|
||||||
}
|
}
|
||||||
ext.substitute(substitutions);
|
ext.substitute(substitutions);
|
||||||
}
|
}
|
||||||
Alias(_, zipped, actual_type) => {
|
Alias {
|
||||||
for (_, value) in zipped.iter_mut() {
|
type_arguments,
|
||||||
|
lambda_set_variables,
|
||||||
|
actual,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
for (_, value) in type_arguments.iter_mut() {
|
||||||
value.substitute(substitutions);
|
value.substitute(substitutions);
|
||||||
}
|
}
|
||||||
actual_type.substitute(substitutions);
|
|
||||||
|
for lambda_set in lambda_set_variables.iter_mut() {
|
||||||
|
lambda_set.substitute(substitutions);
|
||||||
|
}
|
||||||
|
|
||||||
|
actual.substitute(substitutions);
|
||||||
}
|
}
|
||||||
HostExposedAlias {
|
HostExposedAlias {
|
||||||
arguments,
|
type_arguments: arguments,
|
||||||
actual: actual_type,
|
actual: actual_type,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
|
@ -496,8 +555,11 @@ impl Type {
|
||||||
}
|
}
|
||||||
ext.substitute_alias(rep_symbol, actual);
|
ext.substitute_alias(rep_symbol, actual);
|
||||||
}
|
}
|
||||||
Alias(_, _, actual_type) => {
|
Alias {
|
||||||
actual_type.substitute_alias(rep_symbol, actual);
|
actual: alias_actual,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
alias_actual.substitute_alias(rep_symbol, actual);
|
||||||
}
|
}
|
||||||
HostExposedAlias {
|
HostExposedAlias {
|
||||||
actual: actual_type,
|
actual: actual_type,
|
||||||
|
@ -546,9 +608,11 @@ impl Type {
|
||||||
ext.contains_symbol(rep_symbol)
|
ext.contains_symbol(rep_symbol)
|
||||||
|| fields.values().any(|arg| arg.contains_symbol(rep_symbol))
|
|| fields.values().any(|arg| arg.contains_symbol(rep_symbol))
|
||||||
}
|
}
|
||||||
Alias(alias_symbol, _, actual_type) => {
|
Alias {
|
||||||
alias_symbol == &rep_symbol || actual_type.contains_symbol(rep_symbol)
|
symbol: alias_symbol,
|
||||||
}
|
actual: actual_type,
|
||||||
|
..
|
||||||
|
} => alias_symbol == &rep_symbol || actual_type.contains_symbol(rep_symbol),
|
||||||
HostExposedAlias { name, actual, .. } => {
|
HostExposedAlias { name, actual, .. } => {
|
||||||
name == &rep_symbol || actual.contains_symbol(rep_symbol)
|
name == &rep_symbol || actual.contains_symbol(rep_symbol)
|
||||||
}
|
}
|
||||||
|
@ -584,7 +648,10 @@ impl Type {
|
||||||
.values()
|
.values()
|
||||||
.any(|arg| arg.contains_variable(rep_variable))
|
.any(|arg| arg.contains_variable(rep_variable))
|
||||||
}
|
}
|
||||||
Alias(_, _, actual_type) => actual_type.contains_variable(rep_variable),
|
Alias {
|
||||||
|
actual: actual_type,
|
||||||
|
..
|
||||||
|
} => actual_type.contains_variable(rep_variable),
|
||||||
HostExposedAlias { actual, .. } => actual.contains_variable(rep_variable),
|
HostExposedAlias { actual, .. } => actual.contains_variable(rep_variable),
|
||||||
Apply(_, args) => args.iter().any(|arg| arg.contains_variable(rep_variable)),
|
Apply(_, args) => args.iter().any(|arg| arg.contains_variable(rep_variable)),
|
||||||
EmptyRec | EmptyTagUnion | Erroneous(_) => false,
|
EmptyRec | EmptyTagUnion | Erroneous(_) => false,
|
||||||
|
@ -601,7 +668,7 @@ impl Type {
|
||||||
/// a shallow dealias, continue until the first constructor is not an alias.
|
/// a shallow dealias, continue until the first constructor is not an alias.
|
||||||
pub fn shallow_dealias(&self) -> &Self {
|
pub fn shallow_dealias(&self) -> &Self {
|
||||||
match self {
|
match self {
|
||||||
Type::Alias(_, _, actual) => actual.shallow_dealias(),
|
Type::Alias { actual, .. } => actual.shallow_dealias(),
|
||||||
_ => self,
|
_ => self,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -641,16 +708,26 @@ impl Type {
|
||||||
ext.instantiate_aliases(region, aliases, var_store, introduced);
|
ext.instantiate_aliases(region, aliases, var_store, introduced);
|
||||||
}
|
}
|
||||||
HostExposedAlias {
|
HostExposedAlias {
|
||||||
arguments: type_args,
|
type_arguments: type_args,
|
||||||
|
lambda_set_variables,
|
||||||
actual: actual_type,
|
actual: actual_type,
|
||||||
..
|
..
|
||||||
}
|
}
|
||||||
| Alias(_, type_args, actual_type) => {
|
| Alias {
|
||||||
|
type_arguments: type_args,
|
||||||
|
lambda_set_variables,
|
||||||
|
actual: actual_type,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
for arg in type_args {
|
for arg in type_args {
|
||||||
arg.1
|
arg.1
|
||||||
.instantiate_aliases(region, aliases, var_store, introduced);
|
.instantiate_aliases(region, aliases, var_store, introduced);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for arg in lambda_set_variables {
|
||||||
|
arg.instantiate_aliases(region, aliases, var_store, introduced);
|
||||||
|
}
|
||||||
|
|
||||||
actual_type.instantiate_aliases(region, aliases, var_store, introduced);
|
actual_type.instantiate_aliases(region, aliases, var_store, introduced);
|
||||||
}
|
}
|
||||||
Apply(symbol, args) => {
|
Apply(symbol, args) => {
|
||||||
|
@ -685,40 +762,16 @@ impl Type {
|
||||||
substitution.insert(*placeholder, filler);
|
substitution.insert(*placeholder, filler);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Instantiate "hidden" uniqueness variables
|
let mut lambda_set_variables =
|
||||||
//
|
Vec::with_capacity(alias.lambda_set_variables.len());
|
||||||
// Aliases can hide uniqueness variables: e.g. in
|
for lambda_set in alias.lambda_set_variables.iter() {
|
||||||
//
|
let fresh = var_store.fresh();
|
||||||
// Model : { x : Int, y : Bool }
|
introduced.insert(fresh);
|
||||||
//
|
|
||||||
// Its lifted variant is
|
|
||||||
//
|
|
||||||
// Attr a Model
|
|
||||||
//
|
|
||||||
// where the `a` doesn't really mention the attributes on the fields.
|
|
||||||
for variable in actual.variables() {
|
|
||||||
if !substitution.contains_key(&variable) {
|
|
||||||
// Leave attributes on recursion variables untouched!
|
|
||||||
//
|
|
||||||
// In a recursive type like
|
|
||||||
//
|
|
||||||
// > [ Z, S Peano ] as Peano
|
|
||||||
//
|
|
||||||
// By default the lifted version is
|
|
||||||
//
|
|
||||||
// > Attr a ([ Z, S (Attr b Peano) ] as Peano)
|
|
||||||
//
|
|
||||||
// But, it must be true that a = b because Peano is self-recursive.
|
|
||||||
// Therefore we earlier have substituted
|
|
||||||
//
|
|
||||||
// > Attr a ([ Z, S (Attr a Peano) ] as Peano)
|
|
||||||
//
|
|
||||||
// And now we must make sure the `a`s stay the same variable, i.e.
|
|
||||||
// don't re-instantiate it here.
|
|
||||||
let var = var_store.fresh();
|
|
||||||
substitution.insert(variable, Type::Variable(var));
|
|
||||||
|
|
||||||
introduced.insert(var);
|
lambda_set_variables.push(LambdaSet(Type::Variable(fresh)));
|
||||||
|
|
||||||
|
if let Type::Variable(lambda_set_var) = lambda_set.0 {
|
||||||
|
substitution.insert(lambda_set_var, Type::Variable(fresh));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -736,13 +789,19 @@ impl Type {
|
||||||
}
|
}
|
||||||
ext.substitute(&substitution);
|
ext.substitute(&substitution);
|
||||||
|
|
||||||
*self = Type::Alias(
|
*self = Type::Alias {
|
||||||
*symbol,
|
symbol: *symbol,
|
||||||
named_args,
|
type_arguments: named_args,
|
||||||
Box::new(Type::RecursiveTagUnion(new_rec_var, tags, ext)),
|
lambda_set_variables: alias.lambda_set_variables.clone(),
|
||||||
);
|
actual: Box::new(Type::RecursiveTagUnion(new_rec_var, tags, ext)),
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
*self = Type::Alias(*symbol, named_args, Box::new(actual));
|
*self = Type::Alias {
|
||||||
|
symbol: *symbol,
|
||||||
|
type_arguments: named_args,
|
||||||
|
lambda_set_variables: alias.lambda_set_variables.clone(),
|
||||||
|
actual: Box::new(actual),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// one of the special-cased Apply types.
|
// one of the special-cased Apply types.
|
||||||
|
@ -788,7 +847,11 @@ fn symbols_help(tipe: &Type, accum: &mut ImSet<Symbol>) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Alias(alias_symbol, _, actual_type) => {
|
Alias {
|
||||||
|
symbol: alias_symbol,
|
||||||
|
actual: actual_type,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
accum.insert(*alias_symbol);
|
accum.insert(*alias_symbol);
|
||||||
symbols_help(actual_type, accum);
|
symbols_help(actual_type, accum);
|
||||||
}
|
}
|
||||||
|
@ -858,14 +921,20 @@ fn variables_help(tipe: &Type, accum: &mut ImSet<Variable>) {
|
||||||
// this rec var doesn't need to be in flex_vars or rigid_vars
|
// this rec var doesn't need to be in flex_vars or rigid_vars
|
||||||
accum.remove(rec);
|
accum.remove(rec);
|
||||||
}
|
}
|
||||||
Alias(_, args, actual) => {
|
Alias {
|
||||||
for (_, arg) in args {
|
type_arguments,
|
||||||
|
actual,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
for (_, arg) in type_arguments {
|
||||||
variables_help(arg, accum);
|
variables_help(arg, accum);
|
||||||
}
|
}
|
||||||
variables_help(actual, accum);
|
variables_help(actual, accum);
|
||||||
}
|
}
|
||||||
HostExposedAlias {
|
HostExposedAlias {
|
||||||
arguments, actual, ..
|
type_arguments: arguments,
|
||||||
|
actual,
|
||||||
|
..
|
||||||
} => {
|
} => {
|
||||||
for (_, arg) in arguments {
|
for (_, arg) in arguments {
|
||||||
variables_help(arg, accum);
|
variables_help(arg, accum);
|
||||||
|
@ -883,7 +952,7 @@ fn variables_help(tipe: &Type, accum: &mut ImSet<Variable>) {
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct VariableDetail {
|
pub struct VariableDetail {
|
||||||
pub type_variables: MutSet<Variable>,
|
pub type_variables: MutSet<Variable>,
|
||||||
pub lambda_set_variables: MutSet<LambdaSet>,
|
pub lambda_set_variables: Vec<Variable>,
|
||||||
pub recursion_variables: MutSet<Variable>,
|
pub recursion_variables: MutSet<Variable>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -910,7 +979,7 @@ fn variables_help_detailed(tipe: &Type, accum: &mut VariableDetail) {
|
||||||
variables_help_detailed(arg, accum);
|
variables_help_detailed(arg, accum);
|
||||||
}
|
}
|
||||||
if let Type::Variable(v) = **closure {
|
if let Type::Variable(v) = **closure {
|
||||||
accum.lambda_set_variables.insert(LambdaSet::from(v));
|
accum.lambda_set_variables.push(v);
|
||||||
} else {
|
} else {
|
||||||
variables_help_detailed(closure, accum);
|
variables_help_detailed(closure, accum);
|
||||||
}
|
}
|
||||||
|
@ -956,14 +1025,20 @@ fn variables_help_detailed(tipe: &Type, accum: &mut VariableDetail) {
|
||||||
|
|
||||||
accum.recursion_variables.insert(*rec);
|
accum.recursion_variables.insert(*rec);
|
||||||
}
|
}
|
||||||
Alias(_, args, actual) => {
|
Alias {
|
||||||
for (_, arg) in args {
|
type_arguments,
|
||||||
|
actual,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
for (_, arg) in type_arguments {
|
||||||
variables_help_detailed(arg, accum);
|
variables_help_detailed(arg, accum);
|
||||||
}
|
}
|
||||||
variables_help_detailed(actual, accum);
|
variables_help_detailed(actual, accum);
|
||||||
}
|
}
|
||||||
HostExposedAlias {
|
HostExposedAlias {
|
||||||
arguments, actual, ..
|
type_arguments: arguments,
|
||||||
|
actual,
|
||||||
|
..
|
||||||
} => {
|
} => {
|
||||||
for (_, arg) in arguments {
|
for (_, arg) in arguments {
|
||||||
variables_help_detailed(arg, accum);
|
variables_help_detailed(arg, accum);
|
||||||
|
@ -985,6 +1060,13 @@ pub struct RecordStructure {
|
||||||
pub ext: Variable,
|
pub ext: Variable,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TagUnionStructure<'a> {
|
||||||
|
/// Invariant: these should be sorted!
|
||||||
|
pub fields: Vec<(TagName, &'a [Variable])>,
|
||||||
|
pub ext: Variable,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum PReason {
|
pub enum PReason {
|
||||||
TypedArg {
|
TypedArg {
|
||||||
|
@ -1107,7 +1189,7 @@ pub struct Alias {
|
||||||
|
|
||||||
/// lambda set variables, e.g. the one annotating the arrow in
|
/// lambda set variables, e.g. the one annotating the arrow in
|
||||||
/// a |c|-> b
|
/// a |c|-> b
|
||||||
pub lambda_set_variables: MutSet<LambdaSet>,
|
pub lambda_set_variables: Vec<LambdaSet>,
|
||||||
|
|
||||||
pub recursion_variables: MutSet<Variable>,
|
pub recursion_variables: MutSet<Variable>,
|
||||||
|
|
||||||
|
@ -1547,7 +1629,13 @@ pub fn gather_fields_unsorted_iter(
|
||||||
var = *actual_var;
|
var = *actual_var;
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => break,
|
Structure(EmptyRecord) => break,
|
||||||
|
FlexVar(_) => break,
|
||||||
|
|
||||||
|
// TODO investigate apparently this one pops up in the reporting tests!
|
||||||
|
RigidVar(_) => break,
|
||||||
|
|
||||||
|
other => unreachable!("something weird ended up in a record type: {:?}", other),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1580,3 +1668,104 @@ pub fn gather_fields(subs: &Subs, other_fields: RecordFields, var: Variable) ->
|
||||||
ext,
|
ext,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn gather_tags_unsorted_iter(
|
||||||
|
subs: &Subs,
|
||||||
|
other_fields: UnionTags,
|
||||||
|
mut var: Variable,
|
||||||
|
) -> (
|
||||||
|
impl Iterator<Item = (&TagName, VariableSubsSlice)> + '_,
|
||||||
|
Variable,
|
||||||
|
) {
|
||||||
|
use crate::subs::Content::*;
|
||||||
|
use crate::subs::FlatType::*;
|
||||||
|
|
||||||
|
let mut stack = vec![other_fields];
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match subs.get_content_without_compacting(var) {
|
||||||
|
Structure(TagUnion(sub_fields, sub_ext)) => {
|
||||||
|
stack.push(*sub_fields);
|
||||||
|
|
||||||
|
var = *sub_ext;
|
||||||
|
}
|
||||||
|
|
||||||
|
Structure(FunctionOrTagUnion(_tag_name_index, _, _sub_ext)) => {
|
||||||
|
todo!("this variant does not use SOA yet, and therefore this case is unreachable right now")
|
||||||
|
// let sub_fields: UnionTags = (*tag_name_index).into();
|
||||||
|
// stack.push(sub_fields);
|
||||||
|
//
|
||||||
|
// var = *sub_ext;
|
||||||
|
}
|
||||||
|
|
||||||
|
Structure(RecursiveTagUnion(_, _sub_fields, _sub_ext)) => {
|
||||||
|
todo!("this variant does not use SOA yet, and therefore this case is unreachable right now")
|
||||||
|
// stack.push(*sub_fields);
|
||||||
|
//
|
||||||
|
// var = *sub_ext;
|
||||||
|
}
|
||||||
|
|
||||||
|
Alias(_, _, actual_var) => {
|
||||||
|
// TODO according to elm/compiler: "TODO may be dropping useful alias info here"
|
||||||
|
var = *actual_var;
|
||||||
|
}
|
||||||
|
|
||||||
|
Structure(EmptyTagUnion) => break,
|
||||||
|
FlexVar(_) => break,
|
||||||
|
|
||||||
|
// TODO investigate this likely can happen when there is a type error
|
||||||
|
RigidVar(_) => break,
|
||||||
|
|
||||||
|
other => unreachable!("something weird ended up in a tag union type: {:?}", other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let it = stack
|
||||||
|
.into_iter()
|
||||||
|
.map(|union_tags| union_tags.iter_all())
|
||||||
|
.flatten()
|
||||||
|
.map(move |(i1, i2)| {
|
||||||
|
let tag_name: &TagName = &subs[i1];
|
||||||
|
let subs_slice = subs[i2];
|
||||||
|
|
||||||
|
(tag_name, subs_slice)
|
||||||
|
});
|
||||||
|
|
||||||
|
(it, var)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gather_tags_slices(
|
||||||
|
subs: &Subs,
|
||||||
|
other_fields: UnionTags,
|
||||||
|
var: Variable,
|
||||||
|
) -> (Vec<(TagName, VariableSubsSlice)>, Variable) {
|
||||||
|
let (it, ext) = gather_tags_unsorted_iter(subs, other_fields, var);
|
||||||
|
|
||||||
|
let mut result: Vec<_> = it
|
||||||
|
.map(|(ref_label, field)| (ref_label.clone(), field))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
result.sort_by(|(a, _), (b, _)| a.cmp(b));
|
||||||
|
|
||||||
|
(result, ext)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gather_tags(subs: &Subs, other_fields: UnionTags, var: Variable) -> TagUnionStructure {
|
||||||
|
let (it, ext) = gather_tags_unsorted_iter(subs, other_fields, var);
|
||||||
|
|
||||||
|
let mut result: Vec<_> = it
|
||||||
|
.map(|(ref_label, field)| {
|
||||||
|
(
|
||||||
|
ref_label.clone(),
|
||||||
|
subs.get_subs_slice(*field.as_subs_slice()),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
result.sort_by(|(a, _), (b, _)| a.cmp(b));
|
||||||
|
|
||||||
|
TagUnionStructure {
|
||||||
|
fields: result,
|
||||||
|
ext,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -653,7 +653,7 @@ fn should_be_multiline(type_ann: &TypeAnnotation) -> bool {
|
||||||
is_multiline
|
is_multiline
|
||||||
}
|
}
|
||||||
TypeAnnotation::Function { args, output } => {
|
TypeAnnotation::Function { args, output } => {
|
||||||
let mut is_multiline = should_be_multiline(output) || args.len() > 1;
|
let mut is_multiline = should_be_multiline(output) || args.len() > 2;
|
||||||
|
|
||||||
for arg in args {
|
for arg in args {
|
||||||
if is_multiline {
|
if is_multiline {
|
||||||
|
|
|
@ -155,6 +155,10 @@ e.g. you have a test `calculate_sum_test` that only uses the function `add`, whe
|
||||||
* ...
|
* ...
|
||||||
* smart insert: press a shortcut and enter a plain english description of a code snippet you need. Examples: "convert string to list of chars", "sort list of records by field foo descending", "plot this list with date on x-axis"...
|
* smart insert: press a shortcut and enter a plain english description of a code snippet you need. Examples: "convert string to list of chars", "sort list of records by field foo descending", "plot this list with date on x-axis"...
|
||||||
* After the user has refactored code to be simpler, try finding other places in the code base where the same simplification can be made.
|
* After the user has refactored code to be simpler, try finding other places in the code base where the same simplification can be made.
|
||||||
|
* Show most commonly changed settings on first run so new users can quickly customize their experience. Keeping record of changed settings should be opt-in.
|
||||||
|
* Detection of multiple people within same company/team working on same code at the same time (opt-in).
|
||||||
|
* Autocorrect likely typos for stuff like `-<` when not in string.
|
||||||
|
* If multiple functions are available for import, use function were types would match in insetion position.
|
||||||
|
|
||||||
#### Autocomplete
|
#### Autocomplete
|
||||||
|
|
||||||
|
@ -236,6 +240,7 @@ e.g. you have a test `calculate_sum_test` that only uses the function `add`, whe
|
||||||
### Inspiration
|
### Inspiration
|
||||||
|
|
||||||
- [Boop](https://github.com/IvanMathy/Boop) scriptable scratchpad for developers. Contains collection of useful conversions: json formatting, url encoding, encode to base64...
|
- [Boop](https://github.com/IvanMathy/Boop) scriptable scratchpad for developers. Contains collection of useful conversions: json formatting, url encoding, encode to base64...
|
||||||
|
- [processing](processing.org) Interactive editor, dragging left or right with mouse to change values. Instant results.
|
||||||
|
|
||||||
## High performance
|
## High performance
|
||||||
|
|
||||||
|
@ -243,6 +248,23 @@ e.g. you have a test `calculate_sum_test` that only uses the function `add`, whe
|
||||||
|
|
||||||
- [10x editor](http://www.10xeditor.com/) IDE/Editor targeted at the professional developer with an emphasis on performance and scalability.
|
- [10x editor](http://www.10xeditor.com/) IDE/Editor targeted at the professional developer with an emphasis on performance and scalability.
|
||||||
|
|
||||||
|
|
||||||
|
## Positive feedback
|
||||||
|
|
||||||
|
- It's nice to enhance the feeling of reward after completing a task, this increases motivation.
|
||||||
|
- Great for tutorials and the first run of the editor.
|
||||||
|
- Suggestions of occasions for positive feedback:
|
||||||
|
- Being able to compile successfully after starting out with more than X errors.
|
||||||
|
- Making a test succeed after repeated failures.
|
||||||
|
- Positive feedback could be delivered with messages and/or animations. Animations could be with fireworks, flying roc logo birds, sounds...
|
||||||
|
- The intensity of the message/animation could be increased based on the duration/difficulty of the task.
|
||||||
|
- Suggest to search for help or take a break after being stuck on a test/compile errors... for some time. A search could be done for group chats for relevant libraries.
|
||||||
|
|
||||||
|
### Inspiration
|
||||||
|
|
||||||
|
- [Duolingo](https://www.duolingo.com) app to learn languages
|
||||||
|
- [Khan academy](https://www.khanacademy.org/) free quality education for everyone
|
||||||
|
|
||||||
## General Thoughts/Ideas
|
## General Thoughts/Ideas
|
||||||
|
|
||||||
Thoughts and ideas possibly taken from above inspirations or separate.
|
Thoughts and ideas possibly taken from above inspirations or separate.
|
||||||
|
|
|
@ -32,7 +32,7 @@ fn to_type2(
|
||||||
var_store: &mut VarStore,
|
var_store: &mut VarStore,
|
||||||
) -> Type2 {
|
) -> Type2 {
|
||||||
match solved_type {
|
match solved_type {
|
||||||
SolvedType::Alias(symbol, solved_type_variables, solved_actual) => {
|
SolvedType::Alias(symbol, solved_type_variables, _todo, solved_actual) => {
|
||||||
let type_variables = PoolVec::with_capacity(solved_type_variables.len() as u32, pool);
|
let type_variables = PoolVec::with_capacity(solved_type_variables.len() as u32, pool);
|
||||||
|
|
||||||
for (type_variable_node_id, (lowercase, solved_arg)) in type_variables
|
for (type_variable_node_id, (lowercase, solved_arg)) in type_variables
|
||||||
|
|
|
@ -1,18 +1,22 @@
|
||||||
#![allow(clippy::all)]
|
#![allow(clippy::all)]
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
use crate::lang::constrain::Constraint::{self, *};
|
use crate::lang::constrain::Constraint::{self, *};
|
||||||
use crate::lang::pool::{Pool, ShallowClone};
|
use crate::lang::pool::{Pool, PoolVec, ShallowClone};
|
||||||
use crate::lang::types::Type2;
|
use crate::lang::types::Type2;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use roc_can::expected::{Expected, PExpected};
|
use roc_can::expected::{Expected, PExpected};
|
||||||
use roc_collections::all::{BumpMap, BumpMapDefault, MutMap};
|
use roc_collections::all::{BumpMap, BumpMapDefault, MutMap};
|
||||||
|
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};
|
||||||
use roc_types::solved_types::Solved;
|
use roc_types::solved_types::Solved;
|
||||||
use roc_types::subs::{
|
use roc_types::subs::{
|
||||||
Content, Descriptor, FlatType, Mark, OptVariable, Rank, RecordFields, Subs, SubsSlice, Variable,
|
AliasVariables, Content, Descriptor, FlatType, Mark, OptVariable, Rank, RecordFields, Subs,
|
||||||
|
SubsSlice, UnionTags, Variable, VariableSubsSlice,
|
||||||
|
};
|
||||||
|
use roc_types::types::{
|
||||||
|
gather_fields_unsorted_iter, Alias, Category, ErrorType, PatternCategory, RecordField,
|
||||||
};
|
};
|
||||||
use roc_types::types::{Alias, Category, ErrorType, PatternCategory, RecordField};
|
|
||||||
use roc_unify::unify::unify;
|
use roc_unify::unify::unify;
|
||||||
use roc_unify::unify::Unified::*;
|
use roc_unify::unify::Unified::*;
|
||||||
|
|
||||||
|
@ -701,6 +705,7 @@ fn type_to_variable<'a>(
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let arg_vars = VariableSubsSlice::insert_into_subs(subs, arg_vars);
|
||||||
let flat_type = FlatType::Apply(*symbol, arg_vars);
|
let flat_type = FlatType::Apply(*symbol, arg_vars);
|
||||||
let content = Content::Structure(flat_type);
|
let content = Content::Structure(flat_type);
|
||||||
|
|
||||||
|
@ -711,7 +716,7 @@ fn type_to_variable<'a>(
|
||||||
EmptyTagUnion => roc_types::subs::Variable::EMPTY_TAG_UNION,
|
EmptyTagUnion => roc_types::subs::Variable::EMPTY_TAG_UNION,
|
||||||
|
|
||||||
Record(fields, ext_id) => {
|
Record(fields, ext_id) => {
|
||||||
let mut field_vars = MutMap::default();
|
let mut field_vars = Vec::new();
|
||||||
|
|
||||||
for node_id in fields.iter_node_ids() {
|
for node_id in fields.iter_node_ids() {
|
||||||
use RecordField::*;
|
use RecordField::*;
|
||||||
|
@ -748,31 +753,29 @@ fn type_to_variable<'a>(
|
||||||
)),
|
)),
|
||||||
};
|
};
|
||||||
|
|
||||||
field_vars.insert(field.as_str(mempool).into(), field_var);
|
field_vars.push((field.as_str(mempool).into(), field_var));
|
||||||
}
|
}
|
||||||
|
|
||||||
let ext = mempool.get(*ext_id);
|
let ext = mempool.get(*ext_id);
|
||||||
let temp_ext_var = type_to_variable(arena, mempool, subs, rank, pools, cached, ext);
|
let temp_ext_var = type_to_variable(arena, mempool, subs, rank, pools, cached, ext);
|
||||||
let new_ext_var = match roc_types::pretty_print::chase_ext_record(
|
|
||||||
subs,
|
|
||||||
temp_ext_var,
|
|
||||||
&mut field_vars,
|
|
||||||
) {
|
|
||||||
Ok(()) => roc_types::subs::Variable::EMPTY_RECORD,
|
|
||||||
Err((new, _)) => new,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut all_fields: Vec<_> = field_vars.into_iter().collect();
|
let (it, new_ext_var) =
|
||||||
all_fields.sort_unstable_by(RecordFields::compare);
|
gather_fields_unsorted_iter(subs, RecordFields::empty(), temp_ext_var);
|
||||||
|
|
||||||
let record_fields = RecordFields::insert_into_subs(subs, all_fields);
|
let it = it
|
||||||
|
.into_iter()
|
||||||
|
.map(|(field, field_type)| (field.clone(), field_type));
|
||||||
|
|
||||||
|
field_vars.extend(it);
|
||||||
|
field_vars.sort_unstable_by(RecordFields::compare);
|
||||||
|
|
||||||
|
let record_fields = RecordFields::insert_into_subs(subs, field_vars);
|
||||||
|
|
||||||
let content = Content::Structure(FlatType::Record(record_fields, new_ext_var));
|
let content = Content::Structure(FlatType::Record(record_fields, new_ext_var));
|
||||||
|
|
||||||
register(subs, rank, pools, content)
|
register(subs, rank, pools, content)
|
||||||
}
|
}
|
||||||
|
|
||||||
Alias(Symbol::BOOL_BOOL, _, _) => roc_types::subs::Variable::BOOL,
|
|
||||||
Alias(symbol, args, alias_type_id) => {
|
Alias(symbol, args, alias_type_id) => {
|
||||||
// TODO cache in uniqueness inference gives problems! all Int's get the same uniqueness var!
|
// TODO cache in uniqueness inference gives problems! all Int's get the same uniqueness var!
|
||||||
// Cache aliases without type arguments. Commonly used aliases like `Int` would otherwise get O(n)
|
// Cache aliases without type arguments. Commonly used aliases like `Int` would otherwise get O(n)
|
||||||
|
@ -815,6 +818,8 @@ fn type_to_variable<'a>(
|
||||||
new_aliases.insert(arg_str, arg_var);
|
new_aliases.insert(arg_str, arg_var);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let arg_vars = AliasVariables::insert_into_subs(subs, arg_vars, []);
|
||||||
|
|
||||||
let alias_var = type_to_variable(arena, mempool, subs, rank, pools, cached, alias_type);
|
let alias_var = type_to_variable(arena, mempool, subs, rank, pools, cached, alias_type);
|
||||||
let content = Content::Alias(*symbol, arg_vars, alias_var);
|
let content = Content::Alias(*symbol, arg_vars, alias_var);
|
||||||
|
|
||||||
|
@ -827,34 +832,11 @@ fn type_to_variable<'a>(
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
TagUnion(tags, ext_id) => {
|
TagUnion(tags, ext_id) => {
|
||||||
let mut tag_vars = MutMap::default();
|
|
||||||
let ext = mempool.get(*ext_id);
|
let ext = mempool.get(*ext_id);
|
||||||
|
|
||||||
for (tag_name, tag_argument_types) in tags.iter(mempool) {
|
let (union_tags, ext) =
|
||||||
let mut tag_argument_vars = Vec::with_capacity(tag_argument_types.len());
|
type_to_union_tags(arena, mempool, subs, rank, pools, cached, tags, ext);
|
||||||
|
let content = Content::Structure(FlatType::TagUnion(union_tags, ext));
|
||||||
for arg_type in tag_argument_types.iter(mempool) {
|
|
||||||
tag_argument_vars.push(type_to_variable(
|
|
||||||
arena, mempool, subs, rank, pools, cached, arg_type,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
tag_vars.insert(tag_name.clone(), tag_argument_vars);
|
|
||||||
}
|
|
||||||
|
|
||||||
let temp_ext_var = type_to_variable(arena, mempool, subs, rank, pools, cached, ext);
|
|
||||||
let mut ext_tag_vec = Vec::new();
|
|
||||||
let new_ext_var = match roc_types::pretty_print::chase_ext_tag_union(
|
|
||||||
subs,
|
|
||||||
temp_ext_var,
|
|
||||||
&mut ext_tag_vec,
|
|
||||||
) {
|
|
||||||
Ok(()) => roc_types::subs::Variable::EMPTY_TAG_UNION,
|
|
||||||
Err((new, _)) => new,
|
|
||||||
};
|
|
||||||
tag_vars.extend(ext_tag_vec.into_iter());
|
|
||||||
|
|
||||||
let content = Content::Structure(FlatType::TagUnion(tag_vars, new_ext_var));
|
|
||||||
|
|
||||||
register(subs, rank, pools, content)
|
register(subs, rank, pools, content)
|
||||||
}
|
}
|
||||||
|
@ -871,11 +853,7 @@ fn type_to_variable<'a>(
|
||||||
new_arg_vars.push(var)
|
new_arg_vars.push(var)
|
||||||
}
|
}
|
||||||
|
|
||||||
let start = subs.variables.len() as u32;
|
let arg_vars = VariableSubsSlice::insert_into_subs(subs, new_arg_vars);
|
||||||
let length = arg_vars.len() as u16;
|
|
||||||
let arg_vars = SubsSlice::new(start, length);
|
|
||||||
|
|
||||||
subs.variables.extend(new_arg_vars);
|
|
||||||
|
|
||||||
let ret_var = type_to_variable(arena, mempool, subs, rank, pools, cached, ret_type);
|
let ret_var = type_to_variable(arena, mempool, subs, rank, pools, cached, ret_type);
|
||||||
let closure_var =
|
let closure_var =
|
||||||
|
@ -973,6 +951,63 @@ fn type_to_variable<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn type_to_union_tags<'a>(
|
||||||
|
arena: &'a Bump,
|
||||||
|
mempool: &Pool,
|
||||||
|
subs: &mut Subs,
|
||||||
|
rank: Rank,
|
||||||
|
pools: &mut Pools,
|
||||||
|
cached: &mut MutMap<Symbol, Variable>,
|
||||||
|
tags: &PoolVec<(TagName, PoolVec<Type2>)>,
|
||||||
|
ext: &Type2,
|
||||||
|
) -> (UnionTags, Variable) {
|
||||||
|
let mut tag_vars = Vec::with_capacity(tags.len());
|
||||||
|
|
||||||
|
let mut tag_argument_vars = Vec::new();
|
||||||
|
for id in tags.iter_node_ids() {
|
||||||
|
let (tag, tag_argument_types) = mempool.get(id);
|
||||||
|
for arg_id in tag_argument_types.iter_node_ids() {
|
||||||
|
let arg_type = mempool.get(arg_id);
|
||||||
|
let new_var = type_to_variable(arena, mempool, subs, rank, pools, cached, arg_type);
|
||||||
|
tag_argument_vars.push(new_var);
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_slice = VariableSubsSlice::insert_into_subs(subs, tag_argument_vars.drain(..));
|
||||||
|
|
||||||
|
tag_vars.push((tag.clone(), new_slice));
|
||||||
|
}
|
||||||
|
|
||||||
|
let temp_ext_var = type_to_variable(arena, mempool, subs, rank, pools, cached, ext);
|
||||||
|
|
||||||
|
let ext = {
|
||||||
|
let (it, ext) =
|
||||||
|
roc_types::types::gather_tags_unsorted_iter(subs, UnionTags::default(), temp_ext_var);
|
||||||
|
|
||||||
|
tag_vars.extend(it.map(|(n, v)| (n.clone(), v)));
|
||||||
|
tag_vars.sort_unstable_by(|(a, _), (b, _)| a.cmp(b));
|
||||||
|
|
||||||
|
// deduplicate, keeping the right-most occurrence of a tag name
|
||||||
|
let mut i = 0;
|
||||||
|
|
||||||
|
while i < tag_vars.len() {
|
||||||
|
match (tag_vars.get(i), tag_vars.get(i + 1)) {
|
||||||
|
(Some((t1, _)), Some((t2, _))) => {
|
||||||
|
if t1 == t2 {
|
||||||
|
tag_vars.remove(i);
|
||||||
|
} else {
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ext
|
||||||
|
};
|
||||||
|
|
||||||
|
(UnionTags::insert_slices_into_subs(subs, tag_vars), ext)
|
||||||
|
}
|
||||||
|
|
||||||
fn check_for_infinite_type(
|
fn check_for_infinite_type(
|
||||||
subs: &mut Subs,
|
subs: &mut Subs,
|
||||||
problems: &mut Vec<TypeError>,
|
problems: &mut Vec<TypeError>,
|
||||||
|
@ -981,7 +1016,7 @@ fn check_for_infinite_type(
|
||||||
) {
|
) {
|
||||||
let var = loc_var.value;
|
let var = loc_var.value;
|
||||||
|
|
||||||
while let Some((recursive, _chain)) = subs.occurs(var) {
|
while let Err((recursive, _chain)) = subs.occurs(var) {
|
||||||
let description = subs.get(recursive);
|
let description = subs.get(recursive);
|
||||||
let content = description.content;
|
let content = description.content;
|
||||||
|
|
||||||
|
@ -998,19 +1033,24 @@ fn check_for_infinite_type(
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut new_tags = MutMap::default();
|
let mut new_tags = Vec::with_capacity(tags.len());
|
||||||
|
|
||||||
for (label, args) in &tags {
|
for (name_index, slice_index) in tags.iter_all() {
|
||||||
let new_args: Vec<_> = args
|
let slice = subs[slice_index];
|
||||||
.iter()
|
|
||||||
.map(|var| subs.explicit_substitute(recursive, rec_var, *var))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
new_tags.insert(label.clone(), new_args);
|
let mut new_vars = Vec::new();
|
||||||
|
for var_index in slice {
|
||||||
|
let var = subs[var_index];
|
||||||
|
new_vars.push(subs.explicit_substitute(recursive, rec_var, var));
|
||||||
|
}
|
||||||
|
|
||||||
|
new_tags.push((subs[name_index].clone(), new_vars));
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_ext_var = subs.explicit_substitute(recursive, rec_var, ext_var);
|
let new_ext_var = subs.explicit_substitute(recursive, rec_var, ext_var);
|
||||||
|
|
||||||
|
let new_tags = UnionTags::insert_into_subs(subs, new_tags);
|
||||||
|
|
||||||
let flat_type = FlatType::RecursiveTagUnion(rec_var, new_tags, new_ext_var);
|
let flat_type = FlatType::RecursiveTagUnion(rec_var, new_tags, new_ext_var);
|
||||||
|
|
||||||
subs.set_content(recursive, Content::Structure(flat_type));
|
subs.set_content(recursive, Content::Structure(flat_type));
|
||||||
|
@ -1176,9 +1216,9 @@ fn adjust_rank_content(
|
||||||
Apply(_, args) => {
|
Apply(_, args) => {
|
||||||
let mut rank = Rank::toplevel();
|
let mut rank = Rank::toplevel();
|
||||||
|
|
||||||
for var in args {
|
for var_index in args.into_iter() {
|
||||||
rank =
|
let var = subs[var_index];
|
||||||
rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, *var));
|
rank = rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, var));
|
||||||
}
|
}
|
||||||
|
|
||||||
rank
|
rank
|
||||||
|
@ -1231,9 +1271,13 @@ fn adjust_rank_content(
|
||||||
TagUnion(tags, ext_var) => {
|
TagUnion(tags, ext_var) => {
|
||||||
let mut rank = adjust_rank(subs, young_mark, visit_mark, group_rank, *ext_var);
|
let mut rank = adjust_rank(subs, young_mark, visit_mark, group_rank, *ext_var);
|
||||||
|
|
||||||
for var in tags.values().flatten() {
|
for (_, index) in tags.iter_all() {
|
||||||
rank =
|
let slice = subs[index];
|
||||||
rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, *var));
|
for var_index in slice {
|
||||||
|
let var = subs[var_index];
|
||||||
|
rank = rank
|
||||||
|
.max(adjust_rank(subs, young_mark, visit_mark, group_rank, var));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rank
|
rank
|
||||||
|
@ -1246,9 +1290,13 @@ fn adjust_rank_content(
|
||||||
RecursiveTagUnion(rec_var, tags, ext_var) => {
|
RecursiveTagUnion(rec_var, tags, ext_var) => {
|
||||||
let mut rank = adjust_rank(subs, young_mark, visit_mark, group_rank, *ext_var);
|
let mut rank = adjust_rank(subs, young_mark, visit_mark, group_rank, *ext_var);
|
||||||
|
|
||||||
for var in tags.values().flatten() {
|
for (_, index) in tags.iter_all() {
|
||||||
rank =
|
let slice = subs[index];
|
||||||
rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, *var));
|
for var_index in slice {
|
||||||
|
let var = subs[var_index];
|
||||||
|
rank = rank
|
||||||
|
.max(adjust_rank(subs, young_mark, visit_mark, group_rank, var));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// THEORY: the recursion var has the same rank as the tag union itself
|
// THEORY: the recursion var has the same rank as the tag union itself
|
||||||
|
@ -1268,8 +1316,9 @@ fn adjust_rank_content(
|
||||||
Alias(_, args, real_var) => {
|
Alias(_, args, real_var) => {
|
||||||
let mut rank = Rank::toplevel();
|
let mut rank = Rank::toplevel();
|
||||||
|
|
||||||
for (_, var) in args {
|
for var_index in args.variables() {
|
||||||
rank = rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, *var));
|
let var = subs[var_index];
|
||||||
|
rank = rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, var));
|
||||||
}
|
}
|
||||||
|
|
||||||
// from elm-compiler: THEORY: anything in the real_var would be Rank::toplevel()
|
// from elm-compiler: THEORY: anything in the real_var would be Rank::toplevel()
|
||||||
|
@ -1352,7 +1401,8 @@ fn instantiate_rigids_help(
|
||||||
Structure(flat_type) => {
|
Structure(flat_type) => {
|
||||||
match flat_type {
|
match flat_type {
|
||||||
Apply(_, args) => {
|
Apply(_, args) => {
|
||||||
for var in args.into_iter() {
|
for var_index in args.into_iter() {
|
||||||
|
let var = subs[var_index];
|
||||||
instantiate_rigids_help(subs, max_rank, pools, var);
|
instantiate_rigids_help(subs, max_rank, pools, var);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1379,8 +1429,10 @@ fn instantiate_rigids_help(
|
||||||
}
|
}
|
||||||
|
|
||||||
TagUnion(tags, ext_var) => {
|
TagUnion(tags, ext_var) => {
|
||||||
for (_, vars) in tags {
|
for (_, index) in tags.iter_all() {
|
||||||
for var in vars.into_iter() {
|
let slice = subs[index];
|
||||||
|
for var_index in slice {
|
||||||
|
let var = subs[var_index];
|
||||||
instantiate_rigids_help(subs, max_rank, pools, var);
|
instantiate_rigids_help(subs, max_rank, pools, var);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1395,8 +1447,10 @@ fn instantiate_rigids_help(
|
||||||
RecursiveTagUnion(rec_var, tags, ext_var) => {
|
RecursiveTagUnion(rec_var, tags, ext_var) => {
|
||||||
instantiate_rigids_help(subs, max_rank, pools, rec_var);
|
instantiate_rigids_help(subs, max_rank, pools, rec_var);
|
||||||
|
|
||||||
for (_, vars) in tags {
|
for (_, index) in tags.iter_all() {
|
||||||
for var in vars.into_iter() {
|
let slice = subs[index];
|
||||||
|
for var_index in slice {
|
||||||
|
let var = subs[var_index];
|
||||||
instantiate_rigids_help(subs, max_rank, pools, var);
|
instantiate_rigids_help(subs, max_rank, pools, var);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1418,7 +1472,8 @@ fn instantiate_rigids_help(
|
||||||
}
|
}
|
||||||
|
|
||||||
Alias(_, args, real_type_var) => {
|
Alias(_, args, real_type_var) => {
|
||||||
for (_, var) in args.into_iter() {
|
for var_index in args.variables() {
|
||||||
|
let var = subs[var_index];
|
||||||
instantiate_rigids_help(subs, max_rank, pools, var);
|
instantiate_rigids_help(subs, max_rank, pools, var);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1487,12 +1542,17 @@ fn deep_copy_var_help(
|
||||||
Structure(flat_type) => {
|
Structure(flat_type) => {
|
||||||
let new_flat_type = match flat_type {
|
let new_flat_type = match flat_type {
|
||||||
Apply(symbol, args) => {
|
Apply(symbol, args) => {
|
||||||
let args = args
|
let mut new_arg_vars = Vec::with_capacity(args.len());
|
||||||
.into_iter()
|
|
||||||
.map(|var| deep_copy_var_help(subs, max_rank, pools, var))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Apply(symbol, args)
|
for index in args.into_iter() {
|
||||||
|
let var = subs[index];
|
||||||
|
let copy_var = deep_copy_var_help(subs, max_rank, pools, var);
|
||||||
|
new_arg_vars.push(copy_var);
|
||||||
|
}
|
||||||
|
|
||||||
|
let arg_vars = VariableSubsSlice::insert_into_subs(subs, new_arg_vars);
|
||||||
|
|
||||||
|
Apply(symbol, arg_vars)
|
||||||
}
|
}
|
||||||
|
|
||||||
Func(arg_vars, closure_var, ret_var) => {
|
Func(arg_vars, closure_var, ret_var) => {
|
||||||
|
@ -1507,11 +1567,7 @@ fn deep_copy_var_help(
|
||||||
new_arg_vars.push(copy_var);
|
new_arg_vars.push(copy_var);
|
||||||
}
|
}
|
||||||
|
|
||||||
let start = subs.variables.len() as u32;
|
let arg_vars = VariableSubsSlice::insert_into_subs(subs, new_arg_vars);
|
||||||
let length = arg_vars.len() as u16;
|
|
||||||
let arg_vars = SubsSlice::new(start, length);
|
|
||||||
|
|
||||||
subs.variables.extend(new_arg_vars);
|
|
||||||
|
|
||||||
Func(arg_vars, new_closure_var, new_ret_var)
|
Func(arg_vars, new_closure_var, new_ret_var)
|
||||||
}
|
}
|
||||||
|
@ -1560,17 +1616,35 @@ fn deep_copy_var_help(
|
||||||
}
|
}
|
||||||
|
|
||||||
TagUnion(tags, ext_var) => {
|
TagUnion(tags, ext_var) => {
|
||||||
let mut new_tags = MutMap::default();
|
let mut new_variable_slices = Vec::with_capacity(tags.len());
|
||||||
|
|
||||||
for (tag, vars) in tags {
|
let mut new_variables = Vec::new();
|
||||||
let new_vars: Vec<Variable> = vars
|
for index in tags.variables() {
|
||||||
.into_iter()
|
let slice = subs[index];
|
||||||
.map(|var| deep_copy_var_help(subs, max_rank, pools, var))
|
for var_index in slice {
|
||||||
.collect();
|
let var = subs[var_index];
|
||||||
new_tags.insert(tag, new_vars);
|
let new_var = deep_copy_var_help(subs, max_rank, pools, var);
|
||||||
|
new_variables.push(new_var);
|
||||||
}
|
}
|
||||||
|
|
||||||
TagUnion(new_tags, deep_copy_var_help(subs, max_rank, pools, ext_var))
|
let new_slice =
|
||||||
|
VariableSubsSlice::insert_into_subs(subs, new_variables.drain(..));
|
||||||
|
|
||||||
|
new_variable_slices.push(new_slice);
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_variables = {
|
||||||
|
let start = subs.variable_slices.len() as u32;
|
||||||
|
let length = new_variable_slices.len() as u16;
|
||||||
|
subs.variable_slices.extend(new_variable_slices);
|
||||||
|
|
||||||
|
SubsSlice::new(start, length)
|
||||||
|
};
|
||||||
|
|
||||||
|
let union_tags = UnionTags::from_slices(tags.tag_names(), new_variables);
|
||||||
|
|
||||||
|
let new_ext = deep_copy_var_help(subs, max_rank, pools, ext_var);
|
||||||
|
TagUnion(union_tags, new_ext)
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionOrTagUnion(tag_name, symbol, ext_var) => FunctionOrTagUnion(
|
FunctionOrTagUnion(tag_name, symbol, ext_var) => FunctionOrTagUnion(
|
||||||
|
@ -1580,23 +1654,36 @@ fn deep_copy_var_help(
|
||||||
),
|
),
|
||||||
|
|
||||||
RecursiveTagUnion(rec_var, tags, ext_var) => {
|
RecursiveTagUnion(rec_var, tags, ext_var) => {
|
||||||
let mut new_tags = MutMap::default();
|
let mut new_variable_slices = Vec::with_capacity(tags.len());
|
||||||
|
|
||||||
let new_rec_var = deep_copy_var_help(subs, max_rank, pools, rec_var);
|
let mut new_variables = Vec::new();
|
||||||
|
for index in tags.variables() {
|
||||||
for (tag, vars) in tags {
|
let slice = subs[index];
|
||||||
let new_vars: Vec<Variable> = vars
|
for var_index in slice {
|
||||||
.into_iter()
|
let var = subs[var_index];
|
||||||
.map(|var| deep_copy_var_help(subs, max_rank, pools, var))
|
let new_var = deep_copy_var_help(subs, max_rank, pools, var);
|
||||||
.collect();
|
new_variables.push(new_var);
|
||||||
new_tags.insert(tag, new_vars);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RecursiveTagUnion(
|
let new_slice =
|
||||||
new_rec_var,
|
VariableSubsSlice::insert_into_subs(subs, new_variables.drain(..));
|
||||||
new_tags,
|
|
||||||
deep_copy_var_help(subs, max_rank, pools, ext_var),
|
new_variable_slices.push(new_slice);
|
||||||
)
|
}
|
||||||
|
|
||||||
|
let new_variables = {
|
||||||
|
let start = subs.variable_slices.len() as u32;
|
||||||
|
let length = new_variable_slices.len() as u16;
|
||||||
|
subs.variable_slices.extend(new_variable_slices);
|
||||||
|
|
||||||
|
SubsSlice::new(start, length)
|
||||||
|
};
|
||||||
|
|
||||||
|
let union_tags = UnionTags::from_slices(tags.tag_names(), new_variables);
|
||||||
|
|
||||||
|
let new_ext = deep_copy_var_help(subs, max_rank, pools, ext_var);
|
||||||
|
let new_rec_var = deep_copy_var_help(subs, max_rank, pools, rec_var);
|
||||||
|
FlatType::RecursiveTagUnion(new_rec_var, union_tags, new_ext)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1630,13 +1717,19 @@ fn deep_copy_var_help(
|
||||||
copy
|
copy
|
||||||
}
|
}
|
||||||
|
|
||||||
Alias(symbol, args, real_type_var) => {
|
Alias(symbol, mut args, real_type_var) => {
|
||||||
let new_args = args
|
let mut new_args = Vec::with_capacity(args.variables().len());
|
||||||
.into_iter()
|
|
||||||
.map(|(name, var)| (name, deep_copy_var_help(subs, max_rank, pools, var)))
|
for var_index in args.variables() {
|
||||||
.collect();
|
let var = subs[var_index];
|
||||||
|
let new_var = deep_copy_var_help(subs, max_rank, pools, var);
|
||||||
|
new_args.push(new_var);
|
||||||
|
}
|
||||||
|
|
||||||
|
args.replace_variables(subs, new_args);
|
||||||
|
|
||||||
let new_real_type_var = deep_copy_var_help(subs, max_rank, pools, real_type_var);
|
let new_real_type_var = deep_copy_var_help(subs, max_rank, pools, real_type_var);
|
||||||
let new_content = Alias(symbol, new_args, new_real_type_var);
|
let new_content = Alias(symbol, args, new_real_type_var);
|
||||||
|
|
||||||
subs.set(copy, make_descriptor(new_content));
|
subs.set(copy, make_descriptor(new_content));
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,7 @@ encode = \encoder ->
|
||||||
encodeHelp encoder 0 output
|
encodeHelp encoder 0 output
|
||||||
|> .output
|
|> .output
|
||||||
|
|
||||||
|
encodeHelp : Encoder, Nat, List U8 -> { output: List U8, offset: Nat }
|
||||||
encodeHelp = \encoder, offset, output ->
|
encodeHelp = \encoder, offset, output ->
|
||||||
when encoder is
|
when encoder is
|
||||||
Unsigned8 value ->
|
Unsigned8 value ->
|
||||||
|
|
|
@ -45,6 +45,13 @@ export fn roc_dealloc(c_ptr: *c_void, alignment: u32) callconv(.C) void {
|
||||||
free(@alignCast(16, @ptrCast([*]u8, c_ptr)));
|
free(@alignCast(16, @ptrCast([*]u8, c_ptr)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export fn roc_panic(c_ptr: *c_void, tag_id: u32) callconv(.C) void {
|
||||||
|
const stderr = std.io.getStdErr().writer();
|
||||||
|
const msg = @ptrCast([*:0]const u8, c_ptr);
|
||||||
|
stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg}) catch unreachable;
|
||||||
|
std.process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
const Unit = extern struct {};
|
const Unit = extern struct {};
|
||||||
|
|
||||||
pub export fn main() u8 {
|
pub export fn main() u8 {
|
||||||
|
|
|
@ -45,6 +45,13 @@ export fn roc_dealloc(c_ptr: *c_void, alignment: u32) callconv(.C) void {
|
||||||
free(@alignCast(16, @ptrCast([*]u8, c_ptr)));
|
free(@alignCast(16, @ptrCast([*]u8, c_ptr)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export fn roc_panic(c_ptr: *c_void, tag_id: u32) callconv(.C) void {
|
||||||
|
const stderr = std.io.getStdErr().writer();
|
||||||
|
const msg = @ptrCast([*:0]const u8, c_ptr);
|
||||||
|
stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg}) catch unreachable;
|
||||||
|
std.process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
const Unit = extern struct {};
|
const Unit = extern struct {};
|
||||||
|
|
||||||
pub export fn main() u8 {
|
pub export fn main() u8 {
|
||||||
|
|
|
@ -2,7 +2,9 @@
|
||||||
|
|
||||||
use core::ffi::c_void;
|
use core::ffi::c_void;
|
||||||
use core::mem::MaybeUninit;
|
use core::mem::MaybeUninit;
|
||||||
|
use libc::c_char;
|
||||||
use roc_std::{RocCallResult, RocStr};
|
use roc_std::{RocCallResult, RocStr};
|
||||||
|
use std::ffi::CStr;
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#[link_name = "roc__mainForHost_1_exposed"]
|
#[link_name = "roc__mainForHost_1_exposed"]
|
||||||
|
@ -29,6 +31,19 @@ pub unsafe fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) {
|
||||||
return libc::free(c_ptr);
|
return libc::free(c_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
|
||||||
|
match tag_id {
|
||||||
|
0 => {
|
||||||
|
let slice = CStr::from_ptr(c_ptr as *const c_char);
|
||||||
|
let string = slice.to_str().unwrap();
|
||||||
|
eprintln!("Roc hit a panic: {}", string);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn rust_main() -> isize {
|
pub fn rust_main() -> isize {
|
||||||
let mut call_result: MaybeUninit<RocCallResult<RocStr>> = MaybeUninit::uninit();
|
let mut call_result: MaybeUninit<RocCallResult<RocStr>> = MaybeUninit::uninit();
|
||||||
|
|
|
@ -17,6 +17,12 @@ void roc_dealloc(void* ptr, unsigned int alignment) {
|
||||||
free(ptr);
|
free(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void roc_panic(void* ptr, unsigned int alignment) {
|
||||||
|
char* msg = (char *)ptr;
|
||||||
|
fprintf(stderr, "Application crashed with message\n\n %s\n\nShutting down\n", msg);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
struct RocStr {
|
struct RocStr {
|
||||||
char* bytes;
|
char* bytes;
|
||||||
size_t len;
|
size_t len;
|
||||||
|
|
|
@ -35,6 +35,13 @@ export fn roc_dealloc(c_ptr: *c_void, alignment: u32) callconv(.C) void {
|
||||||
free(@alignCast(16, @ptrCast([*]u8, c_ptr)));
|
free(@alignCast(16, @ptrCast([*]u8, c_ptr)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export fn roc_panic(c_ptr: *c_void, tag_id: u32) callconv(.C) void {
|
||||||
|
const stderr = std.io.getStdErr().writer();
|
||||||
|
const msg = @ptrCast([*:0]const u8, c_ptr);
|
||||||
|
stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg}) catch unreachable;
|
||||||
|
std.process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
const mem = std.mem;
|
const mem = std.mem;
|
||||||
const Allocator = mem.Allocator;
|
const Allocator = mem.Allocator;
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,13 @@ export fn roc_dealloc(c_ptr: *c_void, alignment: u32) callconv(.C) void {
|
||||||
free(@alignCast(16, @ptrCast([*]u8, c_ptr)));
|
free(@alignCast(16, @ptrCast([*]u8, c_ptr)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export fn roc_panic(c_ptr: *c_void, tag_id: u32) callconv(.C) void {
|
||||||
|
const stderr = std.io.getStdErr().writer();
|
||||||
|
const msg = @ptrCast([*:0]const u8, c_ptr);
|
||||||
|
stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg}) catch unreachable;
|
||||||
|
std.process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
// warning! the array is currently stack-allocated so don't make this too big
|
// warning! the array is currently stack-allocated so don't make this too big
|
||||||
const NUM_NUMS = 100;
|
const NUM_NUMS = 100;
|
||||||
|
|
||||||
|
|
|
@ -91,12 +91,16 @@ impl<T> RocList<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_storage_ptr(&self) -> *const isize {
|
fn get_storage_ptr_help(elements: *mut T) -> *mut isize {
|
||||||
let ptr = self.elements as *const isize;
|
let ptr = elements as *mut isize;
|
||||||
|
|
||||||
unsafe { ptr.offset(-1) }
|
unsafe { ptr.offset(-1) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_storage_ptr(&self) -> *const isize {
|
||||||
|
Self::get_storage_ptr_help(self.elements)
|
||||||
|
}
|
||||||
|
|
||||||
fn get_storage_ptr_mut(&mut self) -> *mut isize {
|
fn get_storage_ptr_mut(&mut self) -> *mut isize {
|
||||||
self.get_storage_ptr() as *mut isize
|
self.get_storage_ptr() as *mut isize
|
||||||
}
|
}
|
||||||
|
@ -278,6 +282,103 @@ impl<T> RocList<T> {
|
||||||
fn align_of_storage_ptr() -> u32 {
|
fn align_of_storage_ptr() -> u32 {
|
||||||
mem::align_of::<T>().max(mem::align_of::<usize>()) as u32
|
mem::align_of::<T>().max(mem::align_of::<usize>()) as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe fn drop_pointer_to_first_argument(ptr: *mut T) {
|
||||||
|
let storage_ptr = Self::get_storage_ptr_help(ptr);
|
||||||
|
let storage_val = *storage_ptr;
|
||||||
|
|
||||||
|
if storage_val == REFCOUNT_1 || storage_val > 0 {
|
||||||
|
// If we have no more references, or if this was unique,
|
||||||
|
// deallocate it.
|
||||||
|
roc_dealloc(storage_ptr as *mut c_void, Self::align_of_storage_ptr());
|
||||||
|
} else if storage_val < 0 {
|
||||||
|
// If this still has more references, decrement one.
|
||||||
|
*storage_ptr = storage_val - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The only remaining option is that this is in readonly memory,
|
||||||
|
// in which case we shouldn't attempt to do anything to it.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> IntoIterator for &'a RocList<T> {
|
||||||
|
type Item = &'a T;
|
||||||
|
|
||||||
|
type IntoIter = <&'a [T] as IntoIterator>::IntoIter;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.as_slice().iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> IntoIterator for RocList<T> {
|
||||||
|
type Item = T;
|
||||||
|
|
||||||
|
type IntoIter = IntoIter<T>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
let remaining = self.len();
|
||||||
|
|
||||||
|
let buf = unsafe { NonNull::new_unchecked(self.elements as _) };
|
||||||
|
let ptr = self.elements;
|
||||||
|
|
||||||
|
IntoIter {
|
||||||
|
buf,
|
||||||
|
ptr,
|
||||||
|
remaining,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use core::ptr::NonNull;
|
||||||
|
|
||||||
|
pub struct IntoIter<T> {
|
||||||
|
buf: NonNull<T>,
|
||||||
|
// pub cap: usize,
|
||||||
|
ptr: *const T,
|
||||||
|
remaining: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Iterator for IntoIter<T> {
|
||||||
|
type Item = T;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
next_help(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_help<T>(this: &mut IntoIter<T>) -> Option<T> {
|
||||||
|
if this.remaining == 0 {
|
||||||
|
None
|
||||||
|
} else if mem::size_of::<T>() == 0 {
|
||||||
|
// purposefully don't use 'ptr.offset' because for
|
||||||
|
// vectors with 0-size elements this would return the
|
||||||
|
// same pointer.
|
||||||
|
this.remaining -= 1;
|
||||||
|
|
||||||
|
// Make up a value of this ZST.
|
||||||
|
Some(unsafe { mem::zeroed() })
|
||||||
|
} else {
|
||||||
|
let old = this.ptr;
|
||||||
|
this.ptr = unsafe { this.ptr.offset(1) };
|
||||||
|
this.remaining -= 1;
|
||||||
|
|
||||||
|
Some(unsafe { ptr::read(old) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Drop for IntoIter<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// drop the elements that we have not yet returned.
|
||||||
|
while let Some(item) = next_help(self) {
|
||||||
|
drop(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// deallocate the whole buffer
|
||||||
|
unsafe {
|
||||||
|
RocList::drop_pointer_to_first_argument(self.buf.as_mut());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Default for RocList<T> {
|
impl<T> Default for RocList<T> {
|
||||||
|
|
|
@ -50,10 +50,7 @@ let
|
||||||
|
|
||||||
# lib deps
|
# lib deps
|
||||||
glibc_multi
|
glibc_multi
|
||||||
llvmPkgs.libcxx
|
|
||||||
llvmPkgs.libcxxabi
|
|
||||||
libffi
|
libffi
|
||||||
libunwind
|
|
||||||
libxml2
|
libxml2
|
||||||
ncurses
|
ncurses
|
||||||
zlib
|
zlib
|
||||||
|
@ -78,9 +75,6 @@ in pkgs.mkShell {
|
||||||
lib.makeLibraryPath ([
|
lib.makeLibraryPath ([
|
||||||
pkg-config
|
pkg-config
|
||||||
stdenv.cc.cc.lib
|
stdenv.cc.cc.lib
|
||||||
llvmPkgs.libcxx
|
|
||||||
llvmPkgs.libcxxabi
|
|
||||||
libunwind
|
|
||||||
libffi
|
libffi
|
||||||
ncurses
|
ncurses
|
||||||
zlib
|
zlib
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue