mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 06:44:46 +00:00
Merge branch 'trunk' into dec-div
This commit is contained in:
commit
94dd8a7253
29 changed files with 2448 additions and 168 deletions
|
@ -519,7 +519,7 @@ where
|
|||
}
|
||||
Stmt::Join {
|
||||
parameters,
|
||||
continuation,
|
||||
body: continuation,
|
||||
remainder,
|
||||
..
|
||||
} => {
|
||||
|
|
|
@ -830,9 +830,10 @@ pub fn build_exp_call<'a, 'ctx, 'env>(
|
|||
|
||||
CallType::HigherOrderLowLevel {
|
||||
op,
|
||||
closure_layout,
|
||||
function_owns_closure_data,
|
||||
specialization_id,
|
||||
arg_layouts,
|
||||
ret_layout,
|
||||
..
|
||||
} => {
|
||||
let bytes = specialization_id.to_bytes();
|
||||
|
@ -846,8 +847,9 @@ pub fn build_exp_call<'a, 'ctx, 'env>(
|
|||
scope,
|
||||
layout,
|
||||
*op,
|
||||
*closure_layout,
|
||||
func_spec,
|
||||
arg_layouts,
|
||||
ret_layout,
|
||||
*function_owns_closure_data,
|
||||
arguments,
|
||||
)
|
||||
|
@ -1436,6 +1438,12 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
structure,
|
||||
wrapped: Wrapped::RecordOrSingleTagUnion,
|
||||
..
|
||||
}
|
||||
| AccessAtIndex {
|
||||
index,
|
||||
structure,
|
||||
wrapped: Wrapped::LikeARoseTree,
|
||||
..
|
||||
} => {
|
||||
// extract field from a record
|
||||
match load_symbol_and_layout(scope, structure) {
|
||||
|
@ -2137,7 +2145,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
|||
id,
|
||||
parameters,
|
||||
remainder,
|
||||
continuation,
|
||||
body: continuation,
|
||||
} => {
|
||||
let builder = env.builder;
|
||||
let context = env.context;
|
||||
|
@ -3161,7 +3169,7 @@ fn build_procedures_help<'a, 'ctx, 'env>(
|
|||
let it = procedures.iter().map(|x| x.1);
|
||||
|
||||
let solutions = match roc_mono::alias_analysis::spec_program(entry_point, it) {
|
||||
Err(e) => panic!("Error in alias analysis: {:?}", e),
|
||||
Err(e) => panic!("Error in alias analysis: {}", e),
|
||||
Ok(solutions) => solutions,
|
||||
};
|
||||
|
||||
|
@ -3815,8 +3823,9 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
|
|||
scope: &Scope<'a, 'ctx>,
|
||||
return_layout: &Layout<'a>,
|
||||
op: LowLevel,
|
||||
function_layout: Layout<'a>,
|
||||
func_spec: FuncSpec,
|
||||
argument_layouts: &[Layout<'a>],
|
||||
result_layout: &Layout<'a>,
|
||||
function_owns_closure_data: bool,
|
||||
args: &[Symbol],
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
|
@ -3828,6 +3837,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
|
|||
macro_rules! passed_function_at_index {
|
||||
($index:expr) => {{
|
||||
let function_symbol = args[$index];
|
||||
let function_layout = Layout::FunctionPointer(argument_layouts, return_layout);
|
||||
|
||||
function_value_by_func_spec(env, func_spec, function_symbol, function_layout)
|
||||
}};
|
||||
|
@ -4095,7 +4105,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
|
|||
env,
|
||||
layout_ids,
|
||||
roc_function_call,
|
||||
&function_layout,
|
||||
result_layout,
|
||||
list,
|
||||
before_layout,
|
||||
after_layout,
|
||||
|
@ -4139,7 +4149,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
|
|||
env,
|
||||
layout_ids,
|
||||
roc_function_call,
|
||||
&function_layout,
|
||||
result_layout,
|
||||
list,
|
||||
before_layout,
|
||||
after_layout,
|
||||
|
|
|
@ -601,18 +601,12 @@ pub fn list_keep_oks<'a, 'ctx, 'env>(
|
|||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
roc_function_call: RocFunctionCall<'ctx>,
|
||||
function_layout: &Layout<'a>,
|
||||
// Layout of the `Result after *`
|
||||
result_layout: &Layout<'a>,
|
||||
list: BasicValueEnum<'ctx>,
|
||||
before_layout: &Layout<'a>,
|
||||
after_layout: &Layout<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
// Layout of the `Result after *`
|
||||
let result_layout = match function_layout {
|
||||
Layout::FunctionPointer(_, ret) => ret,
|
||||
Layout::Closure(_, _, ret) => ret,
|
||||
_ => unreachable!("not a callable layout"),
|
||||
};
|
||||
|
||||
let dec_result_fn = build_dec_wrapper(env, layout_ids, result_layout);
|
||||
|
||||
call_bitcode_fn(
|
||||
|
@ -638,18 +632,12 @@ pub fn list_keep_errs<'a, 'ctx, 'env>(
|
|||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
roc_function_call: RocFunctionCall<'ctx>,
|
||||
function_layout: &Layout<'a>,
|
||||
// Layout of the `Result * err`
|
||||
result_layout: &Layout<'a>,
|
||||
list: BasicValueEnum<'ctx>,
|
||||
before_layout: &Layout<'a>,
|
||||
after_layout: &Layout<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
// Layout of the `Result after *`
|
||||
let result_layout = match function_layout {
|
||||
Layout::FunctionPointer(_, ret) => ret,
|
||||
Layout::Closure(_, _, ret) => ret,
|
||||
_ => unreachable!("not a callable layout"),
|
||||
};
|
||||
|
||||
let dec_result_fn = build_dec_wrapper(env, layout_ids, result_layout);
|
||||
|
||||
call_bitcode_fn(
|
||||
|
|
|
@ -19,19 +19,22 @@ pub const STATIC_STR_NAME: ConstName = ConstName(&Symbol::STR_ALIAS_ANALYSIS_STA
|
|||
|
||||
const ENTRY_POINT_NAME: &[u8] = b"mainForHost";
|
||||
|
||||
pub fn func_name_bytes(proc: &Proc) -> [u8; 16] {
|
||||
pub fn func_name_bytes(proc: &Proc) -> [u8; SIZE] {
|
||||
func_name_bytes_help(proc.name, proc.args.iter().map(|x| x.0), proc.ret_layout)
|
||||
}
|
||||
|
||||
const DEBUG: bool = false;
|
||||
const SIZE: usize = if DEBUG { 50 } else { 16 };
|
||||
|
||||
pub fn func_name_bytes_help<'a, I>(
|
||||
symbol: Symbol,
|
||||
argument_layouts: I,
|
||||
return_layout: Layout<'a>,
|
||||
) -> [u8; 16]
|
||||
) -> [u8; SIZE]
|
||||
where
|
||||
I: Iterator<Item = Layout<'a>>,
|
||||
{
|
||||
let mut name_bytes = [0u8; 16];
|
||||
let mut name_bytes = [0u8; SIZE];
|
||||
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::Hash;
|
||||
|
@ -75,9 +78,27 @@ where
|
|||
*target = *source;
|
||||
}
|
||||
|
||||
if DEBUG {
|
||||
for (i, c) in (format!("{:?}", symbol)).chars().take(25).enumerate() {
|
||||
name_bytes[25 + i] = c as u8;
|
||||
}
|
||||
}
|
||||
|
||||
name_bytes
|
||||
}
|
||||
|
||||
fn bytes_as_ascii(bytes: &[u8]) -> String {
|
||||
use std::fmt::Write;
|
||||
|
||||
let mut buf = String::new();
|
||||
|
||||
for byte in bytes {
|
||||
write!(buf, "{:02X}", byte).unwrap();
|
||||
}
|
||||
|
||||
buf
|
||||
}
|
||||
|
||||
pub fn spec_program<'a, I>(
|
||||
entry_point: crate::ir::EntryPoint<'a>,
|
||||
procs: I,
|
||||
|
@ -102,15 +123,34 @@ where
|
|||
m.add_const(STATIC_STR_NAME, static_str_def)?;
|
||||
|
||||
// the entry point wrapper
|
||||
let roc_main_bytes = func_name_bytes_help(
|
||||
entry_point.symbol,
|
||||
entry_point.layout.arguments.iter().copied(),
|
||||
entry_point.layout.result,
|
||||
);
|
||||
let roc_main = FuncName(&roc_main_bytes);
|
||||
|
||||
let entry_point_function = build_entry_point(entry_point.layout, roc_main)?;
|
||||
let entry_point_name = FuncName(ENTRY_POINT_NAME);
|
||||
let entry_point_function = build_entry_point(entry_point.layout, entry_point_name)?;
|
||||
m.add_func(entry_point_name, entry_point_function)?;
|
||||
|
||||
// all other functions
|
||||
for proc in procs {
|
||||
let bytes = func_name_bytes(proc);
|
||||
let func_name = FuncName(&bytes);
|
||||
|
||||
if DEBUG {
|
||||
eprintln!(
|
||||
"{:?}: {:?} with {:?} args",
|
||||
proc.name,
|
||||
bytes_as_ascii(&bytes),
|
||||
proc.args.len()
|
||||
);
|
||||
}
|
||||
|
||||
let spec = proc_spec(proc)?;
|
||||
|
||||
m.add_func(FuncName(&func_name_bytes(proc)), spec)?;
|
||||
m.add_func(func_name, spec)?;
|
||||
}
|
||||
|
||||
m.build()?
|
||||
|
@ -126,7 +166,9 @@ where
|
|||
p.build()?
|
||||
};
|
||||
|
||||
// eprintln!("{}", program.to_source_string());
|
||||
if DEBUG {
|
||||
eprintln!("{}", program.to_source_string());
|
||||
}
|
||||
|
||||
morphic_lib::solve(program)
|
||||
}
|
||||
|
@ -198,11 +240,25 @@ fn stmt_spec(
|
|||
use Stmt::*;
|
||||
|
||||
match stmt {
|
||||
Let(symbol, expr, layout, continuation) => {
|
||||
let value_id = expr_spec(builder, env, block, layout, expr)?;
|
||||
Let(symbol, expr, expr_layout, mut continuation) => {
|
||||
let value_id = expr_spec(builder, env, block, expr_layout, expr)?;
|
||||
env.symbols.insert(*symbol, value_id);
|
||||
|
||||
let mut queue = vec![symbol];
|
||||
|
||||
while let Let(symbol, expr, expr_layout, c) = continuation {
|
||||
let value_id = expr_spec(builder, env, block, expr_layout, expr)?;
|
||||
env.symbols.insert(*symbol, value_id);
|
||||
|
||||
queue.push(symbol);
|
||||
continuation = c;
|
||||
}
|
||||
|
||||
let result = stmt_spec(builder, env, block, layout, continuation)?;
|
||||
env.symbols.remove(symbol);
|
||||
|
||||
for symbol in queue {
|
||||
env.symbols.remove(symbol);
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
@ -235,7 +291,7 @@ fn stmt_spec(
|
|||
cond_layout: _,
|
||||
branches,
|
||||
default_branch,
|
||||
ret_layout,
|
||||
ret_layout: _lies,
|
||||
} => {
|
||||
let mut cases = Vec::with_capacity(branches.len() + 1);
|
||||
|
||||
|
@ -246,7 +302,7 @@ fn stmt_spec(
|
|||
|
||||
for branch in it {
|
||||
let block = builder.add_block();
|
||||
let value_id = stmt_spec(builder, env, block, ret_layout, branch)?;
|
||||
let value_id = stmt_spec(builder, env, block, layout, branch)?;
|
||||
cases.push(BlockExpr(block, value_id));
|
||||
}
|
||||
|
||||
|
@ -282,7 +338,7 @@ fn stmt_spec(
|
|||
Join {
|
||||
id,
|
||||
parameters,
|
||||
continuation,
|
||||
body,
|
||||
remainder,
|
||||
} => {
|
||||
let mut type_ids = Vec::new();
|
||||
|
@ -298,27 +354,33 @@ fn stmt_spec(
|
|||
let (jpid, jp_argument) =
|
||||
builder.declare_continuation(block, jp_arg_type_id, ret_type_id)?;
|
||||
|
||||
// NOTE join point arguments can shadow variables from the outer scope
|
||||
// the ordering of steps here is important
|
||||
|
||||
// add this ID so both body and remainder can reference it
|
||||
env.join_points.insert(*id, jpid);
|
||||
|
||||
// first, with the current variable bindings, process the remainder
|
||||
let cont_block = builder.add_block();
|
||||
let cont_value_id = stmt_spec(builder, env, cont_block, layout, remainder)?;
|
||||
|
||||
// only then introduce variables bound by the jump point, and process its body
|
||||
let join_body_sub_block = {
|
||||
env.join_points.insert(*id, jpid);
|
||||
let jp_body_block = builder.add_block();
|
||||
|
||||
// unpack the argument
|
||||
for (i, p) in parameters.iter().enumerate() {
|
||||
let value_id =
|
||||
builder.add_get_tuple_field(jp_body_block, jp_argument, i as u32)?;
|
||||
|
||||
env.symbols.insert(p.symbol, value_id);
|
||||
}
|
||||
|
||||
let jp_body_value_id = stmt_spec(builder, env, jp_body_block, layout, remainder)?;
|
||||
let jp_body_value_id = stmt_spec(builder, env, jp_body_block, layout, body)?;
|
||||
|
||||
BlockExpr(jp_body_block, jp_body_value_id)
|
||||
};
|
||||
|
||||
// NOTE the symbols bound by the join point can shadow the argument symbols of the
|
||||
// surrounding function, so we don't remove them from the env here
|
||||
|
||||
let cont_block = builder.add_block();
|
||||
let cont_value_id = stmt_spec(builder, env, cont_block, layout, continuation)?;
|
||||
|
||||
env.join_points.remove(id);
|
||||
builder.define_continuation(jpid, join_body_sub_block)?;
|
||||
|
||||
|
@ -423,7 +485,7 @@ fn call_spec(
|
|||
),
|
||||
HigherOrderLowLevel {
|
||||
specialization_id,
|
||||
closure_layout: _,
|
||||
closure_env_layout,
|
||||
op,
|
||||
arg_layouts,
|
||||
ret_layout,
|
||||
|
@ -458,15 +520,160 @@ fn call_spec(
|
|||
DictWalk => {
|
||||
let dict = env.symbols[&call.arguments[0]];
|
||||
let default = env.symbols[&call.arguments[1]];
|
||||
let closure_env = env.symbols[&call.arguments[3]];
|
||||
|
||||
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 first = builder.add_bag_get(block, bag)?;
|
||||
|
||||
let argument = builder.add_make_tuple(block, &[first, default])?;
|
||||
let key = builder.add_get_tuple_field(block, first, 0)?;
|
||||
let val = builder.add_get_tuple_field(block, first, 1)?;
|
||||
|
||||
let argument = if closure_env_layout.is_none() {
|
||||
builder.add_make_tuple(block, &[key, val, default])?
|
||||
} else {
|
||||
builder.add_make_tuple(block, &[key, val, default, closure_env])?
|
||||
};
|
||||
builder.add_call(block, spec_var, module, name, argument)?;
|
||||
}
|
||||
|
||||
ListWalk | ListWalkBackwards | ListWalkUntil => {
|
||||
let list = env.symbols[&call.arguments[0]];
|
||||
let default = env.symbols[&call.arguments[1]];
|
||||
let closure_env = env.symbols[&call.arguments[3]];
|
||||
|
||||
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 first = builder.add_bag_get(block, bag)?;
|
||||
|
||||
let argument = if closure_env_layout.is_none() {
|
||||
builder.add_make_tuple(block, &[first, default])?
|
||||
} else {
|
||||
builder.add_make_tuple(block, &[first, default, closure_env])?
|
||||
};
|
||||
builder.add_call(block, spec_var, module, name, argument)?;
|
||||
}
|
||||
|
||||
ListMapWithIndex => {
|
||||
let list = env.symbols[&call.arguments[0]];
|
||||
let closure_env = env.symbols[&call.arguments[2]];
|
||||
|
||||
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 first = builder.add_bag_get(block, bag)?;
|
||||
let index = builder.add_make_tuple(block, &[])?;
|
||||
|
||||
let argument = if closure_env_layout.is_none() {
|
||||
builder.add_make_tuple(block, &[first, index])?
|
||||
} else {
|
||||
builder.add_make_tuple(block, &[first, index, closure_env])?
|
||||
};
|
||||
builder.add_call(block, spec_var, module, name, argument)?;
|
||||
}
|
||||
|
||||
ListMap => {
|
||||
let list1 = env.symbols[&call.arguments[0]];
|
||||
let closure_env = env.symbols[&call.arguments[2]];
|
||||
|
||||
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 argument = if closure_env_layout.is_none() {
|
||||
builder.add_make_tuple(block, &[elem1])?
|
||||
} else {
|
||||
builder.add_make_tuple(block, &[elem1, closure_env])?
|
||||
};
|
||||
builder.add_call(block, spec_var, module, name, argument)?;
|
||||
}
|
||||
|
||||
ListSortWith => {
|
||||
let list1 = env.symbols[&call.arguments[0]];
|
||||
let closure_env = env.symbols[&call.arguments[2]];
|
||||
|
||||
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 argument = if closure_env_layout.is_none() {
|
||||
builder.add_make_tuple(block, &[elem1, elem1])?
|
||||
} else {
|
||||
builder.add_make_tuple(block, &[elem1, elem1, closure_env])?
|
||||
};
|
||||
builder.add_call(block, spec_var, module, name, argument)?;
|
||||
}
|
||||
|
||||
ListMap2 => {
|
||||
let list1 = env.symbols[&call.arguments[0]];
|
||||
let list2 = env.symbols[&call.arguments[1]];
|
||||
let closure_env = env.symbols[&call.arguments[3]];
|
||||
|
||||
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 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 argument = if closure_env_layout.is_none() {
|
||||
builder.add_make_tuple(block, &[elem1, elem2])?
|
||||
} else {
|
||||
builder.add_make_tuple(block, &[elem1, elem2, closure_env])?
|
||||
};
|
||||
builder.add_call(block, spec_var, module, name, argument)?;
|
||||
}
|
||||
|
||||
ListMap3 => {
|
||||
let list1 = env.symbols[&call.arguments[0]];
|
||||
let list2 = env.symbols[&call.arguments[1]];
|
||||
let list3 = env.symbols[&call.arguments[2]];
|
||||
let closure_env = env.symbols[&call.arguments[4]];
|
||||
|
||||
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 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 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 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])?
|
||||
};
|
||||
builder.add_call(block, spec_var, module, name, argument)?;
|
||||
}
|
||||
|
||||
ListKeepIf | ListKeepOks | ListKeepErrs => {
|
||||
let list = env.symbols[&call.arguments[0]];
|
||||
let closure_env = env.symbols[&call.arguments[2]];
|
||||
|
||||
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 first = 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 result = builder.add_call(block, spec_var, module, name, argument)?;
|
||||
let unit = builder.add_tuple_type(&[])?;
|
||||
builder.add_unknown_with(block, &[result], unit)?;
|
||||
}
|
||||
|
||||
_ => {
|
||||
// fake a call to the function argument
|
||||
// to make sure the function is specialized
|
||||
|
@ -653,7 +860,7 @@ fn expr_spec(
|
|||
Struct(fields) => build_tuple_value(builder, env, block, fields),
|
||||
AccessAtIndex {
|
||||
index,
|
||||
field_layouts,
|
||||
field_layouts: _,
|
||||
structure,
|
||||
wrapped,
|
||||
} => {
|
||||
|
@ -673,17 +880,20 @@ fn expr_spec(
|
|||
Wrapped::RecordOrSingleTagUnion => {
|
||||
builder.add_get_tuple_field(block, value_id, *index as u32)
|
||||
}
|
||||
Wrapped::LikeARoseTree => {
|
||||
let result_type = layout_spec(builder, layout)?;
|
||||
builder.add_unknown_with(block, &[value_id], result_type)
|
||||
}
|
||||
Wrapped::MultiTagUnion => {
|
||||
// Clearly this is not generally correct, but it should be for our examples
|
||||
let hacky_is_recursive =
|
||||
field_layouts.iter().any(|l| l == &Layout::RecursivePointer);
|
||||
// let hacky_is_recursive = field_layouts.iter().any(|l| l == &Layout::RecursivePointer);
|
||||
// if hacky_is_recursive {
|
||||
|
||||
if hacky_is_recursive {
|
||||
let result_type = layout_spec(builder, layout)?;
|
||||
builder.add_unknown_with(block, &[value_id], result_type)
|
||||
} else {
|
||||
builder.add_get_tuple_field(block, value_id, *index as u32)
|
||||
}
|
||||
// we don't know what constructor we are at this point, so how can we get a
|
||||
// field from an enum value?
|
||||
|
||||
let result_type = layout_spec(builder, layout)?;
|
||||
builder.add_unknown_with(block, &[value_id], result_type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -700,7 +910,9 @@ fn expr_spec(
|
|||
bag = builder.add_bag_insert(block, bag, value_id)?;
|
||||
}
|
||||
|
||||
Ok(bag)
|
||||
let cell = builder.add_new_heap_cell(block)?;
|
||||
|
||||
builder.add_make_tuple(block, &[cell, bag])
|
||||
}
|
||||
|
||||
EmptyArray => {
|
||||
|
@ -827,8 +1039,7 @@ fn builtin_spec(builder: &mut FuncDefBuilder, builtin: &Builtin) -> Result<TypeI
|
|||
|
||||
fn str_type<TC: TypeContext>(builder: &mut TC) -> Result<TypeId> {
|
||||
let cell_id = builder.add_heap_cell_type();
|
||||
let len_id = builder.add_tuple_type(&[])?;
|
||||
builder.add_tuple_type(&[cell_id, len_id])
|
||||
builder.add_tuple_type(&[cell_id])
|
||||
}
|
||||
|
||||
// const OK_TAG_ID: u8 = 1u8;
|
||||
|
|
|
@ -193,7 +193,7 @@ impl<'a> ParamMap<'a> {
|
|||
id: j,
|
||||
parameters: xs,
|
||||
remainder: v,
|
||||
continuation: b,
|
||||
body: b,
|
||||
} => {
|
||||
let already_in_there = self
|
||||
.items
|
||||
|
@ -393,15 +393,20 @@ impl<'a> BorrowInfState<'a> {
|
|||
}
|
||||
|
||||
HigherOrderLowLevel {
|
||||
op, closure_layout, ..
|
||||
op,
|
||||
arg_layouts,
|
||||
ret_layout,
|
||||
..
|
||||
} => {
|
||||
use roc_module::low_level::LowLevel::*;
|
||||
|
||||
debug_assert!(op.is_higher_order());
|
||||
|
||||
let closure_layout = Layout::FunctionPointer(arg_layouts, ret_layout);
|
||||
|
||||
match op {
|
||||
ListMap | ListKeepIf | ListKeepOks | ListKeepErrs => {
|
||||
match self.param_map.get_symbol(arguments[1], *closure_layout) {
|
||||
match self.param_map.get_symbol(arguments[1], closure_layout) {
|
||||
Some(function_ps) => {
|
||||
// own the list if the function wants to own the element
|
||||
if !function_ps[0].borrow {
|
||||
|
@ -417,7 +422,7 @@ impl<'a> BorrowInfState<'a> {
|
|||
}
|
||||
}
|
||||
ListMapWithIndex => {
|
||||
match self.param_map.get_symbol(arguments[1], *closure_layout) {
|
||||
match self.param_map.get_symbol(arguments[1], closure_layout) {
|
||||
Some(function_ps) => {
|
||||
// own the list if the function wants to own the element
|
||||
if !function_ps[1].borrow {
|
||||
|
@ -432,7 +437,7 @@ impl<'a> BorrowInfState<'a> {
|
|||
None => unreachable!(),
|
||||
}
|
||||
}
|
||||
ListMap2 => match self.param_map.get_symbol(arguments[2], *closure_layout) {
|
||||
ListMap2 => match self.param_map.get_symbol(arguments[2], closure_layout) {
|
||||
Some(function_ps) => {
|
||||
// own the lists if the function wants to own the element
|
||||
if !function_ps[0].borrow {
|
||||
|
@ -450,7 +455,7 @@ impl<'a> BorrowInfState<'a> {
|
|||
}
|
||||
None => unreachable!(),
|
||||
},
|
||||
ListMap3 => match self.param_map.get_symbol(arguments[3], *closure_layout) {
|
||||
ListMap3 => match self.param_map.get_symbol(arguments[3], closure_layout) {
|
||||
Some(function_ps) => {
|
||||
// own the lists if the function wants to own the element
|
||||
if !function_ps[0].borrow {
|
||||
|
@ -471,7 +476,7 @@ impl<'a> BorrowInfState<'a> {
|
|||
None => unreachable!(),
|
||||
},
|
||||
ListSortWith => {
|
||||
match self.param_map.get_symbol(arguments[1], *closure_layout) {
|
||||
match self.param_map.get_symbol(arguments[1], closure_layout) {
|
||||
Some(function_ps) => {
|
||||
// always own the input list
|
||||
self.own_var(arguments[0]);
|
||||
|
@ -485,7 +490,7 @@ impl<'a> BorrowInfState<'a> {
|
|||
}
|
||||
}
|
||||
ListWalk | ListWalkUntil | ListWalkBackwards | DictWalk => {
|
||||
match self.param_map.get_symbol(arguments[2], *closure_layout) {
|
||||
match self.param_map.get_symbol(arguments[2], closure_layout) {
|
||||
Some(function_ps) => {
|
||||
// own the data structure if the function wants to own the element
|
||||
if !function_ps[0].borrow {
|
||||
|
@ -618,7 +623,7 @@ impl<'a> BorrowInfState<'a> {
|
|||
id: j,
|
||||
parameters: ys,
|
||||
remainder: v,
|
||||
continuation: b,
|
||||
body: b,
|
||||
} => {
|
||||
let old = self.param_set.clone();
|
||||
self.update_param_set(ys);
|
||||
|
|
|
@ -597,7 +597,7 @@ fn to_relevant_branch_help<'a>(
|
|||
start.extend(end);
|
||||
}
|
||||
}
|
||||
Wrapped::RecordOrSingleTagUnion => {
|
||||
Wrapped::RecordOrSingleTagUnion | Wrapped::LikeARoseTree => {
|
||||
let sub_positions = arguments.into_iter().enumerate().map(
|
||||
|(index, (pattern, _))| {
|
||||
let mut new_path = path.to_vec();
|
||||
|
@ -956,7 +956,7 @@ pub fn optimize_when<'a>(
|
|||
stmt = Stmt::Join {
|
||||
id,
|
||||
parameters: &[],
|
||||
continuation: env.arena.alloc(body),
|
||||
body: env.arena.alloc(body),
|
||||
remainder: env.arena.alloc(stmt),
|
||||
};
|
||||
}
|
||||
|
@ -1329,7 +1329,7 @@ fn compile_guard<'a>(
|
|||
id,
|
||||
parameters: arena.alloc([param]),
|
||||
remainder: stmt,
|
||||
continuation: arena.alloc(cond),
|
||||
body: arena.alloc(cond),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1622,7 +1622,7 @@ fn decide_to_branching<'a>(
|
|||
Stmt::Join {
|
||||
id: fail_jp_id,
|
||||
parameters: &[],
|
||||
continuation: fail,
|
||||
body: fail,
|
||||
remainder: arena.alloc(test_stmt),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -622,7 +622,7 @@ fn expand_and_cancel<'a>(env: &mut Env<'a, '_>, stmt: &'a Stmt<'a>) -> &'a Stmt<
|
|||
Join {
|
||||
id,
|
||||
parameters,
|
||||
continuation,
|
||||
body: continuation,
|
||||
remainder,
|
||||
} => {
|
||||
let continuation = expand_and_cancel(env, continuation);
|
||||
|
@ -631,7 +631,7 @@ fn expand_and_cancel<'a>(env: &mut Env<'a, '_>, stmt: &'a Stmt<'a>) -> &'a Stmt<
|
|||
let stmt = Join {
|
||||
id: *id,
|
||||
parameters,
|
||||
continuation,
|
||||
body: continuation,
|
||||
remainder,
|
||||
};
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ pub fn occurring_variables(stmt: &Stmt<'_>) -> (MutSet<Symbol>, MutSet<Symbol>)
|
|||
|
||||
Join {
|
||||
parameters,
|
||||
continuation,
|
||||
body: continuation,
|
||||
remainder,
|
||||
..
|
||||
} => {
|
||||
|
@ -455,7 +455,7 @@ impl<'a> Context<'a> {
|
|||
|
||||
HigherOrderLowLevel {
|
||||
op,
|
||||
closure_layout,
|
||||
closure_env_layout,
|
||||
specialization_id,
|
||||
arg_layouts,
|
||||
ret_layout,
|
||||
|
@ -467,7 +467,7 @@ impl<'a> Context<'a> {
|
|||
call_type: if let Some(OWNED) = $borrows.map(|p| p.borrow) {
|
||||
HigherOrderLowLevel {
|
||||
op: *op,
|
||||
closure_layout: *closure_layout,
|
||||
closure_env_layout: *closure_env_layout,
|
||||
function_owns_closure_data: true,
|
||||
specialization_id: *specialization_id,
|
||||
arg_layouts,
|
||||
|
@ -497,12 +497,14 @@ impl<'a> Context<'a> {
|
|||
const FUNCTION: bool = BORROWED;
|
||||
const CLOSURE_DATA: bool = BORROWED;
|
||||
|
||||
let function_layout = Layout::FunctionPointer(arg_layouts, ret_layout);
|
||||
|
||||
match op {
|
||||
roc_module::low_level::LowLevel::ListMap
|
||||
| roc_module::low_level::LowLevel::ListKeepIf
|
||||
| roc_module::low_level::LowLevel::ListKeepOks
|
||||
| roc_module::low_level::LowLevel::ListKeepErrs => {
|
||||
match self.param_map.get_symbol(arguments[1], *closure_layout) {
|
||||
match self.param_map.get_symbol(arguments[1], function_layout) {
|
||||
Some(function_ps) => {
|
||||
let borrows = [function_ps[0].borrow, FUNCTION, CLOSURE_DATA];
|
||||
|
||||
|
@ -524,7 +526,7 @@ impl<'a> Context<'a> {
|
|||
}
|
||||
}
|
||||
roc_module::low_level::LowLevel::ListMapWithIndex => {
|
||||
match self.param_map.get_symbol(arguments[1], *closure_layout) {
|
||||
match self.param_map.get_symbol(arguments[1], function_layout) {
|
||||
Some(function_ps) => {
|
||||
let borrows = [function_ps[1].borrow, FUNCTION, CLOSURE_DATA];
|
||||
|
||||
|
@ -545,7 +547,7 @@ impl<'a> Context<'a> {
|
|||
}
|
||||
}
|
||||
roc_module::low_level::LowLevel::ListMap2 => {
|
||||
match self.param_map.get_symbol(arguments[2], *closure_layout) {
|
||||
match self.param_map.get_symbol(arguments[2], function_layout) {
|
||||
Some(function_ps) => {
|
||||
let borrows = [
|
||||
function_ps[0].borrow,
|
||||
|
@ -572,7 +574,7 @@ impl<'a> Context<'a> {
|
|||
}
|
||||
}
|
||||
roc_module::low_level::LowLevel::ListMap3 => {
|
||||
match self.param_map.get_symbol(arguments[3], *closure_layout) {
|
||||
match self.param_map.get_symbol(arguments[3], function_layout) {
|
||||
Some(function_ps) => {
|
||||
let borrows = [
|
||||
function_ps[0].borrow,
|
||||
|
@ -601,7 +603,7 @@ impl<'a> Context<'a> {
|
|||
}
|
||||
}
|
||||
roc_module::low_level::LowLevel::ListSortWith => {
|
||||
match self.param_map.get_symbol(arguments[1], *closure_layout) {
|
||||
match self.param_map.get_symbol(arguments[1], function_layout) {
|
||||
Some(function_ps) => {
|
||||
let borrows = [OWNED, FUNCTION, CLOSURE_DATA];
|
||||
|
||||
|
@ -623,7 +625,7 @@ impl<'a> Context<'a> {
|
|||
| roc_module::low_level::LowLevel::ListWalkUntil
|
||||
| roc_module::low_level::LowLevel::ListWalkBackwards
|
||||
| roc_module::low_level::LowLevel::DictWalk => {
|
||||
match self.param_map.get_symbol(arguments[2], *closure_layout) {
|
||||
match self.param_map.get_symbol(arguments[2], function_layout) {
|
||||
Some(function_ps) => {
|
||||
// borrow data structure based on first argument of the folded function
|
||||
// borrow the default based on second argument of the folded function
|
||||
|
@ -978,7 +980,7 @@ impl<'a> Context<'a> {
|
|||
id: j,
|
||||
parameters: _,
|
||||
remainder: b,
|
||||
continuation: v,
|
||||
body: v,
|
||||
} => {
|
||||
// get the parameters with borrow signature
|
||||
let xs = self.param_map.get_join_point(*j);
|
||||
|
@ -1000,7 +1002,7 @@ impl<'a> Context<'a> {
|
|||
id: *j,
|
||||
parameters: xs,
|
||||
remainder: b,
|
||||
continuation: v,
|
||||
body: v,
|
||||
}),
|
||||
b_live_vars,
|
||||
)
|
||||
|
@ -1143,7 +1145,7 @@ pub fn collect_stmt(
|
|||
id: j,
|
||||
parameters,
|
||||
remainder: b,
|
||||
continuation: v,
|
||||
body: v,
|
||||
} => {
|
||||
let mut j_live_vars = collect_stmt(v, jp_live_vars, MutSet::default());
|
||||
for param in parameters.iter() {
|
||||
|
|
|
@ -890,9 +890,10 @@ pub enum Stmt<'a> {
|
|||
Join {
|
||||
id: JoinPointId,
|
||||
parameters: &'a [Param<'a>],
|
||||
/// does not contain jumps to this id
|
||||
continuation: &'a Stmt<'a>,
|
||||
/// the "body" of the join point, contains the jumps to this id
|
||||
/// body of the join point
|
||||
/// what happens after _jumping to_ the join point
|
||||
body: &'a Stmt<'a>,
|
||||
/// what happens after _defining_ the join point
|
||||
remainder: &'a Stmt<'a>,
|
||||
},
|
||||
Jump(JoinPointId, &'a [Symbol]),
|
||||
|
@ -1013,6 +1014,8 @@ pub enum Wrapped {
|
|||
EmptyRecord,
|
||||
SingleElementRecord,
|
||||
RecordOrSingleTagUnion,
|
||||
/// Like a rose tree; recursive, but only one tag
|
||||
LikeARoseTree,
|
||||
MultiTagUnion,
|
||||
}
|
||||
|
||||
|
@ -1045,7 +1048,7 @@ impl Wrapped {
|
|||
},
|
||||
_ => Some(Wrapped::MultiTagUnion),
|
||||
},
|
||||
NonNullableUnwrapped(_) => Some(Wrapped::RecordOrSingleTagUnion),
|
||||
NonNullableUnwrapped(_) => Some(Wrapped::LikeARoseTree),
|
||||
|
||||
NullableWrapped { .. } | NullableUnwrapped { .. } => {
|
||||
Some(Wrapped::MultiTagUnion)
|
||||
|
@ -1151,7 +1154,7 @@ pub enum CallType<'a> {
|
|||
HigherOrderLowLevel {
|
||||
op: LowLevel,
|
||||
/// the layout of the closure argument, if any
|
||||
closure_layout: Layout<'a>,
|
||||
closure_env_layout: Option<Layout<'a>>,
|
||||
/// specialization id of the function argument
|
||||
specialization_id: CallSpecId,
|
||||
/// does the function need to own the closure data
|
||||
|
@ -1476,7 +1479,7 @@ impl<'a> Stmt<'a> {
|
|||
Join {
|
||||
id,
|
||||
parameters,
|
||||
continuation,
|
||||
body: continuation,
|
||||
remainder,
|
||||
} => {
|
||||
let it = parameters.iter().map(|p| symbol_to_doc(alloc, p.symbol));
|
||||
|
@ -2712,8 +2715,6 @@ macro_rules! match_on_closure_argument {
|
|||
|
||||
let arena = $env.arena;
|
||||
|
||||
let function_layout = arena.alloc(top_level).full();
|
||||
|
||||
let arg_layouts = top_level.arguments;
|
||||
let ret_layout = top_level.result;
|
||||
|
||||
|
@ -2723,10 +2724,10 @@ macro_rules! match_on_closure_argument {
|
|||
$env,
|
||||
lambda_set,
|
||||
$closure_data_symbol,
|
||||
|top_level_function, closure_data, function_layout, specialization_id| self::Call {
|
||||
|top_level_function, closure_data, closure_env_layout, specialization_id| self::Call {
|
||||
call_type: CallType::HigherOrderLowLevel {
|
||||
op: $op,
|
||||
closure_layout: function_layout,
|
||||
closure_env_layout,
|
||||
specialization_id,
|
||||
function_owns_closure_data: false,
|
||||
arg_layouts,
|
||||
|
@ -2734,7 +2735,6 @@ macro_rules! match_on_closure_argument {
|
|||
},
|
||||
arguments: arena.alloc([$($x,)* top_level_function, closure_data]),
|
||||
},
|
||||
function_layout,
|
||||
$layout,
|
||||
$assigned,
|
||||
$hole,
|
||||
|
@ -3328,7 +3328,7 @@ pub fn with_hole<'a>(
|
|||
id,
|
||||
parameters: env.arena.alloc([param]),
|
||||
remainder: env.arena.alloc(stmt),
|
||||
continuation: hole,
|
||||
body: hole,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3381,7 +3381,7 @@ pub fn with_hole<'a>(
|
|||
id,
|
||||
parameters: env.arena.alloc([param]),
|
||||
remainder: env.arena.alloc(stmt),
|
||||
continuation: env.arena.alloc(hole),
|
||||
body: env.arena.alloc(hole),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4275,7 +4275,10 @@ fn convert_tag_union<'a>(
|
|||
)
|
||||
}
|
||||
|
||||
Unwrapped(_, field_layouts) => {
|
||||
Unwrapped {
|
||||
arguments: field_layouts,
|
||||
..
|
||||
} => {
|
||||
let field_symbols_temp = sorted_field_symbols(env, procs, layout_cache, args);
|
||||
|
||||
let mut field_symbols = Vec::with_capacity_in(field_layouts.len(), env.arena);
|
||||
|
@ -5327,7 +5330,7 @@ fn substitute_in_stmt_help<'a>(
|
|||
id,
|
||||
parameters,
|
||||
remainder,
|
||||
continuation,
|
||||
body: continuation,
|
||||
} => {
|
||||
let opt_remainder = substitute_in_stmt_help(arena, remainder, subs);
|
||||
let opt_continuation = substitute_in_stmt_help(arena, continuation, subs);
|
||||
|
@ -5340,7 +5343,7 @@ fn substitute_in_stmt_help<'a>(
|
|||
id: *id,
|
||||
parameters,
|
||||
remainder,
|
||||
continuation,
|
||||
body: continuation,
|
||||
}))
|
||||
} else {
|
||||
None
|
||||
|
@ -7013,7 +7016,10 @@ fn from_can_pattern_help<'a>(
|
|||
union,
|
||||
}
|
||||
}
|
||||
Unwrapped(_, field_layouts) => {
|
||||
Unwrapped {
|
||||
arguments: field_layouts,
|
||||
..
|
||||
} => {
|
||||
let union = crate::exhaustive::Union {
|
||||
render_as: RenderAs::Tag,
|
||||
alternatives: vec![Ctor {
|
||||
|
@ -7708,13 +7714,12 @@ fn lowlevel_match_on_lambda_set<'a, ToLowLevelCall>(
|
|||
lambda_set: LambdaSet<'a>,
|
||||
closure_data_symbol: Symbol,
|
||||
to_lowlevel_call: ToLowLevelCall,
|
||||
function_layout: Layout<'a>,
|
||||
return_layout: Layout<'a>,
|
||||
assigned: Symbol,
|
||||
hole: &'a Stmt<'a>,
|
||||
) -> Stmt<'a>
|
||||
where
|
||||
ToLowLevelCall: Fn(Symbol, Symbol, Layout<'a>, CallSpecId) -> Call<'a> + Copy,
|
||||
ToLowLevelCall: Fn(Symbol, Symbol, Option<Layout<'a>>, CallSpecId) -> Call<'a> + Copy,
|
||||
{
|
||||
match lambda_set.runtime_representation() {
|
||||
Layout::Union(_) => {
|
||||
|
@ -7726,8 +7731,8 @@ where
|
|||
closure_tag_id_symbol,
|
||||
Layout::Builtin(crate::layout::TAG_SIZE),
|
||||
closure_data_symbol,
|
||||
lambda_set.is_represented(),
|
||||
to_lowlevel_call,
|
||||
function_layout,
|
||||
return_layout,
|
||||
assigned,
|
||||
hole,
|
||||
|
@ -7755,7 +7760,7 @@ where
|
|||
let call = to_lowlevel_call(
|
||||
function_symbol,
|
||||
closure_data_symbol,
|
||||
function_layout,
|
||||
lambda_set.is_represented(),
|
||||
call_spec_id,
|
||||
);
|
||||
|
||||
|
@ -7770,8 +7775,8 @@ where
|
|||
closure_tag_id_symbol,
|
||||
Layout::Builtin(Builtin::Int1),
|
||||
closure_data_symbol,
|
||||
lambda_set.is_represented(),
|
||||
to_lowlevel_call,
|
||||
function_layout,
|
||||
return_layout,
|
||||
assigned,
|
||||
hole,
|
||||
|
@ -7786,8 +7791,8 @@ where
|
|||
closure_tag_id_symbol,
|
||||
Layout::Builtin(Builtin::Int8),
|
||||
closure_data_symbol,
|
||||
lambda_set.is_represented(),
|
||||
to_lowlevel_call,
|
||||
function_layout,
|
||||
return_layout,
|
||||
assigned,
|
||||
hole,
|
||||
|
@ -7804,14 +7809,14 @@ fn lowlevel_union_lambda_set_to_switch<'a, ToLowLevelCall>(
|
|||
closure_tag_id_symbol: Symbol,
|
||||
closure_tag_id_layout: Layout<'a>,
|
||||
closure_data_symbol: Symbol,
|
||||
closure_env_layout: Option<Layout<'a>>,
|
||||
to_lowlevel_call: ToLowLevelCall,
|
||||
function_layout: Layout<'a>,
|
||||
return_layout: Layout<'a>,
|
||||
assigned: Symbol,
|
||||
hole: &'a Stmt<'a>,
|
||||
) -> Stmt<'a>
|
||||
where
|
||||
ToLowLevelCall: Fn(Symbol, Symbol, Layout<'a>, CallSpecId) -> Call<'a> + Copy,
|
||||
ToLowLevelCall: Fn(Symbol, Symbol, Option<Layout<'a>>, CallSpecId) -> Call<'a> + Copy,
|
||||
{
|
||||
debug_assert!(!lambda_set.is_empty());
|
||||
|
||||
|
@ -7828,7 +7833,7 @@ where
|
|||
let call = to_lowlevel_call(
|
||||
*function_symbol,
|
||||
closure_data_symbol,
|
||||
function_layout,
|
||||
closure_env_layout,
|
||||
call_spec_id,
|
||||
);
|
||||
let stmt = build_call(env, call, assigned, return_layout, env.arena.alloc(hole));
|
||||
|
@ -7859,7 +7864,7 @@ where
|
|||
Stmt::Join {
|
||||
id: join_point_id,
|
||||
parameters: &*env.arena.alloc([param]),
|
||||
continuation: hole,
|
||||
body: hole,
|
||||
remainder: env.arena.alloc(switch),
|
||||
}
|
||||
}
|
||||
|
@ -8017,7 +8022,7 @@ fn union_lambda_set_to_switch<'a>(
|
|||
Stmt::Join {
|
||||
id: join_point_id,
|
||||
parameters: &*env.arena.alloc([param]),
|
||||
continuation: hole,
|
||||
body: hole,
|
||||
remainder: env.arena.alloc(switch),
|
||||
}
|
||||
}
|
||||
|
@ -8161,7 +8166,7 @@ fn enum_lambda_set_to_switch<'a>(
|
|||
Stmt::Join {
|
||||
id: join_point_id,
|
||||
parameters: &*env.arena.alloc([param]),
|
||||
continuation: hole,
|
||||
body: hole,
|
||||
remainder: env.arena.alloc(switch),
|
||||
}
|
||||
}
|
||||
|
@ -8229,14 +8234,14 @@ fn lowlevel_enum_lambda_set_to_switch<'a, ToLowLevelCall>(
|
|||
closure_tag_id_symbol: Symbol,
|
||||
closure_tag_id_layout: Layout<'a>,
|
||||
closure_data_symbol: Symbol,
|
||||
closure_env_layout: Option<Layout<'a>>,
|
||||
to_lowlevel_call: ToLowLevelCall,
|
||||
function_layout: Layout<'a>,
|
||||
return_layout: Layout<'a>,
|
||||
assigned: Symbol,
|
||||
hole: &'a Stmt<'a>,
|
||||
) -> Stmt<'a>
|
||||
where
|
||||
ToLowLevelCall: Fn(Symbol, Symbol, Layout<'a>, CallSpecId) -> Call<'a> + Copy,
|
||||
ToLowLevelCall: Fn(Symbol, Symbol, Option<Layout<'a>>, CallSpecId) -> Call<'a> + Copy,
|
||||
{
|
||||
debug_assert!(!lambda_set.is_empty());
|
||||
|
||||
|
@ -8253,7 +8258,7 @@ where
|
|||
let call = to_lowlevel_call(
|
||||
*function_symbol,
|
||||
closure_data_symbol,
|
||||
function_layout,
|
||||
closure_env_layout,
|
||||
call_spec_id,
|
||||
);
|
||||
let stmt = build_call(
|
||||
|
@ -8290,7 +8295,7 @@ where
|
|||
Stmt::Join {
|
||||
id: join_point_id,
|
||||
parameters: &*env.arena.alloc([param]),
|
||||
continuation: hole,
|
||||
body: hole,
|
||||
remainder: env.arena.alloc(switch),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -143,6 +143,14 @@ impl<'a> LambdaSet<'a> {
|
|||
*self.representation
|
||||
}
|
||||
|
||||
pub fn is_represented(&self) -> Option<Layout<'a>> {
|
||||
if let Layout::Struct(&[]) = self.representation {
|
||||
None
|
||||
} else {
|
||||
Some(*self.representation)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn layout_for_member(&self, function_symbol: Symbol) -> ClosureRepresentation<'a> {
|
||||
debug_assert!(
|
||||
self.set.iter().any(|(s, _)| *s == function_symbol),
|
||||
|
@ -281,7 +289,9 @@ impl<'a> LambdaSet<'a> {
|
|||
Unit | UnitWithArguments => Layout::Struct(&[]),
|
||||
BoolUnion { .. } => Layout::Builtin(Builtin::Int1),
|
||||
ByteUnion(_) => Layout::Builtin(Builtin::Int8),
|
||||
Unwrapped(_tag_name, layouts) => Layout::Struct(layouts.into_bump_slice()),
|
||||
Unwrapped {
|
||||
arguments: layouts, ..
|
||||
} => Layout::Struct(layouts.into_bump_slice()),
|
||||
Wrapped(variant) => {
|
||||
use WrappedVariant::*;
|
||||
|
||||
|
@ -1266,9 +1276,15 @@ pub enum UnionVariant<'a> {
|
|||
Never,
|
||||
Unit,
|
||||
UnitWithArguments,
|
||||
BoolUnion { ttrue: TagName, ffalse: TagName },
|
||||
BoolUnion {
|
||||
ttrue: TagName,
|
||||
ffalse: TagName,
|
||||
},
|
||||
ByteUnion(Vec<'a, TagName>),
|
||||
Unwrapped(TagName, Vec<'a, Layout<'a>>),
|
||||
Unwrapped {
|
||||
tag_name: TagName,
|
||||
arguments: Vec<'a, Layout<'a>>,
|
||||
},
|
||||
Wrapped(WrappedVariant<'a>),
|
||||
}
|
||||
|
||||
|
@ -1486,7 +1502,10 @@ pub fn union_sorted_tags_help<'a>(
|
|||
fields: layouts.into_bump_slice(),
|
||||
})
|
||||
} else {
|
||||
UnionVariant::Unwrapped(tag_name, layouts)
|
||||
UnionVariant::Unwrapped {
|
||||
tag_name,
|
||||
arguments: layouts,
|
||||
}
|
||||
}
|
||||
}
|
||||
num_tags => {
|
||||
|
@ -1638,7 +1657,10 @@ pub fn layout_from_tag_union<'a>(
|
|||
Unit | UnitWithArguments => Layout::Struct(&[]),
|
||||
BoolUnion { .. } => Layout::Builtin(Builtin::Int1),
|
||||
ByteUnion(_) => Layout::Builtin(Builtin::Int8),
|
||||
Unwrapped(_, mut field_layouts) => {
|
||||
Unwrapped {
|
||||
arguments: mut field_layouts,
|
||||
..
|
||||
} => {
|
||||
if field_layouts.len() == 1 {
|
||||
field_layouts.pop().unwrap()
|
||||
} else {
|
||||
|
|
|
@ -60,7 +60,7 @@ pub fn make_tail_recursive<'a>(
|
|||
id,
|
||||
remainder: jump,
|
||||
parameters: params,
|
||||
continuation: new,
|
||||
body: new,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -160,7 +160,7 @@ fn insert_jumps<'a>(
|
|||
id,
|
||||
parameters,
|
||||
remainder,
|
||||
continuation,
|
||||
body: continuation,
|
||||
} => {
|
||||
let opt_remainder = insert_jumps(arena, remainder, goal_id, needle);
|
||||
let opt_continuation = insert_jumps(arena, continuation, goal_id, needle);
|
||||
|
@ -173,7 +173,7 @@ fn insert_jumps<'a>(
|
|||
id: *id,
|
||||
parameters,
|
||||
remainder,
|
||||
continuation,
|
||||
body: continuation,
|
||||
}))
|
||||
} else {
|
||||
None
|
||||
|
|
|
@ -6,7 +6,5 @@ platform examples/quicksort
|
|||
provides [ mainForHost ]
|
||||
effects fx.Effect {}
|
||||
|
||||
update : Model -> Model
|
||||
|
||||
mainForHost : List I64 -> List I64
|
||||
mainForHost = \list -> quicksort list
|
||||
|
|
5
vendor/morphic_lib/src/api.rs
vendored
5
vendor/morphic_lib/src/api.rs
vendored
|
@ -2,6 +2,7 @@ use sha2::{digest::Digest, Sha256};
|
|||
use smallvec::SmallVec;
|
||||
use std::collections::{btree_map::Entry, BTreeMap};
|
||||
|
||||
use crate::preprocess;
|
||||
use crate::render_api_ir;
|
||||
use crate::util::blocks::Blocks;
|
||||
use crate::util::id_bi_map::IdBiMap;
|
||||
|
@ -57,6 +58,8 @@ enum ErrorKind {
|
|||
ModNotFound(ModNameBuf),
|
||||
#[error("entry point {0:?} not found in program")]
|
||||
EntryPointNotFound(EntryPointNameBuf),
|
||||
#[error("{0}")]
|
||||
PreprocessError(preprocess::Error),
|
||||
}
|
||||
|
||||
#[derive(Clone, thiserror::Error, Debug)]
|
||||
|
@ -1550,6 +1553,8 @@ fn populate_specs(
|
|||
}
|
||||
|
||||
pub fn solve(program: Program) -> Result<Solutions> {
|
||||
preprocess::preprocess(&program).map_err(ErrorKind::PreprocessError)?;
|
||||
|
||||
Ok(Solutions {
|
||||
mods: program
|
||||
.mods
|
||||
|
|
303
vendor/morphic_lib/src/ir.rs
vendored
Normal file
303
vendor/morphic_lib/src/ir.rs
vendored
Normal file
|
@ -0,0 +1,303 @@
|
|||
//! The core IR used for analysis.
|
||||
//!
|
||||
//! The IR uses a variant of SSA in which control-dependent values are represented using block
|
||||
//! parameters instead of phi nodes.
|
||||
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::api::{CalleeSpecVarId, UpdateModeVarId};
|
||||
use crate::name_cache::{ConstId, FuncId};
|
||||
use crate::type_cache::TypeId;
|
||||
use crate::util::blocks::Blocks;
|
||||
use crate::util::flat_slices::{FlatSlices, Slice};
|
||||
use crate::util::op_graph::OpGraph;
|
||||
use crate::util::strongly_connected::{strongly_connected, SccKind};
|
||||
|
||||
/// The "core payload" of a node in the op graph, determining its semantics.
|
||||
///
|
||||
/// The `OpKind` of a node is *not* sufficient to uniquely determine either the ids or the types of
|
||||
/// its inputs or output. This information is stored separately in the graph.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub(crate) enum OpKind {
|
||||
// Variadic inputs
|
||||
// Inputs may have any type
|
||||
// Output may have any type
|
||||
UnknownWith,
|
||||
// 1 input
|
||||
// Input type must match argument type of callee
|
||||
// Output type must match return type of callee
|
||||
Call {
|
||||
callee_spec_var: CalleeSpecVarId,
|
||||
callee: FuncId,
|
||||
},
|
||||
// 0 inputs
|
||||
// Output type must match type of const
|
||||
ConstRef {
|
||||
const_: ConstId,
|
||||
},
|
||||
// 0 inputs
|
||||
// Output type is heap cell
|
||||
NewHeapCell,
|
||||
// 1 input
|
||||
// Input may have any type
|
||||
// Output type is unit
|
||||
RecursiveTouch,
|
||||
// 1 input
|
||||
// Input type is heap cell
|
||||
// Output type is unit
|
||||
UpdateWriteOnly {
|
||||
update_mode_var: UpdateModeVarId,
|
||||
},
|
||||
// 0 inputs
|
||||
// Output type is `Bag<T>` for some `T`
|
||||
EmptyBag,
|
||||
// 2 inputs: `(bag, to_insert)`
|
||||
// `bag` has type `Bag<T>`
|
||||
// `to_insert` has type `T`
|
||||
// Output type is `Bag<T>`
|
||||
BagInsert,
|
||||
// 1 input
|
||||
// Input type is `Bag<T>`
|
||||
// Output type is `T`
|
||||
BagGet,
|
||||
// 1 input
|
||||
// Input type is `Bag<T>`
|
||||
// Output type is `(Bag<T>, T)`
|
||||
BagRemove,
|
||||
// Variadic inputs
|
||||
// Input types are `T_1, ..., T_N`
|
||||
// Output type is `(T_1, ..., T_N)`
|
||||
MakeTuple,
|
||||
// 1 input
|
||||
// Input type is `(T_1, ..., T_N)`
|
||||
// Output type is `T_(field_idx)`
|
||||
GetTupleField {
|
||||
field_idx: u32,
|
||||
},
|
||||
// 1 input
|
||||
// Input type is `T_(variant_idx)`
|
||||
// Output type is `union { T_1, ..., T_N }`
|
||||
MakeUnion {
|
||||
variant_idx: u32,
|
||||
},
|
||||
// 1 input
|
||||
// Input type is `union { T_1, ..., T_N }`
|
||||
// Output type is `T_(variant_idx)`
|
||||
UnwrapUnion {
|
||||
variant_idx: u32,
|
||||
},
|
||||
// 1 input
|
||||
// Input type is content type of a named type `T`
|
||||
// Output type is `T`
|
||||
MakeNamed,
|
||||
// 1 input
|
||||
// Input type is a named type `T`
|
||||
// Output type is the content type of `T`
|
||||
UnwrapNamed,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub(crate) enum ValueKind {
|
||||
Op(OpKind),
|
||||
// 0 inputs
|
||||
BlockParam,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub(crate) struct ValueInfo {
|
||||
pub(crate) kind: ValueKind,
|
||||
pub(crate) result_type: TypeId,
|
||||
}
|
||||
|
||||
id_type! {
|
||||
pub(crate) ValueId(u32);
|
||||
}
|
||||
|
||||
id_type! {
|
||||
pub(crate) BlockId(u32);
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub(crate) enum JumpTarget {
|
||||
Block(BlockId),
|
||||
Ret,
|
||||
}
|
||||
|
||||
pub(crate) const JUMP_TARGETS_INLINE_COUNT: usize = 8;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub(crate) struct BlockInfo {
|
||||
/// A block may optionally have a single parameter, which serves the same role as a phi node in
|
||||
/// SSA.
|
||||
///
|
||||
/// Invariant: If `param` is `Some`, it must point to a `BlockParam` value, not an `Op`.
|
||||
pub(crate) param: Option<ValueId>,
|
||||
/// List of zero or more jump targets to nondeterministically choose from.
|
||||
pub(crate) jump_targets: SmallVec<[JumpTarget; JUMP_TARGETS_INLINE_COUNT]>,
|
||||
/// Optional argument which will be passed to the chosen jump target.
|
||||
pub(crate) target_arg: Option<ValueId>,
|
||||
}
|
||||
|
||||
id_type! {
|
||||
/// Identifies a strongly connected component in the control flow graph.
|
||||
///
|
||||
/// https://en.wikipedia.org/wiki/Strongly_connected_component
|
||||
pub(crate) SccId(u32);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct GraphBuilder {
|
||||
values: OpGraph<ValueId, ValueInfo>,
|
||||
blocks: Blocks<BlockId, ValueId, BlockInfo>,
|
||||
}
|
||||
|
||||
impl GraphBuilder {
|
||||
pub(crate) fn new() -> Self {
|
||||
GraphBuilder {
|
||||
values: OpGraph::new(),
|
||||
blocks: Blocks::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_op(
|
||||
&mut self,
|
||||
block: BlockId,
|
||||
op_kind: OpKind,
|
||||
inputs: &[ValueId],
|
||||
result_type: TypeId,
|
||||
) -> ValueId {
|
||||
let val_id = self.values.add_node(
|
||||
ValueInfo {
|
||||
kind: ValueKind::Op(op_kind),
|
||||
result_type,
|
||||
},
|
||||
inputs,
|
||||
);
|
||||
self.blocks.add_value(block, val_id);
|
||||
val_id
|
||||
}
|
||||
|
||||
pub(crate) fn add_block(&mut self) -> BlockId {
|
||||
self.blocks.add_block(
|
||||
self.values.count().0,
|
||||
BlockInfo {
|
||||
param: None,
|
||||
jump_targets: SmallVec::new(),
|
||||
target_arg: None,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn add_block_with_param(&mut self, param_type: TypeId) -> (BlockId, ValueId) {
|
||||
let param_id = self.values.add_node(
|
||||
ValueInfo {
|
||||
kind: ValueKind::BlockParam,
|
||||
result_type: param_type,
|
||||
},
|
||||
&[],
|
||||
);
|
||||
let block_id = self.blocks.add_block(
|
||||
self.values.count().0,
|
||||
BlockInfo {
|
||||
param: Some(param_id),
|
||||
jump_targets: SmallVec::new(),
|
||||
target_arg: None,
|
||||
},
|
||||
);
|
||||
(block_id, param_id)
|
||||
}
|
||||
|
||||
pub(crate) fn set_jump_targets(
|
||||
&mut self,
|
||||
block: BlockId,
|
||||
target_arg: Option<ValueId>,
|
||||
jump_targets: SmallVec<[JumpTarget; JUMP_TARGETS_INLINE_COUNT]>,
|
||||
) {
|
||||
let info = self.blocks.block_info_mut(block);
|
||||
info.target_arg = target_arg;
|
||||
info.jump_targets = jump_targets;
|
||||
}
|
||||
|
||||
pub(crate) fn values(&self) -> &OpGraph<ValueId, ValueInfo> {
|
||||
&self.values
|
||||
}
|
||||
|
||||
pub(crate) fn blocks(&self) -> &Blocks<BlockId, ValueId, BlockInfo> {
|
||||
&self.blocks
|
||||
}
|
||||
|
||||
pub(crate) fn build(self, entry_block: BlockId) -> Graph {
|
||||
debug_assert!(entry_block < self.blocks.block_count());
|
||||
let rev_sccs = strongly_connected(self.blocks.block_count(), |block| {
|
||||
self.blocks
|
||||
.block_info(block)
|
||||
.jump_targets
|
||||
.iter()
|
||||
.filter_map(|&jump_target| match jump_target {
|
||||
JumpTarget::Ret => None,
|
||||
JumpTarget::Block(target) => Some(target),
|
||||
})
|
||||
});
|
||||
Graph {
|
||||
values: self.values,
|
||||
blocks: self.blocks,
|
||||
entry_block,
|
||||
rev_sccs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct Graph {
|
||||
values: OpGraph<ValueId, ValueInfo>,
|
||||
blocks: Blocks<BlockId, ValueId, BlockInfo>,
|
||||
entry_block: BlockId,
|
||||
|
||||
// Invariant: `rev_sccs` must be stored in *reverse* topological order. If an SCC 'A' can jump
|
||||
// to an SCC 'B', then 'A' must appear *after* 'B' in `rev_sccs`.
|
||||
//
|
||||
// We don't store the SCCs in topological order because control flow graph edges point from
|
||||
// *source block* to *target block*, so running Tarjan's algorithm on the control flow graph
|
||||
// gives us a reverse topological sort rather than a topological sort.
|
||||
rev_sccs: FlatSlices<SccId, SccKind, BlockId>,
|
||||
}
|
||||
|
||||
impl Graph {
|
||||
pub(crate) fn values(&self) -> &OpGraph<ValueId, ValueInfo> {
|
||||
&self.values
|
||||
}
|
||||
|
||||
pub(crate) fn blocks(&self) -> &Blocks<BlockId, ValueId, BlockInfo> {
|
||||
&self.blocks
|
||||
}
|
||||
|
||||
pub(crate) fn entry_block(&self) -> BlockId {
|
||||
self.entry_block
|
||||
}
|
||||
|
||||
pub(crate) fn rev_sccs(&self) -> &FlatSlices<SccId, SccKind, BlockId> {
|
||||
&self.rev_sccs
|
||||
}
|
||||
|
||||
/// Iterate over sccs in topological order.
|
||||
///
|
||||
/// IF an SCC 'A' can jump to an SCC 'B', then 'A' is guaranteed to appear *before* 'B' in the
|
||||
/// returned iterator.
|
||||
pub(crate) fn iter_sccs(&self) -> impl Iterator<Item = Slice<'_, SccKind, BlockId>> + '_ {
|
||||
self.rev_sccs
|
||||
.count()
|
||||
.iter()
|
||||
.rev()
|
||||
.map(move |scc_id| self.rev_sccs.get(scc_id))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct FuncDef {
|
||||
pub(crate) graph: Graph,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct ConstDef {
|
||||
pub(crate) graph: Graph,
|
||||
}
|
5
vendor/morphic_lib/src/lib.rs
vendored
5
vendor/morphic_lib/src/lib.rs
vendored
|
@ -1,10 +1,15 @@
|
|||
#![allow(dead_code)]
|
||||
#![allow(clippy::too_many_arguments)]
|
||||
|
||||
#[macro_use]
|
||||
mod util;
|
||||
|
||||
mod api;
|
||||
mod bindings;
|
||||
mod ir;
|
||||
mod name_cache;
|
||||
mod preprocess;
|
||||
mod render_api_ir;
|
||||
mod type_cache;
|
||||
|
||||
pub use api::*;
|
||||
|
|
26
vendor/morphic_lib/src/name_cache.rs
vendored
Normal file
26
vendor/morphic_lib/src/name_cache.rs
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
use crate::api;
|
||||
use crate::util::id_bi_map::IdBiMap;
|
||||
|
||||
id_type! {
|
||||
pub NamedTypeId(u32);
|
||||
}
|
||||
|
||||
id_type! {
|
||||
pub FuncId(u32);
|
||||
}
|
||||
|
||||
id_type! {
|
||||
pub ConstId(u32);
|
||||
}
|
||||
|
||||
id_type! {
|
||||
pub EntryPointId(u32);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub(crate) struct NameCache {
|
||||
pub(crate) named_types: IdBiMap<NamedTypeId, (api::ModNameBuf, api::TypeNameBuf)>,
|
||||
pub(crate) funcs: IdBiMap<FuncId, (api::ModNameBuf, api::FuncNameBuf)>,
|
||||
pub(crate) consts: IdBiMap<ConstId, (api::ModNameBuf, api::ConstNameBuf)>,
|
||||
pub(crate) entry_points: IdBiMap<EntryPointId, api::EntryPointNameBuf>,
|
||||
}
|
1204
vendor/morphic_lib/src/preprocess.rs
vendored
Normal file
1204
vendor/morphic_lib/src/preprocess.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
22
vendor/morphic_lib/src/type_cache.rs
vendored
Normal file
22
vendor/morphic_lib/src/type_cache.rs
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
use smallvec::SmallVec;
|
||||
|
||||
use crate::name_cache::NamedTypeId;
|
||||
use crate::util::id_bi_map::IdBiMap;
|
||||
|
||||
id_type! {
|
||||
pub TypeId(u32);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum TypeData {
|
||||
Named { named: NamedTypeId },
|
||||
Tuple { fields: SmallVec<[TypeId; 10]> },
|
||||
Union { variants: SmallVec<[TypeId; 10]> },
|
||||
HeapCell,
|
||||
Bag { item: TypeId },
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct TypeCache {
|
||||
pub types: IdBiMap<TypeId, TypeData>,
|
||||
}
|
4
vendor/morphic_lib/src/util/blocks.rs
vendored
4
vendor/morphic_lib/src/util/blocks.rs
vendored
|
@ -75,8 +75,8 @@ impl<BlockId: Id, ValId: Id, BlockInfo> Blocks<BlockId, ValId, BlockInfo> {
|
|||
tail_frag.max_val = next_val;
|
||||
} else {
|
||||
let new_tail = BlockFrag {
|
||||
min_val: val_id,
|
||||
max_val: next_val,
|
||||
min_val: val_id.clone(),
|
||||
max_val: ValId::from_index_or_panic(val_id.to_index() + 1),
|
||||
next: None,
|
||||
};
|
||||
let new_tail_id = self.frags.push(new_tail);
|
||||
|
|
32
vendor/morphic_lib/src/util/bytes_id.rs
vendored
32
vendor/morphic_lib/src/util/bytes_id.rs
vendored
|
@ -4,15 +4,15 @@ macro_rules! bytes_id {
|
|||
$(#[$annot_borrowed:meta])* $borrowed_vis:vis $borrowed:ident;
|
||||
$(#[$annot_owned:meta])* $owned_vis:vis $owned:ident;
|
||||
) => {
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
$(#[$annot_borrowed])*
|
||||
$borrowed_vis struct $borrowed<'a>($borrowed_vis &'a [u8]);
|
||||
|
||||
// The stack-allocated array portion of a `SmallVec` shares a union with two `usize`s, so on
|
||||
// 64-bit platforms we can make the array up to 16 bytes long with no space penalty.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
// On 64-bit platforms we can store up to `23` bytes inline in a `SmallVec` with no space
|
||||
// penalty.
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
$(#[$annot_owned])*
|
||||
$owned_vis struct $owned($owned_vis ::smallvec::SmallVec<[u8; 16]>);
|
||||
$owned_vis struct $owned($owned_vis ::smallvec::SmallVec<[u8; 23]>);
|
||||
|
||||
impl $owned {
|
||||
fn borrowed<'a>(&'a self) -> $borrowed<'a> {
|
||||
|
@ -25,5 +25,27 @@ macro_rules! bytes_id {
|
|||
$owned(::smallvec::SmallVec::from_slice(&borrowed.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ::std::fmt::Debug for $borrowed<'a> {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
// TODO: Consolidate this with render_api_ir.rs
|
||||
write!(f, "{}(\"", stringify!($borrowed))?;
|
||||
for &byte in self.0 {
|
||||
write!(f, "{}", ::std::ascii::escape_default(byte))?;
|
||||
}
|
||||
write!(f, "\")")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::fmt::Debug for $owned {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
// We intentionally use the name of $borrowed to render values of type $owned,
|
||||
// because only the borrowed version of each bytestring-based identifier type are
|
||||
// exposed/documented in the public API, and we use this debug formatting logic to
|
||||
// render public-facing error messages.
|
||||
write!(f, "{:?}", self.borrowed())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
112
vendor/morphic_lib/src/util/flat_slices.rs
vendored
Normal file
112
vendor/morphic_lib/src/util/flat_slices.rs
vendored
Normal file
|
@ -0,0 +1,112 @@
|
|||
use std::borrow::Borrow;
|
||||
use std::fmt;
|
||||
|
||||
use crate::util::id_type::{self, Count, Id};
|
||||
use crate::util::id_vec::IdVec;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
struct SliceData<SliceInfo> {
|
||||
info: SliceInfo,
|
||||
|
||||
/// Determines which slice of the `flat_data` buffer contains the items of this slice.
|
||||
///
|
||||
/// If the *previous* slice has `slice_end_idx == a`, and this slice has `inputs_end_idx == b`,
|
||||
/// then the items of this slice are given by `flat_data[a..b]`.
|
||||
slice_end_idx: usize,
|
||||
}
|
||||
|
||||
/// Conceptually represents a collection of the form `IdVec<SliceId, (SliceInfo, Vec<T>)>`.
|
||||
///
|
||||
/// The notional `Vec<T>` values are actually stored in a single contiguous buffer to reduce the
|
||||
/// number of heap allocations. Because these values are all different slices of the same
|
||||
/// underlying buffer, we refer to each tuple `(SliceInfo, Vec<T>)` as a "slice" for the purposes of
|
||||
/// this data structure's API.
|
||||
///
|
||||
/// The `SliceInfo` parameter should be regarded as optional, and for some purposes it is perfectly
|
||||
/// fine (stylistically speaking) to set `SliceInfo = ()`.
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct FlatSlices<SliceId: Id, SliceInfo, T> {
|
||||
flat_data: Vec<T>,
|
||||
slices: IdVec<SliceId, SliceData<SliceInfo>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Slice<'a, SliceInfo, T> {
|
||||
pub info: &'a SliceInfo,
|
||||
pub items: &'a [T],
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct SliceMut<'a, SliceInfo, T> {
|
||||
pub info: &'a mut SliceInfo,
|
||||
pub items: &'a mut [T],
|
||||
}
|
||||
|
||||
impl<SliceId: Id, SliceInfo, T> Default for FlatSlices<SliceId, SliceInfo, T> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<SliceId: Id, SliceInfo, T> FlatSlices<SliceId, SliceInfo, T> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
flat_data: Vec::new(),
|
||||
slices: IdVec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.slices.len()
|
||||
}
|
||||
|
||||
pub fn count(&self) -> Count<SliceId> {
|
||||
self.slices.count()
|
||||
}
|
||||
|
||||
pub fn push_slice(&mut self, info: SliceInfo, slice: &[T]) -> SliceId
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
self.flat_data.extend_from_slice(slice);
|
||||
self.slices.push(SliceData {
|
||||
info,
|
||||
slice_end_idx: self.flat_data.len(),
|
||||
})
|
||||
}
|
||||
|
||||
fn data_range(&self, idx: SliceId) -> (usize, usize) {
|
||||
let start = match id_type::decrement(idx.clone()) {
|
||||
None => 0,
|
||||
Some(prev_idx) => self.slices[prev_idx].slice_end_idx,
|
||||
};
|
||||
let end = self.slices[idx].slice_end_idx;
|
||||
(start, end)
|
||||
}
|
||||
|
||||
pub fn get<I: Borrow<SliceId>>(&self, idx: I) -> Slice<SliceInfo, T> {
|
||||
let (start, end) = self.data_range(idx.borrow().clone());
|
||||
Slice {
|
||||
info: &self.slices[idx].info,
|
||||
items: &self.flat_data[start..end],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_mut<I: Borrow<SliceId>>(&mut self, idx: I) -> SliceMut<SliceInfo, T> {
|
||||
let (start, end) = self.data_range(idx.borrow().clone());
|
||||
SliceMut {
|
||||
info: &mut self.slices[idx].info,
|
||||
items: &mut self.flat_data[start..end],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<SliceId: Id + fmt::Debug, SliceInfo: fmt::Debug, T: fmt::Debug> fmt::Debug
|
||||
for FlatSlices<SliceId, SliceInfo, T>
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_map()
|
||||
.entries(self.count().iter().map(|i| (i.clone(), self.get(i))))
|
||||
.finish()
|
||||
}
|
||||
}
|
12
vendor/morphic_lib/src/util/id_bi_map.rs
vendored
12
vendor/morphic_lib/src/util/id_bi_map.rs
vendored
|
@ -52,6 +52,18 @@ impl<K: Id, V: Hash + Eq + Clone> IdBiMap<K, V> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Insert a value into the bi-map, or get its key if it is already present.
|
||||
pub fn get_or_insert(&mut self, val: V) -> K {
|
||||
match self.val_to_key.entry(val) {
|
||||
Entry::Occupied(occupied) => occupied.get().clone(),
|
||||
Entry::Vacant(vacant) => {
|
||||
let new_index = self.key_to_val.push(vacant.key().clone());
|
||||
vacant.insert(new_index.clone());
|
||||
new_index
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_by_val(&self, val: &V) -> Option<K> {
|
||||
self.val_to_key.get(val).cloned()
|
||||
}
|
||||
|
|
9
vendor/morphic_lib/src/util/id_type.rs
vendored
9
vendor/morphic_lib/src/util/id_type.rs
vendored
|
@ -98,7 +98,14 @@ impl<T: PartialOrd> PartialOrd<T> for Count<T> {
|
|||
}
|
||||
|
||||
impl<T: Id> Count<T> {
|
||||
pub fn iter(&self) -> impl Iterator<Item = T> {
|
||||
pub fn iter(&self) -> impl DoubleEndedIterator<Item = T> {
|
||||
(0..self.0.to_index()).map(T::from_index_unchecked)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decrement<T: Id>(id: T) -> Option<T> {
|
||||
match id.to_index() {
|
||||
0 => None,
|
||||
index => Some(T::from_index_unchecked(index - 1)),
|
||||
}
|
||||
}
|
||||
|
|
7
vendor/morphic_lib/src/util/id_vec.rs
vendored
7
vendor/morphic_lib/src/util/id_vec.rs
vendored
|
@ -107,6 +107,13 @@ impl<K: Id, V> IdVec<K, V> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
IdVec {
|
||||
key: PhantomData,
|
||||
items: Vec::with_capacity(capacity),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_items(items: Vec<V>) -> Self {
|
||||
K::assert_in_range(items.len());
|
||||
IdVec {
|
||||
|
|
2
vendor/morphic_lib/src/util/mod.rs
vendored
2
vendor/morphic_lib/src/util/mod.rs
vendored
|
@ -8,7 +8,9 @@ pub mod bytes_id;
|
|||
pub mod forward_trait;
|
||||
|
||||
pub mod blocks;
|
||||
pub mod flat_slices;
|
||||
pub mod id_bi_map;
|
||||
pub mod id_vec;
|
||||
pub mod op_graph;
|
||||
pub mod replace_none;
|
||||
pub mod strongly_connected;
|
||||
|
|
51
vendor/morphic_lib/src/util/op_graph.rs
vendored
51
vendor/morphic_lib/src/util/op_graph.rs
vendored
|
@ -1,18 +1,7 @@
|
|||
use std::borrow::Borrow;
|
||||
|
||||
use crate::util::flat_slices::{FlatSlices, Slice};
|
||||
use crate::util::id_type::{Count, Id};
|
||||
use crate::util::id_vec::IdVec;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
struct NodeData<Op> {
|
||||
op: Op,
|
||||
|
||||
/// Determines which slice of the `flat_inputs` buffer contains the inputs to this node.
|
||||
///
|
||||
/// If the *previous* op has `inputs_end_idx == a`, and this node has `inputs_end_idx == b`,
|
||||
/// then the inputs to this node are given by `flat_inputs[a..b]`.
|
||||
inputs_end_idx: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Node<'a, K, Op> {
|
||||
|
@ -27,47 +16,45 @@ pub struct Node<'a, K, Op> {
|
|||
///
|
||||
/// The input lists are actually stored in a single contiguous buffer to reduce the number of heap
|
||||
/// allocations.
|
||||
///
|
||||
/// This is essentially just a newtype wrapper around `FlatSlices`. We use this wrapper to
|
||||
/// represent op graphs (instead of using `FlatSlices` directly) just so that the names of types,
|
||||
/// functions, and fields in the API are more meaningful for this use case.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct OpGraph<K: Id, Op> {
|
||||
flat_inputs: Vec<K>,
|
||||
nodes: IdVec<K, NodeData<Op>>,
|
||||
inner: FlatSlices<K, Op, K>,
|
||||
}
|
||||
|
||||
impl<K: Id, Op> Default for OpGraph<K, Op> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Id, Op> OpGraph<K, Op> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
flat_inputs: Vec::new(),
|
||||
nodes: IdVec::new(),
|
||||
inner: FlatSlices::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.nodes.len()
|
||||
self.inner.len()
|
||||
}
|
||||
|
||||
pub fn count(&self) -> Count<K> {
|
||||
self.nodes.count()
|
||||
self.inner.count()
|
||||
}
|
||||
|
||||
pub fn add_node(&mut self, op: Op, inputs: &[K]) -> K {
|
||||
self.flat_inputs.extend_from_slice(inputs);
|
||||
let node = NodeData {
|
||||
op,
|
||||
inputs_end_idx: self.flat_inputs.len(),
|
||||
};
|
||||
self.nodes.push(node)
|
||||
self.inner.push_slice(op, inputs)
|
||||
}
|
||||
|
||||
pub fn node<I: Borrow<K>>(&self, idx: I) -> Node<K, Op> {
|
||||
let node = &self.nodes[idx.borrow()];
|
||||
let inputs_start_idx = if idx.borrow().to_index() == 0 {
|
||||
0
|
||||
} else {
|
||||
self.nodes[K::from_index_unchecked(idx.borrow().to_index() - 1)].inputs_end_idx
|
||||
};
|
||||
let Slice { info, items } = self.inner.get(idx);
|
||||
Node {
|
||||
op: &node.op,
|
||||
inputs: &self.flat_inputs[inputs_start_idx..node.inputs_end_idx],
|
||||
op: info,
|
||||
inputs: items,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
153
vendor/morphic_lib/src/util/strongly_connected.rs
vendored
Normal file
153
vendor/morphic_lib/src/util/strongly_connected.rs
vendored
Normal file
|
@ -0,0 +1,153 @@
|
|||
use crate::util::flat_slices::FlatSlices;
|
||||
use crate::util::id_type::{Count, Id};
|
||||
use crate::util::id_vec::IdVec;
|
||||
use crate::util::replace_none::replace_none;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum SccKind {
|
||||
Acyclic,
|
||||
Cyclic,
|
||||
}
|
||||
|
||||
pub fn strongly_connected<SccId, NodeId, NodeSuccessors>(
|
||||
node_count: Count<NodeId>,
|
||||
mut node_successors: impl FnMut(NodeId) -> NodeSuccessors,
|
||||
) -> FlatSlices<SccId, SccKind, NodeId>
|
||||
where
|
||||
SccId: Id,
|
||||
NodeId: Id + Eq + Copy,
|
||||
NodeSuccessors: Iterator<Item = NodeId>,
|
||||
{
|
||||
// We use Tarjan's algorithm, performing the depth-first search using an explicit Vec-based
|
||||
// stack instead of recursion to avoid stack overflows on large graphs.
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum NodeState {
|
||||
Unvisited,
|
||||
OnSearchStack { index: u32, low_link: u32 },
|
||||
OnSccStack { index: u32 },
|
||||
Complete,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum Action<NodeId> {
|
||||
TryVisit {
|
||||
parent: Option<NodeId>,
|
||||
node: NodeId,
|
||||
},
|
||||
FinishVisit {
|
||||
parent: Option<NodeId>,
|
||||
node: NodeId,
|
||||
},
|
||||
}
|
||||
|
||||
let mut sccs = FlatSlices::new();
|
||||
|
||||
let mut node_states = IdVec::filled_with(node_count, || NodeState::Unvisited);
|
||||
let mut node_self_loops = IdVec::filled_with(node_count, || None);
|
||||
let mut scc_stack = Vec::new();
|
||||
let mut search_stack = Vec::new();
|
||||
let mut next_index = 0;
|
||||
|
||||
for search_root in node_count.iter() {
|
||||
search_stack.push(Action::TryVisit {
|
||||
parent: None,
|
||||
node: search_root,
|
||||
});
|
||||
while let Some(action) = search_stack.pop() {
|
||||
match action {
|
||||
Action::TryVisit { parent, node } => match node_states[node] {
|
||||
NodeState::Unvisited => {
|
||||
node_states[node] = NodeState::OnSearchStack {
|
||||
index: next_index,
|
||||
low_link: next_index,
|
||||
};
|
||||
next_index += 1;
|
||||
scc_stack.push(node);
|
||||
|
||||
search_stack.push(Action::FinishVisit { parent, node });
|
||||
// We need to explicitly track self-loops so that when we obtain a size-1
|
||||
// SCC we can determine if it's cyclic or acyclic.
|
||||
let mut has_self_loop = false;
|
||||
for successor in node_successors(node) {
|
||||
if successor == node {
|
||||
has_self_loop = true;
|
||||
}
|
||||
search_stack.push(Action::TryVisit {
|
||||
parent: Some(node),
|
||||
node: successor,
|
||||
});
|
||||
}
|
||||
replace_none(&mut node_self_loops[node], has_self_loop).unwrap();
|
||||
}
|
||||
|
||||
NodeState::OnSearchStack { index, low_link: _ }
|
||||
| NodeState::OnSccStack { index } => {
|
||||
if let Some(parent) = parent {
|
||||
if let NodeState::OnSearchStack {
|
||||
index: _,
|
||||
low_link: parent_low_link,
|
||||
} = &mut node_states[parent]
|
||||
{
|
||||
*parent_low_link = (*parent_low_link).min(index);
|
||||
} else {
|
||||
unreachable!("parent should be on search stack");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NodeState::Complete => {}
|
||||
},
|
||||
|
||||
Action::FinishVisit { parent, node } => {
|
||||
let (index, low_link) =
|
||||
if let NodeState::OnSearchStack { index, low_link } = node_states[node] {
|
||||
(index, low_link)
|
||||
} else {
|
||||
unreachable!("node should be on search stack");
|
||||
};
|
||||
|
||||
node_states[node] = NodeState::OnSccStack { index };
|
||||
|
||||
if let Some(parent) = parent {
|
||||
if let NodeState::OnSearchStack {
|
||||
index: _,
|
||||
low_link: parent_low_link,
|
||||
} = &mut node_states[parent]
|
||||
{
|
||||
*parent_low_link = (*parent_low_link).min(low_link);
|
||||
} else {
|
||||
unreachable!("parent should be on search stack")
|
||||
}
|
||||
}
|
||||
|
||||
if low_link == index {
|
||||
let mut scc_start = scc_stack.len();
|
||||
loop {
|
||||
scc_start -= 1;
|
||||
let scc_node = scc_stack[scc_start];
|
||||
debug_assert!(matches!(
|
||||
node_states[scc_node],
|
||||
NodeState::OnSccStack { .. }
|
||||
));
|
||||
node_states[scc_node] = NodeState::Complete;
|
||||
if scc_node == node {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let scc_slice = &scc_stack[scc_start..];
|
||||
let scc_kind = if scc_slice.len() == 1 && !node_self_loops[node].unwrap() {
|
||||
SccKind::Acyclic
|
||||
} else {
|
||||
SccKind::Cyclic
|
||||
};
|
||||
sccs.push_slice(scc_kind, scc_slice);
|
||||
scc_stack.truncate(scc_start);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sccs
|
||||
}
|
77
vendor/morphic_lib/tests/basic.rs
vendored
Normal file
77
vendor/morphic_lib/tests/basic.rs
vendored
Normal file
|
@ -0,0 +1,77 @@
|
|||
use morphic_lib::{
|
||||
BlockExpr, CalleeSpecVar, EntryPointName, Error, ExprContext, FuncDefBuilder, FuncName,
|
||||
ModDefBuilder, ModName, ProgramBuilder, TypeContext, UpdateModeVar,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_basic() {
|
||||
fn run() -> Result<(), Error> {
|
||||
let func1_def = {
|
||||
let mut f = FuncDefBuilder::new();
|
||||
let b = f.add_block();
|
||||
f.add_update(b, UpdateModeVar(b"var1"), f.get_argument())?;
|
||||
let ret = f.add_make_tuple(b, &[])?;
|
||||
let arg_type = f.add_heap_cell_type();
|
||||
let ret_type = f.add_tuple_type(&[])?;
|
||||
f.build(arg_type, ret_type, BlockExpr(b, ret))?
|
||||
};
|
||||
|
||||
let func2_def = {
|
||||
let mut f = FuncDefBuilder::new();
|
||||
let b = f.add_block();
|
||||
let cell = f.add_new_heap_cell(b)?;
|
||||
let ret = f.add_call(
|
||||
b,
|
||||
CalleeSpecVar(b"var2"),
|
||||
ModName(b"main_mod"),
|
||||
FuncName(b"func1"),
|
||||
cell,
|
||||
)?;
|
||||
let unit_type = f.add_tuple_type(&[])?;
|
||||
f.build(unit_type, unit_type, BlockExpr(b, ret))?
|
||||
};
|
||||
|
||||
let main_mod = {
|
||||
let mut m = ModDefBuilder::new();
|
||||
m.add_func(FuncName(b"func1"), func1_def)?;
|
||||
m.add_func(FuncName(b"func2"), func2_def)?;
|
||||
m.build()?
|
||||
};
|
||||
|
||||
let program = {
|
||||
let mut p = ProgramBuilder::new();
|
||||
p.add_mod(ModName(b"main_mod"), main_mod)?;
|
||||
p.add_entry_point(
|
||||
EntryPointName(b"main"),
|
||||
ModName(b"main_mod"),
|
||||
FuncName(b"func2"),
|
||||
)?;
|
||||
p.build()?
|
||||
};
|
||||
|
||||
let program_sol = morphic_lib::solve(program)?;
|
||||
|
||||
let (_, _, func2_spec) = program_sol.entry_point_solution(EntryPointName(b"main"))?;
|
||||
|
||||
let main_mod_sol = program_sol.mod_solutions(ModName(b"main_mod"))?;
|
||||
|
||||
let func2_sol = main_mod_sol
|
||||
.func_solutions(FuncName(b"func2"))?
|
||||
.spec(&func2_spec)?;
|
||||
|
||||
let func1_spec = func2_sol.callee_spec(CalleeSpecVar(b"var2"))?;
|
||||
|
||||
let func1_sol = main_mod_sol
|
||||
.func_solutions(FuncName(b"func1"))?
|
||||
.spec(&func1_spec)?;
|
||||
|
||||
let _update_mode = func1_sol.update_mode(UpdateModeVar(b"var1"))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
let result = run();
|
||||
if let Err(err) = result {
|
||||
panic!("error: {}", err);
|
||||
}
|
||||
}
|
95
vendor/morphic_lib/tests/recursive.rs
vendored
Normal file
95
vendor/morphic_lib/tests/recursive.rs
vendored
Normal file
|
@ -0,0 +1,95 @@
|
|||
use morphic_lib::{
|
||||
BlockExpr, CalleeSpecVar, EntryPointName, Error, ExprContext, FuncDefBuilder, FuncName,
|
||||
ModDefBuilder, ModName, ProgramBuilder, TypeContext, UpdateModeVar,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_recursive() {
|
||||
fn run() -> Result<(), Error> {
|
||||
let rec_def = {
|
||||
let mut f = FuncDefBuilder::new();
|
||||
let b = f.add_block();
|
||||
let arg = f.get_argument();
|
||||
let case1 = {
|
||||
let b = f.add_block();
|
||||
BlockExpr(b, arg)
|
||||
};
|
||||
let case2 = {
|
||||
let b = f.add_block();
|
||||
f.add_update(b, UpdateModeVar(b"mode"), arg)?;
|
||||
let new = f.add_new_heap_cell(b)?;
|
||||
let result = f.add_call(
|
||||
b,
|
||||
CalleeSpecVar(b"call"),
|
||||
ModName(b"main"),
|
||||
FuncName(b"rec"),
|
||||
new,
|
||||
)?;
|
||||
BlockExpr(b, result)
|
||||
};
|
||||
let result = f.add_choice(b, &[case1, case2])?;
|
||||
let heap_cell_type = f.add_heap_cell_type();
|
||||
f.build(heap_cell_type, heap_cell_type, BlockExpr(b, result))?
|
||||
};
|
||||
|
||||
let main_def = {
|
||||
let mut f = FuncDefBuilder::new();
|
||||
let b = f.add_block();
|
||||
let init = f.add_new_heap_cell(b)?;
|
||||
let final_ = f.add_call(
|
||||
b,
|
||||
CalleeSpecVar(b"call"),
|
||||
ModName(b"main"),
|
||||
FuncName(b"rec"),
|
||||
init,
|
||||
)?;
|
||||
f.add_touch(b, final_)?;
|
||||
let result = f.add_make_tuple(b, &[])?;
|
||||
let unit_type = f.add_tuple_type(&[])?;
|
||||
f.build(unit_type, unit_type, BlockExpr(b, result))?
|
||||
};
|
||||
|
||||
let mod_ = {
|
||||
let mut m = ModDefBuilder::new();
|
||||
m.add_func(FuncName(b"rec"), rec_def)?;
|
||||
m.add_func(FuncName(b"main"), main_def)?;
|
||||
m.build()?
|
||||
};
|
||||
|
||||
let program = {
|
||||
let mut p = ProgramBuilder::new();
|
||||
p.add_mod(ModName(b"main"), mod_)?;
|
||||
p.add_entry_point(
|
||||
EntryPointName(b"entry"),
|
||||
ModName(b"main"),
|
||||
FuncName(b"main"),
|
||||
)?;
|
||||
p.build()?
|
||||
};
|
||||
|
||||
let program_sol = morphic_lib::solve(program)?;
|
||||
|
||||
let (_, _, main_spec) = program_sol.entry_point_solution(EntryPointName(b"entry"))?;
|
||||
|
||||
let main_mod_sol = program_sol.mod_solutions(ModName(b"main"))?;
|
||||
|
||||
let main_sol = main_mod_sol
|
||||
.func_solutions(FuncName(b"main"))?
|
||||
.spec(&main_spec)?;
|
||||
|
||||
let rec_spec = main_sol.callee_spec(CalleeSpecVar(b"call"))?;
|
||||
|
||||
let rec_sol = main_mod_sol
|
||||
.func_solutions(FuncName(b"rec"))?
|
||||
.spec(&rec_spec)?;
|
||||
|
||||
let _update_mode = rec_sol.update_mode(UpdateModeVar(b"mode"))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
let result = run();
|
||||
if let Err(err) = result {
|
||||
panic!("error: {}", err);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue