mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 06:14:46 +00:00
Attempt a mono/ approach for List.get
This commit is contained in:
parent
5992b42ebe
commit
f0b76f41da
2 changed files with 147 additions and 5 deletions
|
@ -480,7 +480,7 @@ fn from_can<'a>(
|
||||||
Expr::FunctionPointer(symbol)
|
Expr::FunctionPointer(symbol)
|
||||||
}
|
}
|
||||||
|
|
||||||
Call(boxed, loc_args, _) => {
|
Call(boxed, mut loc_args, _) => {
|
||||||
use IntOrFloat::*;
|
use IntOrFloat::*;
|
||||||
|
|
||||||
let (fn_var, loc_expr, ret_var) = *boxed;
|
let (fn_var, loc_expr, ret_var) = *boxed;
|
||||||
|
@ -555,6 +555,106 @@ fn from_can<'a>(
|
||||||
};
|
};
|
||||||
|
|
||||||
match from_can(env, loc_expr.value, procs, None) {
|
match from_can(env, loc_expr.value, procs, None) {
|
||||||
|
Expr::Load(Symbol::LIST_GET) => {
|
||||||
|
// Expand to a bounds check around List.getUnsafe.
|
||||||
|
//
|
||||||
|
// We do this during monomorphization instead of code gen
|
||||||
|
// because it returns an open union around `Err` and
|
||||||
|
// we discard tag names prior to code gen. This means that
|
||||||
|
// (for example) if this `Err` ends up unifying to
|
||||||
|
// [ Foo, OutOfBounds ], then code gen won't know what
|
||||||
|
// integer discriminant OutOfBounds corresponds to anymore,
|
||||||
|
// so it won't know what integer to return.
|
||||||
|
//
|
||||||
|
// At this stage we still have tag names to work with,
|
||||||
|
// so we can return Err OutOfBounds and let the subsequent
|
||||||
|
// stages turn it into the appropriate integer discriminant.
|
||||||
|
|
||||||
|
let arena = env.arena;
|
||||||
|
let list_symbol = env.fresh_symbol();
|
||||||
|
|
||||||
|
// We need owned versions of the arguments' Expr values
|
||||||
|
// for later. We obtain them using .drain() because the
|
||||||
|
// alternative of calling .remove(0) twice would result in
|
||||||
|
// wasted work shifting around the remaining Vec elements.
|
||||||
|
let ((list_expr, list_layout), (index_expr, index_layout)) = {
|
||||||
|
let mut opt_arg0 = None;
|
||||||
|
let mut opt_arg1 = None;
|
||||||
|
|
||||||
|
for (index, (var, loc_expr)) in loc_args.drain(..).enumerate() {
|
||||||
|
let mono_expr = from_can(env, loc_expr.value, procs, None);
|
||||||
|
let layout =
|
||||||
|
Layout::from_var(env.arena, var, env.subs, env.pointer_size)
|
||||||
|
.expect("invalid arg layout when converting List.get");
|
||||||
|
|
||||||
|
match index {
|
||||||
|
0 => opt_arg0 = Some((mono_expr, layout)),
|
||||||
|
1 => opt_arg1 = Some((mono_expr, layout)),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(opt_arg0.unwrap(), opt_arg1.unwrap())
|
||||||
|
};
|
||||||
|
let ret_layout =
|
||||||
|
Layout::from_var(env.arena, ret_var, env.subs, env.pointer_size)
|
||||||
|
.expect("invalid ret_layout");
|
||||||
|
let cond = Expr::CallByName(
|
||||||
|
Symbol::LIST_IS_EMPTY, // TODO FIXME proper bounds check instead of just isEmpty
|
||||||
|
bumpalo::vec![in arena; (Expr::Load(list_symbol), list_layout.clone())]
|
||||||
|
.into_bump_slice(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let (ok_layout, err_layout) = match ret_layout {
|
||||||
|
// Err comes before Ok because they're sorted alphabetically
|
||||||
|
Layout::Union([[_err_discrim, err_layout], [_ok_discrim, ok_layout]]) => {
|
||||||
|
(ok_layout, err_layout)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
unreachable!("Invalid ret_layout for Result");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let then_branch = {
|
||||||
|
let payload_expr = Expr::CallByName(
|
||||||
|
Symbol::LIST_GET_UNSAFE,
|
||||||
|
bumpalo::vec![in arena;
|
||||||
|
(Expr::Load(list_symbol), list_layout.clone()),
|
||||||
|
(index_expr, index_layout),
|
||||||
|
]
|
||||||
|
.into_bump_slice(),
|
||||||
|
);
|
||||||
|
|
||||||
|
build_ok(arena, ret_layout.clone(), payload_expr, ok_layout.clone())
|
||||||
|
};
|
||||||
|
|
||||||
|
let else_branch = {
|
||||||
|
// OutOfBounds is zero-sized
|
||||||
|
// TODO FIXME this isn't always zero-sized! Could be an int if union is nonempty.
|
||||||
|
let payload_expr = Expr::Struct(&[]);
|
||||||
|
|
||||||
|
build_err(arena, ret_layout.clone(), payload_expr, err_layout.clone())
|
||||||
|
};
|
||||||
|
|
||||||
|
let branch_symbol = env.fresh_symbol();
|
||||||
|
let cond_expr = Expr::Cond {
|
||||||
|
cond_symbol: branch_symbol,
|
||||||
|
branch_symbol,
|
||||||
|
cond_layout: Layout::Builtin(Builtin::Bool),
|
||||||
|
pass: (&[], arena.alloc(then_branch)),
|
||||||
|
fail: (&[], arena.alloc(else_branch)),
|
||||||
|
ret_layout,
|
||||||
|
};
|
||||||
|
|
||||||
|
Expr::Store(
|
||||||
|
bumpalo::vec![in &arena;
|
||||||
|
(list_symbol, list_layout, list_expr),
|
||||||
|
(branch_symbol, Layout::Builtin(Builtin::Bool), cond)
|
||||||
|
]
|
||||||
|
.into_bump_slice(),
|
||||||
|
env.arena.alloc(cond_expr),
|
||||||
|
)
|
||||||
|
}
|
||||||
Expr::Load(proc_name) => {
|
Expr::Load(proc_name) => {
|
||||||
// Some functions can potentially mutate in-place.
|
// Some functions can potentially mutate in-place.
|
||||||
// If we have one of those, switch to the in-place version if appropriate.
|
// If we have one of those, switch to the in-place version if appropriate.
|
||||||
|
@ -633,8 +733,7 @@ fn from_can<'a>(
|
||||||
};
|
};
|
||||||
|
|
||||||
expr = Expr::Store(
|
expr = Expr::Store(
|
||||||
env.arena
|
bumpalo::vec![in env.arena; (branch_symbol, Layout::Builtin(Builtin::Bool), cond)].into_bump_slice(),
|
||||||
.alloc(vec![(branch_symbol, Layout::Builtin(Builtin::Bool), cond)]),
|
|
||||||
env.arena.alloc(cond_expr),
|
env.arena.alloc(cond_expr),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1701,3 +1800,45 @@ pub fn specialize_equality<'a>(
|
||||||
arena.alloc([(lhs, layout.clone()), (rhs, layout.clone())]),
|
arena.alloc([(lhs, layout.clone()), (rhs, layout.clone())]),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Discriminant is always 0, and Err is alphabetically before Ok
|
||||||
|
const ERR_TAG_ID: u8 = 1;
|
||||||
|
const OK_TAG_ID: u8 = 2;
|
||||||
|
|
||||||
|
fn build_ok<'a>(
|
||||||
|
arena: &'a Bump,
|
||||||
|
result_layout: Layout<'a>,
|
||||||
|
payload_expr: Expr<'a>,
|
||||||
|
payload_layout: Layout<'a>,
|
||||||
|
) -> Expr<'a> {
|
||||||
|
let mut arguments = Vec::with_capacity_in(1, arena);
|
||||||
|
|
||||||
|
arguments.push((payload_expr, payload_layout));
|
||||||
|
|
||||||
|
Expr::Tag {
|
||||||
|
tag_layout: result_layout,
|
||||||
|
tag_name: TagName::Global("Ok".into()),
|
||||||
|
tag_id: OK_TAG_ID,
|
||||||
|
union_size: 2,
|
||||||
|
arguments: arguments.into_bump_slice(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_err<'a>(
|
||||||
|
arena: &'a Bump,
|
||||||
|
result_layout: Layout<'a>,
|
||||||
|
payload_expr: Expr<'a>,
|
||||||
|
payload_layout: Layout<'a>,
|
||||||
|
) -> Expr<'a> {
|
||||||
|
let mut arguments = Vec::with_capacity_in(1, arena);
|
||||||
|
|
||||||
|
arguments.push((payload_expr, payload_layout));
|
||||||
|
|
||||||
|
Expr::Tag {
|
||||||
|
tag_layout: result_layout,
|
||||||
|
tag_name: TagName::Global("Err".into()),
|
||||||
|
tag_id: ERR_TAG_ID,
|
||||||
|
union_size: 2,
|
||||||
|
arguments: arguments.into_bump_slice(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -420,10 +420,10 @@ fn union_sorted_tags_help<'a>(
|
||||||
let mut result = Vec::with_capacity_in(tags_vec.len(), arena);
|
let mut result = Vec::with_capacity_in(tags_vec.len(), arena);
|
||||||
|
|
||||||
for (tag_name, arguments) in tags_vec {
|
for (tag_name, arguments) in tags_vec {
|
||||||
// resverse space for the tag discriminant
|
// +1 to reserve space for the discriminant
|
||||||
let mut arg_layouts = Vec::with_capacity_in(arguments.len() + 1, arena);
|
let mut arg_layouts = Vec::with_capacity_in(arguments.len() + 1, arena);
|
||||||
|
|
||||||
// add the tag discriminant
|
// add the discriminant
|
||||||
arg_layouts.push(Layout::Builtin(Builtin::Int64));
|
arg_layouts.push(Layout::Builtin(Builtin::Int64));
|
||||||
|
|
||||||
for var in arguments {
|
for var in arguments {
|
||||||
|
@ -434,6 +434,7 @@ fn union_sorted_tags_help<'a>(
|
||||||
|
|
||||||
result.push((tag_name, arg_layouts.into_bump_slice()));
|
result.push((tag_name, arg_layouts.into_bump_slice()));
|
||||||
}
|
}
|
||||||
|
|
||||||
UnionVariant::Wrapped(result)
|
UnionVariant::Wrapped(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue