diff --git a/compiler/can/src/expr.rs b/compiler/can/src/expr.rs index 490d90e2cb..ad62f52ee5 100644 --- a/compiler/can/src/expr.rs +++ b/compiler/can/src/expr.rs @@ -1,4 +1,5 @@ use crate::annotation::IntroducedVariables; +use crate::builtins::builtin_defs; use crate::def::{can_defs_with_return, Def}; use crate::env::Env; use crate::num::{ @@ -1186,9 +1187,62 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) -> let (fn_var, loc_expr, expr_var) = *boxed_tuple; match loc_expr.value { - Var(symbol) if symbol.is_builtin() => { - todo!("Inline this builtin: {:?}", symbol); - } + Var(symbol) if symbol.is_builtin() => match builtin_defs(var_store).get(&symbol) { + Some(Closure(_var, _, recursive, params, boxed_body)) => { + debug_assert_eq!(*recursive, Recursive::NotRecursive); + + // Since this is a canonicalized Expr, we should have + // already detected any arity mismatches and replaced this + // with a RuntimeError if there was a mismatch. + debug_assert_eq!(params.len(), args.len()); + + // Start with the function's body as the answer. + let (mut loc_answer, _body_var) = *boxed_body.clone(); + + // Wrap the body in one LetNonRec for each argument, + // such that at the end we have all the arguments in + // scope with the values the caller provided. + for ((_param_var, loc_pattern), (expr_var, loc_expr)) in + params.iter().cloned().zip(args.into_iter()).rev() + { + // TODO get the correct vars into here. + // Not sure if param_var should be involved. + let pattern_vars = SendMap::default(); + + // TODO get the actual correct aliases + let aliases = SendMap::default(); + + let def = Def { + loc_pattern, + loc_expr, + expr_var, + pattern_vars, + annotation: None, + }; + + loc_answer = Located { + region: Region::zero(), + value: LetNonRec( + Box::new(def), + Box::new(loc_answer), + var_store.fresh(), + aliases, + ), + }; + } + + loc_answer.value + } + Some(_) => { + unreachable!("Tried to inline a non-function"); + } + None => { + unreachable!( + "Tried to inline a builtin that wasn't registered: {:?}", + symbol + ); + } + }, _ => { // For now, we only inline calls to builtins. Leave this alone! Call(Box::new((fn_var, loc_expr, expr_var)), args, called_via) diff --git a/compiler/can/tests/can_inline.rs b/compiler/can/tests/can_inline.rs index 7275bc971e..bcf2c944e5 100644 --- a/compiler/can/tests/can_inline.rs +++ b/compiler/can/tests/can_inline.rs @@ -14,13 +14,16 @@ mod helpers; mod can_inline { use crate::helpers::{can_expr_with, test_home}; use bumpalo::Bump; + use roc_can::def::Def; use roc_can::expr::inline_calls; use roc_can::expr::Expr::{self, *}; + use roc_can::pattern::Pattern; use roc_can::scope::Scope; + use roc_collections::all::SendMap; use roc_module::operator::CalledVia; use roc_module::symbol::Symbol; use roc_region::all::{Located, Region}; - use roc_types::subs::VarStore; + use roc_types::subs::{VarStore, Variable}; fn assert_inlines_to(input: &str, expected: Expr, var_store: &mut VarStore) { let arena = Bump::new(); @@ -34,6 +37,7 @@ mod can_inline { #[test] fn inline_list_len() { let var_store = &mut VarStore::default(); + let aliases = SendMap::default(); assert_inlines_to( indoc!( @@ -41,23 +45,55 @@ mod can_inline { Int.isZero 5 "# ), - Expr::Call( - Box::new(( - var_store.fresh(), - Located { + LetNonRec( + Box::new(Def { + loc_pattern: Located { region: Region::zero(), - value: Expr::Var(Symbol::FLOAT_EQ), + value: Pattern::Identifier(Symbol::INT_IS_ZERO_ARG), }, - var_store.fresh(), - )), - vec![( - var_store.fresh(), - Located { - region: Region::zero(), - value: Int(var_store.fresh(), 5), + pattern_vars: SendMap::default(), + loc_expr: Located { + region: Region::new(0, 0, 11, 12), + value: Num(unsafe { Variable::unsafe_test_debug_variable(7) }, 5), }, - )], - CalledVia::Space, + expr_var: unsafe { Variable::unsafe_test_debug_variable(8) }, + annotation: None, + }), + Box::new(Located { + region: Region::zero(), + value: Expr::Call( + Box::new(( + unsafe { Variable::unsafe_test_debug_variable(138) }, + Located { + region: Region::zero(), + value: Expr::Var(Symbol::INT_EQ_I64), + }, + unsafe { Variable::unsafe_test_debug_variable(139) }, + )), + vec![ + ( + unsafe { Variable::unsafe_test_debug_variable(140) }, + Located { + region: Region::zero(), + value: Var(Symbol::INT_IS_ZERO_ARG), + }, + ), + ( + unsafe { Variable::unsafe_test_debug_variable(141) }, + Located { + region: Region::zero(), + value: Int( + unsafe { Variable::unsafe_test_debug_variable(137) }, + 0, + ), + }, + ), + ], + CalledVia::Space, + ), + }), + unsafe { Variable::unsafe_test_debug_variable(198) }, + aliases, ), var_store, )