diff --git a/compiler/builtins/src/std.rs b/compiler/builtins/src/std.rs index fbcd54e300..86434dc388 100644 --- a/compiler/builtins/src/std.rs +++ b/compiler/builtins/src/std.rs @@ -429,14 +429,6 @@ pub fn types() -> MutMap { ), ); - 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, diff --git a/compiler/builtins/src/unique.rs b/compiler/builtins/src/unique.rs index ad90e621b2..aea4233d8b 100644 --- a/compiler/builtins/src/unique.rs +++ b/compiler/builtins/src/unique.rs @@ -541,11 +541,6 @@ pub fn types() -> MutMap { ), ); - 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 diff --git a/compiler/can/src/annotation.rs b/compiler/can/src/annotation.rs index 17148fadcb..25e8dd1463 100644 --- a/compiler/can/src/annotation.rs +++ b/compiler/can/src/annotation.rs @@ -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}; diff --git a/compiler/can/src/builtins.rs b/compiler/can/src/builtins.rs index cf82ff1eb6..05c60dbfad 100644 --- a/compiler/can/src/builtins.rs +++ b/compiler/can/src/builtins.rs @@ -52,6 +52,7 @@ pub fn builtin_defs(var_store: &mut VarStore) -> MutMap { 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, 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, var_store: &mut VarStore, body: Expr }, expr_var: var_store.fresh(), pattern_vars: SendMap::default(), - annotation, + annotation: None, } } diff --git a/compiler/can/src/module.rs b/compiler/can/src/module.rs index aa09748ee8..38b9947d4f 100644 --- a/compiler/can/src/module.rs +++ b/compiler/can/src/module.rs @@ -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, diff --git a/compiler/constrain/src/expr.rs b/compiler/constrain/src/expr.rs index 864813816f..31678dcd43 100644 --- a/compiler/constrain/src/expr.rs +++ b/compiler/constrain/src/expr.rs @@ -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. diff --git a/compiler/constrain/src/module.rs b/compiler/constrain/src/module.rs index 7a5d16729e..7730f96b1f 100644 --- a/compiler/constrain/src/module.rs +++ b/compiler/constrain/src/module.rs @@ -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() { diff --git a/compiler/gen/src/llvm/build.rs b/compiler/gen/src/llvm/build.rs index 528252b32d..95a09d6106 100644 --- a/compiler/gen/src/llvm/build.rs +++ b/compiler/gen/src/llvm/build.rs @@ -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"); + } } } diff --git a/compiler/gen/tests/gen_builtins.rs b/compiler/gen/tests/gen_builtins.rs index 72622d2732..5f2b9d9313 100644 --- a/compiler/gen/tests/gen_builtins.rs +++ b/compiler/gen/tests/gen_builtins.rs @@ -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,17 +95,15 @@ mod gen_builtins { #[test] fn gen_add_f64() { - with_larger_debug_stack(|| { - assert_evals_to!( - indoc!( - r#" + assert_evals_to!( + indoc!( + r#" 1.1 + 2.4 + 3 "# - ), - 6.5, - f64 - ); - }) + ), + 6.5, + f64 + ); } #[test] @@ -149,17 +147,15 @@ mod gen_builtins { #[test] fn gen_add_i64() { - with_larger_debug_stack(|| { - assert_evals_to!( - indoc!( - r#" + assert_evals_to!( + indoc!( + r#" 1 + 2 + 3 "# - ), - 6, - i64 - ); - }) + ), + 6, + i64 + ); } #[test] @@ -451,22 +447,20 @@ mod gen_builtins { } #[test] fn tail_call_elimination() { - with_larger_debug_stack(|| { - assert_evals_to!( - indoc!( - r#" - sum = \n, accum -> - when n is - 0 -> accum - _ -> sum (n - 1) (n + accum) + assert_evals_to!( + indoc!( + r#" + sum = \n, accum -> + when n is + 0 -> accum + _ -> sum (n - 1) (n + accum) - sum 1_000_000 0 + sum 1_000_000 0 "# - ), - 500000500000, - i64 - ); - }) + ), + 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); - }) + 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); - }) + assert_evals_to!("List.len [ 12, 9, 6, 3 ]", 4, usize); } #[test] @@ -553,43 +543,37 @@ mod gen_builtins { #[test] fn empty_list_is_empty() { - with_larger_debug_stack(|| { - assert_evals_to!("List.isEmpty []", true, bool); - }) + assert_evals_to!("List.isEmpty []", true, bool); } #[test] fn first_int_list() { - with_larger_debug_stack(|| { - assert_evals_to!( - indoc!( - r#" + assert_evals_to!( + indoc!( + r#" when List.first [ 12, 9, 6, 3 ] is Ok val -> val Err _ -> -1 "# - ), - 12, - i64 - ); - }) + ), + 12, + i64 + ); } #[test] fn first_empty_list() { - with_larger_debug_stack(|| { - assert_evals_to!( - indoc!( - r#" + assert_evals_to!( + indoc!( + r#" when List.first [] is Ok val -> val Err _ -> -1 "# - ), - -1, - i64 - ); - }) + ), + -1, + i64 + ); } #[test] @@ -711,10 +695,9 @@ mod gen_builtins { #[test] fn gen_quicksort() { - with_larger_debug_stack(|| { - assert_evals_to!( - indoc!( - r#" + assert_evals_to!( + indoc!( + r#" quicksort : List (Num a) -> List (Num a) quicksort = \list -> quicksortHelp list 0 (List.len list - 1) @@ -774,10 +757,9 @@ mod gen_builtins { quicksort [ 7, 4, 21, 19 ] "# - ), - &[4, 7, 19, 21], - &'static [i64] - ); - }) + ), + &[4, 7, 19, 21], + &'static [i64] + ); } } diff --git a/compiler/gen/tests/gen_primitives.rs b/compiler/gen/tests/gen_primitives.rs index 8923cb24f8..8b5b9037cd 100644 --- a/compiler/gen/tests/gen_primitives.rs +++ b/compiler/gen/tests/gen_primitives.rs @@ -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,30 +406,27 @@ mod gen_primitives { #[test] fn gen_chained_defs() { - with_larger_debug_stack(|| { - assert_evals_to!( - indoc!( - r#" - x = i1 - i3 = i2 - i1 = 1337 - i2 = i1 - y = 12.4 - - i3 - "# - ), - 1337, - i64 - ); - }) + assert_evals_to!( + indoc!( + r#" + x = i1 + i3 = i2 + i1 = 1337 + i2 = i1 + y = 12.4 + + i3 + "# + ), + 1337, + i64 + ); } #[test] fn gen_nested_defs() { - with_larger_debug_stack(|| { - assert_evals_to!( - indoc!( - r#" + assert_evals_to!( + indoc!( + r#" x = 5 answer = @@ -460,10 +457,9 @@ mod gen_primitives { answer "# - ), - 1337, - i64 - ); - }) + ), + 1337, + i64 + ); } } diff --git a/compiler/gen/tests/helpers/mod.rs b/compiler/gen/tests/helpers/mod.rs index e57cab6b5e..9b25d3294f 100644 --- a/compiler/gen/tests/helpers/mod.rs +++ b/compiler/gen/tests/helpers/mod.rs @@ -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(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(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, 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>, 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 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(pairs: I) -> MutMap -where - I: IntoIterator, - 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(pairs: I) -> ImMap -where - I: IntoIterator, - 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(elems: I) -> SendSet -where - I: IntoIterator, - 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 = used.clone().into(); - let mut decl: ImSet = 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, - pub flex_vars: Vec, -} - -pub fn variable_usage(con: &Constraint) -> (SeenVariables, Vec) { - 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 = 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) { - 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); - } - } - } -} diff --git a/compiler/module/src/low_level.rs b/compiler/module/src/low_level.rs index a4a60e70d0..4017f38069 100644 --- a/compiler/module/src/low_level.rs +++ b/compiler/module/src/low_level.rs @@ -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, diff --git a/compiler/module/src/symbol.rs b/compiler/module/src/symbol.rs index 62385cd107..636636a251 100644 --- a/compiler/module/src/symbol.rs +++ b/compiler/module/src/symbol.rs @@ -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 diff --git a/compiler/mono/src/layout.rs b/compiler/mono/src/layout.rs index 3842bfd9c6..acd29effbd 100644 --- a/compiler/mono/src/layout.rs +++ b/compiler/mono/src/layout.rs @@ -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), diff --git a/compiler/mono/tests/test_mono.rs b/compiler/mono/tests/test_mono.rs index b775a7f4ac..4367f602a5 100644 --- a/compiler/mono/tests/test_mono.rs +++ b/compiler/mono/tests/test_mono.rs @@ -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))), diff --git a/compiler/mono/tests/test_opt.rs b/compiler/mono/tests/test_opt.rs index cf2bbbfd74..6bf98fb9de 100644 --- a/compiler/mono/tests/test_opt.rs +++ b/compiler/mono/tests/test_opt.rs @@ -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 */ + ], ); } }