mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 06:14:46 +00:00
wip
This commit is contained in:
parent
920b530008
commit
461e166254
16 changed files with 279 additions and 488 deletions
|
@ -429,14 +429,6 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
add_type(
|
|
||||||
Symbol::LIST_GET_UNSAFE,
|
|
||||||
SolvedType::Func(
|
|
||||||
vec![list_type(flex(TVAR1)), int_type()],
|
|
||||||
Box::new(flex(TVAR1)),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// set : List elem, Int, elem -> List elem
|
// set : List elem, Int, elem -> List elem
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::LIST_SET,
|
Symbol::LIST_SET,
|
||||||
|
|
|
@ -541,11 +541,6 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
add_type(
|
|
||||||
Symbol::LIST_GET_UNSAFE,
|
|
||||||
unique_function(vec![list_type(UVAR1, TVAR1), int_type(UVAR2)], flex(TVAR1)),
|
|
||||||
);
|
|
||||||
|
|
||||||
// set : Attr (w | u | v) (List (Attr u a))
|
// set : Attr (w | u | v) (List (Attr u a))
|
||||||
// , Attr * Int
|
// , Attr * Int
|
||||||
// , Attr (u | v) a
|
// , Attr (u | v) a
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
use crate::env::Env;
|
use crate::env::Env;
|
||||||
use crate::scope::Scope;
|
use crate::scope::Scope;
|
||||||
use roc_collections::all::{MutMap, MutSet, SendMap};
|
use roc_collections::all::{MutMap, MutSet, SendMap};
|
||||||
use roc_module::ident::Ident;
|
use roc_module::ident::{Ident, Lowercase, TagName};
|
||||||
use roc_module::ident::{Lowercase, TagName};
|
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_parse::ast::{AssignedField, Tag, TypeAnnotation};
|
use roc_parse::ast::{AssignedField, Tag, TypeAnnotation};
|
||||||
use roc_region::all::Located;
|
use roc_region::all::{Located, Region};
|
||||||
use roc_region::all::Region;
|
|
||||||
use roc_types::subs::{VarStore, Variable};
|
use roc_types::subs::{VarStore, Variable};
|
||||||
use roc_types::types::{Alias, Problem, Type};
|
use roc_types::types::{Alias, Problem, Type};
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,7 @@ pub fn builtin_defs(var_store: &mut VarStore) -> MutMap<Symbol, Def> {
|
||||||
Symbol::BOOL_NOT => bool_not,
|
Symbol::BOOL_NOT => bool_not,
|
||||||
Symbol::LIST_LEN => list_len,
|
Symbol::LIST_LEN => list_len,
|
||||||
Symbol::LIST_GET => list_get,
|
Symbol::LIST_GET => list_get,
|
||||||
|
Symbol::LIST_SET => list_set,
|
||||||
Symbol::LIST_FIRST => list_first,
|
Symbol::LIST_FIRST => list_first,
|
||||||
Symbol::INT_DIV => int_div,
|
Symbol::INT_DIV => int_div,
|
||||||
Symbol::INT_ABS => int_abs,
|
Symbol::INT_ABS => int_abs,
|
||||||
|
@ -343,7 +344,7 @@ fn list_len(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
fn list_get(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
fn list_get(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
use crate::expr::Expr::*;
|
use crate::expr::Expr::*;
|
||||||
|
|
||||||
// Perform a bounds check. If it passes, delegate to List.#getUnsafe
|
// Perform a bounds check. If it passes, run LowLevel::ListGetUnsafe
|
||||||
let body = If {
|
let body = If {
|
||||||
cond_var: var_store.fresh(),
|
cond_var: var_store.fresh(),
|
||||||
branch_var: var_store.fresh(),
|
branch_var: var_store.fresh(),
|
||||||
|
@ -370,22 +371,15 @@ fn list_get(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
tag(
|
tag(
|
||||||
"Ok",
|
"Ok",
|
||||||
vec![
|
vec![
|
||||||
// List.getUnsafe list index
|
// List#getUnsafe list index
|
||||||
Call(
|
RunLowLevel {
|
||||||
Box::new((
|
op: LowLevel::ListGetUnsafe,
|
||||||
var_store.fresh(),
|
args: vec![
|
||||||
no_region(Var(Symbol::LIST_GET_UNSAFE)),
|
(var_store.fresh(), Var(Symbol::LIST_GET_ARG_LIST)),
|
||||||
var_store.fresh(),
|
(var_store.fresh(), Var(Symbol::LIST_GET_ARG_INDEX)),
|
||||||
)),
|
|
||||||
vec![
|
|
||||||
(var_store.fresh(), no_region(Var(Symbol::LIST_GET_ARG_LIST))),
|
|
||||||
(
|
|
||||||
var_store.fresh(),
|
|
||||||
no_region(Var(Symbol::LIST_GET_ARG_INDEX)),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
CalledVia::Space,
|
ret_var: var_store.fresh(),
|
||||||
),
|
},
|
||||||
],
|
],
|
||||||
var_store,
|
var_store,
|
||||||
),
|
),
|
||||||
|
@ -412,6 +406,60 @@ fn list_get(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// List.set : List elem, Int, elem -> List elem
|
||||||
|
fn list_set(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
|
use crate::expr::Expr::*;
|
||||||
|
|
||||||
|
// Perform a bounds check. If it passes, run LowLevel::ListSetUnsafe.
|
||||||
|
// Otherwise, return the list unmodified.
|
||||||
|
let body = If {
|
||||||
|
cond_var: var_store.fresh(),
|
||||||
|
branch_var: var_store.fresh(),
|
||||||
|
branches: vec![(
|
||||||
|
// if-condition
|
||||||
|
no_region(
|
||||||
|
// index < List.len list
|
||||||
|
call(
|
||||||
|
Symbol::NUM_LT,
|
||||||
|
vec![
|
||||||
|
Var(Symbol::LIST_SET_ARG_INDEX),
|
||||||
|
RunLowLevel {
|
||||||
|
op: LowLevel::ListLen,
|
||||||
|
args: vec![(var_store.fresh(), Var(Symbol::LIST_SET_ARG_LIST))],
|
||||||
|
ret_var: var_store.fresh(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
var_store,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// then-branch
|
||||||
|
no_region(
|
||||||
|
// List.setUnsafe list index
|
||||||
|
RunLowLevel {
|
||||||
|
op: LowLevel::ListSetUnsafe,
|
||||||
|
args: vec![
|
||||||
|
(var_store.fresh(), Var(Symbol::LIST_SET_ARG_LIST)),
|
||||||
|
(var_store.fresh(), Var(Symbol::LIST_SET_ARG_INDEX)),
|
||||||
|
(var_store.fresh(), Var(Symbol::LIST_SET_ARG_ELEM)),
|
||||||
|
],
|
||||||
|
ret_var: var_store.fresh(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)],
|
||||||
|
final_else: Box::new(
|
||||||
|
// else-branch
|
||||||
|
no_region(Var(Symbol::LIST_SET_ARG_LIST)),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
defn(
|
||||||
|
symbol,
|
||||||
|
vec![Symbol::LIST_GET_ARG_LIST, Symbol::LIST_GET_ARG_INDEX],
|
||||||
|
var_store,
|
||||||
|
body,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// Int.rem : Int, Int -> Int
|
/// Int.rem : Int, Int -> Int
|
||||||
fn int_rem(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
fn int_rem(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
use crate::expr::Expr::*;
|
use crate::expr::Expr::*;
|
||||||
|
@ -596,12 +644,15 @@ fn list_first(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
tag(
|
tag(
|
||||||
"Ok",
|
"Ok",
|
||||||
vec![
|
vec![
|
||||||
// List.#getUnsafe list 0
|
// List#getUnsafe list 0
|
||||||
call(
|
RunLowLevel {
|
||||||
Symbol::LIST_GET_UNSAFE,
|
op: LowLevel::ListGetUnsafe,
|
||||||
vec![(Var(Symbol::LIST_FIRST_ARG)), (Int(var_store.fresh(), 0))],
|
args: vec![
|
||||||
var_store,
|
(var_store.fresh(), Var(Symbol::LIST_GET_ARG_LIST)),
|
||||||
),
|
(var_store.fresh(), Int(var_store.fresh(), 0)),
|
||||||
|
],
|
||||||
|
ret_var: var_store.fresh(),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
var_store,
|
var_store,
|
||||||
),
|
),
|
||||||
|
@ -666,8 +717,6 @@ fn defn(fn_name: Symbol, args: Vec<Symbol>, var_store: &mut VarStore, body: Expr
|
||||||
Box::new((no_region(body), var_store.fresh())),
|
Box::new((no_region(body), var_store.fresh())),
|
||||||
);
|
);
|
||||||
|
|
||||||
let annotation = None; // TODO
|
|
||||||
|
|
||||||
Def {
|
Def {
|
||||||
loc_pattern: Located {
|
loc_pattern: Located {
|
||||||
region: Region::zero(),
|
region: Region::zero(),
|
||||||
|
@ -679,6 +728,6 @@ fn defn(fn_name: Symbol, args: Vec<Symbol>, var_store: &mut VarStore, body: Expr
|
||||||
},
|
},
|
||||||
expr_var: var_store.fresh(),
|
expr_var: var_store.fresh(),
|
||||||
pattern_vars: SendMap::default(),
|
pattern_vars: SendMap::default(),
|
||||||
annotation,
|
annotation: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -137,11 +137,16 @@ pub fn canonicalize_module_defs<'a>(
|
||||||
|
|
||||||
let mut references = MutSet::default();
|
let mut references = MutSet::default();
|
||||||
|
|
||||||
// Gather up all the symbols that were referenced across all the defs.
|
// Gather up all the symbols that were referenced across all the defs' lookups.
|
||||||
for symbol in output.references.lookups.iter() {
|
for symbol in output.references.lookups.iter() {
|
||||||
references.insert(*symbol);
|
references.insert(*symbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gather up all the symbols that were referenced across all the defs' calls.
|
||||||
|
for symbol in output.references.calls.iter() {
|
||||||
|
references.insert(*symbol);
|
||||||
|
}
|
||||||
|
|
||||||
// Gather up all the symbols that were referenced from other modules.
|
// Gather up all the symbols that were referenced from other modules.
|
||||||
for symbol in env.referenced_symbols.iter() {
|
for symbol in env.referenced_symbols.iter() {
|
||||||
references.insert(*symbol);
|
references.insert(*symbol);
|
||||||
|
@ -231,6 +236,11 @@ pub fn canonicalize_module_defs<'a>(
|
||||||
references.insert(symbol);
|
references.insert(symbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Incorporate any remaining output.calls entries into references.
|
||||||
|
for symbol in output.references.calls {
|
||||||
|
references.insert(symbol);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(ModuleOutput {
|
Ok(ModuleOutput {
|
||||||
aliases,
|
aliases,
|
||||||
rigid_variables,
|
rigid_variables,
|
||||||
|
|
|
@ -778,43 +778,8 @@ pub fn constrain_expr(
|
||||||
|
|
||||||
exists(vars, And(arg_cons))
|
exists(vars, And(arg_cons))
|
||||||
}
|
}
|
||||||
RunLowLevel { args, ret_var, op } => {
|
RunLowLevel { .. } => {
|
||||||
// This is a modified version of what we do for function calls.
|
unreachable!("RunLowLevel should never participate in constraint generation.");
|
||||||
|
|
||||||
// The operation's return type
|
|
||||||
let ret_type = Variable(*ret_var);
|
|
||||||
|
|
||||||
// This will be used in the occurs check
|
|
||||||
let mut vars = Vec::with_capacity(1 + args.len());
|
|
||||||
|
|
||||||
vars.push(*ret_var);
|
|
||||||
|
|
||||||
let mut arg_types = Vec::with_capacity(args.len());
|
|
||||||
let mut arg_cons = Vec::with_capacity(args.len());
|
|
||||||
|
|
||||||
for (index, (arg_var, arg)) in args.iter().enumerate() {
|
|
||||||
let arg_type = Variable(*arg_var);
|
|
||||||
let reason = Reason::LowLevelOpArg {
|
|
||||||
op: *op,
|
|
||||||
arg_index: Index::zero_based(index),
|
|
||||||
};
|
|
||||||
let expected_arg = ForReason(reason, arg_type.clone(), Region::zero());
|
|
||||||
let arg_con = constrain_expr(env, Region::zero(), arg, expected_arg);
|
|
||||||
|
|
||||||
vars.push(*arg_var);
|
|
||||||
arg_types.push(arg_type);
|
|
||||||
arg_cons.push(arg_con);
|
|
||||||
}
|
|
||||||
|
|
||||||
let category = Category::LowLevelOpResult(*op);
|
|
||||||
|
|
||||||
exists(
|
|
||||||
vars,
|
|
||||||
And(vec![
|
|
||||||
And(arg_cons),
|
|
||||||
Eq(ret_type, expected, category, region),
|
|
||||||
]),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
RuntimeError(_) => {
|
RuntimeError(_) => {
|
||||||
// Runtime Errors have no constraints because they're going to crash.
|
// Runtime Errors have no constraints because they're going to crash.
|
||||||
|
|
|
@ -411,7 +411,7 @@ pub fn pre_constrain_imports(
|
||||||
for &symbol in references.iter() {
|
for &symbol in references.iter() {
|
||||||
let module_id = symbol.module_id();
|
let module_id = symbol.module_id();
|
||||||
|
|
||||||
// We used this one, so clearly it is not unused!
|
// We used this module, so clearly it is not unused!
|
||||||
unused_imports.remove(&module_id);
|
unused_imports.remove(&module_id);
|
||||||
|
|
||||||
if module_id.is_builtin() {
|
if module_id.is_builtin() {
|
||||||
|
|
|
@ -1259,39 +1259,6 @@ fn call_with_args<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
BasicValueEnum::IntValue(int_val)
|
BasicValueEnum::IntValue(int_val)
|
||||||
}
|
}
|
||||||
Symbol::LIST_GET_UNSAFE => {
|
|
||||||
let builder = env.builder;
|
|
||||||
|
|
||||||
// List.get : List elem, Int -> [ Ok elem, OutOfBounds ]*
|
|
||||||
debug_assert!(args.len() == 2);
|
|
||||||
|
|
||||||
let (_, list_layout) = &args[0];
|
|
||||||
|
|
||||||
let wrapper_struct = args[0].0.into_struct_value();
|
|
||||||
let elem_index = args[1].0.into_int_value();
|
|
||||||
|
|
||||||
match list_layout {
|
|
||||||
Layout::Builtin(Builtin::List(elem_layout)) => {
|
|
||||||
let ctx = env.context;
|
|
||||||
let elem_type =
|
|
||||||
basic_type_from_layout(env.arena, ctx, elem_layout, env.ptr_bytes);
|
|
||||||
let ptr_type = get_ptr_type(&elem_type, AddressSpace::Generic);
|
|
||||||
// Load the pointer to the array data
|
|
||||||
let array_data_ptr = load_list_ptr(builder, wrapper_struct, ptr_type);
|
|
||||||
|
|
||||||
// Assume the bounds have already been checked earlier
|
|
||||||
// (e.g. by List.get or List.first, which wrap List.#getUnsafe)
|
|
||||||
let elem_ptr = unsafe {
|
|
||||||
builder.build_in_bounds_gep(array_data_ptr, &[elem_index], "elem")
|
|
||||||
};
|
|
||||||
|
|
||||||
builder.build_load(elem_ptr, "List.get")
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
unreachable!("Invalid List layout for List.get: {:?}", list_layout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Symbol::FLOAT_SQRT => call_intrinsic(LLVM_SQRT_F64, env, args),
|
Symbol::FLOAT_SQRT => call_intrinsic(LLVM_SQRT_F64, env, args),
|
||||||
Symbol::FLOAT_ROUND => call_intrinsic(LLVM_LROUND_I64_F64, env, args),
|
Symbol::FLOAT_ROUND => call_intrinsic(LLVM_LROUND_I64_F64, env, args),
|
||||||
Symbol::LIST_SET => list_set(parent, args, env, InPlace::Clone),
|
Symbol::LIST_SET => list_set(parent, args, env, InPlace::Clone),
|
||||||
|
@ -1705,5 +1672,41 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
BasicValueEnum::IntValue(bool_val)
|
BasicValueEnum::IntValue(bool_val)
|
||||||
}
|
}
|
||||||
|
ListGetUnsafe => {
|
||||||
|
// List.get : List elem, Int -> [ Ok elem, OutOfBounds ]*
|
||||||
|
debug_assert_eq!(args.len(), 2);
|
||||||
|
|
||||||
|
let builder = env.builder;
|
||||||
|
let (_, list_layout) = &args[0];
|
||||||
|
let wrapper_struct =
|
||||||
|
build_expr(env, layout_ids, scope, parent, &args[0].0).into_struct_value();
|
||||||
|
let elem_index =
|
||||||
|
build_expr(env, layout_ids, scope, parent, &args[1].0).into_int_value();
|
||||||
|
|
||||||
|
match list_layout {
|
||||||
|
Layout::Builtin(Builtin::List(elem_layout)) => {
|
||||||
|
let ctx = env.context;
|
||||||
|
let elem_type =
|
||||||
|
basic_type_from_layout(env.arena, ctx, elem_layout, env.ptr_bytes);
|
||||||
|
let ptr_type = get_ptr_type(&elem_type, AddressSpace::Generic);
|
||||||
|
// Load the pointer to the array data
|
||||||
|
let array_data_ptr = load_list_ptr(builder, wrapper_struct, ptr_type);
|
||||||
|
|
||||||
|
// Assume the bounds have already been checked earlier
|
||||||
|
// (e.g. by List.get or List.first, which wrap List.#getUnsafe)
|
||||||
|
let elem_ptr = unsafe {
|
||||||
|
builder.build_in_bounds_gep(array_data_ptr, &[elem_index], "elem")
|
||||||
|
};
|
||||||
|
|
||||||
|
builder.build_load(elem_ptr, "List.get")
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
unreachable!("Invalid List layout for List.get: {:?}", list_layout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ListSetUnsafe => {
|
||||||
|
todo!("re-implement List#setUnsafe");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ mod helpers;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod gen_builtins {
|
mod gen_builtins {
|
||||||
use crate::helpers::{can_expr, infer_expr, uniq_expr, with_larger_debug_stack, CanExprOut};
|
use crate::helpers::{can_expr, infer_expr, uniq_expr, CanExprOut};
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use inkwell::context::Context;
|
use inkwell::context::Context;
|
||||||
use inkwell::execution_engine::JitFunction;
|
use inkwell::execution_engine::JitFunction;
|
||||||
|
@ -95,17 +95,15 @@ mod gen_builtins {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn gen_add_f64() {
|
fn gen_add_f64() {
|
||||||
with_larger_debug_stack(|| {
|
assert_evals_to!(
|
||||||
assert_evals_to!(
|
indoc!(
|
||||||
indoc!(
|
r#"
|
||||||
r#"
|
|
||||||
1.1 + 2.4 + 3
|
1.1 + 2.4 + 3
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
6.5,
|
6.5,
|
||||||
f64
|
f64
|
||||||
);
|
);
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -149,17 +147,15 @@ mod gen_builtins {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn gen_add_i64() {
|
fn gen_add_i64() {
|
||||||
with_larger_debug_stack(|| {
|
assert_evals_to!(
|
||||||
assert_evals_to!(
|
indoc!(
|
||||||
indoc!(
|
r#"
|
||||||
r#"
|
|
||||||
1 + 2 + 3
|
1 + 2 + 3
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
6,
|
6,
|
||||||
i64
|
i64
|
||||||
);
|
);
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -451,22 +447,20 @@ mod gen_builtins {
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn tail_call_elimination() {
|
fn tail_call_elimination() {
|
||||||
with_larger_debug_stack(|| {
|
assert_evals_to!(
|
||||||
assert_evals_to!(
|
indoc!(
|
||||||
indoc!(
|
r#"
|
||||||
r#"
|
sum = \n, accum ->
|
||||||
sum = \n, accum ->
|
when n is
|
||||||
when n is
|
0 -> accum
|
||||||
0 -> accum
|
_ -> sum (n - 1) (n + accum)
|
||||||
_ -> sum (n - 1) (n + accum)
|
|
||||||
|
|
||||||
sum 1_000_000 0
|
sum 1_000_000 0
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
500000500000,
|
500000500000,
|
||||||
i64
|
i64
|
||||||
);
|
);
|
||||||
})
|
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn int_negate() {
|
fn int_negate() {
|
||||||
|
@ -502,16 +496,12 @@ mod gen_builtins {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty_list_len() {
|
fn empty_list_len() {
|
||||||
with_larger_debug_stack(|| {
|
assert_evals_to!("List.len []", 0, usize);
|
||||||
assert_evals_to!("List.len []", 0, usize);
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn basic_int_list_len() {
|
fn basic_int_list_len() {
|
||||||
with_larger_debug_stack(|| {
|
assert_evals_to!("List.len [ 12, 9, 6, 3 ]", 4, usize);
|
||||||
assert_evals_to!("List.len [ 12, 9, 6, 3 ]", 4, usize);
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -553,43 +543,37 @@ mod gen_builtins {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty_list_is_empty() {
|
fn empty_list_is_empty() {
|
||||||
with_larger_debug_stack(|| {
|
assert_evals_to!("List.isEmpty []", true, bool);
|
||||||
assert_evals_to!("List.isEmpty []", true, bool);
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn first_int_list() {
|
fn first_int_list() {
|
||||||
with_larger_debug_stack(|| {
|
assert_evals_to!(
|
||||||
assert_evals_to!(
|
indoc!(
|
||||||
indoc!(
|
r#"
|
||||||
r#"
|
|
||||||
when List.first [ 12, 9, 6, 3 ] is
|
when List.first [ 12, 9, 6, 3 ] is
|
||||||
Ok val -> val
|
Ok val -> val
|
||||||
Err _ -> -1
|
Err _ -> -1
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
12,
|
12,
|
||||||
i64
|
i64
|
||||||
);
|
);
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn first_empty_list() {
|
fn first_empty_list() {
|
||||||
with_larger_debug_stack(|| {
|
assert_evals_to!(
|
||||||
assert_evals_to!(
|
indoc!(
|
||||||
indoc!(
|
r#"
|
||||||
r#"
|
|
||||||
when List.first [] is
|
when List.first [] is
|
||||||
Ok val -> val
|
Ok val -> val
|
||||||
Err _ -> -1
|
Err _ -> -1
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
-1,
|
-1,
|
||||||
i64
|
i64
|
||||||
);
|
);
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -711,10 +695,9 @@ mod gen_builtins {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn gen_quicksort() {
|
fn gen_quicksort() {
|
||||||
with_larger_debug_stack(|| {
|
assert_evals_to!(
|
||||||
assert_evals_to!(
|
indoc!(
|
||||||
indoc!(
|
r#"
|
||||||
r#"
|
|
||||||
quicksort : List (Num a) -> List (Num a)
|
quicksort : List (Num a) -> List (Num a)
|
||||||
quicksort = \list ->
|
quicksort = \list ->
|
||||||
quicksortHelp list 0 (List.len list - 1)
|
quicksortHelp list 0 (List.len list - 1)
|
||||||
|
@ -774,10 +757,9 @@ mod gen_builtins {
|
||||||
|
|
||||||
quicksort [ 7, 4, 21, 19 ]
|
quicksort [ 7, 4, 21, 19 ]
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
&[4, 7, 19, 21],
|
&[4, 7, 19, 21],
|
||||||
&'static [i64]
|
&'static [i64]
|
||||||
);
|
);
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ mod helpers;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod gen_primitives {
|
mod gen_primitives {
|
||||||
use crate::helpers::{can_expr, infer_expr, uniq_expr, with_larger_debug_stack, CanExprOut};
|
use crate::helpers::{can_expr, infer_expr, uniq_expr, CanExprOut};
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use inkwell::context::Context;
|
use inkwell::context::Context;
|
||||||
use inkwell::execution_engine::JitFunction;
|
use inkwell::execution_engine::JitFunction;
|
||||||
|
@ -298,7 +298,7 @@ mod gen_primitives {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn apply_unnamed_fn() {
|
fn apply_unnamed_identity() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
|
@ -406,30 +406,27 @@ mod gen_primitives {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn gen_chained_defs() {
|
fn gen_chained_defs() {
|
||||||
with_larger_debug_stack(|| {
|
assert_evals_to!(
|
||||||
assert_evals_to!(
|
indoc!(
|
||||||
indoc!(
|
r#"
|
||||||
r#"
|
x = i1
|
||||||
x = i1
|
i3 = i2
|
||||||
i3 = i2
|
i1 = 1337
|
||||||
i1 = 1337
|
i2 = i1
|
||||||
i2 = i1
|
y = 12.4
|
||||||
y = 12.4
|
|
||||||
|
i3
|
||||||
i3
|
"#
|
||||||
"#
|
),
|
||||||
),
|
1337,
|
||||||
1337,
|
i64
|
||||||
i64
|
);
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn gen_nested_defs() {
|
fn gen_nested_defs() {
|
||||||
with_larger_debug_stack(|| {
|
assert_evals_to!(
|
||||||
assert_evals_to!(
|
indoc!(
|
||||||
indoc!(
|
r#"
|
||||||
r#"
|
|
||||||
x = 5
|
x = 5
|
||||||
|
|
||||||
answer =
|
answer =
|
||||||
|
@ -460,10 +457,9 @@ mod gen_primitives {
|
||||||
|
|
||||||
answer
|
answer
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
1337,
|
1337,
|
||||||
i64
|
i64
|
||||||
);
|
);
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,14 +6,12 @@ pub mod eval;
|
||||||
use self::bumpalo::Bump;
|
use self::bumpalo::Bump;
|
||||||
use roc_builtins::unique::uniq_stdlib;
|
use roc_builtins::unique::uniq_stdlib;
|
||||||
use roc_can::constraint::Constraint;
|
use roc_can::constraint::Constraint;
|
||||||
use roc_can::def::Def;
|
|
||||||
use roc_can::env::Env;
|
use roc_can::env::Env;
|
||||||
use roc_can::expected::Expected;
|
use roc_can::expected::Expected;
|
||||||
use roc_can::expr::{canonicalize_expr, Expr, Output};
|
use roc_can::expr::{canonicalize_expr, Expr, Output};
|
||||||
use roc_can::operator;
|
use roc_can::operator;
|
||||||
use roc_can::pattern::Pattern;
|
|
||||||
use roc_can::scope::Scope;
|
use roc_can::scope::Scope;
|
||||||
use roc_collections::all::{ImMap, ImSet, MutMap, SendMap, SendSet};
|
use roc_collections::all::{ImMap, MutMap, SendMap};
|
||||||
use roc_constrain::expr::constrain_expr;
|
use roc_constrain::expr::constrain_expr;
|
||||||
use roc_constrain::module::{constrain_imported_values, load_builtin_aliases, Import};
|
use roc_constrain::module::{constrain_imported_values, load_builtin_aliases, Import};
|
||||||
use roc_module::ident::Ident;
|
use roc_module::ident::Ident;
|
||||||
|
@ -26,8 +24,6 @@ use roc_region::all::{Located, Region};
|
||||||
use roc_solve::solve;
|
use roc_solve::solve;
|
||||||
use roc_types::subs::{Content, Subs, VarStore, Variable};
|
use roc_types::subs::{Content, Subs, VarStore, Variable};
|
||||||
use roc_types::types::Type;
|
use roc_types::types::Type;
|
||||||
use std::hash::Hash;
|
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
|
|
||||||
pub fn test_home() -> ModuleId {
|
pub fn test_home() -> ModuleId {
|
||||||
ModuleIds::default().get_or_insert(&"Test".into())
|
ModuleIds::default().get_or_insert(&"Test".into())
|
||||||
|
@ -50,52 +46,6 @@ pub fn infer_expr(
|
||||||
(content, solved.into_inner())
|
(content, solved.into_inner())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used in the with_larger_debug_stack() function, for tests that otherwise
|
|
||||||
/// run out of stack space in debug builds (but don't in --release builds)
|
|
||||||
#[allow(dead_code)]
|
|
||||||
const EXPANDED_STACK_SIZE: usize = 4 * 1024 * 1024;
|
|
||||||
|
|
||||||
/// Without this, some tests pass in `cargo test --release` but fail without
|
|
||||||
/// the --release flag because they run out of stack space. This increases
|
|
||||||
/// stack size for debug builds only, while leaving the stack space at the default
|
|
||||||
/// amount for release builds.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
pub fn with_larger_debug_stack<F>(run_test: F)
|
|
||||||
where
|
|
||||||
F: FnOnce() -> (),
|
|
||||||
F: Send,
|
|
||||||
F: 'static,
|
|
||||||
{
|
|
||||||
std::thread::Builder::new()
|
|
||||||
.stack_size(EXPANDED_STACK_SIZE)
|
|
||||||
.spawn(run_test)
|
|
||||||
.expect("Error while spawning expanded dev stack size thread")
|
|
||||||
.join()
|
|
||||||
.expect("Error while joining expanded dev stack size thread")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// In --release builds, don't increase the stack size. Run the test normally.
|
|
||||||
/// This way, we find out if any of our tests are blowing the stack even after
|
|
||||||
/// optimizations in release builds.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[cfg(not(debug_assertions))]
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn with_larger_debug_stack<F>(run_test: F)
|
|
||||||
where
|
|
||||||
F: FnOnce() -> (),
|
|
||||||
F: Send,
|
|
||||||
F: 'static,
|
|
||||||
{
|
|
||||||
run_test()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn parse_with<'a>(arena: &'a Bump, input: &'a str) -> Result<ast::Expr<'a>, Fail> {
|
|
||||||
parse_loc_with(arena, input).map(|loc_expr| loc_expr.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn parse_loc_with<'a>(arena: &'a Bump, input: &'a str) -> Result<Located<ast::Expr<'a>>, Fail> {
|
pub fn parse_loc_with<'a>(arena: &'a Bump, input: &'a str) -> Result<Located<ast::Expr<'a>>, Fail> {
|
||||||
let state = State::new(&input, Attempting::Module);
|
let state = State::new(&input, Attempting::Module);
|
||||||
let parser = space0_before(loc(roc_parse::expr::expr(0)), 0);
|
let parser = space0_before(loc(roc_parse::expr::expr(0)), 0);
|
||||||
|
@ -106,12 +56,10 @@ pub fn parse_loc_with<'a>(arena: &'a Bump, input: &'a str) -> Result<Located<ast
|
||||||
.map_err(|(fail, _)| fail)
|
.map_err(|(fail, _)| fail)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn can_expr(expr_str: &str) -> CanExprOut {
|
pub fn can_expr(expr_str: &str) -> CanExprOut {
|
||||||
can_expr_with(&Bump::new(), test_home(), expr_str)
|
can_expr_with(&Bump::new(), test_home(), expr_str)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn uniq_expr(
|
pub fn uniq_expr(
|
||||||
expr_str: &str,
|
expr_str: &str,
|
||||||
) -> (
|
) -> (
|
||||||
|
@ -129,7 +77,6 @@ pub fn uniq_expr(
|
||||||
uniq_expr_with(&Bump::new(), expr_str, declared_idents)
|
uniq_expr_with(&Bump::new(), expr_str, declared_idents)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn uniq_expr_with(
|
pub fn uniq_expr_with(
|
||||||
arena: &Bump,
|
arena: &Bump,
|
||||||
expr_str: &str,
|
expr_str: &str,
|
||||||
|
@ -241,32 +188,6 @@ pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut
|
||||||
&loc_expr.value,
|
&loc_expr.value,
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut with_builtins = loc_expr.value;
|
|
||||||
|
|
||||||
// Add builtin defs (e.g. List.get) directly to the canonical Expr,
|
|
||||||
// since we aren't using modules here.
|
|
||||||
let builtin_defs = roc_can::builtins::builtin_defs(&mut var_store);
|
|
||||||
|
|
||||||
for (symbol, def) in builtin_defs {
|
|
||||||
if output.references.lookups.contains(&symbol) || output.references.calls.contains(&symbol)
|
|
||||||
{
|
|
||||||
with_builtins = Expr::LetNonRec(
|
|
||||||
Box::new(def),
|
|
||||||
Box::new(Located {
|
|
||||||
region: Region::zero(),
|
|
||||||
value: with_builtins,
|
|
||||||
}),
|
|
||||||
var_store.fresh(),
|
|
||||||
SendMap::default(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let loc_expr = Located {
|
|
||||||
region: loc_expr.region,
|
|
||||||
value: with_builtins,
|
|
||||||
};
|
|
||||||
|
|
||||||
let constraint = constrain_expr(
|
let constraint = constrain_expr(
|
||||||
&roc_constrain::expr::Env {
|
&roc_constrain::expr::Env {
|
||||||
rigids: ImMap::default(),
|
rigids: ImMap::default(),
|
||||||
|
@ -317,6 +238,35 @@ pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut
|
||||||
all_ident_ids,
|
all_ident_ids,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Finally, add the builtins' defs. We add these defs *after* incorporating
|
||||||
|
// all their hardcoded constraints, so that their expressions do not affect
|
||||||
|
// constraint generation. (Only their hardcoded types should generate constraints.)
|
||||||
|
let mut with_builtins = loc_expr.value;
|
||||||
|
|
||||||
|
// Add builtin defs (e.g. List.get) directly to the canonical Expr,
|
||||||
|
// since we aren't using modules here.
|
||||||
|
let builtin_defs = roc_can::builtins::builtin_defs(&mut var_store);
|
||||||
|
|
||||||
|
for (symbol, def) in builtin_defs {
|
||||||
|
if output.references.lookups.contains(&symbol) || output.references.calls.contains(&symbol)
|
||||||
|
{
|
||||||
|
with_builtins = Expr::LetNonRec(
|
||||||
|
Box::new(def),
|
||||||
|
Box::new(Located {
|
||||||
|
region: Region::zero(),
|
||||||
|
value: with_builtins,
|
||||||
|
}),
|
||||||
|
var_store.fresh(),
|
||||||
|
SendMap::default(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let loc_expr = Located {
|
||||||
|
region: loc_expr.region,
|
||||||
|
value: with_builtins,
|
||||||
|
};
|
||||||
|
|
||||||
CanExprOut {
|
CanExprOut {
|
||||||
loc_expr,
|
loc_expr,
|
||||||
output,
|
output,
|
||||||
|
@ -328,162 +278,3 @@ pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut
|
||||||
constraint,
|
constraint,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn mut_map_from_pairs<K, V, I>(pairs: I) -> MutMap<K, V>
|
|
||||||
where
|
|
||||||
I: IntoIterator<Item = (K, V)>,
|
|
||||||
K: Hash + Eq,
|
|
||||||
{
|
|
||||||
let mut answer = MutMap::default();
|
|
||||||
|
|
||||||
for (key, value) in pairs {
|
|
||||||
answer.insert(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
answer
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn im_map_from_pairs<K, V, I>(pairs: I) -> ImMap<K, V>
|
|
||||||
where
|
|
||||||
I: IntoIterator<Item = (K, V)>,
|
|
||||||
K: Hash + Eq + Clone,
|
|
||||||
V: Clone,
|
|
||||||
{
|
|
||||||
let mut answer = ImMap::default();
|
|
||||||
|
|
||||||
for (key, value) in pairs {
|
|
||||||
answer.insert(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
answer
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn send_set_from<V, I>(elems: I) -> SendSet<V>
|
|
||||||
where
|
|
||||||
I: IntoIterator<Item = V>,
|
|
||||||
V: Hash + Eq + Clone,
|
|
||||||
{
|
|
||||||
let mut answer = SendSet::default();
|
|
||||||
|
|
||||||
for elem in elems {
|
|
||||||
answer.insert(elem);
|
|
||||||
}
|
|
||||||
|
|
||||||
answer
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn fixtures_dir<'a>() -> PathBuf {
|
|
||||||
Path::new("tests").join("fixtures").join("build")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn builtins_dir<'a>() -> PathBuf {
|
|
||||||
PathBuf::new().join("builtins")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check constraints
|
|
||||||
//
|
|
||||||
// Keep track of the used (in types or expectations) variables, and the declared variables (in
|
|
||||||
// flex_vars or rigid_vars fields of LetConstraint. These roc_collections should match: no duplicates
|
|
||||||
// and no variables that are used but not declared are allowed.
|
|
||||||
//
|
|
||||||
// There is one exception: the initial variable (that stores the type of the whole expression) is
|
|
||||||
// never declared, but is used.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn assert_correct_variable_usage(constraint: &Constraint) {
|
|
||||||
// variables declared in constraint (flex_vars or rigid_vars)
|
|
||||||
// and variables actually used in constraints
|
|
||||||
let (declared, used) = variable_usage(constraint);
|
|
||||||
|
|
||||||
let used: ImSet<Variable> = used.clone().into();
|
|
||||||
let mut decl: ImSet<Variable> = declared.rigid_vars.clone().into();
|
|
||||||
|
|
||||||
for var in declared.flex_vars.clone() {
|
|
||||||
decl.insert(var);
|
|
||||||
}
|
|
||||||
|
|
||||||
let diff = used.clone().relative_complement(decl);
|
|
||||||
|
|
||||||
// NOTE: this checks whether we're using variables that are not declared. For recursive type
|
|
||||||
// definitions, their rigid types are declared twice, which is correct!
|
|
||||||
if !diff.is_empty() {
|
|
||||||
println!("VARIABLE USAGE PROBLEM");
|
|
||||||
|
|
||||||
println!("used: {:?}", &used);
|
|
||||||
println!("rigids: {:?}", &declared.rigid_vars);
|
|
||||||
println!("flexs: {:?}", &declared.flex_vars);
|
|
||||||
|
|
||||||
println!("difference: {:?}", &diff);
|
|
||||||
|
|
||||||
panic!("variable usage problem (see stdout for details)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct SeenVariables {
|
|
||||||
pub rigid_vars: Vec<Variable>,
|
|
||||||
pub flex_vars: Vec<Variable>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn variable_usage(con: &Constraint) -> (SeenVariables, Vec<Variable>) {
|
|
||||||
let mut declared = SeenVariables::default();
|
|
||||||
let mut used = ImSet::default();
|
|
||||||
variable_usage_help(con, &mut declared, &mut used);
|
|
||||||
|
|
||||||
used.remove(unsafe { &Variable::unsafe_test_debug_variable(1) });
|
|
||||||
|
|
||||||
let mut used_vec: Vec<Variable> = used.into_iter().collect();
|
|
||||||
used_vec.sort();
|
|
||||||
|
|
||||||
declared.rigid_vars.sort();
|
|
||||||
declared.flex_vars.sort();
|
|
||||||
|
|
||||||
(declared, used_vec)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn variable_usage_help(con: &Constraint, declared: &mut SeenVariables, used: &mut ImSet<Variable>) {
|
|
||||||
use Constraint::*;
|
|
||||||
|
|
||||||
match con {
|
|
||||||
True | SaveTheEnvironment => (),
|
|
||||||
Eq(tipe, expectation, _, _) => {
|
|
||||||
for v in tipe.variables() {
|
|
||||||
used.insert(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
for v in expectation.get_type_ref().variables() {
|
|
||||||
used.insert(v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Lookup(_, expectation, _) => {
|
|
||||||
for v in expectation.get_type_ref().variables() {
|
|
||||||
used.insert(v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Pattern(_, _, tipe, pexpectation) => {
|
|
||||||
for v in tipe.variables() {
|
|
||||||
used.insert(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
for v in pexpectation.get_type_ref().variables() {
|
|
||||||
used.insert(v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Let(letcon) => {
|
|
||||||
declared.rigid_vars.extend(letcon.rigid_vars.clone());
|
|
||||||
declared.flex_vars.extend(letcon.flex_vars.clone());
|
|
||||||
|
|
||||||
variable_usage_help(&letcon.defs_constraint, declared, used);
|
|
||||||
variable_usage_help(&letcon.ret_constraint, declared, used);
|
|
||||||
}
|
|
||||||
And(constraints) => {
|
|
||||||
for sub in constraints {
|
|
||||||
variable_usage_help(sub, declared, used);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,8 +3,9 @@
|
||||||
/// into an Expr when added directly by can::builtins
|
/// into an Expr when added directly by can::builtins
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
pub enum LowLevel {
|
pub enum LowLevel {
|
||||||
/// List.len
|
|
||||||
ListLen,
|
ListLen,
|
||||||
|
ListGetUnsafe,
|
||||||
|
ListSetUnsafe,
|
||||||
Eq,
|
Eq,
|
||||||
NotEq,
|
NotEq,
|
||||||
And,
|
And,
|
||||||
|
|
|
@ -684,11 +684,13 @@ define_builtins! {
|
||||||
11 LIST_LEN_ARG: "len#list"
|
11 LIST_LEN_ARG: "len#list"
|
||||||
12 LIST_FOLDL: "foldl"
|
12 LIST_FOLDL: "foldl"
|
||||||
13 LIST_FOLDR: "foldr"
|
13 LIST_FOLDR: "foldr"
|
||||||
14 LIST_GET_UNSAFE: "getUnsafe"
|
14 LIST_CONCAT: "concat"
|
||||||
15 LIST_CONCAT: "concat"
|
15 LIST_FIRST: "first"
|
||||||
16 LIST_FIRST: "first"
|
16 LIST_FIRST_ARG: "first#list"
|
||||||
17 LIST_FIRST_ARG: "first#list"
|
17 LIST_SINGLE: "single"
|
||||||
18 LIST_SINGLE: "single"
|
18 LIST_SET_ARG_LIST: "set#list"
|
||||||
|
19 LIST_SET_ARG_INDEX: "set#index"
|
||||||
|
20 LIST_SET_ARG_ELEM: "set#elem"
|
||||||
}
|
}
|
||||||
7 RESULT: "Result" => {
|
7 RESULT: "Result" => {
|
||||||
0 RESULT_RESULT: "Result" imported // the Result.Result type alias
|
0 RESULT_RESULT: "Result" imported // the Result.Result type alias
|
||||||
|
|
|
@ -50,10 +50,7 @@ impl<'a> Layout<'a> {
|
||||||
|
|
||||||
match content {
|
match content {
|
||||||
var @ FlexVar(_) | var @ RigidVar(_) => {
|
var @ FlexVar(_) | var @ RigidVar(_) => {
|
||||||
panic!(
|
panic!("Layout::new encountered an unresolved {:?}", var);
|
||||||
"Layout::new encountered an unresolved {:?} - subs was {:?}",
|
|
||||||
var, subs
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Structure(flat_type) => layout_from_flat_type(arena, flat_type, subs, pointer_size),
|
Structure(flat_type) => layout_from_flat_type(arena, flat_type, subs, pointer_size),
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate pretty_assertions;
|
extern crate pretty_assertions;
|
||||||
// #[macro_use]
|
#[macro_use]
|
||||||
// extern crate indoc;
|
extern crate indoc;
|
||||||
|
|
||||||
extern crate bumpalo;
|
extern crate bumpalo;
|
||||||
extern crate roc_mono;
|
extern crate roc_mono;
|
||||||
|
@ -80,6 +80,20 @@ mod test_mono {
|
||||||
compiles_to("0.5", Float(0.5));
|
compiles_to("0.5", Float(0.5));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn apply_identity() {
|
||||||
|
compiles_to(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
identity = \a -> a
|
||||||
|
|
||||||
|
identity 5
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
Int(5),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn float_addition() {
|
fn float_addition() {
|
||||||
compiles_to(
|
compiles_to(
|
||||||
|
@ -201,13 +215,13 @@ mod test_mono {
|
||||||
Store(
|
Store(
|
||||||
&[(
|
&[(
|
||||||
gen_symbol_0,
|
gen_symbol_0,
|
||||||
Layout::Builtin(layout::Builtin::Bool),
|
Layout::Builtin(layout::Builtin::Int1),
|
||||||
Expr::Bool(true),
|
Expr::Bool(true),
|
||||||
)],
|
)],
|
||||||
&Cond {
|
&Cond {
|
||||||
cond_symbol: gen_symbol_0,
|
cond_symbol: gen_symbol_0,
|
||||||
branch_symbol: gen_symbol_0,
|
branch_symbol: gen_symbol_0,
|
||||||
cond_layout: Builtin(Bool),
|
cond_layout: Builtin(Int1),
|
||||||
pass: (&[] as &[_], &Expr::Str("bar")),
|
pass: (&[] as &[_], &Expr::Str("bar")),
|
||||||
fail: (&[] as &[_], &Expr::Str("foo")),
|
fail: (&[] as &[_], &Expr::Str("foo")),
|
||||||
ret_layout: Builtin(Str),
|
ret_layout: Builtin(Str),
|
||||||
|
@ -239,26 +253,26 @@ mod test_mono {
|
||||||
Store(
|
Store(
|
||||||
&[(
|
&[(
|
||||||
gen_symbol_0,
|
gen_symbol_0,
|
||||||
Layout::Builtin(layout::Builtin::Bool),
|
Layout::Builtin(layout::Builtin::Int1),
|
||||||
Expr::Bool(true),
|
Expr::Bool(true),
|
||||||
)],
|
)],
|
||||||
&Cond {
|
&Cond {
|
||||||
cond_symbol: gen_symbol_0,
|
cond_symbol: gen_symbol_0,
|
||||||
branch_symbol: gen_symbol_0,
|
branch_symbol: gen_symbol_0,
|
||||||
cond_layout: Builtin(Bool),
|
cond_layout: Builtin(Int1),
|
||||||
pass: (&[] as &[_], &Expr::Str("bar")),
|
pass: (&[] as &[_], &Expr::Str("bar")),
|
||||||
fail: (
|
fail: (
|
||||||
&[] as &[_],
|
&[] as &[_],
|
||||||
&Store(
|
&Store(
|
||||||
&[(
|
&[(
|
||||||
gen_symbol_1,
|
gen_symbol_1,
|
||||||
Layout::Builtin(layout::Builtin::Bool),
|
Layout::Builtin(layout::Builtin::Int1),
|
||||||
Expr::Bool(false),
|
Expr::Bool(false),
|
||||||
)],
|
)],
|
||||||
&Cond {
|
&Cond {
|
||||||
cond_symbol: gen_symbol_1,
|
cond_symbol: gen_symbol_1,
|
||||||
branch_symbol: gen_symbol_1,
|
branch_symbol: gen_symbol_1,
|
||||||
cond_layout: Builtin(Bool),
|
cond_layout: Builtin(Int1),
|
||||||
pass: (&[] as &[_], &Expr::Str("foo")),
|
pass: (&[] as &[_], &Expr::Str("foo")),
|
||||||
fail: (&[] as &[_], &Expr::Str("baz")),
|
fail: (&[] as &[_], &Expr::Str("baz")),
|
||||||
ret_layout: Builtin(Str),
|
ret_layout: Builtin(Str),
|
||||||
|
@ -297,13 +311,13 @@ mod test_mono {
|
||||||
Store(
|
Store(
|
||||||
&[(
|
&[(
|
||||||
gen_symbol_0,
|
gen_symbol_0,
|
||||||
Layout::Builtin(layout::Builtin::Bool),
|
Layout::Builtin(layout::Builtin::Int1),
|
||||||
Expr::Bool(true),
|
Expr::Bool(true),
|
||||||
)],
|
)],
|
||||||
&Cond {
|
&Cond {
|
||||||
cond_symbol: gen_symbol_0,
|
cond_symbol: gen_symbol_0,
|
||||||
branch_symbol: gen_symbol_0,
|
branch_symbol: gen_symbol_0,
|
||||||
cond_layout: Builtin(Bool),
|
cond_layout: Builtin(Int1),
|
||||||
pass: (&[] as &[_], &Expr::Str("bar")),
|
pass: (&[] as &[_], &Expr::Str("bar")),
|
||||||
fail: (&[] as &[_], &Expr::Str("foo")),
|
fail: (&[] as &[_], &Expr::Str("foo")),
|
||||||
ret_layout: Builtin(Str),
|
ret_layout: Builtin(Str),
|
||||||
|
@ -443,7 +457,7 @@ mod test_mono {
|
||||||
let home = test_home();
|
let home = test_home();
|
||||||
let var_x = interns.symbol(home, "x".into());
|
let var_x = interns.symbol(home, "x".into());
|
||||||
|
|
||||||
let stores = [(var_x, Layout::Builtin(Builtin::Bool), Bool(true))];
|
let stores = [(var_x, Layout::Builtin(Builtin::Int1), Bool(true))];
|
||||||
|
|
||||||
let load = Load(var_x);
|
let load = Load(var_x);
|
||||||
|
|
||||||
|
@ -467,7 +481,7 @@ mod test_mono {
|
||||||
let home = test_home();
|
let home = test_home();
|
||||||
let var_x = interns.symbol(home, "x".into());
|
let var_x = interns.symbol(home, "x".into());
|
||||||
|
|
||||||
let stores = [(var_x, Layout::Builtin(Builtin::Bool), Bool(false))];
|
let stores = [(var_x, Layout::Builtin(Builtin::Int1), Bool(false))];
|
||||||
|
|
||||||
let load = Load(var_x);
|
let load = Load(var_x);
|
||||||
|
|
||||||
|
@ -493,7 +507,7 @@ mod test_mono {
|
||||||
let var_x = interns.symbol(home, "x".into());
|
let var_x = interns.symbol(home, "x".into());
|
||||||
|
|
||||||
// orange gets index (and therefore tag_id) 1
|
// orange gets index (and therefore tag_id) 1
|
||||||
let stores = [(var_x, Layout::Builtin(Builtin::Byte), Byte(2))];
|
let stores = [(var_x, Layout::Builtin(Builtin::Int8), Byte(2))];
|
||||||
|
|
||||||
let load = Load(var_x);
|
let load = Load(var_x);
|
||||||
|
|
||||||
|
@ -504,9 +518,9 @@ mod test_mono {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn set_unique_int_list() {
|
fn set_unique_int_list() {
|
||||||
compiles_to("List.getUnsafe (List.set [ 12, 9, 7, 3 ] 1 42) 1", {
|
compiles_to("List.get (List.set [ 12, 9, 7, 3 ] 1 42) 1", {
|
||||||
CallByName {
|
CallByName {
|
||||||
name: Symbol::LIST_GET_UNSAFE,
|
name: Symbol::LIST_GET,
|
||||||
layout: Layout::FunctionPointer(
|
layout: Layout::FunctionPointer(
|
||||||
&[
|
&[
|
||||||
Layout::Builtin(Builtin::List(&Layout::Builtin(Builtin::Int64))),
|
Layout::Builtin(Builtin::List(&Layout::Builtin(Builtin::Int64))),
|
||||||
|
|
|
@ -13,6 +13,7 @@ mod helpers;
|
||||||
mod test_opt {
|
mod test_opt {
|
||||||
use crate::helpers::{infer_expr, uniq_expr};
|
use crate::helpers::{infer_expr, uniq_expr};
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
|
use roc_module::low_level::LowLevel;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_mono::expr::Expr::{self, *};
|
use roc_mono::expr::Expr::{self, *};
|
||||||
use roc_mono::expr::Procs;
|
use roc_mono::expr::Procs;
|
||||||
|
@ -86,7 +87,7 @@ mod test_opt {
|
||||||
| Byte(_)
|
| Byte(_)
|
||||||
| Load(_)
|
| Load(_)
|
||||||
| FunctionPointer(_, _)
|
| FunctionPointer(_, _)
|
||||||
| RunLowLevel(_)
|
| RunLowLevel(_, _)
|
||||||
| RuntimeError(_)
|
| RuntimeError(_)
|
||||||
| RuntimeErrorFunction(_) => (),
|
| RuntimeErrorFunction(_) => (),
|
||||||
|
|
||||||
|
@ -234,16 +235,9 @@ mod test_opt {
|
||||||
// This should optimize List.set to List.set_in_place
|
// This should optimize List.set to List.set_in_place
|
||||||
compiles_to(
|
compiles_to(
|
||||||
"List.getUnsafe (List.set [ 12, 9, 7, 3 ] 1 42) 1",
|
"List.getUnsafe (List.set [ 12, 9, 7, 3 ] 1 42) 1",
|
||||||
CallByName {
|
RunLowLevel(
|
||||||
name: Symbol::LIST_GET_UNSAFE,
|
LowLevel::ListGetUnsafe,
|
||||||
layout: Layout::FunctionPointer(
|
&vec![
|
||||||
&[
|
|
||||||
Layout::Builtin(Builtin::List(&Layout::Builtin(Builtin::Int64))),
|
|
||||||
Layout::Builtin(Builtin::Int64),
|
|
||||||
],
|
|
||||||
&Layout::Builtin(Builtin::Int64),
|
|
||||||
),
|
|
||||||
args: &vec![
|
|
||||||
(
|
(
|
||||||
CallByName {
|
CallByName {
|
||||||
name: Symbol::LIST_SET_IN_PLACE,
|
name: Symbol::LIST_SET_IN_PLACE,
|
||||||
|
@ -275,7 +269,7 @@ mod test_opt {
|
||||||
),
|
),
|
||||||
(Int(1), Layout::Builtin(Builtin::Int64)),
|
(Int(1), Layout::Builtin(Builtin::Int64)),
|
||||||
],
|
],
|
||||||
},
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,7 +287,9 @@ mod test_opt {
|
||||||
{ x, y: List.getUnsafe shared 1 }
|
{ x, y: List.getUnsafe shared 1 }
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
vec![Symbol::LIST_SET, Symbol::LIST_GET_UNSAFE],
|
vec![
|
||||||
|
Symbol::LIST_SET, /* Symbol::LIST_GET_UNSAFE TODO revise this test */
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue