diff --git a/compiler/can/src/builtins.rs b/compiler/can/src/builtins.rs index 115ff93b0f..0bd716b279 100644 --- a/compiler/can/src/builtins.rs +++ b/compiler/can/src/builtins.rs @@ -1,5 +1,7 @@ +use crate::def::Def; use crate::expr::{Expr, Recursive}; -use roc_collections::all::MutMap; +use crate::pattern::Pattern; +use roc_collections::all::{MutMap, SendMap}; use roc_module::ident::TagName; use roc_module::low_level::LowLevel; use roc_module::operator::CalledVia; @@ -24,10 +26,13 @@ use roc_types::subs::{VarStore, Variable}; /// delegates to the compiler-internal List.getUnsafe function to do the actual /// lookup (if the bounds check passed). That internal function is hardcoded in code gen, /// which works fine because it doesn't involve any open tag unions. -pub fn builtin_defs(var_store: &mut VarStore) -> MutMap { +pub fn builtin_defs(var_store: &mut VarStore) -> MutMap { mut_map! { Symbol::BOOL_EQ => bool_eq(var_store), Symbol::BOOL_NEQ => bool_neq(var_store), + Symbol::BOOL_AND => bool_and(var_store), + Symbol::BOOL_OR => bool_or(var_store), + Symbol::BOOL_NOT => bool_not(var_store), Symbol::LIST_LEN => list_len(var_store), Symbol::LIST_GET => list_get(var_store), Symbol::LIST_FIRST => list_first(var_store), @@ -47,49 +52,109 @@ pub fn builtin_defs(var_store: &mut VarStore) -> MutMap { } /// Bool.isEq : val, val -> Bool -fn bool_eq(var_store: &mut VarStore) -> Expr { +fn bool_eq(var_store: &mut VarStore) -> Def { use crate::expr::Expr::*; let body = RunLowLevel { op: LowLevel::Eq, args: vec![ - (var_store.fresh(), Var(Symbol::BOOL_EQ_LHS)), - (var_store.fresh(), Var(Symbol::BOOL_EQ_RHS)), + (var_store.fresh(), Var(Symbol::BOOL_BINOP_LHS)), + (var_store.fresh(), Var(Symbol::BOOL_BINOP_RHS)), ], ret_var: var_store.fresh(), }; defn( Symbol::BOOL_EQ, - vec![Symbol::BOOL_EQ_LHS, Symbol::BOOL_EQ_RHS], + vec![Symbol::BOOL_BINOP_LHS, Symbol::BOOL_BINOP_RHS], var_store, body, ) } /// Bool.isNotEq : val, val -> Bool -fn bool_neq(var_store: &mut VarStore) -> Expr { +fn bool_neq(var_store: &mut VarStore) -> Def { use crate::expr::Expr::*; let body = RunLowLevel { op: LowLevel::NotEq, args: vec![ - (var_store.fresh(), Var(Symbol::BOOL_EQ_LHS)), - (var_store.fresh(), Var(Symbol::BOOL_EQ_RHS)), + (var_store.fresh(), Var(Symbol::BOOL_BINOP_LHS)), + (var_store.fresh(), Var(Symbol::BOOL_BINOP_RHS)), ], ret_var: var_store.fresh(), }; defn( Symbol::BOOL_NEQ, - vec![Symbol::BOOL_EQ_LHS, Symbol::BOOL_EQ_RHS], + vec![Symbol::BOOL_BINOP_LHS, Symbol::BOOL_BINOP_RHS], + var_store, + body, + ) +} + +/// Bool.or : val, val -> Bool +fn bool_or(var_store: &mut VarStore) -> Def { + use crate::expr::Expr::*; + + let body = RunLowLevel { + op: LowLevel::Or, + args: vec![ + (var_store.fresh(), Var(Symbol::BOOL_BINOP_LHS)), + (var_store.fresh(), Var(Symbol::BOOL_BINOP_RHS)), + ], + ret_var: var_store.fresh(), + }; + + defn( + Symbol::BOOL_OR, + vec![Symbol::BOOL_BINOP_LHS, Symbol::BOOL_BINOP_RHS], + var_store, + body, + ) +} + +/// Bool.not : Bool -> Bool +fn bool_not(var_store: &mut VarStore) -> Def { + use crate::expr::Expr::*; + + let body = RunLowLevel { + op: LowLevel::Not, + args: vec![(var_store.fresh(), Var(Symbol::BOOL_BINOP_LHS))], + ret_var: var_store.fresh(), + }; + + defn( + Symbol::BOOL_NOT, + vec![Symbol::BOOL_BINOP_LHS], + var_store, + body, + ) +} + +/// Bool.and : val, val -> Bool +fn bool_and(var_store: &mut VarStore) -> Def { + use crate::expr::Expr::*; + + let body = RunLowLevel { + op: LowLevel::And, + args: vec![ + (var_store.fresh(), Var(Symbol::BOOL_BINOP_LHS)), + (var_store.fresh(), Var(Symbol::BOOL_BINOP_RHS)), + ], + ret_var: var_store.fresh(), + }; + + defn( + Symbol::BOOL_AND, + vec![Symbol::BOOL_BINOP_LHS, Symbol::BOOL_BINOP_RHS], var_store, body, ) } /// Float.tan : Float -> Float -fn float_tan(var_store: &mut VarStore) -> Expr { +fn float_tan(var_store: &mut VarStore) -> Def { use crate::expr::Expr::*; let body = call( @@ -118,7 +183,7 @@ fn float_tan(var_store: &mut VarStore) -> Expr { } /// Float.isZero : Float -> Bool -fn float_is_zero(var_store: &mut VarStore) -> Expr { +fn float_is_zero(var_store: &mut VarStore) -> Def { use crate::expr::Expr::*; let body = call( @@ -139,7 +204,7 @@ fn float_is_zero(var_store: &mut VarStore) -> Expr { } /// Float.isNegative : Float -> Bool -fn float_is_negative(var_store: &mut VarStore) -> Expr { +fn float_is_negative(var_store: &mut VarStore) -> Def { use crate::expr::Expr::*; let body = call( @@ -160,7 +225,7 @@ fn float_is_negative(var_store: &mut VarStore) -> Expr { } /// Float.isPositive : Float -> Bool -fn float_is_positive(var_store: &mut VarStore) -> Expr { +fn float_is_positive(var_store: &mut VarStore) -> Def { use crate::expr::Expr::*; let body = call( @@ -181,7 +246,7 @@ fn float_is_positive(var_store: &mut VarStore) -> Expr { } /// Int.isNegative : Int -> Bool -fn int_is_negative(var_store: &mut VarStore) -> Expr { +fn int_is_negative(var_store: &mut VarStore) -> Def { use crate::expr::Expr::*; let body = call( @@ -199,7 +264,7 @@ fn int_is_negative(var_store: &mut VarStore) -> Expr { } /// Int.isPositive : Int -> Bool -fn int_is_positive(var_store: &mut VarStore) -> Expr { +fn int_is_positive(var_store: &mut VarStore) -> Def { use crate::expr::Expr::*; let body = call( @@ -217,7 +282,7 @@ fn int_is_positive(var_store: &mut VarStore) -> Expr { } /// Int.isZero : Int -> Bool -fn int_is_zero(var_store: &mut VarStore) -> Expr { +fn int_is_zero(var_store: &mut VarStore) -> Def { use crate::expr::Expr::*; let body = RunLowLevel { @@ -238,7 +303,7 @@ fn int_is_zero(var_store: &mut VarStore) -> Expr { } /// Int.isOdd : Int -> Bool -fn int_is_odd(var_store: &mut VarStore) -> Expr { +fn int_is_odd(var_store: &mut VarStore) -> Def { use crate::expr::Expr::*; let body = RunLowLevel { @@ -266,7 +331,7 @@ fn int_is_odd(var_store: &mut VarStore) -> Expr { } /// Int.isEven : Int -> Bool -fn int_is_even(var_store: &mut VarStore) -> Expr { +fn int_is_even(var_store: &mut VarStore) -> Def { use crate::expr::Expr::*; let body = RunLowLevel { @@ -287,7 +352,7 @@ fn int_is_even(var_store: &mut VarStore) -> Expr { } /// List.len : List * -> Int -fn list_len(var_store: &mut VarStore) -> Expr { +fn list_len(var_store: &mut VarStore) -> Def { use crate::expr::Expr::*; // Polymorphic wrapper around LowLevel::ListLen @@ -308,7 +373,7 @@ fn list_len(var_store: &mut VarStore) -> Expr { } /// List.get : List elem, Int -> Result elem [ OutOfBounds ]* -fn list_get(var_store: &mut VarStore) -> Expr { +fn list_get(var_store: &mut VarStore) -> Def { use crate::expr::Expr::*; // Perform a bounds check. If it passes, delegate to List.#getUnsafe @@ -380,7 +445,7 @@ fn list_get(var_store: &mut VarStore) -> Expr { ) } /// Int.rem : Int, Int -> Int -fn int_rem(var_store: &mut VarStore) -> Expr { +fn int_rem(var_store: &mut VarStore) -> Def { use crate::expr::Expr::*; let body = If { @@ -432,7 +497,7 @@ fn int_rem(var_store: &mut VarStore) -> Expr { } /// Int.abs : Int -> Int -fn int_abs(var_store: &mut VarStore) -> Expr { +fn int_abs(var_store: &mut VarStore) -> Def { use crate::expr::Expr::*; let body = If { @@ -466,7 +531,7 @@ fn int_abs(var_store: &mut VarStore) -> Expr { } /// Int.div : Int, Int -> Result Int [ DivByZero ]* -fn int_div(var_store: &mut VarStore) -> Expr { +fn int_div(var_store: &mut VarStore) -> Def { use crate::expr::Expr::*; let body = If { @@ -527,7 +592,7 @@ fn int_div(var_store: &mut VarStore) -> Expr { } /// List.first : List elem -> Result elem [ ListWasEmpty ]* -fn list_first(var_store: &mut VarStore) -> Expr { +fn list_first(var_store: &mut VarStore) -> Def { use crate::expr::Expr::*; // Perform a bounds check. If it passes, delegate to List.getUnsafe. @@ -621,7 +686,7 @@ fn call(symbol: Symbol, args: Vec, var_store: &mut VarStore) -> Expr { } #[inline(always)] -fn defn(fn_name: Symbol, args: Vec, var_store: &mut VarStore, body: Expr) -> Expr { +fn defn(fn_name: Symbol, args: Vec, var_store: &mut VarStore, body: Expr) -> Def { use crate::expr::Expr::*; use crate::pattern::Pattern::*; @@ -630,11 +695,27 @@ fn defn(fn_name: Symbol, args: Vec, var_store: &mut VarStore, body: Expr .map(|symbol| (var_store.fresh(), no_region(Identifier(symbol)))) .collect(); - Closure( + let expr = Closure( var_store.fresh(), fn_name, Recursive::NotRecursive, closure_args, Box::new((no_region(body), var_store.fresh())), - ) + ); + + let annotation = None; // TODO + + Def { + loc_pattern: Located { + region: Region::zero(), + value: Pattern::Identifier(fn_name), + }, + loc_expr: Located { + region: Region::zero(), + value: expr, + }, + expr_var: var_store.fresh(), + pattern_vars: SendMap::default(), + annotation, + } } diff --git a/compiler/gen/src/llvm/build.rs b/compiler/gen/src/llvm/build.rs index cab3d1e696..528252b32d 100644 --- a/compiler/gen/src/llvm/build.rs +++ b/compiler/gen/src/llvm/build.rs @@ -287,66 +287,23 @@ pub fn build_expr<'a, 'ctx, 'env>( build_expr(env, layout_ids, &scope, parent, ret) } - CallByName { name, layout, args } => match *name { - Symbol::BOOL_OR => { - // The (||) operator - debug_assert!(args.len() == 2); + CallByName { name, layout, args } => { + let mut arg_tuples: Vec<(BasicValueEnum, &'a Layout<'a>)> = + Vec::with_capacity_in(args.len(), env.arena); - let comparison = - build_expr(env, layout_ids, scope, parent, &args[0].0).into_int_value(); - let build_then = || env.context.bool_type().const_int(true as u64, false).into(); - let build_else = || build_expr(env, layout_ids, scope, parent, &args[1].0); - - let ret_type = env.context.bool_type().into(); - - build_basic_phi2(env, parent, comparison, build_then, build_else, ret_type) + for (arg, arg_layout) in args.iter() { + arg_tuples.push((build_expr(env, layout_ids, scope, parent, arg), arg_layout)); } - Symbol::BOOL_AND => { - // The (&&) operator - debug_assert!(args.len() == 2); - let comparison = - build_expr(env, layout_ids, scope, parent, &args[0].0).into_int_value(); - let build_then = || build_expr(env, layout_ids, scope, parent, &args[1].0); - let build_else = || { - env.context - .bool_type() - .const_int(false as u64, false) - .into() - }; - - let ret_type = env.context.bool_type().into(); - - build_basic_phi2(env, parent, comparison, build_then, build_else, ret_type) - } - Symbol::BOOL_NOT => { - // The (!) operator - debug_assert!(args.len() == 1); - - let arg = build_expr(env, layout_ids, scope, parent, &args[0].0); - - let int_val = env.builder.build_not(arg.into_int_value(), "bool_not"); - - BasicValueEnum::IntValue(int_val) - } - _ => { - let mut arg_tuples: Vec<(BasicValueEnum, &'a Layout<'a>)> = - Vec::with_capacity_in(args.len(), env.arena); - - for (arg, arg_layout) in args.iter() { - arg_tuples.push((build_expr(env, layout_ids, scope, parent, arg), arg_layout)); - } - - call_with_args( - env, - layout_ids, - layout, - *name, - parent, - arg_tuples.into_bump_slice(), - ) - } - }, + call_with_args( + env, + layout_ids, + layout, + *name, + parent, + arg_tuples.into_bump_slice(), + ) + } FunctionPointer(symbol, layout) => { let fn_name = layout_ids .get(*symbol, layout) @@ -1425,7 +1382,13 @@ fn call_with_args<'a, 'ctx, 'env>( let fn_val = env .module .get_function(fn_name.as_str()) - .unwrap_or_else(|| panic!("Unrecognized function: {:?}", symbol)); + .unwrap_or_else(|| { + if symbol.is_builtin() { + panic!("Unrecognized builtin function: {:?}", symbol) + } else { + panic!("Unrecognized non-builtin function: {:?}", symbol) + } + }); let mut arg_vals: Vec = Vec::with_capacity_in(args.len(), env.arena); @@ -1705,5 +1668,42 @@ fn run_low_level<'a, 'ctx, 'env>( build_neq(env, lhs_arg, rhs_arg, lhs_layout, rhs_layout) } + And => { + // The (&&) operator + debug_assert_eq!(args.len(), 2); + + let lhs_arg = build_expr(env, layout_ids, scope, parent, &args[0].0); + let rhs_arg = build_expr(env, layout_ids, scope, parent, &args[1].0); + let bool_val = env.builder.build_and( + lhs_arg.into_int_value(), + rhs_arg.into_int_value(), + "bool_and", + ); + + BasicValueEnum::IntValue(bool_val) + } + Or => { + // The (||) operator + debug_assert_eq!(args.len(), 2); + + let lhs_arg = build_expr(env, layout_ids, scope, parent, &args[0].0); + let rhs_arg = build_expr(env, layout_ids, scope, parent, &args[1].0); + let bool_val = env.builder.build_or( + lhs_arg.into_int_value(), + rhs_arg.into_int_value(), + "bool_or", + ); + + BasicValueEnum::IntValue(bool_val) + } + Not => { + // The (!) operator + debug_assert_eq!(args.len(), 1); + + let arg = build_expr(env, layout_ids, scope, parent, &args[0].0); + let bool_val = env.builder.build_not(arg.into_int_value(), "bool_not"); + + BasicValueEnum::IntValue(bool_val) + } } } diff --git a/compiler/gen/tests/gen_tags.rs b/compiler/gen/tests/gen_tags.rs index 1e04a956ca..96ae1a3477 100644 --- a/compiler/gen/tests/gen_tags.rs +++ b/compiler/gen/tests/gen_tags.rs @@ -13,7 +13,7 @@ mod helpers; #[cfg(test)] mod gen_tags { - 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; @@ -213,10 +213,9 @@ mod gen_tags { #[test] fn even_odd() { - with_larger_debug_stack(|| { - assert_evals_to!( - indoc!( - r#" + assert_evals_to!( + indoc!( + r#" even = \n -> when n is 0 -> True @@ -231,11 +230,10 @@ mod gen_tags { odd 5 && even 42 "# - ), - true, - bool - ); - }) + ), + true, + bool + ); } #[test] diff --git a/compiler/gen/tests/helpers/mod.rs b/compiler/gen/tests/helpers/mod.rs index 8c58841d7c..e57cab6b5e 100644 --- a/compiler/gen/tests/helpers/mod.rs +++ b/compiler/gen/tests/helpers/mod.rs @@ -247,23 +247,11 @@ pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut // since we aren't using modules here. let builtin_defs = roc_can::builtins::builtin_defs(&mut var_store); - for (symbol, expr) in builtin_defs { + for (symbol, def) in builtin_defs { if output.references.lookups.contains(&symbol) || output.references.calls.contains(&symbol) { with_builtins = Expr::LetNonRec( - Box::new(Def { - loc_pattern: Located { - region: Region::zero(), - value: Pattern::Identifier(symbol), - }, - loc_expr: Located { - region: Region::zero(), - value: expr, - }, - expr_var: var_store.fresh(), - pattern_vars: SendMap::default(), - annotation: None, - }), + Box::new(def), Box::new(Located { region: Region::zero(), value: with_builtins, diff --git a/compiler/module/src/low_level.rs b/compiler/module/src/low_level.rs index fb2cc1e510..a4a60e70d0 100644 --- a/compiler/module/src/low_level.rs +++ b/compiler/module/src/low_level.rs @@ -7,4 +7,7 @@ pub enum LowLevel { ListLen, Eq, NotEq, + And, + Or, + Not, } diff --git a/compiler/module/src/symbol.rs b/compiler/module/src/symbol.rs index fec2f08ea3..62385cd107 100644 --- a/compiler/module/src/symbol.rs +++ b/compiler/module/src/symbol.rs @@ -661,8 +661,8 @@ define_builtins! { 4 BOOL_XOR: "xor" 5 BOOL_EQ: "isEq" 6 BOOL_NEQ: "isNotEq" - 7 BOOL_EQ_LHS: "eq#lhs" - 8 BOOL_EQ_RHS: "eq#rhs" + 7 BOOL_BINOP_LHS: "bool#lhs" + 8 BOOL_BINOP_RHS: "bool#rhs" } 5 STR: "Str" => { 0 STR_STR: "Str" imported // the Str.Str type alias