Use helpers in can builtins

This commit is contained in:
Richard Feldman 2020-04-25 07:51:45 -04:00
parent d0da4bf926
commit a399614824

View file

@ -1,11 +1,12 @@
use crate::def::Def; use crate::def::Def;
use crate::expr::Expr;
use crate::expr::Recursive; use crate::expr::Recursive;
use roc_collections::all::SendMap; use roc_collections::all::SendMap;
use roc_module::ident::TagName; use roc_module::ident::TagName;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_parse::operator::CalledVia; use roc_parse::operator::CalledVia;
use roc_region::all::{Located, Region}; use roc_region::all::{Located, Region};
use roc_types::subs::VarStore; use roc_types::subs::{VarStore, Variable};
/// Some builtins cannot be constructed in code gen alone, and need to be defined /// Some builtins cannot be constructed in code gen alone, and need to be defined
/// as separate Roc defs. For example, List.get has this type: /// as separate Roc defs. For example, List.get has this type:
@ -44,77 +45,63 @@ fn list_get(var_store: &VarStore) -> Def {
), ),
]; ];
// Perform a bounds check. If it passes, delegate to List.getUnsafe // Perform a bounds check. If it passes, delegate to List.#getUnsafe
let body = If { let body = If {
cond_var: var_store.fresh(), cond_var: var_store.fresh(),
branch_var: var_store.fresh(), branch_var: var_store.fresh(),
branches: vec![( branches: vec![(
// if-condition // if-condition
Located { no_region(
region: Region::zero(), // index < List.len list
value: Call( call(
Box::new(( Symbol::NUM_LT,
var_store.fresh(), vec![
Located { Var(Symbol::LIST_GET_ARG_INDEX),
region: Region::zero(), call(
value: Var(Symbol::LIST_IS_EMPTY), // TODO actually check if (index < List.len list) Symbol::LIST_LEN,
}, vec![Var(Symbol::LIST_GET_ARG_LIST)],
var_store.fresh(), var_store,
)), ),
vec![( ],
var_store.fresh(), var_store,
Located {
region: Region::zero(),
value: Var(Symbol::LIST_GET_ARG_LIST),
},
)],
CalledVia::Space,
), ),
}, ),
// then-branch // then-branch
Located { no_region(
region: Region::zero(),
value:
// Ok // Ok
Tag { tag(
variant_var: var_store.fresh(), "Ok",
ext_var: var_store.fresh(), // TODO Variable::EMPTY_TAG_UNION should work here vec![
name: TagName::Global("Ok".into()), // List.getUnsafe list index
arguments: vec![( Call(
var_store.fresh(), Box::new((
no_region( var_store.fresh(),
// List.getUnsafe list index no_region(Var(Symbol::LIST_GET_UNSAFE)),
Call( var_store.fresh(),
Box::new((var_store.fresh(), no_region(Var(Symbol::LIST_GET_UNSAFE)), var_store.fresh())), )),
vec![ vec![
(var_store.fresh(), no_region(Var(Symbol::LIST_GET_ARG_LIST))), (var_store.fresh(), no_region(Var(Symbol::LIST_GET_ARG_LIST))),
(var_store.fresh(), no_region(Var(Symbol::LIST_GET_ARG_INDEX))), (
], var_store.fresh(),
CalledVia::Space, no_region(Var(Symbol::LIST_GET_ARG_INDEX)),
) ),
) ],
)] CalledVia::Space,
}, ),
}, ],
var_store,
),
),
)], )],
final_else: Box::new( final_else: Box::new(
// else-branch // else-branch
no_region( no_region(
// Err // Err
Tag { tag(
variant_var: var_store.fresh(), "Err",
ext_var: var_store.fresh(), vec![tag("OutOfBounds", Vec::new(), var_store)],
name: TagName::Global("Err".into()), var_store,
arguments: vec![( ),
var_store.fresh(),
no_region(Tag {
variant_var: var_store.fresh(),
ext_var: var_store.fresh(),
name: TagName::Global("OutOfBounds".into()),
arguments: std::vec::Vec::new(),
}),
)],
},
), ),
), ),
}; };
@ -147,79 +134,47 @@ fn list_first(var_store: &VarStore) -> Def {
)]; )];
// Perform a bounds check. If it passes, delegate to List.getUnsafe. // Perform a bounds check. If it passes, delegate to List.getUnsafe.
let body = let body = If {
// Use "when" instead of "if" so that we can have False be the first branch. // 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. // We want that for branch prediction; usually we expect the list to be nonempty.
If {
cond_var: var_store.fresh(), cond_var: var_store.fresh(),
branch_var: var_store.fresh(), branch_var: var_store.fresh(),
branches: vec![( branches: vec![(
// if-condition // if-condition
Located { no_region(
region: Region::zero(), // List.isEmpty list
value: Call( call(
Box::new(( Symbol::LIST_IS_EMPTY,
var_store.fresh(), vec![Var(Symbol::LIST_FIRST_ARG)],
Located { var_store,
region: Region::zero(),
value: Var(Symbol::LIST_IS_EMPTY),
},
var_store.fresh(),
)),
vec![(
var_store.fresh(),
Located {
region: Region::zero(),
value: Var(Symbol::LIST_GET_ARG_LIST),
},
)],
CalledVia::Space,
), ),
}, ),
// then-branch // then-branch
Located { no_region(
region: Region::zero(), // Err OutOfBounds
value: tag(
// Ok "Err",
Tag { vec![tag("OutOfBounds", Vec::new(), var_store)],
variant_var: var_store.fresh(), var_store,
ext_var: var_store.fresh(), ),
name: TagName::Global("Ok".into()), ),
arguments: vec![(
var_store.fresh(),
no_region(
// 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,
)
)
)]
},
},
)], )],
final_else: Box::new( final_else: Box::new(
// default branch // default branch
no_region( no_region(
// Err // Ok (List.#getUnsafe list 0)
Tag { tag(
variant_var: var_store.fresh(), "Ok",
ext_var: var_store.fresh(), vec![
name: TagName::Global("Err".into()), // List.#getUnsafe list 0
arguments: vec![( call(
var_store.fresh(), Symbol::LIST_GET_UNSAFE,
no_region(Tag { vec![(Var(Symbol::LIST_FIRST_ARG)), (Int(var_store.fresh(), 0))],
variant_var: var_store.fresh(), var_store,
ext_var: var_store.fresh(), ),
name: TagName::Global("OutOfBounds".into()), ],
arguments: std::vec::Vec::new(), var_store,
}), ),
)],
},
), ),
), ),
}; };
@ -248,3 +203,31 @@ fn no_region<T>(value: T) -> Located<T> {
value, value,
} }
} }
#[inline(always)]
fn tag(name: &'static str, args: Vec<Expr>, var_store: &VarStore) -> Expr {
Expr::Tag {
variant_var: var_store.fresh(),
ext_var: var_store.fresh(), // TODO Variable::EMPTY_TAG_UNION should work here
name: TagName::Global(name.into()),
arguments: args
.into_iter()
.map(|expr| (var_store.fresh(), no_region(expr)))
.collect::<Vec<(Variable, Located<Expr>)>>(),
}
}
#[inline(always)]
fn call(symbol: Symbol, args: Vec<Expr>, var_store: &VarStore) -> Expr {
Expr::Call(
Box::new((
var_store.fresh(),
no_region(Expr::Var(symbol)),
var_store.fresh(),
)),
args.into_iter()
.map(|expr| (var_store.fresh(), no_region(expr)))
.collect::<Vec<(Variable, Located<Expr>)>>(),
CalledVia::Space,
)
}