This commit is contained in:
Richard Feldman 2020-06-20 13:45:03 -04:00
parent 920b530008
commit 461e166254
16 changed files with 279 additions and 488 deletions

View file

@ -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
add_type(
Symbol::LIST_SET,

View file

@ -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))
// , Attr * Int
// , Attr (u | v) a

View file

@ -1,12 +1,10 @@
use crate::env::Env;
use crate::scope::Scope;
use roc_collections::all::{MutMap, MutSet, SendMap};
use roc_module::ident::Ident;
use roc_module::ident::{Lowercase, TagName};
use roc_module::ident::{Ident, Lowercase, TagName};
use roc_module::symbol::Symbol;
use roc_parse::ast::{AssignedField, Tag, TypeAnnotation};
use roc_region::all::Located;
use roc_region::all::Region;
use roc_region::all::{Located, Region};
use roc_types::subs::{VarStore, Variable};
use roc_types::types::{Alias, Problem, Type};

View file

@ -52,6 +52,7 @@ pub fn builtin_defs(var_store: &mut VarStore) -> MutMap<Symbol, Def> {
Symbol::BOOL_NOT => bool_not,
Symbol::LIST_LEN => list_len,
Symbol::LIST_GET => list_get,
Symbol::LIST_SET => list_set,
Symbol::LIST_FIRST => list_first,
Symbol::INT_DIV => int_div,
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 {
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 {
cond_var: var_store.fresh(),
branch_var: var_store.fresh(),
@ -370,22 +371,15 @@ fn list_get(symbol: Symbol, var_store: &mut VarStore) -> Def {
tag(
"Ok",
vec![
// List.getUnsafe list index
Call(
Box::new((
var_store.fresh(),
no_region(Var(Symbol::LIST_GET_UNSAFE)),
var_store.fresh(),
)),
vec![
(var_store.fresh(), no_region(Var(Symbol::LIST_GET_ARG_LIST))),
(
var_store.fresh(),
no_region(Var(Symbol::LIST_GET_ARG_INDEX)),
),
// List#getUnsafe list index
RunLowLevel {
op: LowLevel::ListGetUnsafe,
args: vec![
(var_store.fresh(), Var(Symbol::LIST_GET_ARG_LIST)),
(var_store.fresh(), Var(Symbol::LIST_GET_ARG_INDEX)),
],
CalledVia::Space,
),
ret_var: var_store.fresh(),
},
],
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
fn int_rem(symbol: Symbol, var_store: &mut VarStore) -> Def {
use crate::expr::Expr::*;
@ -596,12 +644,15 @@ fn list_first(symbol: Symbol, var_store: &mut VarStore) -> Def {
tag(
"Ok",
vec![
// List.#getUnsafe list 0
call(
Symbol::LIST_GET_UNSAFE,
vec![(Var(Symbol::LIST_FIRST_ARG)), (Int(var_store.fresh(), 0))],
var_store,
),
// List#getUnsafe list 0
RunLowLevel {
op: LowLevel::ListGetUnsafe,
args: vec![
(var_store.fresh(), Var(Symbol::LIST_GET_ARG_LIST)),
(var_store.fresh(), Int(var_store.fresh(), 0)),
],
ret_var: var_store.fresh(),
},
],
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())),
);
let annotation = None; // TODO
Def {
loc_pattern: Located {
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(),
pattern_vars: SendMap::default(),
annotation,
annotation: None,
}
}

View file

@ -137,11 +137,16 @@ pub fn canonicalize_module_defs<'a>(
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() {
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.
for symbol in env.referenced_symbols.iter() {
references.insert(*symbol);
@ -231,6 +236,11 @@ pub fn canonicalize_module_defs<'a>(
references.insert(symbol);
}
// Incorporate any remaining output.calls entries into references.
for symbol in output.references.calls {
references.insert(symbol);
}
Ok(ModuleOutput {
aliases,
rigid_variables,

View file

@ -778,43 +778,8 @@ pub fn constrain_expr(
exists(vars, And(arg_cons))
}
RunLowLevel { args, ret_var, op } => {
// This is a modified version of what we do for function calls.
// 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),
]),
)
RunLowLevel { .. } => {
unreachable!("RunLowLevel should never participate in constraint generation.");
}
RuntimeError(_) => {
// Runtime Errors have no constraints because they're going to crash.

View file

@ -411,7 +411,7 @@ pub fn pre_constrain_imports(
for &symbol in references.iter() {
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);
if module_id.is_builtin() {

View file

@ -1259,39 +1259,6 @@ fn call_with_args<'a, 'ctx, 'env>(
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_ROUND => call_intrinsic(LLVM_LROUND_I64_F64, env, args),
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)
}
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");
}
}
}

View file

@ -13,7 +13,7 @@ mod helpers;
#[cfg(test)]
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 inkwell::context::Context;
use inkwell::execution_engine::JitFunction;
@ -95,7 +95,6 @@ mod gen_builtins {
#[test]
fn gen_add_f64() {
with_larger_debug_stack(|| {
assert_evals_to!(
indoc!(
r#"
@ -105,7 +104,6 @@ mod gen_builtins {
6.5,
f64
);
})
}
#[test]
@ -149,7 +147,6 @@ mod gen_builtins {
#[test]
fn gen_add_i64() {
with_larger_debug_stack(|| {
assert_evals_to!(
indoc!(
r#"
@ -159,7 +156,6 @@ mod gen_builtins {
6,
i64
);
})
}
#[test]
@ -451,7 +447,6 @@ mod gen_builtins {
}
#[test]
fn tail_call_elimination() {
with_larger_debug_stack(|| {
assert_evals_to!(
indoc!(
r#"
@ -466,7 +461,6 @@ mod gen_builtins {
500000500000,
i64
);
})
}
#[test]
fn int_negate() {
@ -502,16 +496,12 @@ mod gen_builtins {
#[test]
fn empty_list_len() {
with_larger_debug_stack(|| {
assert_evals_to!("List.len []", 0, usize);
})
}
#[test]
fn basic_int_list_len() {
with_larger_debug_stack(|| {
assert_evals_to!("List.len [ 12, 9, 6, 3 ]", 4, usize);
})
}
#[test]
@ -553,14 +543,11 @@ mod gen_builtins {
#[test]
fn empty_list_is_empty() {
with_larger_debug_stack(|| {
assert_evals_to!("List.isEmpty []", true, bool);
})
}
#[test]
fn first_int_list() {
with_larger_debug_stack(|| {
assert_evals_to!(
indoc!(
r#"
@ -572,12 +559,10 @@ mod gen_builtins {
12,
i64
);
})
}
#[test]
fn first_empty_list() {
with_larger_debug_stack(|| {
assert_evals_to!(
indoc!(
r#"
@ -589,7 +574,6 @@ mod gen_builtins {
-1,
i64
);
})
}
#[test]
@ -711,7 +695,6 @@ mod gen_builtins {
#[test]
fn gen_quicksort() {
with_larger_debug_stack(|| {
assert_evals_to!(
indoc!(
r#"
@ -778,6 +761,5 @@ mod gen_builtins {
&[4, 7, 19, 21],
&'static [i64]
);
})
}
}

View file

@ -13,7 +13,7 @@ mod helpers;
#[cfg(test)]
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 inkwell::context::Context;
use inkwell::execution_engine::JitFunction;
@ -298,7 +298,7 @@ mod gen_primitives {
}
#[test]
fn apply_unnamed_fn() {
fn apply_unnamed_identity() {
assert_evals_to!(
indoc!(
r#"
@ -406,7 +406,6 @@ mod gen_primitives {
#[test]
fn gen_chained_defs() {
with_larger_debug_stack(|| {
assert_evals_to!(
indoc!(
r#"
@ -422,11 +421,9 @@ mod gen_primitives {
1337,
i64
);
})
}
#[test]
fn gen_nested_defs() {
with_larger_debug_stack(|| {
assert_evals_to!(
indoc!(
r#"
@ -464,6 +461,5 @@ mod gen_primitives {
1337,
i64
);
})
}
}

View file

@ -6,14 +6,12 @@ pub mod eval;
use self::bumpalo::Bump;
use roc_builtins::unique::uniq_stdlib;
use roc_can::constraint::Constraint;
use roc_can::def::Def;
use roc_can::env::Env;
use roc_can::expected::Expected;
use roc_can::expr::{canonicalize_expr, Expr, Output};
use roc_can::operator;
use roc_can::pattern::Pattern;
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::module::{constrain_imported_values, load_builtin_aliases, Import};
use roc_module::ident::Ident;
@ -26,8 +24,6 @@ use roc_region::all::{Located, Region};
use roc_solve::solve;
use roc_types::subs::{Content, Subs, VarStore, Variable};
use roc_types::types::Type;
use std::hash::Hash;
use std::path::{Path, PathBuf};
pub fn test_home() -> ModuleId {
ModuleIds::default().get_or_insert(&"Test".into())
@ -50,52 +46,6 @@ pub fn infer_expr(
(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> {
let state = State::new(&input, Attempting::Module);
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)
}
#[allow(dead_code)]
pub fn can_expr(expr_str: &str) -> CanExprOut {
can_expr_with(&Bump::new(), test_home(), expr_str)
}
#[allow(dead_code)]
pub fn uniq_expr(
expr_str: &str,
) -> (
@ -129,7 +77,6 @@ pub fn uniq_expr(
uniq_expr_with(&Bump::new(), expr_str, declared_idents)
}
#[allow(dead_code)]
pub fn uniq_expr_with(
arena: &Bump,
expr_str: &str,
@ -241,32 +188,6 @@ pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut
&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(
&roc_constrain::expr::Env {
rigids: ImMap::default(),
@ -317,6 +238,35 @@ pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut
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 {
loc_expr,
output,
@ -328,162 +278,3 @@ pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut
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);
}
}
}
}

View file

@ -3,8 +3,9 @@
/// into an Expr when added directly by can::builtins
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum LowLevel {
/// List.len
ListLen,
ListGetUnsafe,
ListSetUnsafe,
Eq,
NotEq,
And,

View file

@ -684,11 +684,13 @@ define_builtins! {
11 LIST_LEN_ARG: "len#list"
12 LIST_FOLDL: "foldl"
13 LIST_FOLDR: "foldr"
14 LIST_GET_UNSAFE: "getUnsafe"
15 LIST_CONCAT: "concat"
16 LIST_FIRST: "first"
17 LIST_FIRST_ARG: "first#list"
18 LIST_SINGLE: "single"
14 LIST_CONCAT: "concat"
15 LIST_FIRST: "first"
16 LIST_FIRST_ARG: "first#list"
17 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" => {
0 RESULT_RESULT: "Result" imported // the Result.Result type alias

View file

@ -50,10 +50,7 @@ impl<'a> Layout<'a> {
match content {
var @ FlexVar(_) | var @ RigidVar(_) => {
panic!(
"Layout::new encountered an unresolved {:?} - subs was {:?}",
var, subs
);
panic!("Layout::new encountered an unresolved {:?}", var);
}
Structure(flat_type) => layout_from_flat_type(arena, flat_type, subs, pointer_size),

View file

@ -1,7 +1,7 @@
#[macro_use]
extern crate pretty_assertions;
// #[macro_use]
// extern crate indoc;
#[macro_use]
extern crate indoc;
extern crate bumpalo;
extern crate roc_mono;
@ -80,6 +80,20 @@ mod test_mono {
compiles_to("0.5", Float(0.5));
}
#[test]
fn apply_identity() {
compiles_to(
indoc!(
r#"
identity = \a -> a
identity 5
"#
),
Int(5),
);
}
#[test]
fn float_addition() {
compiles_to(
@ -201,13 +215,13 @@ mod test_mono {
Store(
&[(
gen_symbol_0,
Layout::Builtin(layout::Builtin::Bool),
Layout::Builtin(layout::Builtin::Int1),
Expr::Bool(true),
)],
&Cond {
cond_symbol: gen_symbol_0,
branch_symbol: gen_symbol_0,
cond_layout: Builtin(Bool),
cond_layout: Builtin(Int1),
pass: (&[] as &[_], &Expr::Str("bar")),
fail: (&[] as &[_], &Expr::Str("foo")),
ret_layout: Builtin(Str),
@ -239,26 +253,26 @@ mod test_mono {
Store(
&[(
gen_symbol_0,
Layout::Builtin(layout::Builtin::Bool),
Layout::Builtin(layout::Builtin::Int1),
Expr::Bool(true),
)],
&Cond {
cond_symbol: gen_symbol_0,
branch_symbol: gen_symbol_0,
cond_layout: Builtin(Bool),
cond_layout: Builtin(Int1),
pass: (&[] as &[_], &Expr::Str("bar")),
fail: (
&[] as &[_],
&Store(
&[(
gen_symbol_1,
Layout::Builtin(layout::Builtin::Bool),
Layout::Builtin(layout::Builtin::Int1),
Expr::Bool(false),
)],
&Cond {
cond_symbol: gen_symbol_1,
branch_symbol: gen_symbol_1,
cond_layout: Builtin(Bool),
cond_layout: Builtin(Int1),
pass: (&[] as &[_], &Expr::Str("foo")),
fail: (&[] as &[_], &Expr::Str("baz")),
ret_layout: Builtin(Str),
@ -297,13 +311,13 @@ mod test_mono {
Store(
&[(
gen_symbol_0,
Layout::Builtin(layout::Builtin::Bool),
Layout::Builtin(layout::Builtin::Int1),
Expr::Bool(true),
)],
&Cond {
cond_symbol: gen_symbol_0,
branch_symbol: gen_symbol_0,
cond_layout: Builtin(Bool),
cond_layout: Builtin(Int1),
pass: (&[] as &[_], &Expr::Str("bar")),
fail: (&[] as &[_], &Expr::Str("foo")),
ret_layout: Builtin(Str),
@ -443,7 +457,7 @@ mod test_mono {
let home = test_home();
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);
@ -467,7 +481,7 @@ mod test_mono {
let home = test_home();
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);
@ -493,7 +507,7 @@ mod test_mono {
let var_x = interns.symbol(home, "x".into());
// 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);
@ -504,9 +518,9 @@ mod test_mono {
#[test]
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 {
name: Symbol::LIST_GET_UNSAFE,
name: Symbol::LIST_GET,
layout: Layout::FunctionPointer(
&[
Layout::Builtin(Builtin::List(&Layout::Builtin(Builtin::Int64))),

View file

@ -13,6 +13,7 @@ mod helpers;
mod test_opt {
use crate::helpers::{infer_expr, uniq_expr};
use bumpalo::Bump;
use roc_module::low_level::LowLevel;
use roc_module::symbol::Symbol;
use roc_mono::expr::Expr::{self, *};
use roc_mono::expr::Procs;
@ -86,7 +87,7 @@ mod test_opt {
| Byte(_)
| Load(_)
| FunctionPointer(_, _)
| RunLowLevel(_)
| RunLowLevel(_, _)
| RuntimeError(_)
| RuntimeErrorFunction(_) => (),
@ -234,16 +235,9 @@ mod test_opt {
// This should optimize List.set to List.set_in_place
compiles_to(
"List.getUnsafe (List.set [ 12, 9, 7, 3 ] 1 42) 1",
CallByName {
name: Symbol::LIST_GET_UNSAFE,
layout: Layout::FunctionPointer(
&[
Layout::Builtin(Builtin::List(&Layout::Builtin(Builtin::Int64))),
Layout::Builtin(Builtin::Int64),
],
&Layout::Builtin(Builtin::Int64),
),
args: &vec![
RunLowLevel(
LowLevel::ListGetUnsafe,
&vec![
(
CallByName {
name: Symbol::LIST_SET_IN_PLACE,
@ -275,7 +269,7 @@ mod test_opt {
),
(Int(1), Layout::Builtin(Builtin::Int64)),
],
},
),
);
}
@ -293,7 +287,9 @@ mod test_opt {
{ 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 */
],
);
}
}