mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 06:44:46 +00:00
all higher order lowlevels have morphic specs
This commit is contained in:
parent
a36fddd27a
commit
89b850983d
1 changed files with 230 additions and 78 deletions
|
@ -561,6 +561,58 @@ fn build_tuple_type(builder: &mut impl TypeContext, layouts: &[Layout]) -> Resul
|
|||
builder.add_tuple_type(&field_types)
|
||||
}
|
||||
|
||||
#[repr(u32)]
|
||||
#[derive(Clone, Copy)]
|
||||
enum KeepResult {
|
||||
Errs = ERR_TAG_ID,
|
||||
Oks = OK_TAG_ID,
|
||||
}
|
||||
|
||||
impl KeepResult {
|
||||
fn invert(&self) -> Self {
|
||||
match self {
|
||||
KeepResult::Errs => KeepResult::Oks,
|
||||
KeepResult::Oks => KeepResult::Errs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum ResultRepr<'a> {
|
||||
Int1,
|
||||
NonRecursive { err: Layout<'a>, ok: Layout<'a> },
|
||||
}
|
||||
|
||||
impl<'a> ResultRepr<'a> {
|
||||
fn from_layout(layout: &Layout<'a>) -> Self {
|
||||
match layout {
|
||||
Layout::Union(UnionLayout::NonRecursive(tags)) => ResultRepr::NonRecursive {
|
||||
err: tags[ERR_TAG_ID as usize][0],
|
||||
ok: tags[OK_TAG_ID as usize][0],
|
||||
},
|
||||
Layout::Builtin(Builtin::Int1) => ResultRepr::Int1,
|
||||
other => unreachable!("unexpected layout: {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
fn unwrap(
|
||||
&self,
|
||||
builder: &mut FuncDefBuilder,
|
||||
block: BlockId,
|
||||
err_or_ok: ValueId,
|
||||
keep_tag_id: u32,
|
||||
) -> Result<ValueId> {
|
||||
match self {
|
||||
ResultRepr::NonRecursive { .. } => {
|
||||
let unwrapped = builder.add_unwrap_union(block, err_or_ok, keep_tag_id)?;
|
||||
|
||||
builder.add_get_tuple_field(block, unwrapped, 0)
|
||||
}
|
||||
ResultRepr::Int1 => builder.add_make_tuple(block, &[]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_loop(
|
||||
builder: &mut FuncDefBuilder,
|
||||
block: BlockId,
|
||||
|
@ -679,20 +731,24 @@ fn call_spec(
|
|||
let dict = env.symbols[xs];
|
||||
let state = env.symbols[state];
|
||||
|
||||
let bag = builder.add_get_tuple_field(block, dict, DICT_BAG_INDEX)?;
|
||||
let _cell = builder.add_get_tuple_field(block, dict, DICT_CELL_INDEX)?;
|
||||
let loop_body = |builder: &mut FuncDefBuilder, block, state| {
|
||||
let bag = builder.add_get_tuple_field(block, dict, DICT_BAG_INDEX)?;
|
||||
|
||||
let first = builder.add_bag_get(block, bag)?;
|
||||
let element = builder.add_bag_get(block, bag)?;
|
||||
|
||||
let key = builder.add_get_tuple_field(block, first, 0)?;
|
||||
let val = builder.add_get_tuple_field(block, first, 1)?;
|
||||
let key = builder.add_get_tuple_field(block, element, 0)?;
|
||||
let val = builder.add_get_tuple_field(block, element, 1)?;
|
||||
|
||||
let argument = if closure_env_layout.is_none() {
|
||||
builder.add_make_tuple(block, &[state, key, val])?
|
||||
} else {
|
||||
builder.add_make_tuple(block, &[state, key, val, closure_env])?
|
||||
let new_state = call_function!(builder, block, [state, key, val]);
|
||||
|
||||
Ok(new_state)
|
||||
};
|
||||
builder.add_call(block, spec_var, module, name, argument)?;
|
||||
|
||||
let state_layout = arg_layouts[0];
|
||||
let state_type = layout_spec(builder, &state_layout)?;
|
||||
let init_state = state;
|
||||
|
||||
add_loop(builder, block, state_type, init_state, loop_body)
|
||||
}
|
||||
|
||||
ListWalk { xs, state } | ListWalkBackwards { xs, state } => {
|
||||
|
@ -713,24 +769,31 @@ fn call_spec(
|
|||
let state_type = layout_spec(builder, &state_layout)?;
|
||||
let init_state = state;
|
||||
|
||||
return add_loop(builder, block, state_type, init_state, loop_body);
|
||||
add_loop(builder, block, state_type, init_state, loop_body)
|
||||
}
|
||||
ListWalkUntil { xs, state } => {
|
||||
let list = env.symbols[xs];
|
||||
let state = env.symbols[state];
|
||||
let closure_env = env.symbols[function_env];
|
||||
|
||||
let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
|
||||
let _cell = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?;
|
||||
let loop_body = |builder: &mut FuncDefBuilder, block, state| {
|
||||
let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
|
||||
|
||||
let first = builder.add_bag_get(block, bag)?;
|
||||
let element = builder.add_bag_get(block, bag)?;
|
||||
|
||||
let argument = if closure_env_layout.is_none() {
|
||||
builder.add_make_tuple(block, &[state, first])?
|
||||
} else {
|
||||
builder.add_make_tuple(block, &[state, first, closure_env])?
|
||||
let continue_or_stop = call_function!(builder, block, [state, element]);
|
||||
|
||||
// just assume it is a continue
|
||||
let unwrapped = builder.add_unwrap_union(block, continue_or_stop, 0)?;
|
||||
let new_state = builder.add_get_tuple_field(block, unwrapped, 0)?;
|
||||
|
||||
Ok(new_state)
|
||||
};
|
||||
builder.add_call(block, spec_var, module, name, argument)?;
|
||||
|
||||
let state_layout = arg_layouts[0];
|
||||
let state_type = layout_spec(builder, &state_layout)?;
|
||||
let init_state = state;
|
||||
|
||||
add_loop(builder, block, state_type, init_state, loop_body)
|
||||
}
|
||||
|
||||
ListMapWithIndex { xs } => {
|
||||
|
@ -754,7 +817,7 @@ fn call_spec(
|
|||
|
||||
let init_state = new_list(builder, block, output_element_type)?;
|
||||
|
||||
return add_loop(builder, block, state_type, init_state, loop_body);
|
||||
add_loop(builder, block, state_type, init_state, loop_body)
|
||||
}
|
||||
|
||||
ListMap { xs } => {
|
||||
|
@ -777,7 +840,7 @@ fn call_spec(
|
|||
|
||||
let init_state = new_list(builder, block, output_element_type)?;
|
||||
|
||||
return add_loop(builder, block, state_type, init_state, loop_body);
|
||||
add_loop(builder, block, state_type, init_state, loop_body)
|
||||
}
|
||||
|
||||
ListSortWith { xs } => {
|
||||
|
@ -802,89 +865,178 @@ fn call_spec(
|
|||
let state_type = layout_spec(builder, &state_layout)?;
|
||||
let init_state = list;
|
||||
|
||||
return add_loop(builder, block, state_type, init_state, loop_body);
|
||||
add_loop(builder, block, state_type, init_state, loop_body)
|
||||
}
|
||||
|
||||
ListMap2 { xs, ys } => {
|
||||
let list1 = env.symbols[xs];
|
||||
let list2 = env.symbols[ys];
|
||||
let closure_env = env.symbols[function_env];
|
||||
|
||||
let bag1 = builder.add_get_tuple_field(block, list1, LIST_BAG_INDEX)?;
|
||||
let _cell1 = builder.add_get_tuple_field(block, list1, LIST_CELL_INDEX)?;
|
||||
let elem1 = builder.add_bag_get(block, bag1)?;
|
||||
let loop_body = |builder: &mut FuncDefBuilder, block, state| {
|
||||
let input_bag_1 =
|
||||
builder.add_get_tuple_field(block, list1, LIST_BAG_INDEX)?;
|
||||
let input_bag_2 =
|
||||
builder.add_get_tuple_field(block, list2, LIST_BAG_INDEX)?;
|
||||
|
||||
let bag2 = builder.add_get_tuple_field(block, list2, LIST_BAG_INDEX)?;
|
||||
let _cell2 = builder.add_get_tuple_field(block, list2, LIST_CELL_INDEX)?;
|
||||
let elem2 = builder.add_bag_get(block, bag2)?;
|
||||
let element_1 = builder.add_bag_get(block, input_bag_1)?;
|
||||
let element_2 = builder.add_bag_get(block, input_bag_2)?;
|
||||
|
||||
let argument = if closure_env_layout.is_none() {
|
||||
builder.add_make_tuple(block, &[elem1, elem2])?
|
||||
} else {
|
||||
builder.add_make_tuple(block, &[elem1, elem2, closure_env])?
|
||||
let new_element = call_function!(builder, block, [element_1, element_2]);
|
||||
|
||||
list_append(builder, block, update_mode_var, state, new_element)
|
||||
};
|
||||
builder.add_call(block, spec_var, module, name, argument)?;
|
||||
|
||||
let output_element_type = layout_spec(builder, ret_layout)?;
|
||||
|
||||
let state_layout = Layout::Builtin(Builtin::List(ret_layout));
|
||||
let state_type = layout_spec(builder, &state_layout)?;
|
||||
|
||||
let init_state = new_list(builder, block, output_element_type)?;
|
||||
|
||||
add_loop(builder, block, state_type, init_state, loop_body)
|
||||
}
|
||||
|
||||
ListMap3 { xs, ys, zs } => {
|
||||
let list1 = env.symbols[xs];
|
||||
let list2 = env.symbols[ys];
|
||||
let list3 = env.symbols[zs];
|
||||
let closure_env = env.symbols[function_env];
|
||||
|
||||
let bag1 = builder.add_get_tuple_field(block, list1, LIST_BAG_INDEX)?;
|
||||
let _cell1 = builder.add_get_tuple_field(block, list1, LIST_CELL_INDEX)?;
|
||||
let elem1 = builder.add_bag_get(block, bag1)?;
|
||||
let loop_body = |builder: &mut FuncDefBuilder, block, state| {
|
||||
let input_bag_1 =
|
||||
builder.add_get_tuple_field(block, list1, LIST_BAG_INDEX)?;
|
||||
let input_bag_2 =
|
||||
builder.add_get_tuple_field(block, list2, LIST_BAG_INDEX)?;
|
||||
let input_bag_3 =
|
||||
builder.add_get_tuple_field(block, list3, LIST_BAG_INDEX)?;
|
||||
|
||||
let bag2 = builder.add_get_tuple_field(block, list2, LIST_BAG_INDEX)?;
|
||||
let _cell2 = builder.add_get_tuple_field(block, list2, LIST_CELL_INDEX)?;
|
||||
let elem2 = builder.add_bag_get(block, bag2)?;
|
||||
let element_1 = builder.add_bag_get(block, input_bag_1)?;
|
||||
let element_2 = builder.add_bag_get(block, input_bag_2)?;
|
||||
let element_3 = builder.add_bag_get(block, input_bag_3)?;
|
||||
|
||||
let bag3 = builder.add_get_tuple_field(block, list3, LIST_BAG_INDEX)?;
|
||||
let _cell3 = builder.add_get_tuple_field(block, list3, LIST_CELL_INDEX)?;
|
||||
let elem3 = builder.add_bag_get(block, bag3)?;
|
||||
let new_element =
|
||||
call_function!(builder, block, [element_1, element_2, element_3]);
|
||||
|
||||
let argument = if closure_env_layout.is_none() {
|
||||
builder.add_make_tuple(block, &[elem1, elem2, elem3])?
|
||||
} else {
|
||||
builder.add_make_tuple(block, &[elem1, elem2, elem3, closure_env])?
|
||||
list_append(builder, block, update_mode_var, state, new_element)
|
||||
};
|
||||
builder.add_call(block, spec_var, module, name, argument)?;
|
||||
|
||||
let output_element_type = layout_spec(builder, ret_layout)?;
|
||||
|
||||
let state_layout = Layout::Builtin(Builtin::List(ret_layout));
|
||||
let state_type = layout_spec(builder, &state_layout)?;
|
||||
|
||||
let init_state = new_list(builder, block, output_element_type)?;
|
||||
|
||||
add_loop(builder, block, state_type, init_state, loop_body)
|
||||
}
|
||||
|
||||
ListKeepIf { xs } | ListKeepOks { xs } | ListKeepErrs { xs } => {
|
||||
ListKeepIf { xs } => {
|
||||
let list = env.symbols[xs];
|
||||
let closure_env = env.symbols[function_env];
|
||||
|
||||
let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
|
||||
// let _cell = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?;
|
||||
let loop_body = |builder: &mut FuncDefBuilder, block, state| {
|
||||
let bag = builder.add_get_tuple_field(block, state, LIST_BAG_INDEX)?;
|
||||
let cell = builder.add_get_tuple_field(block, state, LIST_CELL_INDEX)?;
|
||||
|
||||
let first = builder.add_bag_get(block, bag)?;
|
||||
let element = builder.add_bag_get(block, bag)?;
|
||||
|
||||
let argument = if closure_env_layout.is_none() {
|
||||
builder.add_make_tuple(block, &[first])?
|
||||
} else {
|
||||
builder.add_make_tuple(block, &[first, closure_env])?
|
||||
let _ = call_function!(builder, block, [element]);
|
||||
|
||||
// NOTE: we assume the element is not kept
|
||||
builder.add_update(block, update_mode_var, cell)?;
|
||||
|
||||
let removed = builder.add_bag_remove(block, bag)?;
|
||||
|
||||
// decrement the removed element
|
||||
let removed_element = builder.add_get_tuple_field(block, removed, 1)?;
|
||||
builder.add_recursive_touch(block, removed_element)?;
|
||||
|
||||
let new_bag = builder.add_get_tuple_field(block, removed, 0)?;
|
||||
let new_cell = builder.add_new_heap_cell(block)?;
|
||||
|
||||
builder.add_make_tuple(block, &[new_cell, new_bag])
|
||||
};
|
||||
let result = builder.add_call(block, spec_var, module, name, argument)?;
|
||||
let unit = builder.add_tuple_type(&[])?;
|
||||
builder.add_unknown_with(block, &[result], unit)?;
|
||||
|
||||
let state_layout = Layout::Builtin(Builtin::List(&arg_layouts[0]));
|
||||
let state_type = layout_spec(builder, &state_layout)?;
|
||||
let init_state = list;
|
||||
|
||||
add_loop(builder, block, state_type, init_state, loop_body)
|
||||
}
|
||||
ListKeepOks { xs } | ListKeepErrs { xs } => {
|
||||
let list = env.symbols[xs];
|
||||
|
||||
let keep_result = match op {
|
||||
ListKeepOks { .. } => KeepResult::Oks,
|
||||
ListKeepErrs { .. } => KeepResult::Errs,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let result_repr = ResultRepr::from_layout(ret_layout);
|
||||
|
||||
let output_element_layout = match (keep_result, result_repr) {
|
||||
(KeepResult::Errs, ResultRepr::NonRecursive { err, .. }) => err,
|
||||
(KeepResult::Oks, ResultRepr::NonRecursive { ok, .. }) => ok,
|
||||
(_, ResultRepr::Int1) => Layout::Struct(&[]),
|
||||
};
|
||||
|
||||
let loop_body = |builder: &mut FuncDefBuilder, block, state| {
|
||||
let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
|
||||
|
||||
let element = builder.add_bag_get(block, bag)?;
|
||||
|
||||
let err_or_ok = call_function!(builder, block, [element]);
|
||||
|
||||
let kept_branch = builder.add_block();
|
||||
let not_kept_branch = builder.add_block();
|
||||
|
||||
let element_kept = {
|
||||
let block = kept_branch;
|
||||
|
||||
// a Result can be represented as a Int1
|
||||
let new_element = result_repr.unwrap(
|
||||
builder,
|
||||
block,
|
||||
err_or_ok,
|
||||
keep_result as u32,
|
||||
)?;
|
||||
|
||||
list_append(builder, block, update_mode_var, state, new_element)?
|
||||
};
|
||||
|
||||
let element_not_kept = {
|
||||
let block = not_kept_branch;
|
||||
|
||||
// a Result can be represented as a Int1
|
||||
let dropped_element = result_repr.unwrap(
|
||||
builder,
|
||||
block,
|
||||
err_or_ok,
|
||||
keep_result.invert() as u32,
|
||||
)?;
|
||||
|
||||
// decrement the element we will not keep
|
||||
builder.add_recursive_touch(block, dropped_element)?;
|
||||
|
||||
state
|
||||
};
|
||||
|
||||
builder.add_choice(
|
||||
block,
|
||||
&[
|
||||
BlockExpr(not_kept_branch, element_not_kept),
|
||||
BlockExpr(kept_branch, element_kept),
|
||||
],
|
||||
)
|
||||
};
|
||||
|
||||
let output_element_type = layout_spec(builder, &output_element_layout)?;
|
||||
let init_state = new_list(builder, block, output_element_type)?;
|
||||
|
||||
let state_layout = Layout::Builtin(Builtin::List(&output_element_layout));
|
||||
let state_type = layout_spec(builder, &state_layout)?;
|
||||
|
||||
add_loop(builder, block, state_type, init_state, loop_body)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO overly pessimstic
|
||||
// filter_map because one of the arguments is a function name, which
|
||||
// is not defined in the env
|
||||
let arguments: Vec<_> = call
|
||||
.arguments
|
||||
.iter()
|
||||
.filter_map(|symbol| env.symbols.get(symbol))
|
||||
.copied()
|
||||
.collect();
|
||||
|
||||
let result_type = layout_spec(builder, layout)?;
|
||||
|
||||
builder.add_unknown_with(block, &arguments, result_type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1519,8 +1671,8 @@ fn static_list_type<TC: TypeContext>(builder: &mut TC) -> Result<TypeId> {
|
|||
builder.add_tuple_type(&[cell, bag])
|
||||
}
|
||||
|
||||
// const OK_TAG_ID: u8 = 1u8;
|
||||
// const ERR_TAG_ID: u8 = 0u8;
|
||||
const OK_TAG_ID: u32 = 1;
|
||||
const ERR_TAG_ID: u32 = 0;
|
||||
|
||||
const LIST_CELL_INDEX: u32 = 0;
|
||||
const LIST_BAG_INDEX: u32 = 1;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue