mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 06:44:46 +00:00
Merge branch 'trunk' of github.com:rtfeldman/roc into str-split
This commit is contained in:
commit
c9e3531ecd
29 changed files with 1599 additions and 408 deletions
|
@ -765,6 +765,13 @@ fn variable_usage_help(con: &Constraint, declared: &mut SeenVariables, used: &mu
|
|||
used.insert(v);
|
||||
}
|
||||
}
|
||||
Store(tipe, var, _, _) => {
|
||||
for v in tipe.variables() {
|
||||
used.insert(v);
|
||||
}
|
||||
|
||||
used.insert(*var);
|
||||
}
|
||||
Lookup(_, expectation, _) => {
|
||||
for v in expectation.get_type_ref().variables() {
|
||||
used.insert(v);
|
||||
|
|
|
@ -6,7 +6,7 @@ use roc_gen::{run_jit_function, run_jit_function_dynamic_type};
|
|||
use roc_module::ident::{Lowercase, TagName};
|
||||
use roc_module::operator::CalledVia;
|
||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||
use roc_mono::layout::{Builtin, Layout};
|
||||
use roc_mono::layout::{union_sorted_tags_help, Builtin, Layout, UnionVariant};
|
||||
use roc_parse::ast::{AssignedField, Expr, StrLiteral};
|
||||
use roc_region::all::{Located, Region};
|
||||
use roc_types::subs::{Content, FlatType, Subs, Variable};
|
||||
|
@ -64,6 +64,10 @@ fn jit_to_ast_help<'a>(
|
|||
env, num, content
|
||||
))
|
||||
}
|
||||
Layout::Builtin(Builtin::Int8) => {
|
||||
// NOTE: this is does not handle 8-bit numbers yet
|
||||
run_jit_function!(lib, main_fn_name, u8, |num| byte_to_ast(env, num, content))
|
||||
}
|
||||
Layout::Builtin(Builtin::Int64) => {
|
||||
run_jit_function!(lib, main_fn_name, i64, |num| num_to_ast(
|
||||
env,
|
||||
|
@ -89,19 +93,20 @@ fn jit_to_ast_help<'a>(
|
|||
Layout::Builtin(Builtin::List(_, elem_layout)) => run_jit_function!(
|
||||
lib,
|
||||
main_fn_name,
|
||||
(*const libc::c_void, usize),
|
||||
|(ptr, len): (*const libc::c_void, usize)| {
|
||||
list_to_ast(env, ptr, len, elem_layout, content)
|
||||
}
|
||||
(*const u8, usize),
|
||||
|(ptr, len): (*const u8, usize)| { list_to_ast(env, ptr, len, elem_layout, content) }
|
||||
),
|
||||
Layout::PhantomEmptyStruct => run_jit_function!(lib, main_fn_name, &'static str, |_| {
|
||||
Layout::Builtin(other) => {
|
||||
todo!("add support for rendering builtin {:?} to the REPL", other)
|
||||
}
|
||||
Layout::PhantomEmptyStruct => run_jit_function!(lib, main_fn_name, &u8, |_| {
|
||||
Expr::Record {
|
||||
update: None,
|
||||
fields: &[],
|
||||
}
|
||||
}),
|
||||
Layout::Struct(field_layouts) => {
|
||||
let ptr_to_ast = |ptr: *const libc::c_void| match content {
|
||||
let ptr_to_ast = |ptr: *const u8| match content {
|
||||
Content::Structure(FlatType::Record(fields, _)) => {
|
||||
struct_to_ast(env, ptr, field_layouts, fields)
|
||||
}
|
||||
|
@ -113,13 +118,7 @@ fn jit_to_ast_help<'a>(
|
|||
|
||||
let (tag_name, payload_vars) = tags.iter().next().unwrap();
|
||||
|
||||
// We expect anything with payload vars
|
||||
// that is a single Tag TagUnion
|
||||
// has a Record content so the above case
|
||||
// should match instead
|
||||
debug_assert_eq!(payload_vars.len(), 0);
|
||||
|
||||
single_tag_union_to_ast(env, field_layouts, tag_name.clone(), payload_vars)
|
||||
single_tag_union_to_ast(env, ptr, field_layouts, tag_name.clone(), payload_vars)
|
||||
}
|
||||
other => {
|
||||
unreachable!(
|
||||
|
@ -132,76 +131,93 @@ fn jit_to_ast_help<'a>(
|
|||
let fields = [Layout::Builtin(Builtin::Int64), layout.clone()];
|
||||
let layout = Layout::Struct(&fields);
|
||||
|
||||
match env.ptr_bytes {
|
||||
// 64-bit target (8-byte pointers, 16-byte structs)
|
||||
8 => match layout.stack_size(env.ptr_bytes) {
|
||||
8 => {
|
||||
// just one eightbyte, returned as-is
|
||||
run_jit_function!(lib, main_fn_name, [u8; 8], |bytes: [u8; 8]| {
|
||||
ptr_to_ast((&bytes).as_ptr() as *const libc::c_void)
|
||||
})
|
||||
}
|
||||
16 => {
|
||||
// two eightbytes, returned as-is
|
||||
run_jit_function!(lib, main_fn_name, [u8; 16], |bytes: [u8; 16]| {
|
||||
ptr_to_ast((&bytes).as_ptr() as *const libc::c_void)
|
||||
})
|
||||
}
|
||||
larger_size => {
|
||||
// anything more than 2 eightbytes
|
||||
// the return "value" is a pointer to the result
|
||||
let result_stack_size = layout.stack_size(env.ptr_bytes);
|
||||
|
||||
run_jit_function_dynamic_type!(
|
||||
lib,
|
||||
main_fn_name,
|
||||
larger_size as usize,
|
||||
|bytes: *const u8| { ptr_to_ast(bytes as *const libc::c_void) }
|
||||
result_stack_size as usize,
|
||||
|bytes: *const u8| { ptr_to_ast(bytes as *const u8) }
|
||||
)
|
||||
}
|
||||
Layout::Union(union_layouts) => match content {
|
||||
Content::Structure(FlatType::TagUnion(tags, _)) => {
|
||||
debug_assert_eq!(union_layouts.len(), tags.len());
|
||||
|
||||
let tags_vec: std::vec::Vec<(TagName, std::vec::Vec<Variable>)> =
|
||||
tags.iter().map(|(a, b)| (a.clone(), b.clone())).collect();
|
||||
|
||||
let union_variant = union_sorted_tags_help(env.arena, tags_vec, None, env.subs);
|
||||
|
||||
let size = layout.stack_size(env.ptr_bytes);
|
||||
match union_variant {
|
||||
UnionVariant::Wrapped(tags_and_layouts) => {
|
||||
run_jit_function_dynamic_type!(
|
||||
lib,
|
||||
main_fn_name,
|
||||
size as usize,
|
||||
|ptr: *const u8| {
|
||||
// Because this is a `Wrapped`, the first 8 bytes encode the tag ID
|
||||
let tag_id = *(ptr as *const i64);
|
||||
|
||||
// use the tag ID as an index, to get its name and layout of any arguments
|
||||
let (tag_name, arg_layouts) = &tags_and_layouts[tag_id as usize];
|
||||
|
||||
let tag_expr = tag_name_to_expr(env, tag_name);
|
||||
let loc_tag_expr = &*env.arena.alloc(Located::at_zero(tag_expr));
|
||||
|
||||
let variables = &tags[tag_name];
|
||||
|
||||
// because the arg_layouts include the tag ID, it is one longer
|
||||
debug_assert_eq!(arg_layouts.len() - 1, variables.len());
|
||||
|
||||
// skip forward to the start of the first element, ignoring the tag id
|
||||
let ptr = ptr.offset(8);
|
||||
|
||||
let it = variables.iter().copied().zip(&arg_layouts[1..]);
|
||||
let output = sequence_of_expr(env, ptr, it);
|
||||
let output = output.into_bump_slice();
|
||||
|
||||
Expr::Apply(loc_tag_expr, output, CalledVia::Space)
|
||||
}
|
||||
)
|
||||
}
|
||||
_ => unreachable!("any other variant would have a different layout"),
|
||||
}
|
||||
}
|
||||
Content::Structure(FlatType::RecursiveTagUnion(_, _, _)) => {
|
||||
todo!("print recursive tag unions in the REPL")
|
||||
}
|
||||
other => unreachable!("Weird content for Union layout: {:?}", other),
|
||||
},
|
||||
// 32-bit target (4-byte pointers, 8-byte structs)
|
||||
4 => {
|
||||
// TODO what are valid return sizes here?
|
||||
// this is just extrapolated from the 64-bit case above
|
||||
// and not (yet) actually tested on a 32-bit system
|
||||
match layout.stack_size(env.ptr_bytes) {
|
||||
4 => {
|
||||
// just one fourbyte, returned as-is
|
||||
run_jit_function!(lib, main_fn_name, [u8; 4], |bytes: [u8; 4]| {
|
||||
ptr_to_ast((&bytes).as_ptr() as *const libc::c_void)
|
||||
})
|
||||
Layout::RecursiveUnion(_) | Layout::RecursivePointer => {
|
||||
todo!("add support for rendering recursive tag unions in the REPL")
|
||||
}
|
||||
8 => {
|
||||
// just one fourbyte, returned as-is
|
||||
run_jit_function!(lib, main_fn_name, [u8; 8], |bytes: [u8; 8]| {
|
||||
ptr_to_ast((&bytes).as_ptr() as *const libc::c_void)
|
||||
})
|
||||
|
||||
Layout::FunctionPointer(_, _) | Layout::Closure(_, _, _) => {
|
||||
todo!("add support for rendering functions in the REPL")
|
||||
}
|
||||
larger_size => {
|
||||
// anything more than 2 fourbytes
|
||||
// the return "value" is a pointer to the result
|
||||
run_jit_function_dynamic_type!(
|
||||
lib,
|
||||
main_fn_name,
|
||||
larger_size as usize,
|
||||
|bytes: *const u8| { ptr_to_ast(bytes as *const libc::c_void) }
|
||||
)
|
||||
Layout::Pointer(_) => todo!("add support for rendering pointers in the REPL"),
|
||||
}
|
||||
}
|
||||
}
|
||||
other => {
|
||||
panic!("Unsupported target: Roc cannot currently compile to systems where pointers are {} bytes in length.", other);
|
||||
}
|
||||
}
|
||||
}
|
||||
other => {
|
||||
todo!("TODO add support for rendering {:?} in the REPL", other);
|
||||
}
|
||||
|
||||
fn tag_name_to_expr<'a>(env: &Env<'a, '_>, tag_name: &TagName) -> Expr<'a> {
|
||||
match tag_name {
|
||||
TagName::Global(_) => Expr::GlobalTag(
|
||||
env.arena
|
||||
.alloc_str(&tag_name.as_string(env.interns, env.home)),
|
||||
),
|
||||
TagName::Private(_) => Expr::PrivateTag(
|
||||
env.arena
|
||||
.alloc_str(&tag_name.as_string(env.interns, env.home)),
|
||||
),
|
||||
TagName::Closure(_) => unreachable!("User cannot type this"),
|
||||
}
|
||||
}
|
||||
|
||||
fn ptr_to_ast<'a>(
|
||||
env: &Env<'a, '_>,
|
||||
ptr: *const libc::c_void,
|
||||
ptr: *const u8,
|
||||
layout: &Layout<'a>,
|
||||
content: &Content,
|
||||
) -> Expr<'a> {
|
||||
|
@ -227,7 +243,7 @@ fn ptr_to_ast<'a>(
|
|||
Layout::Builtin(Builtin::List(_, elem_layout)) => {
|
||||
// Turn the (ptr, len) wrapper struct into actual ptr and len values.
|
||||
let len = unsafe { *(ptr.offset(env.ptr_bytes as isize) as *const usize) };
|
||||
let ptr = unsafe { *(ptr as *const *const libc::c_void) };
|
||||
let ptr = unsafe { *(ptr as *const *const u8) };
|
||||
|
||||
list_to_ast(env, ptr, len, elem_layout, content)
|
||||
}
|
||||
|
@ -241,6 +257,12 @@ fn ptr_to_ast<'a>(
|
|||
Content::Structure(FlatType::Record(fields, _)) => {
|
||||
struct_to_ast(env, ptr, field_layouts, fields)
|
||||
}
|
||||
Content::Structure(FlatType::TagUnion(tags, _)) => {
|
||||
debug_assert_eq!(tags.len(), 1);
|
||||
|
||||
let (tag_name, payload_vars) = tags.iter().next().unwrap();
|
||||
single_tag_union_to_ast(env, ptr, field_layouts, tag_name.clone(), payload_vars)
|
||||
}
|
||||
other => {
|
||||
unreachable!(
|
||||
"Something had a Struct layout, but instead of a Record type, it had: {:?}",
|
||||
|
@ -259,7 +281,7 @@ fn ptr_to_ast<'a>(
|
|||
|
||||
fn list_to_ast<'a>(
|
||||
env: &Env<'a, '_>,
|
||||
ptr: *const libc::c_void,
|
||||
ptr: *const u8,
|
||||
len: usize,
|
||||
elem_layout: &Layout<'a>,
|
||||
content: &Content,
|
||||
|
@ -282,11 +304,11 @@ fn list_to_ast<'a>(
|
|||
|
||||
let arena = env.arena;
|
||||
let mut output = Vec::with_capacity_in(len, &arena);
|
||||
let elem_size = elem_layout.stack_size(env.ptr_bytes);
|
||||
let elem_size = elem_layout.stack_size(env.ptr_bytes) as usize;
|
||||
|
||||
for index in 0..(len as isize) {
|
||||
let offset_bytes: isize = index * elem_size as isize;
|
||||
let elem_ptr = unsafe { ptr.offset(offset_bytes) };
|
||||
for index in 0..len {
|
||||
let offset_bytes = index * elem_size;
|
||||
let elem_ptr = unsafe { ptr.add(offset_bytes) };
|
||||
let loc_expr = &*arena.alloc(Located {
|
||||
value: ptr_to_ast(env, elem_ptr, elem_layout, &elem_content),
|
||||
region: Region::zero(),
|
||||
|
@ -302,7 +324,8 @@ fn list_to_ast<'a>(
|
|||
|
||||
fn single_tag_union_to_ast<'a>(
|
||||
env: &Env<'a, '_>,
|
||||
field_layouts: &[Layout<'a>],
|
||||
ptr: *const u8,
|
||||
field_layouts: &'a [Layout<'a>],
|
||||
tag_name: TagName,
|
||||
payload_vars: &[Variable],
|
||||
) -> Expr<'a> {
|
||||
|
@ -310,28 +333,50 @@ fn single_tag_union_to_ast<'a>(
|
|||
|
||||
let arena = env.arena;
|
||||
|
||||
let tag_expr = match tag_name {
|
||||
TagName::Global(_) => {
|
||||
Expr::GlobalTag(arena.alloc_str(&tag_name.as_string(env.interns, env.home)))
|
||||
}
|
||||
TagName::Private(_) => {
|
||||
Expr::PrivateTag(arena.alloc_str(&tag_name.as_string(env.interns, env.home)))
|
||||
}
|
||||
TagName::Closure(_) => unreachable!("User cannot type this"),
|
||||
};
|
||||
let tag_expr = tag_name_to_expr(env, &tag_name);
|
||||
|
||||
let loc_tag_expr = &*arena.alloc(Located {
|
||||
value: tag_expr,
|
||||
region: Region::zero(),
|
||||
});
|
||||
let loc_tag_expr = &*arena.alloc(Located::at_zero(tag_expr));
|
||||
|
||||
Expr::Apply(loc_tag_expr, &[], CalledVia::Space)
|
||||
let it = payload_vars.iter().copied().zip(field_layouts);
|
||||
let output = sequence_of_expr(env, ptr as *const u8, it).into_bump_slice();
|
||||
|
||||
Expr::Apply(loc_tag_expr, output, CalledVia::Space)
|
||||
}
|
||||
|
||||
fn sequence_of_expr<'a, I>(
|
||||
env: &Env<'a, '_>,
|
||||
ptr: *const u8,
|
||||
sequence: I,
|
||||
) -> Vec<'a, &'a Located<Expr<'a>>>
|
||||
where
|
||||
I: Iterator<Item = (Variable, &'a Layout<'a>)>,
|
||||
I: ExactSizeIterator<Item = (Variable, &'a Layout<'a>)>,
|
||||
{
|
||||
let arena = env.arena;
|
||||
let subs = env.subs;
|
||||
let mut output = Vec::with_capacity_in(sequence.len(), &arena);
|
||||
|
||||
// We'll advance this as we iterate through the fields
|
||||
let mut field_ptr = ptr as *const u8;
|
||||
|
||||
for (var, layout) in sequence {
|
||||
let content = subs.get_without_compacting(var).content;
|
||||
let expr = ptr_to_ast(env, field_ptr, layout, &content);
|
||||
let loc_expr = Located::at_zero(expr);
|
||||
|
||||
output.push(&*arena.alloc(loc_expr));
|
||||
|
||||
// Advance the field pointer to the next field.
|
||||
field_ptr = unsafe { field_ptr.offset(layout.stack_size(env.ptr_bytes) as isize) };
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
fn struct_to_ast<'a>(
|
||||
env: &Env<'a, '_>,
|
||||
ptr: *const libc::c_void,
|
||||
field_layouts: &[Layout<'a>],
|
||||
ptr: *const u8,
|
||||
field_layouts: &'a [Layout<'a>],
|
||||
fields: &MutMap<Lowercase, RecordField<Variable>>,
|
||||
) -> Expr<'a> {
|
||||
let arena = env.arena;
|
||||
|
@ -339,7 +384,7 @@ fn struct_to_ast<'a>(
|
|||
let mut output = Vec::with_capacity_in(field_layouts.len(), &arena);
|
||||
|
||||
// The fields, sorted alphabetically
|
||||
let sorted_fields = {
|
||||
let mut sorted_fields = {
|
||||
let mut vec = fields
|
||||
.iter()
|
||||
.collect::<std::vec::Vec<(&Lowercase, &RecordField<Variable>)>>();
|
||||
|
@ -349,6 +394,33 @@ fn struct_to_ast<'a>(
|
|||
vec
|
||||
};
|
||||
|
||||
if sorted_fields.len() == 1 {
|
||||
// this is a 1-field wrapper record around another record or 1-tag tag union
|
||||
let (label, field) = sorted_fields.pop().unwrap();
|
||||
|
||||
let inner_content = env.subs.get_without_compacting(field.into_inner()).content;
|
||||
|
||||
let loc_expr = &*arena.alloc(Located {
|
||||
value: ptr_to_ast(env, ptr, &Layout::Struct(field_layouts), &inner_content),
|
||||
region: Region::zero(),
|
||||
});
|
||||
|
||||
let field_name = Located {
|
||||
value: &*arena.alloc_str(label.as_str()),
|
||||
region: Region::zero(),
|
||||
};
|
||||
let loc_field = Located {
|
||||
value: AssignedField::RequiredValue(field_name, &[], loc_expr),
|
||||
region: Region::zero(),
|
||||
};
|
||||
|
||||
let output = env.arena.alloc([loc_field]);
|
||||
|
||||
Expr::Record {
|
||||
update: None,
|
||||
fields: output,
|
||||
}
|
||||
} else {
|
||||
debug_assert_eq!(sorted_fields.len(), field_layouts.len());
|
||||
|
||||
// We'll advance this as we iterate through the fields
|
||||
|
@ -373,7 +445,8 @@ fn struct_to_ast<'a>(
|
|||
output.push(loc_field);
|
||||
|
||||
// Advance the field pointer to the next field.
|
||||
field_ptr = unsafe { field_ptr.offset(field_layout.stack_size(env.ptr_bytes) as isize) };
|
||||
field_ptr =
|
||||
unsafe { field_ptr.offset(field_layout.stack_size(env.ptr_bytes) as isize) };
|
||||
}
|
||||
|
||||
let output = output.into_bump_slice();
|
||||
|
@ -383,6 +456,7 @@ fn struct_to_ast<'a>(
|
|||
fields: output,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn bool_to_ast<'a>(env: &Env<'a, '_>, value: bool, content: &Content) -> Expr<'a> {
|
||||
use Content::*;
|
||||
|
@ -468,20 +542,17 @@ fn bool_to_ast<'a>(env: &Env<'a, '_>, value: bool, content: &Content) -> Expr<'a
|
|||
debug_assert!(payload_vars_1.is_empty());
|
||||
debug_assert!(payload_vars_2.is_empty());
|
||||
|
||||
let tag_name_as_str_1 = &tag_name_1.as_string(env.interns, env.home);
|
||||
let tag_name_as_str_2 = &tag_name_2.as_string(env.interns, env.home);
|
||||
|
||||
let tag_name = if value {
|
||||
tag_name_as_str_1.max(tag_name_as_str_2)
|
||||
max_by_key(tag_name_1, tag_name_2, |n| {
|
||||
n.as_string(env.interns, env.home)
|
||||
})
|
||||
} else {
|
||||
tag_name_as_str_1.min(tag_name_as_str_2)
|
||||
min_by_key(tag_name_1, tag_name_2, |n| {
|
||||
n.as_string(env.interns, env.home)
|
||||
})
|
||||
};
|
||||
|
||||
if tag_name.starts_with('@') {
|
||||
Expr::PrivateTag(arena.alloc_str(tag_name))
|
||||
} else {
|
||||
Expr::GlobalTag(arena.alloc_str(tag_name))
|
||||
}
|
||||
tag_name_to_expr(env, tag_name)
|
||||
}
|
||||
other => {
|
||||
unreachable!("Unexpected FlatType {:?} in bool_to_ast", other);
|
||||
|
@ -499,6 +570,117 @@ fn bool_to_ast<'a>(env: &Env<'a, '_>, value: bool, content: &Content) -> Expr<'a
|
|||
}
|
||||
}
|
||||
|
||||
fn byte_to_ast<'a>(env: &Env<'a, '_>, value: u8, content: &Content) -> Expr<'a> {
|
||||
use Content::*;
|
||||
|
||||
let arena = env.arena;
|
||||
|
||||
match content {
|
||||
Structure(flat_type) => {
|
||||
match flat_type {
|
||||
FlatType::Record(fields, _) => {
|
||||
debug_assert_eq!(fields.len(), 1);
|
||||
|
||||
let (label, field) = fields.iter().next().unwrap();
|
||||
let loc_label = Located {
|
||||
value: &*arena.alloc_str(label.as_str()),
|
||||
region: Region::zero(),
|
||||
};
|
||||
|
||||
let assigned_field = {
|
||||
// We may be multiple levels deep in nested tag unions
|
||||
// and/or records (e.g. { a: { b: { c: True } } }),
|
||||
// so we need to do this recursively on the field type.
|
||||
let field_var = *field.as_inner();
|
||||
let field_content = env.subs.get_without_compacting(field_var).content;
|
||||
let loc_expr = Located {
|
||||
value: byte_to_ast(env, value, &field_content),
|
||||
region: Region::zero(),
|
||||
};
|
||||
|
||||
AssignedField::RequiredValue(loc_label, &[], arena.alloc(loc_expr))
|
||||
};
|
||||
|
||||
let loc_assigned_field = Located {
|
||||
value: assigned_field,
|
||||
region: Region::zero(),
|
||||
};
|
||||
|
||||
Expr::Record {
|
||||
update: None,
|
||||
fields: arena.alloc([loc_assigned_field]),
|
||||
}
|
||||
}
|
||||
FlatType::TagUnion(tags, _) if tags.len() == 1 => {
|
||||
let (tag_name, payload_vars) = tags.iter().next().unwrap();
|
||||
|
||||
let loc_tag_expr = {
|
||||
let tag_name = &tag_name.as_string(env.interns, env.home);
|
||||
let tag_expr = if tag_name.starts_with('@') {
|
||||
Expr::PrivateTag(arena.alloc_str(tag_name))
|
||||
} else {
|
||||
Expr::GlobalTag(arena.alloc_str(tag_name))
|
||||
};
|
||||
|
||||
&*arena.alloc(Located {
|
||||
value: tag_expr,
|
||||
region: Region::zero(),
|
||||
})
|
||||
};
|
||||
|
||||
let payload = {
|
||||
// Since this has the layout of a number, there should be
|
||||
// exactly one payload in this tag.
|
||||
debug_assert_eq!(payload_vars.len(), 1);
|
||||
|
||||
let var = *payload_vars.iter().next().unwrap();
|
||||
let content = env.subs.get_without_compacting(var).content;
|
||||
|
||||
let loc_payload = &*arena.alloc(Located {
|
||||
value: byte_to_ast(env, value, &content),
|
||||
region: Region::zero(),
|
||||
});
|
||||
|
||||
arena.alloc([loc_payload])
|
||||
};
|
||||
|
||||
Expr::Apply(loc_tag_expr, payload, CalledVia::Space)
|
||||
}
|
||||
FlatType::TagUnion(tags, _) => {
|
||||
// anything with fewer tags is not a byte
|
||||
debug_assert!(tags.len() > 2);
|
||||
|
||||
let tags_vec: std::vec::Vec<(TagName, std::vec::Vec<Variable>)> =
|
||||
tags.iter().map(|(a, b)| (a.clone(), b.clone())).collect();
|
||||
|
||||
let union_variant = union_sorted_tags_help(env.arena, tags_vec, None, env.subs);
|
||||
|
||||
match union_variant {
|
||||
UnionVariant::ByteUnion(tagnames) => {
|
||||
let tag_name = &tagnames[value as usize];
|
||||
let tag_expr = tag_name_to_expr(env, tag_name);
|
||||
let loc_tag_expr = Located::at_zero(tag_expr);
|
||||
Expr::Apply(env.arena.alloc(loc_tag_expr), &[], CalledVia::Space)
|
||||
}
|
||||
_ => unreachable!("invalid union variant for a Byte!"),
|
||||
}
|
||||
}
|
||||
other => {
|
||||
unreachable!("Unexpected FlatType {:?} in bool_to_ast", other);
|
||||
}
|
||||
}
|
||||
}
|
||||
Alias(_, _, var) => {
|
||||
let content = env.subs.get_without_compacting(*var).content;
|
||||
|
||||
byte_to_ast(env, value, &content)
|
||||
}
|
||||
other => {
|
||||
unreachable!("Unexpected FlatType {:?} in bool_to_ast", other);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn num_to_ast<'a>(env: &Env<'a, '_>, num_expr: Expr<'a>, content: &Content) -> Expr<'a> {
|
||||
use Content::*;
|
||||
|
||||
|
@ -648,3 +830,30 @@ fn str_slice_to_ast<'a>(_arena: &'a Bump, string: &'a str) -> Expr<'a> {
|
|||
Expr::Str(StrLiteral::PlainLine(string))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO this is currently nighly-only: use the implementation in std once it's stabilized
|
||||
pub fn max_by<T, F: FnOnce(&T, &T) -> std::cmp::Ordering>(v1: T, v2: T, compare: F) -> T {
|
||||
use std::cmp::Ordering;
|
||||
|
||||
match compare(&v1, &v2) {
|
||||
Ordering::Less | Ordering::Equal => v2,
|
||||
Ordering::Greater => v1,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn min_by<T, F: FnOnce(&T, &T) -> std::cmp::Ordering>(v1: T, v2: T, compare: F) -> T {
|
||||
use std::cmp::Ordering;
|
||||
|
||||
match compare(&v1, &v2) {
|
||||
Ordering::Less | Ordering::Equal => v1,
|
||||
Ordering::Greater => v2,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn max_by_key<T, F: FnMut(&T) -> K, K: Ord>(v1: T, v2: T, mut f: F) -> T {
|
||||
max_by(v1, v2, |v1, v2| f(v1).cmp(&f(v2)))
|
||||
}
|
||||
|
||||
pub fn min_by_key<T, F: FnMut(&T) -> K, K: Ord>(v1: T, v2: T, mut f: F) -> T {
|
||||
min_by(v1, v2, |v1, v2| f(v1).cmp(&f(v2)))
|
||||
}
|
||||
|
|
|
@ -90,12 +90,15 @@ mod repl_eval {
|
|||
#[test]
|
||||
fn bool_in_record() {
|
||||
expect_success("{ x: 1 == 1 }", "{ x: True } : { x : Bool }");
|
||||
expect_success(
|
||||
"{ z: { y: { x: 1 == 1 } } }",
|
||||
"{ z: { y: { x: True } } } : { z : { y : { x : Bool } } }",
|
||||
);
|
||||
expect_success("{ x: 1 != 1 }", "{ x: False } : { x : Bool }");
|
||||
// TODO: see ptr_to_ast
|
||||
// expect_success(
|
||||
// "{ x: 1 == 1, y: 1 != 1 }",
|
||||
// "{ x: True, y: False } : { x : Bool, y : Bool }",
|
||||
// );
|
||||
expect_success(
|
||||
"{ x: 1 == 1, y: 1 != 1 }",
|
||||
"{ x: True, y: False } : { x : Bool, y : Bool }",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -116,14 +119,46 @@ mod repl_eval {
|
|||
expect_success("False", "False : [ False ]*");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn byte_tag_union() {
|
||||
expect_success(
|
||||
"if 1 == 1 then Red else if 1 == 1 then Green else Blue",
|
||||
"Red : [ Blue, Green, Red ]*",
|
||||
);
|
||||
|
||||
expect_success(
|
||||
"{ y: { x: if 1 == 1 then Red else if 1 == 1 then Green else Blue } }",
|
||||
"{ y: { x: Red } } : { y : { x : [ Blue, Green, Red ]* } }",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tag_in_record() {
|
||||
expect_success(
|
||||
"{ x: Foo 1 2 3, y : 4 }",
|
||||
"{ x: Foo 1 2 3, y: 4 } : { x : [ Foo (Num *) (Num *) (Num *) ]*, y : Num * }",
|
||||
);
|
||||
expect_success(
|
||||
"{ x: Foo 1 2 3 }",
|
||||
"{ x: Foo 1 2 3 } : { x : [ Foo (Num *) (Num *) (Num *) ]* }",
|
||||
);
|
||||
expect_success("{ x: Unit }", "{ x: Unit } : { x : [ Unit ]* }");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_element_tag_union() {
|
||||
expect_success("True 1", "True 1 : [ True (Num *) ]*");
|
||||
expect_success("Foo 1 3.14", "Foo 1 3.14 : [ Foo (Num *) Float ]*");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tag_with_arguments() {
|
||||
expect_success("True 1", "True 1 : [ True (Num *) ]*");
|
||||
// TODO handle more situations
|
||||
// expect_success(
|
||||
// "if 1 == 1 then True 1 else False 3.14",
|
||||
// "True 1 : [ True (Num *), False Float ]*",
|
||||
// )
|
||||
|
||||
expect_success(
|
||||
"if 1 == 1 then True 3 else False 3.14",
|
||||
"True 3 : [ False Float, True (Num *) ]*",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -273,32 +308,29 @@ mod repl_eval {
|
|||
);
|
||||
}
|
||||
|
||||
// TODO uncomment this once https://github.com/rtfeldman/roc/issues/295 is done
|
||||
// #[test]
|
||||
// fn basic_2_field_f64_record() {
|
||||
// expect_success(
|
||||
// "{ foo: 4.1, bar: 2.3 }",
|
||||
// "{ bar: 2.3, foo: 4.1 } : { bar : Float, foo : Float }",
|
||||
// );
|
||||
// }
|
||||
#[test]
|
||||
fn basic_2_field_f64_record() {
|
||||
expect_success(
|
||||
"{ foo: 4.1, bar: 2.3 }",
|
||||
"{ bar: 2.3, foo: 4.1 } : { bar : Float, foo : Float }",
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn basic_2_field_mixed_record() {
|
||||
// expect_success(
|
||||
// "{ foo: 4.1, bar: 2 }",
|
||||
// "{ bar: 2, foo: 4.1 } : { bar : Num *, foo : Float }",
|
||||
// );
|
||||
// }
|
||||
#[test]
|
||||
fn basic_2_field_mixed_record() {
|
||||
expect_success(
|
||||
"{ foo: 4.1, bar: 2 }",
|
||||
"{ bar: 2, foo: 4.1 } : { bar : Num *, foo : Float }",
|
||||
);
|
||||
}
|
||||
|
||||
// TODO uncomment this once https://github.com/rtfeldman/roc/issues/295 is done
|
||||
//
|
||||
// #[test]
|
||||
// fn basic_3_field_record() {
|
||||
// expect_success(
|
||||
// "{ foo: 4.1, bar: 2, baz: 0x5 }",
|
||||
// "{ foo: 4.1, bar: 2, baz: 0x5 } : { foo : Float, bar : Num *, baz : Int }",
|
||||
// );
|
||||
// }
|
||||
#[test]
|
||||
fn basic_3_field_record() {
|
||||
expect_success(
|
||||
"{ foo: 4.1, bar: 2, baz: 0x5 }",
|
||||
"{ bar: 2, baz: 5, foo: 4.1 } : { bar : Num *, baz : Int, foo : Float }",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_of_1_field_records() {
|
||||
|
|
|
@ -8,6 +8,7 @@ use roc_types::types::{Category, PatternCategory, Type};
|
|||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Constraint {
|
||||
Eq(Type, Expected<Type>, Category, Region),
|
||||
Store(Type, Variable, &'static str, u32),
|
||||
Lookup(Symbol, Expected<Type>, Region),
|
||||
Pattern(Region, PatternCategory, Type, PExpected<Type>),
|
||||
True, // Used for things that always unify, e.g. blanks and runtime errors
|
||||
|
|
|
@ -470,14 +470,17 @@ pub fn sort_can_defs(
|
|||
|
||||
// TODO also do the same `addDirects` check elm/compiler does, so we can
|
||||
// report an error if a recursive definition can't possibly terminate!
|
||||
match topological_sort_into_groups(defined_symbols.as_slice(), all_successors_without_self) {
|
||||
match ven_graph::topological_sort_into_groups(
|
||||
defined_symbols.as_slice(),
|
||||
all_successors_without_self,
|
||||
) {
|
||||
Ok(groups) => {
|
||||
let mut declarations = Vec::new();
|
||||
|
||||
// groups are in reversed order
|
||||
for group in groups.into_iter().rev() {
|
||||
group_to_declaration(
|
||||
group,
|
||||
&group,
|
||||
&env.closures,
|
||||
&mut all_successors_with_self,
|
||||
&can_defs_by_symbol,
|
||||
|
@ -487,21 +490,10 @@ pub fn sort_can_defs(
|
|||
|
||||
(Ok(declarations), output)
|
||||
}
|
||||
Err((groups, nodes_in_cycle)) => {
|
||||
Err((mut groups, nodes_in_cycle)) => {
|
||||
let mut declarations = Vec::new();
|
||||
let mut problems = Vec::new();
|
||||
|
||||
// groups are in reversed order
|
||||
for group in groups.into_iter().rev() {
|
||||
group_to_declaration(
|
||||
group,
|
||||
&env.closures,
|
||||
&mut all_successors_with_self,
|
||||
&can_defs_by_symbol,
|
||||
&mut declarations,
|
||||
);
|
||||
}
|
||||
|
||||
// nodes_in_cycle are symbols that form a syntactic cycle. That isn't always a problem,
|
||||
// and in general it's impossible to decide whether it is. So we use a crude heuristic:
|
||||
//
|
||||
|
@ -571,8 +563,50 @@ pub fn sort_can_defs(
|
|||
declarations.push(Declaration::InvalidCycle(symbols_in_cycle, regions));
|
||||
} else {
|
||||
// slightly inefficient, because we know this becomes exactly one DeclareRec already
|
||||
groups.push(cycle);
|
||||
}
|
||||
}
|
||||
|
||||
// now we have a collection of groups whose dependencies are not cyclic.
|
||||
// They are however not yet topologically sorted. Here we have to get a bit
|
||||
// creative to get all the definitions in the correct sorted order.
|
||||
|
||||
let mut group_ids = Vec::with_capacity(groups.len());
|
||||
let mut symbol_to_group_index = MutMap::default();
|
||||
for (i, group) in groups.iter().enumerate() {
|
||||
for symbol in group {
|
||||
symbol_to_group_index.insert(*symbol, i);
|
||||
}
|
||||
|
||||
group_ids.push(i);
|
||||
}
|
||||
|
||||
let successors_of_group = |group_id: &usize| {
|
||||
let mut result = ImSet::default();
|
||||
|
||||
// for each symbol in this group
|
||||
for symbol in &groups[*group_id] {
|
||||
// find its successors
|
||||
for succ in all_successors_without_self(symbol) {
|
||||
// and add its group to the result
|
||||
result.insert(symbol_to_group_index[&succ]);
|
||||
}
|
||||
}
|
||||
|
||||
// don't introduce any cycles to self
|
||||
result.remove(group_id);
|
||||
|
||||
result
|
||||
};
|
||||
|
||||
match ven_graph::topological_sort_into_groups(&group_ids, successors_of_group) {
|
||||
Ok(sorted_group_ids) => {
|
||||
for sorted_group in sorted_group_ids.iter().rev() {
|
||||
for group_id in sorted_group.iter().rev() {
|
||||
let group = &groups[*group_id];
|
||||
|
||||
group_to_declaration(
|
||||
cycle,
|
||||
group,
|
||||
&env.closures,
|
||||
&mut all_successors_with_self,
|
||||
&can_defs_by_symbol,
|
||||
|
@ -580,6 +614,9 @@ pub fn sort_can_defs(
|
|||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => unreachable!("there should be no cycles now!"),
|
||||
}
|
||||
|
||||
for problem in problems {
|
||||
env.problem(problem);
|
||||
|
@ -591,7 +628,7 @@ pub fn sort_can_defs(
|
|||
}
|
||||
|
||||
fn group_to_declaration(
|
||||
group: Vec<Symbol>,
|
||||
group: &[Symbol],
|
||||
closures: &MutMap<Symbol, References>,
|
||||
successors: &mut dyn FnMut(&Symbol) -> ImSet<Symbol>,
|
||||
can_defs_by_symbol: &MutMap<Symbol, Def>,
|
||||
|
|
|
@ -725,10 +725,11 @@ pub fn canonicalize_expr<'a>(
|
|||
}
|
||||
ast::Expr::MalformedIdent(name) => {
|
||||
use roc_problem::can::RuntimeError::*;
|
||||
(
|
||||
RuntimeError(MalformedIdentifier((*name).into(), region)),
|
||||
Output::default(),
|
||||
)
|
||||
|
||||
let problem = MalformedIdentifier((*name).into(), region);
|
||||
env.problem(Problem::RuntimeError(problem.clone()));
|
||||
|
||||
(RuntimeError(problem), Output::default())
|
||||
}
|
||||
ast::Expr::Nested(sub_expr) => {
|
||||
let (answer, output) = canonicalize_expr(env, var_store, scope, region, sub_expr);
|
||||
|
|
|
@ -55,6 +55,40 @@ pub struct Env {
|
|||
pub home: ModuleId,
|
||||
}
|
||||
|
||||
fn constrain_untyped_args(
|
||||
env: &Env,
|
||||
arguments: &[(Variable, Located<Pattern>)],
|
||||
closure_type: Type,
|
||||
return_type: Type,
|
||||
) -> (Vec<Variable>, PatternState, Type) {
|
||||
let mut vars = Vec::with_capacity(arguments.len());
|
||||
let mut pattern_types = Vec::with_capacity(arguments.len());
|
||||
|
||||
let mut pattern_state = PatternState::default();
|
||||
|
||||
for (pattern_var, loc_pattern) in arguments {
|
||||
let pattern_type = Type::Variable(*pattern_var);
|
||||
let pattern_expected = PExpected::NoExpectation(pattern_type.clone());
|
||||
|
||||
pattern_types.push(pattern_type);
|
||||
|
||||
constrain_pattern(
|
||||
env,
|
||||
&loc_pattern.value,
|
||||
loc_pattern.region,
|
||||
pattern_expected,
|
||||
&mut pattern_state,
|
||||
);
|
||||
|
||||
vars.push(*pattern_var);
|
||||
}
|
||||
|
||||
let function_type =
|
||||
Type::Function(pattern_types, Box::new(closure_type), Box::new(return_type));
|
||||
|
||||
(vars, pattern_state, function_type)
|
||||
}
|
||||
|
||||
pub fn constrain_expr(
|
||||
env: &Env,
|
||||
region: Region,
|
||||
|
@ -313,46 +347,25 @@ pub fn constrain_expr(
|
|||
} => {
|
||||
// NOTE defs are treated somewhere else!
|
||||
let loc_body_expr = &**boxed;
|
||||
let mut state = PatternState {
|
||||
headers: SendMap::default(),
|
||||
vars: Vec::with_capacity(arguments.len()),
|
||||
constraints: Vec::with_capacity(1),
|
||||
};
|
||||
let mut vars = Vec::with_capacity(state.vars.capacity() + 1);
|
||||
let mut pattern_types = Vec::with_capacity(state.vars.capacity());
|
||||
|
||||
let ret_var = *ret_var;
|
||||
let closure_var = *closure_var;
|
||||
let closure_ext_var = *closure_ext_var;
|
||||
let ret_type = Type::Variable(ret_var);
|
||||
|
||||
let closure_type = Type::Variable(closure_var);
|
||||
let return_type = Type::Variable(ret_var);
|
||||
let (mut vars, pattern_state, function_type) =
|
||||
constrain_untyped_args(env, arguments, closure_type, return_type.clone());
|
||||
|
||||
vars.push(ret_var);
|
||||
vars.push(closure_var);
|
||||
vars.push(closure_ext_var);
|
||||
vars.push(*fn_var);
|
||||
|
||||
for (pattern_var, loc_pattern) in arguments {
|
||||
let pattern_type = Type::Variable(*pattern_var);
|
||||
let pattern_expected = PExpected::NoExpectation(pattern_type.clone());
|
||||
|
||||
pattern_types.push(pattern_type);
|
||||
|
||||
constrain_pattern(
|
||||
env,
|
||||
&loc_pattern.value,
|
||||
loc_pattern.region,
|
||||
pattern_expected,
|
||||
&mut state,
|
||||
);
|
||||
|
||||
vars.push(*pattern_var);
|
||||
}
|
||||
|
||||
let body_type = NoExpectation(ret_type.clone());
|
||||
let body_type = NoExpectation(return_type);
|
||||
let ret_constraint =
|
||||
constrain_expr(env, loc_body_expr.region, &loc_body_expr.value, body_type);
|
||||
|
||||
vars.push(*fn_var);
|
||||
let defs_constraint = And(state.constraints);
|
||||
|
||||
// make sure the captured symbols are sorted!
|
||||
debug_assert_eq!(captured_symbols.clone(), {
|
||||
let mut copy = captured_symbols.clone();
|
||||
|
@ -369,28 +382,22 @@ pub fn constrain_expr(
|
|||
&mut vars,
|
||||
);
|
||||
|
||||
let fn_type = Type::Function(
|
||||
pattern_types,
|
||||
Box::new(Type::Variable(closure_var)),
|
||||
Box::new(ret_type),
|
||||
);
|
||||
|
||||
exists(
|
||||
vars,
|
||||
And(vec![
|
||||
Let(Box::new(LetConstraint {
|
||||
rigid_vars: Vec::new(),
|
||||
flex_vars: state.vars,
|
||||
def_types: state.headers,
|
||||
defs_constraint,
|
||||
flex_vars: pattern_state.vars,
|
||||
def_types: pattern_state.headers,
|
||||
defs_constraint: And(pattern_state.constraints),
|
||||
ret_constraint,
|
||||
})),
|
||||
// "the closure's type is equal to expected type"
|
||||
Eq(fn_type.clone(), expected, Category::Lambda, region),
|
||||
Eq(function_type.clone(), expected, Category::Lambda, region),
|
||||
// "fn_var is equal to the closure's type" - fn_var is used in code gen
|
||||
Eq(
|
||||
Type::Variable(*fn_var),
|
||||
NoExpectation(fn_type),
|
||||
NoExpectation(function_type),
|
||||
Category::Storage(std::file!(), std::line!()),
|
||||
region,
|
||||
),
|
||||
|
@ -1052,9 +1059,9 @@ fn constrain_def(env: &Env, def: &Def, body_con: Constraint) -> Constraint {
|
|||
let expr_var = def.expr_var;
|
||||
let expr_type = Type::Variable(expr_var);
|
||||
|
||||
let mut pattern_state = constrain_def_pattern(env, &def.loc_pattern, expr_type.clone());
|
||||
let mut def_pattern_state = constrain_def_pattern(env, &def.loc_pattern, expr_type.clone());
|
||||
|
||||
pattern_state.vars.push(expr_var);
|
||||
def_pattern_state.vars.push(expr_var);
|
||||
|
||||
let mut new_rigids = Vec::new();
|
||||
|
||||
|
@ -1070,7 +1077,7 @@ fn constrain_def(env: &Env, def: &Def, body_con: Constraint) -> Constraint {
|
|||
&mut new_rigids,
|
||||
&mut ftv,
|
||||
&def.loc_pattern,
|
||||
&mut pattern_state.headers,
|
||||
&mut def_pattern_state.headers,
|
||||
);
|
||||
|
||||
let env = &Env {
|
||||
|
@ -1087,7 +1094,7 @@ fn constrain_def(env: &Env, def: &Def, body_con: Constraint) -> Constraint {
|
|||
signature.clone(),
|
||||
);
|
||||
|
||||
pattern_state.constraints.push(Eq(
|
||||
def_pattern_state.constraints.push(Eq(
|
||||
expr_type,
|
||||
annotation_expected.clone(),
|
||||
Category::Storage(std::file!(), std::line!()),
|
||||
|
@ -1165,19 +1172,19 @@ fn constrain_def(env: &Env, def: &Def, body_con: Constraint) -> Constraint {
|
|||
}
|
||||
|
||||
{
|
||||
// record the correct type in pattern_var
|
||||
let pattern_type = Type::Variable(*pattern_var);
|
||||
pattern_types.push(pattern_type.clone());
|
||||
// NOTE: because we perform an equality with part of the signature
|
||||
// this constraint must be to the def_pattern_state's constraints
|
||||
def_pattern_state.vars.push(*pattern_var);
|
||||
pattern_types.push(Type::Variable(*pattern_var));
|
||||
|
||||
state.vars.push(*pattern_var);
|
||||
state.constraints.push(Constraint::Eq(
|
||||
pattern_type.clone(),
|
||||
let pattern_con = Eq(
|
||||
Type::Variable(*pattern_var),
|
||||
Expected::NoExpectation(loc_ann.clone()),
|
||||
Category::Storage(std::file!(), std::line!()),
|
||||
loc_pattern.region,
|
||||
));
|
||||
);
|
||||
|
||||
vars.push(*pattern_var);
|
||||
def_pattern_state.constraints.push(pattern_con);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1213,14 +1220,10 @@ fn constrain_def(env: &Env, def: &Def, body_con: Constraint) -> Constraint {
|
|||
ret_constraint,
|
||||
})),
|
||||
// "the closure's type is equal to expected type"
|
||||
Eq(fn_type.clone(), expected, Category::Lambda, region),
|
||||
// "fn_var is equal to the closure's type" - fn_var is used in code gen
|
||||
Eq(
|
||||
Type::Variable(*fn_var),
|
||||
NoExpectation(fn_type),
|
||||
Category::Storage(std::file!(), std::line!()),
|
||||
region,
|
||||
),
|
||||
Eq(fn_type, expected, Category::Lambda, region),
|
||||
// Store type into AST vars. We use Store so errors aren't reported twice
|
||||
Store(signature.clone(), *fn_var, std::file!(), std::line!()),
|
||||
Store(signature, expr_var, std::file!(), std::line!()),
|
||||
closure_constraint,
|
||||
]),
|
||||
)
|
||||
|
@ -1248,13 +1251,13 @@ fn constrain_def(env: &Env, def: &Def, body_con: Constraint) -> Constraint {
|
|||
|
||||
Let(Box::new(LetConstraint {
|
||||
rigid_vars: new_rigids,
|
||||
flex_vars: pattern_state.vars,
|
||||
def_types: pattern_state.headers,
|
||||
flex_vars: def_pattern_state.vars,
|
||||
def_types: def_pattern_state.headers,
|
||||
defs_constraint: Let(Box::new(LetConstraint {
|
||||
rigid_vars: Vec::new(), // always empty
|
||||
flex_vars: Vec::new(), // empty, because our functions have no arguments
|
||||
def_types: SendMap::default(), // empty, because our functions have no arguments!
|
||||
defs_constraint: And(pattern_state.constraints),
|
||||
defs_constraint: And(def_pattern_state.constraints),
|
||||
ret_constraint: expr_con,
|
||||
})),
|
||||
ret_constraint: body_con,
|
||||
|
@ -1369,23 +1372,9 @@ pub fn rec_defs_help(
|
|||
let expr_var = def.expr_var;
|
||||
let expr_type = Type::Variable(expr_var);
|
||||
|
||||
let pattern_expected = PExpected::NoExpectation(expr_type.clone());
|
||||
let mut def_pattern_state = constrain_def_pattern(env, &def.loc_pattern, expr_type.clone());
|
||||
|
||||
let mut pattern_state = PatternState {
|
||||
headers: SendMap::default(),
|
||||
vars: flex_info.vars.clone(),
|
||||
constraints: Vec::with_capacity(1),
|
||||
};
|
||||
|
||||
constrain_pattern(
|
||||
env,
|
||||
&def.loc_pattern.value,
|
||||
def.loc_pattern.region,
|
||||
pattern_expected,
|
||||
&mut pattern_state,
|
||||
);
|
||||
|
||||
pattern_state.vars.push(expr_var);
|
||||
def_pattern_state.vars.push(expr_var);
|
||||
|
||||
let mut new_rigids = Vec::new();
|
||||
match &def.annotation {
|
||||
|
@ -1406,9 +1395,9 @@ pub fn rec_defs_help(
|
|||
ret_constraint: expr_con,
|
||||
}));
|
||||
|
||||
flex_info.vars = pattern_state.vars;
|
||||
flex_info.vars = def_pattern_state.vars;
|
||||
flex_info.constraints.push(def_con);
|
||||
flex_info.def_types.extend(pattern_state.headers);
|
||||
flex_info.def_types.extend(def_pattern_state.headers);
|
||||
}
|
||||
|
||||
Some(annotation) => {
|
||||
|
@ -1421,7 +1410,7 @@ pub fn rec_defs_help(
|
|||
&mut new_rigids,
|
||||
&mut ftv,
|
||||
&def.loc_pattern,
|
||||
&mut pattern_state.headers,
|
||||
&mut def_pattern_state.headers,
|
||||
);
|
||||
|
||||
let annotation_expected = FromAnnotation(
|
||||
|
@ -1433,43 +1422,149 @@ pub fn rec_defs_help(
|
|||
signature.clone(),
|
||||
);
|
||||
|
||||
let expr_con = constrain_expr(
|
||||
&Env {
|
||||
rigids: ftv,
|
||||
home: env.home,
|
||||
// when a def is annotated, and it's body is a closure, treat this
|
||||
// as a named function (in elm terms) for error messages.
|
||||
//
|
||||
// This means we get errors like "the first argument of `f` is weird"
|
||||
// instead of the more generic "something is wrong with the body of `f`"
|
||||
match (&def.loc_expr.value, &signature) {
|
||||
(
|
||||
Closure {
|
||||
function_type: fn_var,
|
||||
closure_type: closure_var,
|
||||
closure_ext_var,
|
||||
return_type: ret_var,
|
||||
captured_symbols,
|
||||
arguments,
|
||||
loc_body,
|
||||
name,
|
||||
..
|
||||
},
|
||||
def.loc_expr.region,
|
||||
&def.loc_expr.value,
|
||||
annotation_expected.clone(),
|
||||
Type::Function(arg_types, _, _),
|
||||
) => {
|
||||
let expected = annotation_expected;
|
||||
let region = def.loc_expr.region;
|
||||
|
||||
let loc_body_expr = &**loc_body;
|
||||
let mut state = PatternState {
|
||||
headers: SendMap::default(),
|
||||
vars: Vec::with_capacity(arguments.len()),
|
||||
constraints: Vec::with_capacity(1),
|
||||
};
|
||||
let mut vars = Vec::with_capacity(state.vars.capacity() + 1);
|
||||
let mut pattern_types = Vec::with_capacity(state.vars.capacity());
|
||||
let ret_var = *ret_var;
|
||||
let closure_var = *closure_var;
|
||||
let closure_ext_var = *closure_ext_var;
|
||||
let ret_type = Type::Variable(ret_var);
|
||||
|
||||
vars.push(ret_var);
|
||||
vars.push(closure_var);
|
||||
vars.push(closure_ext_var);
|
||||
|
||||
let it = arguments.iter().zip(arg_types.iter()).enumerate();
|
||||
for (index, ((pattern_var, loc_pattern), loc_ann)) in it {
|
||||
{
|
||||
// ensure type matches the one in the annotation
|
||||
let opt_label =
|
||||
if let Pattern::Identifier(label) = def.loc_pattern.value {
|
||||
Some(label)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let pattern_type: &Type = loc_ann;
|
||||
|
||||
let pattern_expected = PExpected::ForReason(
|
||||
PReason::TypedArg {
|
||||
index: Index::zero_based(index),
|
||||
opt_name: opt_label,
|
||||
},
|
||||
pattern_type.clone(),
|
||||
loc_pattern.region,
|
||||
);
|
||||
|
||||
// ensure expected type unifies with annotated type
|
||||
let storage_con = Eq(
|
||||
expr_type,
|
||||
annotation_expected.clone(),
|
||||
constrain_pattern(
|
||||
env,
|
||||
&loc_pattern.value,
|
||||
loc_pattern.region,
|
||||
pattern_expected,
|
||||
&mut state,
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
// NOTE: because we perform an equality with part of the signature
|
||||
// this constraint must be to the def_pattern_state's constraints
|
||||
def_pattern_state.vars.push(*pattern_var);
|
||||
pattern_types.push(Type::Variable(*pattern_var));
|
||||
|
||||
let pattern_con = Eq(
|
||||
Type::Variable(*pattern_var),
|
||||
Expected::NoExpectation(loc_ann.clone()),
|
||||
Category::Storage(std::file!(), std::line!()),
|
||||
def.loc_expr.region,
|
||||
loc_pattern.region,
|
||||
);
|
||||
|
||||
// TODO investigate if this let can be safely removed
|
||||
let def_con = Let(Box::new(LetConstraint {
|
||||
def_pattern_state.constraints.push(pattern_con);
|
||||
}
|
||||
}
|
||||
|
||||
let closure_constraint = constrain_closure_size(
|
||||
*name,
|
||||
region,
|
||||
captured_symbols,
|
||||
closure_var,
|
||||
closure_ext_var,
|
||||
&mut vars,
|
||||
);
|
||||
|
||||
let fn_type = Type::Function(
|
||||
pattern_types,
|
||||
Box::new(Type::Variable(closure_var)),
|
||||
Box::new(ret_type.clone()),
|
||||
);
|
||||
let body_type = NoExpectation(ret_type);
|
||||
let expr_con = constrain_expr(
|
||||
env,
|
||||
loc_body_expr.region,
|
||||
&loc_body_expr.value,
|
||||
body_type,
|
||||
);
|
||||
|
||||
vars.push(*fn_var);
|
||||
|
||||
let def_con = exists(
|
||||
vars,
|
||||
And(vec![
|
||||
Let(Box::new(LetConstraint {
|
||||
rigid_vars: Vec::new(),
|
||||
flex_vars: Vec::new(), // empty because Roc function defs have no args
|
||||
def_types: SendMap::default(), // empty because Roc function defs have no args
|
||||
defs_constraint: storage_con,
|
||||
flex_vars: state.vars,
|
||||
def_types: state.headers,
|
||||
defs_constraint: And(state.constraints),
|
||||
ret_constraint: expr_con,
|
||||
}));
|
||||
})),
|
||||
Eq(fn_type.clone(), expected.clone(), Category::Lambda, region),
|
||||
// "fn_var is equal to the closure's type" - fn_var is used in code gen
|
||||
// Store type into AST vars. We use Store so errors aren't reported twice
|
||||
Store(signature.clone(), *fn_var, std::file!(), std::line!()),
|
||||
Store(signature, expr_var, std::file!(), std::line!()),
|
||||
closure_constraint,
|
||||
]),
|
||||
);
|
||||
|
||||
rigid_info.vars.extend(&new_rigids);
|
||||
|
||||
rigid_info.constraints.push(Let(Box::new(LetConstraint {
|
||||
rigid_vars: new_rigids,
|
||||
flex_vars: pattern_state.vars,
|
||||
flex_vars: def_pattern_state.vars,
|
||||
def_types: SendMap::default(), // no headers introduced (at this level)
|
||||
defs_constraint: def_con,
|
||||
ret_constraint: True,
|
||||
})));
|
||||
rigid_info.def_types.extend(pattern_state.headers);
|
||||
rigid_info.def_types.extend(def_pattern_state.headers);
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ use roc_region::all::{Located, Region};
|
|||
use roc_types::subs::Variable;
|
||||
use roc_types::types::{Category, PReason, PatternCategory, Reason, RecordField, Type};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PatternState {
|
||||
pub headers: SendMap<Symbol, Located<Type>>,
|
||||
pub vars: Vec<Variable>,
|
||||
|
|
|
@ -1369,7 +1369,10 @@ pub fn load_symbol<'a, 'ctx, 'env>(
|
|||
Some((_, ptr)) => env
|
||||
.builder
|
||||
.build_load(*ptr, symbol.ident_string(&env.interns)),
|
||||
None => panic!("There was no entry for {:?} in scope {:?}", symbol, scope),
|
||||
None => panic!(
|
||||
"There was no entry for {:?} {} in scope {:?}",
|
||||
symbol, symbol, scope
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1148,7 +1148,7 @@ mod gen_primitives {
|
|||
|
||||
ConsList a : [ Cons a (ConsList a), Nil ]
|
||||
|
||||
# isEmpty : ConsList a -> Bool
|
||||
isEmpty : ConsList a -> Bool
|
||||
isEmpty = \list ->
|
||||
when list is
|
||||
Cons _ _ ->
|
||||
|
@ -1195,4 +1195,167 @@ mod gen_primitives {
|
|||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn rbtree_insert() {
|
||||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
|
||||
NodeColor : [ Red, Black ]
|
||||
|
||||
Dict k v : [ Node NodeColor k v (Dict k v) (Dict k v), Empty ]
|
||||
|
||||
Key k : Num k
|
||||
|
||||
insert : Key k, v, Dict (Key k) v -> Dict (Key k) v
|
||||
insert = \key, value, dict ->
|
||||
when insertHelp key value dict is
|
||||
Node Red k v l r ->
|
||||
Node Black k v l r
|
||||
|
||||
x ->
|
||||
x
|
||||
|
||||
insertHelp : (Key k), v, Dict (Key k) v -> Dict (Key k) v
|
||||
insertHelp = \key, value, dict ->
|
||||
when dict is
|
||||
Empty ->
|
||||
# New nodes are always red. If it violates the rules, it will be fixed
|
||||
# when balancing.
|
||||
Node Red key value Empty Empty
|
||||
|
||||
Node nColor nKey nValue nLeft nRight ->
|
||||
when Num.compare key nKey is
|
||||
LT ->
|
||||
balance nColor nKey nValue (insertHelp key value nLeft) nRight
|
||||
|
||||
EQ ->
|
||||
Node nColor nKey value nLeft nRight
|
||||
|
||||
GT ->
|
||||
balance nColor nKey nValue nLeft (insertHelp key value nRight)
|
||||
|
||||
balance : NodeColor, k, v, Dict k v, Dict k v -> Dict k v
|
||||
balance = \color, key, value, left, right ->
|
||||
when right is
|
||||
Node Red rK rV rLeft rRight ->
|
||||
when left is
|
||||
Node Red lK lV lLeft lRight ->
|
||||
Node
|
||||
Red
|
||||
key
|
||||
value
|
||||
(Node Black lK lV lLeft lRight)
|
||||
(Node Black rK rV rLeft rRight)
|
||||
|
||||
_ ->
|
||||
Node color rK rV (Node Red key value left rLeft) rRight
|
||||
|
||||
_ ->
|
||||
when left is
|
||||
Node Red lK lV (Node Red llK llV llLeft llRight) lRight ->
|
||||
Node
|
||||
Red
|
||||
lK
|
||||
lV
|
||||
(Node Black llK llV llLeft llRight)
|
||||
(Node Black key value lRight right)
|
||||
|
||||
_ ->
|
||||
Node color key value left right
|
||||
|
||||
main : Dict Int {}
|
||||
main =
|
||||
insert 0 {} Empty
|
||||
"#
|
||||
),
|
||||
1,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn rbtree_balance() {
|
||||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
|
||||
NodeColor : [ Red, Black ]
|
||||
|
||||
Dict k v : [ Node NodeColor k v (Dict k v) (Dict k v), Empty ]
|
||||
|
||||
Key k : Num k
|
||||
|
||||
balance : NodeColor, k, v, Dict k v, Dict k v -> Dict k v
|
||||
balance = \color, key, value, left, right ->
|
||||
when right is
|
||||
Node Red lK lV (Node Red llK llV llLeft llRight) lRight -> Empty
|
||||
Empty -> Empty
|
||||
|
||||
|
||||
main : Dict Int {}
|
||||
main =
|
||||
balance Red 0 {} Empty Empty
|
||||
"#
|
||||
),
|
||||
1,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn linked_list_double_pattern_match() {
|
||||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
|
||||
ConsList a : [ Cons a (ConsList a), Nil ]
|
||||
|
||||
foo : ConsList Int -> Int
|
||||
foo = \list ->
|
||||
when list is
|
||||
Cons _ (Cons x _) -> x
|
||||
_ -> 0
|
||||
|
||||
main : Int
|
||||
main =
|
||||
foo (Cons 1 (Cons 32 Nil))
|
||||
"#
|
||||
),
|
||||
32,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn binary_tree_double_pattern_match() {
|
||||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
|
||||
BTree : [ Node BTree BTree, Leaf Int ]
|
||||
|
||||
foo : BTree -> Int
|
||||
foo = \btree ->
|
||||
when btree is
|
||||
Node (Node (Leaf x) _) _ -> x
|
||||
_ -> 0
|
||||
|
||||
main : Int
|
||||
main =
|
||||
foo (Node (Node (Leaf 32) (Leaf 0)) (Leaf 0))
|
||||
"#
|
||||
),
|
||||
32,
|
||||
i64
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -820,4 +820,28 @@ mod gen_records {
|
|||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn booleans_in_record() {
|
||||
assert_evals_to!(
|
||||
indoc!("{ x: 1 == 1, y: 1 == 1 }"),
|
||||
(true, true),
|
||||
(bool, bool)
|
||||
);
|
||||
assert_evals_to!(
|
||||
indoc!("{ x: 1 != 1, y: 1 == 1 }"),
|
||||
(false, true),
|
||||
(bool, bool)
|
||||
);
|
||||
assert_evals_to!(
|
||||
indoc!("{ x: 1 == 1, y: 1 != 1 }"),
|
||||
(true, false),
|
||||
(bool, bool)
|
||||
);
|
||||
assert_evals_to!(
|
||||
indoc!("{ x: 1 != 1, y: 1 != 1 }"),
|
||||
(false, false),
|
||||
(bool, bool)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -389,6 +389,13 @@ fn variable_usage_help(con: &Constraint, declared: &mut SeenVariables, used: &mu
|
|||
used.insert(v);
|
||||
}
|
||||
}
|
||||
Store(tipe, var, _, _) => {
|
||||
for v in tipe.variables() {
|
||||
used.insert(v);
|
||||
}
|
||||
|
||||
used.insert(*var);
|
||||
}
|
||||
Lookup(_, expectation, _) => {
|
||||
for v in expectation.get_type_ref().variables() {
|
||||
used.insert(v);
|
||||
|
|
|
@ -979,7 +979,7 @@ fn path_to_expr_help<'a>(
|
|||
break;
|
||||
}
|
||||
Some(wrapped) => {
|
||||
let field_layouts = match layout {
|
||||
let field_layouts = match &layout {
|
||||
Layout::Union(layouts) | Layout::RecursiveUnion(layouts) => {
|
||||
layouts[*tag_id as usize].to_vec()
|
||||
}
|
||||
|
@ -989,7 +989,10 @@ fn path_to_expr_help<'a>(
|
|||
|
||||
debug_assert!(*index < field_layouts.len() as u64);
|
||||
|
||||
let inner_layout = field_layouts[*index as usize].clone();
|
||||
let inner_layout = match &field_layouts[*index as usize] {
|
||||
Layout::RecursivePointer => layout.clone(),
|
||||
other => other.clone(),
|
||||
};
|
||||
|
||||
let inner_expr = Expr::AccessAtIndex {
|
||||
index: *index,
|
||||
|
@ -1057,7 +1060,7 @@ fn test_to_equality<'a>(
|
|||
let rhs_symbol = env.unique_symbol();
|
||||
|
||||
stores.push((lhs_symbol, Layout::Builtin(Builtin::Int64), lhs));
|
||||
stores.insert(0, (rhs_symbol, Layout::Builtin(Builtin::Int64), rhs));
|
||||
stores.push((rhs_symbol, Layout::Builtin(Builtin::Int64), rhs));
|
||||
|
||||
(
|
||||
stores,
|
||||
|
@ -1324,7 +1327,8 @@ fn decide_to_branching<'a>(
|
|||
arena.alloc(cond),
|
||||
);
|
||||
|
||||
for (symbol, layout, expr) in new_stores.into_iter() {
|
||||
// stores are in top-to-bottom order, so we have to add them in reverse
|
||||
for (symbol, layout, expr) in new_stores.into_iter().rev() {
|
||||
cond = Stmt::Let(symbol, expr, layout, arena.alloc(cond));
|
||||
}
|
||||
|
||||
|
|
|
@ -2171,17 +2171,22 @@ pub fn with_hole<'a>(
|
|||
|
||||
// a variable is aliased
|
||||
if let roc_can::expr::Expr::Var(original) = def.loc_expr.value {
|
||||
substitute_in_exprs(env.arena, &mut stmt, symbol, original);
|
||||
// a variable is aliased, e.g.
|
||||
//
|
||||
// foo = bar
|
||||
//
|
||||
// or
|
||||
//
|
||||
// foo = RBTRee.empty
|
||||
|
||||
// if the substituted variable is a function, make sure we specialize it
|
||||
stmt = reuse_function_symbol(
|
||||
stmt = handle_variable_aliasing(
|
||||
env,
|
||||
procs,
|
||||
layout_cache,
|
||||
Some(def.expr_var),
|
||||
def.expr_var,
|
||||
symbol,
|
||||
original,
|
||||
stmt,
|
||||
original,
|
||||
);
|
||||
|
||||
stmt
|
||||
|
@ -3583,19 +3588,23 @@ pub fn from_can<'a>(
|
|||
|
||||
match def.loc_expr.value {
|
||||
roc_can::expr::Expr::Var(original) => {
|
||||
// a variable is aliased, e.g.
|
||||
//
|
||||
// foo = bar
|
||||
//
|
||||
// or
|
||||
//
|
||||
// foo = RBTRee.empty
|
||||
let mut rest = from_can(env, def.expr_var, cont.value, procs, layout_cache);
|
||||
// a variable is aliased
|
||||
substitute_in_exprs(env.arena, &mut rest, *symbol, original);
|
||||
|
||||
// if the substituted variable is a function, make sure we specialize it
|
||||
rest = reuse_function_symbol(
|
||||
rest = handle_variable_aliasing(
|
||||
env,
|
||||
procs,
|
||||
layout_cache,
|
||||
Some(def.expr_var),
|
||||
def.expr_var,
|
||||
*symbol,
|
||||
original,
|
||||
rest,
|
||||
original,
|
||||
);
|
||||
|
||||
return rest;
|
||||
|
@ -4555,6 +4564,46 @@ fn possible_reuse_symbol<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
fn handle_variable_aliasing<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
procs: &mut Procs<'a>,
|
||||
layout_cache: &mut LayoutCache<'a>,
|
||||
variable: Variable,
|
||||
left: Symbol,
|
||||
right: Symbol,
|
||||
mut result: Stmt<'a>,
|
||||
) -> Stmt<'a> {
|
||||
let is_imported = left.module_id() != right.module_id();
|
||||
// builtins are currently (re)defined in each module, so not really imported
|
||||
let is_builtin = right.is_builtin();
|
||||
|
||||
if is_imported && !is_builtin {
|
||||
// if this is an imported symbol, then we must make sure it is
|
||||
// specialized, and wrap the original in a function pointer.
|
||||
add_needed_external(procs, env, variable, right);
|
||||
|
||||
let layout = layout_cache
|
||||
.from_var(env.arena, variable, env.subs)
|
||||
.unwrap();
|
||||
|
||||
let expr = Expr::FunctionPointer(right, layout.clone());
|
||||
Stmt::Let(left, expr, layout, env.arena.alloc(result))
|
||||
} else {
|
||||
substitute_in_exprs(env.arena, &mut result, left, right);
|
||||
|
||||
// if the substituted variable is a function, make sure we specialize it
|
||||
reuse_function_symbol(
|
||||
env,
|
||||
procs,
|
||||
layout_cache,
|
||||
Some(variable),
|
||||
right,
|
||||
result,
|
||||
right,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// If the symbol is a function, make sure it is properly specialized
|
||||
fn reuse_function_symbol<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
|
@ -4566,7 +4615,15 @@ fn reuse_function_symbol<'a>(
|
|||
original: Symbol,
|
||||
) -> Stmt<'a> {
|
||||
match procs.partial_procs.get(&original) {
|
||||
None => result,
|
||||
None => {
|
||||
// danger: a foreign symbol may not be specialized!
|
||||
debug_assert!(
|
||||
env.home == original.module_id() || original.module_id() == ModuleId::ATTR
|
||||
);
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
Some(partial_proc) => {
|
||||
let arg_var = arg_var.unwrap_or(partial_proc.annotation);
|
||||
// this symbol is a function, that is used by-name (e.g. as an argument to another
|
||||
|
|
|
@ -315,7 +315,10 @@ impl<'a> Layout<'a> {
|
|||
|
||||
match content {
|
||||
FlexVar(_) | RigidVar(_) => Err(LayoutProblem::UnresolvedTypeVar(var)),
|
||||
RecursionVar { .. } => todo!("recursion var"),
|
||||
RecursionVar { structure, .. } => {
|
||||
let structure_content = env.subs.get_without_compacting(structure).content;
|
||||
Self::new_help(env, structure, structure_content)
|
||||
}
|
||||
Structure(flat_type) => layout_from_flat_type(env, flat_type),
|
||||
|
||||
Alias(Symbol::NUM_INT, args, _) => {
|
||||
|
@ -751,7 +754,7 @@ fn layout_from_flat_type<'a>(
|
|||
// Determine the layouts of the fields, maintaining sort order
|
||||
let mut layouts = Vec::with_capacity_in(sorted_fields.len(), arena);
|
||||
|
||||
for (_, field) in sorted_fields {
|
||||
for (label, field) in sorted_fields {
|
||||
use LayoutProblem::*;
|
||||
|
||||
let field_var = {
|
||||
|
@ -776,7 +779,14 @@ fn layout_from_flat_type<'a>(
|
|||
layouts.push(layout);
|
||||
}
|
||||
}
|
||||
Err(UnresolvedTypeVar(_)) | Err(Erroneous) => {
|
||||
Err(UnresolvedTypeVar(v)) => {
|
||||
// Invalid field!
|
||||
panic!(
|
||||
r"I hit an unresolved type var {:?} when determining the layout of {:?} of record field: {:?} : {:?}",
|
||||
field_var, v, label, field
|
||||
);
|
||||
}
|
||||
Err(Erroneous) => {
|
||||
// Invalid field!
|
||||
panic!("TODO gracefully handle record with invalid field.var");
|
||||
}
|
||||
|
@ -936,7 +946,7 @@ fn get_recursion_var(subs: &Subs, var: Variable) -> Option<Variable> {
|
|||
}
|
||||
}
|
||||
|
||||
fn union_sorted_tags_help<'a>(
|
||||
pub fn union_sorted_tags_help<'a>(
|
||||
arena: &'a Bump,
|
||||
mut tags_vec: std::vec::Vec<(TagName, std::vec::Vec<Variable>)>,
|
||||
opt_rec_var: Option<Variable>,
|
||||
|
|
|
@ -402,6 +402,13 @@ fn variable_usage_help(con: &Constraint, declared: &mut SeenVariables, used: &mu
|
|||
used.insert(v);
|
||||
}
|
||||
}
|
||||
Store(tipe, var, _, _) => {
|
||||
for v in tipe.variables() {
|
||||
used.insert(v);
|
||||
}
|
||||
|
||||
used.insert(*var);
|
||||
}
|
||||
Lookup(_, expectation, _) => {
|
||||
for v in expectation.get_type_ref().variables() {
|
||||
used.insert(v);
|
||||
|
|
|
@ -1827,7 +1827,9 @@ mod test_mono {
|
|||
let Test.8 = true;
|
||||
let Test.10 = 0i64;
|
||||
let Test.9 = Index 1 Test.2;
|
||||
inc Test.9;
|
||||
let Test.11 = Index 0 Test.9;
|
||||
dec Test.9;
|
||||
let Test.12 = lowlevel Eq Test.10 Test.11;
|
||||
let Test.7 = lowlevel And Test.12 Test.8;
|
||||
if Test.7 then
|
||||
|
|
|
@ -427,8 +427,15 @@ fn pretty_runtime_error<'b>(
|
|||
// do nothing, reported with PrecedenceProblem
|
||||
unreachable!()
|
||||
}
|
||||
RuntimeError::MalformedIdentifier(_, _) => {
|
||||
todo!("malformed identifier, currently gives a parse error and thus is unreachable")
|
||||
RuntimeError::MalformedIdentifier(box_str, region) => {
|
||||
alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
alloc.reflow("The ")
|
||||
.append(format!("`{}`", box_str))
|
||||
.append(alloc.reflow(" identifier is malformed:")),
|
||||
]),
|
||||
alloc.region(region),
|
||||
])
|
||||
}
|
||||
RuntimeError::MalformedClosure(_) => todo!(""),
|
||||
RuntimeError::InvalidFloat(sign @ FloatErrorKind::PositiveInfinity, region, _raw_str)
|
||||
|
|
|
@ -3930,4 +3930,25 @@ mod test_reporting {
|
|||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn qualified_global_tag() {
|
||||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
Foo.Bar
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
── SYNTAX PROBLEM ──────────────────────────────────────────────────────────────
|
||||
|
||||
The `Foo.Bar` identifier is malformed:
|
||||
|
||||
1│ Foo.Bar
|
||||
^^^^^^^
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -244,6 +244,34 @@ fn solve(
|
|||
}
|
||||
}
|
||||
}
|
||||
Store(source, target, _filename, _linenr) => {
|
||||
// a special version of Eq that is used to store types in the AST.
|
||||
// IT DOES NOT REPORT ERRORS!
|
||||
let actual = type_to_var(subs, rank, pools, cached_aliases, source);
|
||||
let target = *target;
|
||||
|
||||
match unify(subs, actual, target) {
|
||||
Success(vars) => {
|
||||
introduce(subs, rank, pools, &vars);
|
||||
|
||||
state
|
||||
}
|
||||
Failure(vars, _actual_type, _expected_type) => {
|
||||
introduce(subs, rank, pools, &vars);
|
||||
|
||||
// ERROR NOT REPORTED
|
||||
|
||||
state
|
||||
}
|
||||
BadType(vars, _problem) => {
|
||||
introduce(subs, rank, pools, &vars);
|
||||
|
||||
// ERROR NOT REPORTED
|
||||
|
||||
state
|
||||
}
|
||||
}
|
||||
}
|
||||
Lookup(symbol, expectation, region) => {
|
||||
let var = *env.vars_by_symbol.get(&symbol).unwrap_or_else(|| {
|
||||
// TODO Instead of panicking, solve this as True and record
|
||||
|
|
|
@ -417,6 +417,13 @@ fn variable_usage_help(con: &Constraint, declared: &mut SeenVariables, used: &mu
|
|||
used.insert(v);
|
||||
}
|
||||
}
|
||||
Store(tipe, var, _, _) => {
|
||||
for v in tipe.variables() {
|
||||
used.insert(v);
|
||||
}
|
||||
|
||||
used.insert(*var);
|
||||
}
|
||||
Lookup(_, expectation, _) => {
|
||||
for v in expectation.get_type_ref().variables() {
|
||||
used.insert(v);
|
||||
|
|
|
@ -3106,4 +3106,417 @@ mod solve_expr {
|
|||
"Dict Int Int",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rbtree_insert() {
|
||||
// exposed an issue where pattern variables were not introduced
|
||||
// at the correct level in the constraint
|
||||
//
|
||||
// see 22592eff805511fbe1da63849771ee5f367a6a16
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
|
||||
Dict k : [ Node k (Dict k), Empty ]
|
||||
|
||||
balance : Dict k -> Dict k
|
||||
balance = \left ->
|
||||
when left is
|
||||
Node _ Empty -> Empty
|
||||
|
||||
_ -> Empty
|
||||
|
||||
main : Dict {}
|
||||
main =
|
||||
balance Empty
|
||||
"#
|
||||
),
|
||||
"Dict {}",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rbtree_full_remove_min() {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
|
||||
NodeColor : [ Red, Black ]
|
||||
|
||||
Dict k v : [ Node NodeColor k v (Dict k v) (Dict k v), Empty ]
|
||||
|
||||
moveRedLeft : Dict k v -> Dict k v
|
||||
moveRedLeft = \dict ->
|
||||
when dict is
|
||||
# Node clr k v (Node lClr lK lV lLeft lRight) (Node rClr rK rV ((Node Red rlK rlV rlL rlR) as rLeft) rRight) ->
|
||||
# Node clr k v (Node lClr lK lV lLeft lRight) (Node rClr rK rV rLeft rRight) ->
|
||||
Node clr k v (Node _ lK lV lLeft lRight) (Node _ rK rV rLeft rRight) ->
|
||||
when rLeft is
|
||||
Node Red rlK rlV rlL rlR ->
|
||||
Node
|
||||
Red
|
||||
rlK
|
||||
rlV
|
||||
(Node Black k v (Node Red lK lV lLeft lRight) rlL)
|
||||
(Node Black rK rV rlR rRight)
|
||||
|
||||
_ ->
|
||||
when clr is
|
||||
Black ->
|
||||
Node
|
||||
Black
|
||||
k
|
||||
v
|
||||
(Node Red lK lV lLeft lRight)
|
||||
(Node Red rK rV rLeft rRight)
|
||||
|
||||
Red ->
|
||||
Node
|
||||
Black
|
||||
k
|
||||
v
|
||||
(Node Red lK lV lLeft lRight)
|
||||
(Node Red rK rV rLeft rRight)
|
||||
|
||||
_ ->
|
||||
dict
|
||||
|
||||
balance : NodeColor, k, v, Dict k v, Dict k v -> Dict k v
|
||||
balance = \color, key, value, left, right ->
|
||||
when right is
|
||||
Node Red rK rV rLeft rRight ->
|
||||
when left is
|
||||
Node Red lK lV lLeft lRight ->
|
||||
Node
|
||||
Red
|
||||
key
|
||||
value
|
||||
(Node Black lK lV lLeft lRight)
|
||||
(Node Black rK rV rLeft rRight)
|
||||
|
||||
_ ->
|
||||
Node color rK rV (Node Red key value left rLeft) rRight
|
||||
|
||||
_ ->
|
||||
when left is
|
||||
Node Red lK lV (Node Red llK llV llLeft llRight) lRight ->
|
||||
Node
|
||||
Red
|
||||
lK
|
||||
lV
|
||||
(Node Black llK llV llLeft llRight)
|
||||
(Node Black key value lRight right)
|
||||
|
||||
_ ->
|
||||
Node color key value left right
|
||||
|
||||
|
||||
Key k : Num k
|
||||
|
||||
removeHelpEQGT : Key k, Dict (Key k) v -> Dict (Key k) v
|
||||
removeHelpEQGT = \targetKey, dict ->
|
||||
when dict is
|
||||
Node color key value left right ->
|
||||
if targetKey == key then
|
||||
when getMin right is
|
||||
Node _ minKey minValue _ _ ->
|
||||
balance color minKey minValue left (removeMin right)
|
||||
|
||||
Empty ->
|
||||
Empty
|
||||
else
|
||||
balance color key value left (removeHelp targetKey right)
|
||||
|
||||
Empty ->
|
||||
Empty
|
||||
|
||||
getMin : Dict k v -> Dict k v
|
||||
getMin = \dict ->
|
||||
when dict is
|
||||
# Node _ _ _ ((Node _ _ _ _ _) as left) _ ->
|
||||
Node _ _ _ left _ ->
|
||||
when left is
|
||||
Node _ _ _ _ _ -> getMin left
|
||||
_ -> dict
|
||||
|
||||
_ ->
|
||||
dict
|
||||
|
||||
|
||||
moveRedRight : Dict k v -> Dict k v
|
||||
moveRedRight = \dict ->
|
||||
when dict is
|
||||
Node clr k v (Node lClr lK lV (Node Red llK llV llLeft llRight) lRight) (Node rClr rK rV rLeft rRight) ->
|
||||
Node
|
||||
Red
|
||||
lK
|
||||
lV
|
||||
(Node Black llK llV llLeft llRight)
|
||||
(Node Black k v lRight (Node Red rK rV rLeft rRight))
|
||||
|
||||
Node clr k v (Node lClr lK lV lLeft lRight) (Node rClr rK rV rLeft rRight) ->
|
||||
when clr is
|
||||
Black ->
|
||||
Node
|
||||
Black
|
||||
k
|
||||
v
|
||||
(Node Red lK lV lLeft lRight)
|
||||
(Node Red rK rV rLeft rRight)
|
||||
|
||||
Red ->
|
||||
Node
|
||||
Black
|
||||
k
|
||||
v
|
||||
(Node Red lK lV lLeft lRight)
|
||||
(Node Red rK rV rLeft rRight)
|
||||
|
||||
_ ->
|
||||
dict
|
||||
|
||||
|
||||
removeHelpPrepEQGT : Key k, Dict (Key k) v, NodeColor, (Key k), v, Dict (Key k) v, Dict (Key k) v -> Dict (Key k) v
|
||||
removeHelpPrepEQGT = \_, dict, color, key, value, left, right ->
|
||||
when left is
|
||||
Node Red lK lV lLeft lRight ->
|
||||
Node
|
||||
color
|
||||
lK
|
||||
lV
|
||||
lLeft
|
||||
(Node Red key value lRight right)
|
||||
|
||||
_ ->
|
||||
when right is
|
||||
Node Black _ _ (Node Black _ _ _ _) _ ->
|
||||
moveRedRight dict
|
||||
|
||||
Node Black _ _ Empty _ ->
|
||||
moveRedRight dict
|
||||
|
||||
_ ->
|
||||
dict
|
||||
|
||||
|
||||
removeMin : Dict k v -> Dict k v
|
||||
removeMin = \dict ->
|
||||
when dict is
|
||||
Node color key value left right ->
|
||||
when left is
|
||||
Node lColor _ _ lLeft _ ->
|
||||
when lColor is
|
||||
Black ->
|
||||
when lLeft is
|
||||
Node Red _ _ _ _ ->
|
||||
Node color key value (removeMin left) right
|
||||
|
||||
_ ->
|
||||
when moveRedLeft dict is # here 1
|
||||
Node nColor nKey nValue nLeft nRight ->
|
||||
balance nColor nKey nValue (removeMin nLeft) nRight
|
||||
|
||||
Empty ->
|
||||
Empty
|
||||
|
||||
_ ->
|
||||
Node color key value (removeMin left) right
|
||||
|
||||
_ ->
|
||||
Empty
|
||||
_ ->
|
||||
Empty
|
||||
|
||||
removeHelp : Key k, Dict (Key k) v -> Dict (Key k) v
|
||||
removeHelp = \targetKey, dict ->
|
||||
when dict is
|
||||
Empty ->
|
||||
Empty
|
||||
|
||||
Node color key value left right ->
|
||||
if targetKey < key then
|
||||
when left is
|
||||
Node Black _ _ lLeft _ ->
|
||||
when lLeft is
|
||||
Node Red _ _ _ _ ->
|
||||
Node color key value (removeHelp targetKey left) right
|
||||
|
||||
_ ->
|
||||
when moveRedLeft dict is # here 2
|
||||
Node nColor nKey nValue nLeft nRight ->
|
||||
balance nColor nKey nValue (removeHelp targetKey nLeft) nRight
|
||||
|
||||
Empty ->
|
||||
Empty
|
||||
|
||||
_ ->
|
||||
Node color key value (removeHelp targetKey left) right
|
||||
else
|
||||
removeHelpEQGT targetKey (removeHelpPrepEQGT targetKey dict color key value left right)
|
||||
|
||||
|
||||
main : Dict Int Int
|
||||
main =
|
||||
removeHelp 1 Empty
|
||||
"#
|
||||
),
|
||||
"Dict Int Int",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rbtree_remove_min_1() {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
|
||||
Dict k : [ Node k (Dict k) (Dict k), Empty ]
|
||||
|
||||
removeHelp : Num k, Dict (Num k) -> Dict (Num k)
|
||||
removeHelp = \targetKey, dict ->
|
||||
when dict is
|
||||
Empty ->
|
||||
Empty
|
||||
|
||||
Node key left right ->
|
||||
if targetKey < key then
|
||||
when left is
|
||||
Node _ lLeft _ ->
|
||||
when lLeft is
|
||||
Node _ _ _ ->
|
||||
Empty
|
||||
|
||||
_ -> Empty
|
||||
|
||||
_ ->
|
||||
Node key (removeHelp targetKey left) right
|
||||
else
|
||||
Empty
|
||||
|
||||
|
||||
main : Dict Int
|
||||
main =
|
||||
removeHelp 1 Empty
|
||||
"#
|
||||
),
|
||||
"Dict Int",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rbtree_foobar() {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
|
||||
NodeColor : [ Red, Black ]
|
||||
|
||||
Dict k v : [ Node NodeColor k v (Dict k v) (Dict k v), Empty ]
|
||||
|
||||
removeHelp : Num k, Dict (Num k) v -> Dict (Num k) v
|
||||
removeHelp = \targetKey, dict ->
|
||||
when dict is
|
||||
Empty ->
|
||||
Empty
|
||||
|
||||
Node color key value left right ->
|
||||
if targetKey < key then
|
||||
when left is
|
||||
Node Black _ _ lLeft _ ->
|
||||
when lLeft is
|
||||
Node Red _ _ _ _ ->
|
||||
Node color key value (removeHelp targetKey left) right
|
||||
|
||||
_ ->
|
||||
when moveRedLeft dict is # here 2
|
||||
Node nColor nKey nValue nLeft nRight ->
|
||||
balance nColor nKey nValue (removeHelp targetKey nLeft) nRight
|
||||
|
||||
Empty ->
|
||||
Empty
|
||||
|
||||
_ ->
|
||||
Node color key value (removeHelp targetKey left) right
|
||||
else
|
||||
removeHelpEQGT targetKey (removeHelpPrepEQGT targetKey dict color key value left right)
|
||||
|
||||
Key k : Num k
|
||||
|
||||
balance : NodeColor, k, v, Dict k v, Dict k v -> Dict k v
|
||||
|
||||
moveRedLeft : Dict k v -> Dict k v
|
||||
|
||||
removeHelpPrepEQGT : Key k, Dict (Key k) v, NodeColor, (Key k), v, Dict (Key k) v, Dict (Key k) v -> Dict (Key k) v
|
||||
|
||||
removeHelpEQGT : Key k, Dict (Key k) v -> Dict (Key k) v
|
||||
removeHelpEQGT = \targetKey, dict ->
|
||||
when dict is
|
||||
Node color key value left right ->
|
||||
if targetKey == key then
|
||||
when getMin right is
|
||||
Node _ minKey minValue _ _ ->
|
||||
balance color minKey minValue left (removeMin right)
|
||||
|
||||
Empty ->
|
||||
Empty
|
||||
else
|
||||
balance color key value left (removeHelp targetKey right)
|
||||
|
||||
Empty ->
|
||||
Empty
|
||||
|
||||
getMin : Dict k v -> Dict k v
|
||||
|
||||
removeMin : Dict k v -> Dict k v
|
||||
|
||||
main : Dict Int Int
|
||||
main =
|
||||
removeHelp 1 Empty
|
||||
"#
|
||||
),
|
||||
"Dict Int Int",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn quicksort_partition_help() {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ partitionHelp ] imports []
|
||||
|
||||
swap : Int, Int, List a -> List a
|
||||
swap = \i, j, list ->
|
||||
when Pair (List.get list i) (List.get list j) is
|
||||
Pair (Ok atI) (Ok atJ) ->
|
||||
list
|
||||
|> List.set i atJ
|
||||
|> List.set j atI
|
||||
|
||||
_ ->
|
||||
[]
|
||||
|
||||
partitionHelp : Int, Int, List (Num a), Int, (Num a) -> [ Pair Int (List (Num a)) ]
|
||||
partitionHelp = \i, j, list, high, pivot ->
|
||||
if j < high then
|
||||
when List.get list j is
|
||||
Ok value ->
|
||||
if value <= pivot then
|
||||
partitionHelp (i + 1) (j + 1) (swap (i + 1) j list) high pivot
|
||||
else
|
||||
partitionHelp i (j + 1) list high pivot
|
||||
|
||||
Err _ ->
|
||||
Pair i list
|
||||
else
|
||||
Pair i list
|
||||
"#
|
||||
),
|
||||
"Int, Int, List (Num a), Int, Num a -> [ Pair Int (List (Num a)) ]",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1089,7 +1089,7 @@ fn unify_rigid(subs: &mut Subs, ctx: &Context, name: &Lowercase, other: &Content
|
|||
RigidVar(_) | RecursionVar { .. } | Structure(_) | Alias(_, _, _) => {
|
||||
// Type mismatch! Rigid can only unify with flex, even if the
|
||||
// rigid names are the same.
|
||||
mismatch!("Rigid with {:?}", &other)
|
||||
mismatch!("Rigid {:?} with {:?}", ctx.first, &other)
|
||||
}
|
||||
Error => {
|
||||
// Error propagates.
|
||||
|
|
|
@ -414,6 +414,13 @@ fn variable_usage_help(con: &Constraint, declared: &mut SeenVariables, used: &mu
|
|||
used.insert(v);
|
||||
}
|
||||
}
|
||||
Store(tipe, var, _, _) => {
|
||||
for v in tipe.variables() {
|
||||
used.insert(v);
|
||||
}
|
||||
|
||||
used.insert(*var);
|
||||
}
|
||||
Lookup(_, expectation, _) => {
|
||||
for v in expectation.get_type_ref().variables() {
|
||||
used.insert(v);
|
||||
|
|
|
@ -5,6 +5,7 @@ authors = ["Richard Feldman <oss@rtfeldman.com>"]
|
|||
edition = "2018"
|
||||
description = "An editor for Roc"
|
||||
license = "Apache-2.0"
|
||||
exclude = ["src/shaders/*.spv"]
|
||||
|
||||
[dependencies]
|
||||
roc_collections = { path = "../compiler/collections" }
|
||||
|
|
|
@ -64,9 +64,6 @@ fn main() -> Result<()> {
|
|||
// be better just to only compile shaders that have been changed
|
||||
// recently.
|
||||
for shader in shaders {
|
||||
// This tells cargo to rerun this script if something in /src/ changes.
|
||||
println!("cargo:rerun-if-changed={:?}", shader.src_path);
|
||||
|
||||
let compiled = compiler.compile_into_spirv(
|
||||
&shader.src,
|
||||
shader.kind,
|
||||
|
|
|
@ -1,12 +1,22 @@
|
|||
app Main provides [ main ] imports [ Effect, RBTree ]
|
||||
|
||||
foo : RBTree.Dict Int Int
|
||||
foo = Empty # RBTree.empty
|
||||
toAndFro : Int
|
||||
toAndFro =
|
||||
empty : RBTree.Dict Int {}
|
||||
empty = RBTree.empty
|
||||
|
||||
empty
|
||||
|> (\d -> RBTree.insert 1 {} d)
|
||||
|> RBTree.toList
|
||||
|> List.len
|
||||
|
||||
|
||||
|
||||
|
||||
main : Effect.Effect {} as Fx
|
||||
main =
|
||||
# if RBTree.isEmpty empty then
|
||||
if RBTree.size foo == 0 then
|
||||
if toAndFro == 2 then
|
||||
Effect.putLine "Yay"
|
||||
|> Effect.after (\{} -> Effect.getLine)
|
||||
|> Effect.after (\line -> Effect.putLine line)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
interface RBTree exposes [ Dict, empty, size, singleton ] imports []
|
||||
interface RBTree exposes [ Dict, empty, size, singleton, isEmpty, insert, remove, update, fromList, toList ] imports []
|
||||
|
||||
# The color of a node. Leaves are considered Black.
|
||||
NodeColor : [ Red, Black ]
|
||||
|
@ -147,6 +147,8 @@ removeHelp = \targetKey, dict ->
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
removeHelpPrepEQGT : Key k, Dict (Key k) v, NodeColor, (Key k), v, Dict (Key k) v, Dict (Key k) v -> Dict (Key k) v
|
||||
removeHelpPrepEQGT = \_, dict, color, key, value, left, right ->
|
||||
when left is
|
||||
|
@ -171,6 +173,7 @@ removeHelpPrepEQGT = \_, dict, color, key, value, left, right ->
|
|||
|
||||
|
||||
|
||||
|
||||
# When we find the node we are looking for, we can remove by replacing the key-value
|
||||
# pair with the key-value pair of the left-most node on the right side (the closest pair).
|
||||
removeHelpEQGT : Key k, Dict (Key k) v -> Dict (Key k) v
|
||||
|
@ -192,6 +195,7 @@ removeHelpEQGT = \targetKey, dict ->
|
|||
|
||||
|
||||
|
||||
|
||||
getMin : Dict k v -> Dict k v
|
||||
getMin = \dict ->
|
||||
when dict is
|
||||
|
@ -204,12 +208,14 @@ getMin = \dict ->
|
|||
_ ->
|
||||
dict
|
||||
|
||||
|
||||
moveRedLeft : Dict k v -> Dict k v
|
||||
moveRedLeft = \dict ->
|
||||
when dict is
|
||||
# Node clr k v (Node lClr lK lV lLeft lRight) (Node rClr rK rV ((Node Red rlK rlV rlL rlR) as rLeft) rRight) ->
|
||||
Node clr k v (Node lClr lK lV lLeft lRight) (Node rClr rK rV rLeft rRight) ->
|
||||
when rList is
|
||||
# Node clr k v (Node lClr lK lV lLeft lRight) (Node rClr rK rV rLeft rRight) ->
|
||||
Node clr k v (Node _ lK lV lLeft lRight) (Node _ rK rV rLeft rRight) ->
|
||||
when rLeft is
|
||||
Node Red rlK rlV rlL rlR ->
|
||||
Node
|
||||
Red
|
||||
|
@ -242,7 +248,7 @@ moveRedLeft = \dict ->
|
|||
moveRedRight : Dict k v -> Dict k v
|
||||
moveRedRight = \dict ->
|
||||
when dict is
|
||||
Node clr k v (Node lClr lK lV (Node Red llK llV llLeft llRight) lRight) (Node rClr rK rV rLeft rRight) ->
|
||||
Node _ k v (Node _ lK lV (Node Red llK llV llLeft llRight) lRight) (Node _ rK rV rLeft rRight) ->
|
||||
Node
|
||||
Red
|
||||
lK
|
||||
|
@ -250,7 +256,7 @@ moveRedRight = \dict ->
|
|||
(Node Black llK llV llLeft llRight)
|
||||
(Node Black k v lRight (Node Red rK rV rLeft rRight))
|
||||
|
||||
Node clr k v (Node lClr lK lV lLeft lRight) (Node rClr rK rV rLeft rRight) ->
|
||||
Node clr k v (Node _ lK lV lLeft lRight) (Node _ rK rV rLeft rRight) ->
|
||||
when clr is
|
||||
Black ->
|
||||
Node
|
||||
|
@ -274,7 +280,9 @@ moveRedRight = \dict ->
|
|||
removeMin : Dict k v -> Dict k v
|
||||
removeMin = \dict ->
|
||||
when dict is
|
||||
Node color key value ((Node lColor _ _ lLeft _) as left) right ->
|
||||
Node color key value left right ->
|
||||
when left is
|
||||
Node lColor _ _ lLeft _ ->
|
||||
when lColor is
|
||||
Black ->
|
||||
when lLeft is
|
||||
|
@ -294,10 +302,12 @@ removeMin = \dict ->
|
|||
|
||||
_ ->
|
||||
Empty
|
||||
_ ->
|
||||
Empty
|
||||
|
||||
|
||||
# Update the value of a dictionary for a specific key with a given function.
|
||||
update : Key k, (Maybe v, Maybe v), Dict (Key k) v -> Dict (Key k) v
|
||||
update : Key k, (Maybe v -> Maybe v), Dict (Key k) v -> Dict (Key k) v
|
||||
update = \targetKey, alter, dictionary ->
|
||||
when alter (get targetKey dictionary) is
|
||||
Just value ->
|
||||
|
@ -305,3 +315,38 @@ update = \targetKey, alter, dictionary ->
|
|||
|
||||
Nothing ->
|
||||
remove targetKey dictionary
|
||||
|
||||
get : Key k, Dict (Key k) v -> Maybe v
|
||||
get = \targetKey, dict ->
|
||||
when dict is
|
||||
Empty ->
|
||||
Nothing
|
||||
|
||||
Node _ key value left right ->
|
||||
when Num.compare targetKey key is
|
||||
LT ->
|
||||
get targetKey left
|
||||
|
||||
EQ ->
|
||||
Just value
|
||||
|
||||
GT ->
|
||||
get targetKey right
|
||||
|
||||
fromList : List {key : Num k, value : v } -> Dict (Num k) v
|
||||
fromList = \xs ->
|
||||
List.walkRight xs (\{key, value}, dict -> insert key value dict) empty
|
||||
|
||||
foldr : (k, v, b -> b), b, Dict k v -> b
|
||||
foldr = \func, acc, t ->
|
||||
when t is
|
||||
Empty ->
|
||||
acc
|
||||
|
||||
Node _ key value left right ->
|
||||
foldr func (func key value (foldr func acc right)) left
|
||||
|
||||
# Convert a dictionary into an association list of key-value pairs, sorted by keys.
|
||||
toList : Dict k v -> List { key : k, value : v }
|
||||
toList = \dict ->
|
||||
foldr (\key, value, list -> List.append list {key,value}) [] dict
|
||||
|
|
43
shell.nix
43
shell.nix
|
@ -16,8 +16,7 @@ with {
|
|||
with (pkgs);
|
||||
|
||||
let
|
||||
darwin-frameworks =
|
||||
if isMacOS then
|
||||
darwin-frameworks = if isMacOS then
|
||||
with pkgs.darwin.apple_sdk.frameworks; [
|
||||
AppKit
|
||||
CoreFoundation
|
||||
|
@ -29,12 +28,24 @@ let
|
|||
]
|
||||
else
|
||||
[ ];
|
||||
|
||||
linux-only = if !isMacOS then [
|
||||
vulkan-headers
|
||||
vulkan-loader
|
||||
vulkan-tools
|
||||
vulkan-validation-layers
|
||||
xorg.libX11
|
||||
xorg.libXcursor
|
||||
xorg.libXrandr
|
||||
xorg.libXi
|
||||
] else
|
||||
[ ];
|
||||
|
||||
llvmPkg = pkgs.llvm_10;
|
||||
lldPkg = pkgs.lld_10; # this should match llvm's version
|
||||
clangPkg = pkgs.clang_10; # this should match llvm's version
|
||||
zig = import ./nix/zig.nix { inherit pkgs isMacOS; };
|
||||
inputs =
|
||||
[
|
||||
inputs = [
|
||||
# build libraries
|
||||
rustc
|
||||
cargo
|
||||
|
@ -51,12 +62,7 @@ let
|
|||
# llb deps
|
||||
libffi
|
||||
libxml2
|
||||
xorg.libX11
|
||||
zlib
|
||||
vulkan-headers
|
||||
vulkan-loader
|
||||
vulkan-tools
|
||||
vulkan-validation-layers
|
||||
# faster builds - see https://github.com/rtfeldman/roc/blob/trunk/BUILDING_FROM_SOURCE.md#use-lld-for-the-linker
|
||||
lldPkg
|
||||
# dev tools
|
||||
|
@ -64,24 +70,13 @@ let
|
|||
# (import ./nix/zls.nix { inherit pkgs zig; })
|
||||
ccls
|
||||
];
|
||||
|
||||
in mkShell {
|
||||
buildInputs = inputs ++ darwin-frameworks;
|
||||
buildInputs = inputs ++ darwin-frameworks ++ linux-only;
|
||||
LLVM_SYS_100_PREFIX = "${llvmPkg}";
|
||||
|
||||
APPEND_LIBRARY_PATH = stdenv.lib.makeLibraryPath [
|
||||
pkgconfig
|
||||
vulkan-headers
|
||||
vulkan-loader
|
||||
vulkan-tools
|
||||
vulkan-validation-layers
|
||||
xorg.libX11
|
||||
xorg.libXcursor
|
||||
xorg.libXrandr
|
||||
xorg.libXi
|
||||
libcxx
|
||||
libcxxabi
|
||||
libunwind
|
||||
];
|
||||
APPEND_LIBRARY_PATH = stdenv.lib.makeLibraryPath
|
||||
([ pkgconfig libcxx libcxxabi libunwind ] ++ linux-only);
|
||||
|
||||
# Aliases don't work cross shell, so we do this
|
||||
shellHook = ''
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue