From 353377c29ee761719de189e2e93645fb16f16be9 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Tue, 28 Apr 2020 19:33:10 -0400 Subject: [PATCH] Re-enable List.get --- compiler/can/src/builtins.rs | 303 +++++++++++++++++------------------ 1 file changed, 145 insertions(+), 158 deletions(-) diff --git a/compiler/can/src/builtins.rs b/compiler/can/src/builtins.rs index dc4051ae00..f187dff45a 100644 --- a/compiler/can/src/builtins.rs +++ b/compiler/can/src/builtins.rs @@ -26,174 +26,134 @@ use roc_types::subs::{VarStore, Variable}; /// 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: &VarStore) -> Vec { - vec![/*list_get(var_store),*/ list_first(var_store)] + vec![list_get(var_store), list_first(var_store)] } /// List.get : List elem, Int -> Result elem [ OutOfBounds ]* -// fn list_get(var_store: &VarStore) -> Def { -// use crate::expr::Expr::*; -// use crate::pattern::Pattern::*; +fn list_get(var_store: &VarStore) -> Def { + use crate::expr::Expr::*; -// let args = vec![ -// ( -// var_store.fresh(), -// no_region(Identifier(Symbol::LIST_GET_ARG_LIST)), -// ), -// ( -// var_store.fresh(), -// no_region(Identifier(Symbol::LIST_GET_ARG_INDEX)), -// ), -// ]; - -// // Perform a bounds check. If it passes, delegate to List.#getUnsafe -// 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_GET_ARG_INDEX), -// call( -// Symbol::LIST_LEN, -// vec![Var(Symbol::LIST_GET_ARG_LIST)], -// var_store, -// ), -// ], -// var_store, -// ), -// ), -// // then-branch -// no_region( -// // Ok -// 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)), -// ), -// ], -// CalledVia::Space, -// ), -// ], -// var_store, -// ), -// ), -// )], -// final_else: Box::new( -// // else-branch -// no_region( -// // Err -// tag( -// "Err", -// vec![tag("OutOfBounds", Vec::new(), var_store)], -// var_store, -// ), -// ), -// ), -// }; - -// let expr = Closure( -// var_store.fresh(), -// Symbol::LIST_GET, -// Recursive::NotRecursive, -// args, -// Box::new((no_region(body), var_store.fresh())), -// ); - -// Def { -// loc_pattern: no_region(Identifier(Symbol::LIST_GET)), -// loc_expr: no_region(expr), -// expr_var: var_store.fresh(), -// pattern_vars: SendMap::default(), -// annotation: None, -// } -// } + defn( + Symbol::LIST_GET, + vec![Symbol::LIST_GET_ARG_LIST, Symbol::LIST_GET_ARG_INDEX], + var_store, + // Perform a bounds check. If it passes, delegate to List.#getUnsafe + 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_GET_ARG_INDEX), + call( + Symbol::LIST_LEN, + vec![Var(Symbol::LIST_GET_ARG_LIST)], + var_store, + ), + ], + var_store, + ), + ), + // then-branch + no_region( + // Ok + 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)), + ), + ], + CalledVia::Space, + ), + ], + var_store, + ), + ), + )], + final_else: Box::new( + // else-branch + no_region( + // Err + tag( + "Err", + vec![tag("OutOfBounds", Vec::new(), var_store)], + var_store, + ), + ), + ), + }, + ) +} /// List.first : List elem -> Result elem [ ListWasEmpty ]* fn list_first(var_store: &VarStore) -> Def { use crate::expr::Expr::*; - use crate::pattern::Pattern::*; - let args = vec![( - var_store.fresh(), - no_region(Identifier(Symbol::LIST_FIRST_ARG)), - )]; - - // Perform a bounds check. If it passes, delegate to List.getUnsafe. - let body = If { - // TODO Use "when" instead of "if" so that we can have False be the first branch. - // We want that for branch prediction; usually we expect the list to be nonempty. - cond_var: var_store.fresh(), - branch_var: var_store.fresh(), - branches: vec![( - // if-condition - no_region( - // List.isEmpty list - call( - Symbol::LIST_IS_EMPTY, - vec![Var(Symbol::LIST_FIRST_ARG)], - var_store, - ), - ), - // list was empty - no_region( - // Err ListWasEmpty - tag( - "Err", - vec![tag("ListWasEmpty", Vec::new(), var_store)], - var_store, - ), - ), - )], - final_else: Box::new( - // list was not empty - no_region( - // Ok (List.#getUnsafe list 0) - 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, - ), - ], - var_store, - ), - ), - ), - }; - - let expr = Closure( - var_store.fresh(), + defn( Symbol::LIST_FIRST, - Recursive::NotRecursive, - args, - Box::new((no_region(body), var_store.fresh())), - ); - - Def { - loc_pattern: no_region(Identifier(Symbol::LIST_FIRST)), - loc_expr: no_region(expr), - expr_var: var_store.fresh(), - pattern_vars: SendMap::default(), - annotation: None, - } + vec![Symbol::LIST_FIRST_ARG], + var_store, + // Perform a bounds check. If it passes, delegate to List.getUnsafe. + If { + // TODO Use "when" instead of "if" so that we can have False be the first branch. + // We want that for branch prediction; usually we expect the list to be nonempty. + cond_var: var_store.fresh(), + branch_var: var_store.fresh(), + branches: vec![( + // if-condition + no_region( + // List.isEmpty list + call( + Symbol::LIST_IS_EMPTY, + vec![Var(Symbol::LIST_FIRST_ARG)], + var_store, + ), + ), + // list was empty + no_region( + // Err ListWasEmpty + tag( + "Err", + vec![tag("ListWasEmpty", Vec::new(), var_store)], + var_store, + ), + ), + )], + final_else: Box::new( + // list was not empty + no_region( + // Ok (List.#getUnsafe list 0) + 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, + ), + ], + var_store, + ), + ), + ), + }, + ) } #[inline(always)] @@ -231,3 +191,30 @@ fn call(symbol: Symbol, args: Vec, var_store: &VarStore) -> Expr { CalledVia::Space, ) } + +#[inline(always)] +fn defn(fn_name: Symbol, args: Vec, var_store: &VarStore, body: Expr) -> Def { + use crate::expr::Expr::*; + use crate::pattern::Pattern::*; + + let closure_args = args + .into_iter() + .map(|symbol| (var_store.fresh(), no_region(Identifier(symbol)))) + .collect(); + + let expr = Closure( + var_store.fresh(), + fn_name, + Recursive::NotRecursive, + closure_args, + Box::new((no_region(body), var_store.fresh())), + ); + + Def { + loc_pattern: no_region(Identifier(fn_name)), + loc_expr: no_region(expr), + expr_var: var_store.fresh(), + pattern_vars: SendMap::default(), + annotation: None, + } +}