mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-30 15:21:12 +00:00
Merge branch 'trunk' into morphic-lib
This commit is contained in:
commit
31c0c86ccd
8 changed files with 200 additions and 200 deletions
|
@ -848,7 +848,6 @@ fn swapElements(source_ptr: [*]u8, element_width: usize, index_1: usize, index_2
|
||||||
pub fn listJoin(list_of_lists: RocList, alignment: usize, element_width: usize) callconv(.C) RocList {
|
pub fn listJoin(list_of_lists: RocList, alignment: usize, element_width: usize) callconv(.C) RocList {
|
||||||
var total_length: usize = 0;
|
var total_length: usize = 0;
|
||||||
|
|
||||||
const size = list_of_lists.len();
|
|
||||||
const slice_of_lists = @ptrCast([*]RocList, @alignCast(@alignOf(RocList), list_of_lists.bytes));
|
const slice_of_lists = @ptrCast([*]RocList, @alignCast(@alignOf(RocList), list_of_lists.bytes));
|
||||||
|
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
|
@ -873,3 +872,44 @@ pub fn listJoin(list_of_lists: RocList, alignment: usize, element_width: usize)
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn listConcat(list_a: RocList, list_b: RocList, alignment: usize, element_width: usize) callconv(.C) RocList {
|
||||||
|
if (list_a.isEmpty()) {
|
||||||
|
return list_b;
|
||||||
|
} else if (list_b.isEmpty()) {
|
||||||
|
return list_a;
|
||||||
|
} else if (!list_a.isEmpty() and list_a.isUnique()) {
|
||||||
|
const total_length: usize = list_a.len() + list_b.len();
|
||||||
|
|
||||||
|
if (list_a.bytes) |source| {
|
||||||
|
const new_source = utils.unsafeReallocate(
|
||||||
|
source,
|
||||||
|
std.heap.c_allocator,
|
||||||
|
alignment,
|
||||||
|
list_a.len(),
|
||||||
|
total_length,
|
||||||
|
element_width,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (list_b.bytes) |source_b| {
|
||||||
|
@memcpy(new_source + list_a.len() * element_width, source_b, list_b.len() * element_width);
|
||||||
|
}
|
||||||
|
|
||||||
|
return RocList{ .bytes = new_source, .length = total_length };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const total_length: usize = list_a.len() + list_b.len();
|
||||||
|
|
||||||
|
const output = RocList.allocate(std.heap.c_allocator, alignment, total_length, element_width);
|
||||||
|
|
||||||
|
if (output.bytes) |target| {
|
||||||
|
if (list_a.bytes) |source| {
|
||||||
|
@memcpy(target, source, list_a.len() * element_width);
|
||||||
|
}
|
||||||
|
if (list_b.bytes) |source| {
|
||||||
|
@memcpy(target + list_a.len() * element_width, source, list_b.len() * element_width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ comptime {
|
||||||
exportListFn(list.listRange, "range");
|
exportListFn(list.listRange, "range");
|
||||||
exportListFn(list.listReverse, "reverse");
|
exportListFn(list.listReverse, "reverse");
|
||||||
exportListFn(list.listSortWith, "sort_with");
|
exportListFn(list.listSortWith, "sort_with");
|
||||||
|
exportListFn(list.listConcat, "concat");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dict Module
|
// Dict Module
|
||||||
|
|
|
@ -81,3 +81,4 @@ pub const LIST_JOIN: &str = "roc_builtins.list.join";
|
||||||
pub const LIST_RANGE: &str = "roc_builtins.list.range";
|
pub const LIST_RANGE: &str = "roc_builtins.list.range";
|
||||||
pub const LIST_REVERSE: &str = "roc_builtins.list.reverse";
|
pub const LIST_REVERSE: &str = "roc_builtins.list.reverse";
|
||||||
pub const LIST_SORT_WITH: &str = "roc_builtins.list.sort_with";
|
pub const LIST_SORT_WITH: &str = "roc_builtins.list.sort_with";
|
||||||
|
pub const LIST_CONCAT: &str = "roc_builtins.list.concat";
|
||||||
|
|
|
@ -1028,196 +1028,30 @@ pub fn list_map3<'a, 'ctx, 'env>(
|
||||||
/// List.concat : List elem, List elem -> List elem
|
/// List.concat : List elem, List elem -> List elem
|
||||||
pub fn list_concat<'a, 'ctx, 'env>(
|
pub fn list_concat<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
inplace: InPlace,
|
_inplace: InPlace,
|
||||||
parent: FunctionValue<'ctx>,
|
_parent: FunctionValue<'ctx>,
|
||||||
first_list: BasicValueEnum<'ctx>,
|
first_list: BasicValueEnum<'ctx>,
|
||||||
second_list: BasicValueEnum<'ctx>,
|
second_list: BasicValueEnum<'ctx>,
|
||||||
list_layout: &Layout<'a>,
|
list_layout: &Layout<'a>,
|
||||||
) -> BasicValueEnum<'ctx> {
|
) -> BasicValueEnum<'ctx> {
|
||||||
let builder = env.builder;
|
|
||||||
let ctx = env.context;
|
|
||||||
|
|
||||||
let second_list_wrapper = second_list.into_struct_value();
|
|
||||||
|
|
||||||
let second_list_len = list_len(builder, second_list_wrapper);
|
|
||||||
|
|
||||||
// We only match on the first lists layout
|
|
||||||
// because the first and second input lists
|
|
||||||
// necessarily have the same layout
|
|
||||||
match list_layout {
|
match list_layout {
|
||||||
Layout::Builtin(Builtin::EmptyList) => empty_list(env),
|
Layout::Builtin(Builtin::EmptyList) => {
|
||||||
Layout::Builtin(Builtin::List(_, elem_layout)) => {
|
// If the input list is empty, or if it is a list of empty lists
|
||||||
let first_list_wrapper = first_list.into_struct_value();
|
// then simply return an empty list
|
||||||
|
empty_list(env)
|
||||||
let first_list_len = list_len(builder, first_list_wrapper);
|
|
||||||
|
|
||||||
// first_list_len > 0
|
|
||||||
// We do this check to avoid allocating memory. If the first input
|
|
||||||
// list is empty, then we can just return the second list cloned
|
|
||||||
let first_list_length_comparison = list_is_not_empty(env, first_list_len);
|
|
||||||
|
|
||||||
let if_first_list_is_empty = || {
|
|
||||||
// second_list_len > 0
|
|
||||||
// We do this check to avoid allocating memory. If the second input
|
|
||||||
// list is empty, then we can just return an empty list
|
|
||||||
let second_list_length_comparison = list_is_not_empty(env, second_list_len);
|
|
||||||
|
|
||||||
let build_second_list_then = || {
|
|
||||||
let elem_type = basic_type_from_layout(env, elem_layout);
|
|
||||||
let ptr_type = get_ptr_type(&elem_type, AddressSpace::Generic);
|
|
||||||
|
|
||||||
let (new_wrapper, _) = clone_nonempty_list(
|
|
||||||
env,
|
|
||||||
inplace,
|
|
||||||
second_list_len,
|
|
||||||
load_list_ptr(builder, second_list_wrapper, ptr_type),
|
|
||||||
elem_layout,
|
|
||||||
);
|
|
||||||
|
|
||||||
BasicValueEnum::StructValue(new_wrapper)
|
|
||||||
};
|
|
||||||
|
|
||||||
let build_second_list_else = || empty_list(env);
|
|
||||||
|
|
||||||
build_basic_phi2(
|
|
||||||
env,
|
|
||||||
parent,
|
|
||||||
second_list_length_comparison,
|
|
||||||
build_second_list_then,
|
|
||||||
build_second_list_else,
|
|
||||||
BasicTypeEnum::StructType(super::convert::zig_list_type(env)),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let if_first_list_is_not_empty = || {
|
|
||||||
let elem_type = basic_type_from_layout(env, elem_layout);
|
|
||||||
let ptr_type = get_ptr_type(&elem_type, AddressSpace::Generic);
|
|
||||||
|
|
||||||
let if_second_list_is_empty = || {
|
|
||||||
let (new_wrapper, _) = clone_nonempty_list(
|
|
||||||
env,
|
|
||||||
inplace,
|
|
||||||
first_list_len,
|
|
||||||
load_list_ptr(builder, first_list_wrapper, ptr_type),
|
|
||||||
elem_layout,
|
|
||||||
);
|
|
||||||
|
|
||||||
BasicValueEnum::StructValue(new_wrapper)
|
|
||||||
};
|
|
||||||
|
|
||||||
// second_list_len > 0
|
|
||||||
// We do this check to avoid allocating memory. If the second input
|
|
||||||
// list is empty, then we can just return the first list cloned
|
|
||||||
let second_list_length_comparison = list_is_not_empty(env, second_list_len);
|
|
||||||
|
|
||||||
let if_second_list_is_not_empty = || {
|
|
||||||
let combined_list_len =
|
|
||||||
builder.build_int_add(first_list_len, second_list_len, "add_list_lengths");
|
|
||||||
|
|
||||||
let combined_list_ptr =
|
|
||||||
allocate_list(env, inplace, elem_layout, combined_list_len);
|
|
||||||
|
|
||||||
let first_list_ptr = load_list_ptr(builder, first_list_wrapper, ptr_type);
|
|
||||||
|
|
||||||
// FIRST LOOP
|
|
||||||
// TODO when the element type supports it, replace FIRST_LOOP with a memcpy!
|
|
||||||
let first_loop = |first_index, first_list_elem| {
|
|
||||||
// The pointer to the element in the combined list
|
|
||||||
let combined_list_elem_ptr = unsafe {
|
|
||||||
builder.build_in_bounds_gep(
|
|
||||||
combined_list_ptr,
|
|
||||||
&[first_index],
|
|
||||||
"load_index_combined_list",
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Mutate the new array in-place to change the element.
|
|
||||||
builder.build_store(combined_list_elem_ptr, first_list_elem);
|
|
||||||
};
|
|
||||||
|
|
||||||
let index_name = "#index";
|
|
||||||
|
|
||||||
let index_alloca = incrementing_elem_loop(
|
|
||||||
builder,
|
|
||||||
ctx,
|
|
||||||
parent,
|
|
||||||
first_list_ptr,
|
|
||||||
first_list_len,
|
|
||||||
index_name,
|
|
||||||
first_loop,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Reset the index variable to 0
|
|
||||||
builder.build_store(index_alloca, ctx.i64_type().const_int(0, false));
|
|
||||||
|
|
||||||
let second_list_ptr = load_list_ptr(builder, second_list_wrapper, ptr_type);
|
|
||||||
|
|
||||||
// SECOND LOOP
|
|
||||||
// TODO when the element type supports it, replace SECOND_LOOP with a memcpy!
|
|
||||||
let second_loop = |second_index, second_list_elem| {
|
|
||||||
// The pointer to the element in the combined list.
|
|
||||||
// Note that the pointer does not start at the index
|
|
||||||
// 0, it starts at the index of first_list_len. In that
|
|
||||||
// sense it is "offset".
|
|
||||||
let offset_combined_list_elem_ptr = unsafe {
|
|
||||||
builder.build_in_bounds_gep(
|
|
||||||
combined_list_ptr,
|
|
||||||
&[first_list_len],
|
|
||||||
"elem",
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
// The pointer to the element from the second list
|
|
||||||
// in the combined list
|
|
||||||
let combined_list_elem_ptr = unsafe {
|
|
||||||
builder.build_in_bounds_gep(
|
|
||||||
offset_combined_list_elem_ptr,
|
|
||||||
&[second_index],
|
|
||||||
"load_index_combined_list",
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Mutate the new array in-place to change the element.
|
|
||||||
builder.build_store(combined_list_elem_ptr, second_list_elem);
|
|
||||||
};
|
|
||||||
|
|
||||||
incrementing_elem_loop(
|
|
||||||
builder,
|
|
||||||
ctx,
|
|
||||||
parent,
|
|
||||||
second_list_ptr,
|
|
||||||
second_list_len,
|
|
||||||
index_name,
|
|
||||||
second_loop,
|
|
||||||
);
|
|
||||||
|
|
||||||
store_list(env, combined_list_ptr, combined_list_len)
|
|
||||||
};
|
|
||||||
|
|
||||||
build_basic_phi2(
|
|
||||||
env,
|
|
||||||
parent,
|
|
||||||
second_list_length_comparison,
|
|
||||||
if_second_list_is_not_empty,
|
|
||||||
if_second_list_is_empty,
|
|
||||||
BasicTypeEnum::StructType(super::convert::zig_list_type(env)),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
build_basic_phi2(
|
|
||||||
env,
|
|
||||||
parent,
|
|
||||||
first_list_length_comparison,
|
|
||||||
if_first_list_is_not_empty,
|
|
||||||
if_first_list_is_empty,
|
|
||||||
BasicTypeEnum::StructType(super::convert::zig_list_type(env)),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
Layout::Builtin(Builtin::List(_, elem_layout)) => call_bitcode_fn_returns_list(
|
||||||
|
env,
|
||||||
|
&[
|
||||||
|
pass_list_as_i128(env, first_list),
|
||||||
|
pass_list_as_i128(env, second_list),
|
||||||
|
alignment_intvalue(env, elem_layout),
|
||||||
|
layout_width(env, elem_layout),
|
||||||
|
],
|
||||||
|
&bitcode::LIST_CONCAT,
|
||||||
|
),
|
||||||
_ => {
|
_ => {
|
||||||
unreachable!(
|
unreachable!("Invalid List layout for List.concat {:?}", list_layout);
|
||||||
"Invalid List layout for first list in List.concat : {:?}",
|
|
||||||
list_layout
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1428,15 +1262,6 @@ pub fn empty_list<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> BasicValueEnum<'
|
||||||
BasicValueEnum::StructValue(struct_type.const_zero())
|
BasicValueEnum::StructValue(struct_type.const_zero())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_is_not_empty<'ctx>(env: &Env<'_, 'ctx, '_>, len: IntValue<'ctx>) -> IntValue<'ctx> {
|
|
||||||
env.builder.build_int_compare(
|
|
||||||
IntPredicate::UGT,
|
|
||||||
len,
|
|
||||||
env.ptr_int().const_zero(),
|
|
||||||
"list_len_is_nonzero",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_list<'ctx>(
|
pub fn load_list<'ctx>(
|
||||||
builder: &Builder<'ctx>,
|
builder: &Builder<'ctx>,
|
||||||
wrapper_struct: StructValue<'ctx>,
|
wrapper_struct: StructValue<'ctx>,
|
||||||
|
|
|
@ -642,7 +642,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
||||||
ListSet => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]),
|
ListSet => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]),
|
||||||
ListSetInPlace => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]),
|
ListSetInPlace => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]),
|
||||||
ListGetUnsafe => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
ListGetUnsafe => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
||||||
ListConcat => arena.alloc_slice_copy(&[borrowed, borrowed]),
|
ListConcat => arena.alloc_slice_copy(&[owned, owned]),
|
||||||
StrConcat => arena.alloc_slice_copy(&[owned, borrowed]),
|
StrConcat => arena.alloc_slice_copy(&[owned, borrowed]),
|
||||||
StrSplit => arena.alloc_slice_copy(&[borrowed, borrowed]),
|
StrSplit => arena.alloc_slice_copy(&[borrowed, borrowed]),
|
||||||
ListSingle => arena.alloc_slice_copy(&[irrelevant]),
|
ListSingle => arena.alloc_slice_copy(&[irrelevant]),
|
||||||
|
|
|
@ -122,6 +122,8 @@ e.g. you have a test `calculate_sum_test` that only uses the function `add`, whe
|
||||||
* [Kite](https://www.kite.com/) AI autocomplete and doc viewer.
|
* [Kite](https://www.kite.com/) AI autocomplete and doc viewer.
|
||||||
* [Tabnine](https://www.tabnine.com/) AI autocomplete.
|
* [Tabnine](https://www.tabnine.com/) AI autocomplete.
|
||||||
* [Codota](https://www.codota.com) AI autocomplete and example searching.
|
* [Codota](https://www.codota.com) AI autocomplete and example searching.
|
||||||
|
* [Aroma](https://ai.facebook.com/blog/aroma-ml-for-code-recommendation) showing examples similar to current code.
|
||||||
|
* [MISM](https://arxiv.org/abs/2006.05265) neural network based code similarity scoring.
|
||||||
|
|
||||||
### Non-Code Related Inspiration
|
### Non-Code Related Inspiration
|
||||||
|
|
||||||
|
@ -160,6 +162,10 @@ e.g. you have a test `calculate_sum_test` that only uses the function `add`, whe
|
||||||
* Do a normal run, and save the input and output of the selected function.
|
* Do a normal run, and save the input and output of the selected function.
|
||||||
* Generate a unit test with that input-output pair
|
* Generate a unit test with that input-output pair
|
||||||
|
|
||||||
|
### Inspiration
|
||||||
|
|
||||||
|
* [Haskell language server plugin](https://github.com/haskell/haskell-language-server/blob/master/plugins/hls-eval-plugin/README.md) evaluate code in comments, to test and document functions and to quickly evaluate small expressions.
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
* Ability to see module as it would be presented on a package website.
|
* Ability to see module as it would be presented on a package website.
|
||||||
|
|
|
@ -8,7 +8,7 @@ use crate::lang::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use roc_can::expected::Expected;
|
use roc_can::expected::Expected;
|
||||||
use roc_collections::all::{BumpMap, BumpMapDefault};
|
use roc_collections::all::{BumpMap, BumpMapDefault, Index};
|
||||||
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::{
|
use roc_types::{
|
||||||
|
@ -106,6 +106,55 @@ pub fn constrain_expr<'a>(
|
||||||
|
|
||||||
exists(arena, flex_vars, defs_constraint)
|
exists(arena, flex_vars, defs_constraint)
|
||||||
}
|
}
|
||||||
|
Expr2::List {
|
||||||
|
elem_var, elems, ..
|
||||||
|
} => {
|
||||||
|
let mut flex_vars = BumpVec::with_capacity_in(1, arena);
|
||||||
|
|
||||||
|
flex_vars.push(*elem_var);
|
||||||
|
|
||||||
|
if elems.is_empty() {
|
||||||
|
exists(
|
||||||
|
arena,
|
||||||
|
flex_vars,
|
||||||
|
Eq(
|
||||||
|
empty_list_type(env.pool, *elem_var),
|
||||||
|
expected,
|
||||||
|
Category::List,
|
||||||
|
region,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
let mut constraints = BumpVec::with_capacity_in(1 + elems.len(), arena);
|
||||||
|
|
||||||
|
let list_elem_type = Type2::Variable(*elem_var);
|
||||||
|
|
||||||
|
for (index, elem_node_id) in elems.iter_node_ids().enumerate() {
|
||||||
|
let elem_expr = env.pool.get(elem_node_id);
|
||||||
|
|
||||||
|
let elem_expected = Expected::ForReason(
|
||||||
|
Reason::ElemInList {
|
||||||
|
index: Index::zero_based(index),
|
||||||
|
},
|
||||||
|
list_elem_type.shallow_clone(),
|
||||||
|
region,
|
||||||
|
);
|
||||||
|
|
||||||
|
let constraint = constrain_expr(arena, env, elem_expr, elem_expected, region);
|
||||||
|
|
||||||
|
constraints.push(constraint);
|
||||||
|
}
|
||||||
|
|
||||||
|
constraints.push(Eq(
|
||||||
|
list_type(env.pool, list_elem_type),
|
||||||
|
expected,
|
||||||
|
Category::List,
|
||||||
|
region,
|
||||||
|
));
|
||||||
|
|
||||||
|
exists(arena, flex_vars, And(constraints))
|
||||||
|
}
|
||||||
|
}
|
||||||
Expr2::Record { fields, record_var } => {
|
Expr2::Record { fields, record_var } => {
|
||||||
if fields.is_empty() {
|
if fields.is_empty() {
|
||||||
constrain_empty_record(expected, region)
|
constrain_empty_record(expected, region)
|
||||||
|
@ -174,13 +223,13 @@ pub fn constrain_expr<'a>(
|
||||||
fn exists<'a>(
|
fn exists<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
flex_vars: BumpVec<'a, Variable>,
|
flex_vars: BumpVec<'a, Variable>,
|
||||||
constraint: Constraint<'a>,
|
defs_constraint: Constraint<'a>,
|
||||||
) -> Constraint<'a> {
|
) -> Constraint<'a> {
|
||||||
Constraint::Let(arena.alloc(LetConstraint {
|
Constraint::Let(arena.alloc(LetConstraint {
|
||||||
rigid_vars: BumpVec::new_in(arena),
|
rigid_vars: BumpVec::new_in(arena),
|
||||||
flex_vars,
|
flex_vars,
|
||||||
def_types: BumpMap::new_in(arena),
|
def_types: BumpMap::new_in(arena),
|
||||||
defs_constraint: constraint,
|
defs_constraint,
|
||||||
ret_constraint: Constraint::True,
|
ret_constraint: Constraint::True,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
@ -202,10 +251,33 @@ fn constrain_empty_record<'a>(expected: Expected<Type2>, region: Region) -> Cons
|
||||||
Constraint::Eq(Type2::EmptyRec, expected, Category::Record, region)
|
Constraint::Eq(Type2::EmptyRec, expected, Category::Record, region)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn str_type(pool: &mut Pool) -> Type2 {
|
#[inline(always)]
|
||||||
Type2::Apply(Symbol::STR_STR, PoolVec::empty(pool))
|
fn builtin_type(symbol: Symbol, args: PoolVec<Type2>) -> Type2 {
|
||||||
|
Type2::Apply(symbol, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn str_type(pool: &mut Pool) -> Type2 {
|
||||||
|
builtin_type(Symbol::STR_STR, PoolVec::empty(pool))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn empty_list_type(pool: &mut Pool, var: Variable) -> Type2 {
|
||||||
|
list_type(pool, Type2::Variable(var))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn list_type(pool: &mut Pool, typ: Type2) -> Type2 {
|
||||||
|
let args = PoolVec::with_capacity(1, pool);
|
||||||
|
|
||||||
|
for (arg_node_id, arg) in args.iter_node_ids().zip(vec![typ]) {
|
||||||
|
pool[arg_node_id] = arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
builtin_type(Symbol::LIST_LIST, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn num_float(pool: &mut Pool, range: TypeId) -> Type2 {
|
fn num_float(pool: &mut Pool, range: TypeId) -> Type2 {
|
||||||
let num_floatingpoint_type = num_floatingpoint(pool, range);
|
let num_floatingpoint_type = num_floatingpoint(pool, range);
|
||||||
let num_floatingpoint_id = pool.add(num_floatingpoint_type);
|
let num_floatingpoint_id = pool.add(num_floatingpoint_type);
|
||||||
|
@ -220,6 +292,7 @@ fn num_float(pool: &mut Pool, range: TypeId) -> Type2 {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn num_floatingpoint(pool: &mut Pool, range: TypeId) -> Type2 {
|
fn num_floatingpoint(pool: &mut Pool, range: TypeId) -> Type2 {
|
||||||
let range_type = pool.get(range);
|
let range_type = pool.get(range);
|
||||||
|
|
||||||
|
@ -243,6 +316,7 @@ fn num_floatingpoint(pool: &mut Pool, range: TypeId) -> Type2 {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn _num_int(pool: &mut Pool, range: TypeId) -> Type2 {
|
fn _num_int(pool: &mut Pool, range: TypeId) -> Type2 {
|
||||||
let num_integer_type = _num_integer(pool, range);
|
let num_integer_type = _num_integer(pool, range);
|
||||||
let num_integer_id = pool.add(num_integer_type);
|
let num_integer_id = pool.add(num_integer_type);
|
||||||
|
@ -257,6 +331,7 @@ fn _num_int(pool: &mut Pool, range: TypeId) -> Type2 {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn _num_signed64(pool: &mut Pool) -> Type2 {
|
fn _num_signed64(pool: &mut Pool) -> Type2 {
|
||||||
let alias_content = Type2::TagUnion(
|
let alias_content = Type2::TagUnion(
|
||||||
PoolVec::new(
|
PoolVec::new(
|
||||||
|
@ -274,6 +349,7 @@ fn _num_signed64(pool: &mut Pool) -> Type2 {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn _num_integer(pool: &mut Pool, range: TypeId) -> Type2 {
|
fn _num_integer(pool: &mut Pool, range: TypeId) -> Type2 {
|
||||||
let range_type = pool.get(range);
|
let range_type = pool.get(range);
|
||||||
|
|
||||||
|
@ -297,6 +373,7 @@ fn _num_integer(pool: &mut Pool, range: TypeId) -> Type2 {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn num_num(pool: &mut Pool, type_id: TypeId) -> Type2 {
|
fn num_num(pool: &mut Pool, type_id: TypeId) -> Type2 {
|
||||||
let range_type = pool.get(type_id);
|
let range_type = pool.get(type_id);
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ fn run_solve<'a>(
|
||||||
aliases,
|
aliases,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut subs = Subs::new(var_store.into());
|
let mut subs = Subs::new(var_store);
|
||||||
|
|
||||||
for (var, name) in rigid_variables {
|
for (var, name) in rigid_variables {
|
||||||
subs.rigid_var(var, name);
|
subs.rigid_var(var, name);
|
||||||
|
@ -141,6 +141,20 @@ fn constrain_str() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This will be more useful once we actually map
|
||||||
|
// strings less than 15 chars to SmallStr
|
||||||
|
#[test]
|
||||||
|
fn constrain_small_str() {
|
||||||
|
infer_eq(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
"a"
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"Str",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn constrain_empty_record() {
|
fn constrain_empty_record() {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
|
@ -188,3 +202,39 @@ fn constrain_record() {
|
||||||
"{ x : Num *, y : Str }",
|
"{ x : Num *, y : Str }",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn constrain_empty_list() {
|
||||||
|
infer_eq(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
[]
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"List *",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn constrain_list() {
|
||||||
|
infer_eq(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
[ 1, 2 ]
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"List (Num *)",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn constrain_list_of_records() {
|
||||||
|
infer_eq(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
[ { x: 1 }, { x: 3 } ]
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"List { x : Num * }",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue