mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 14:54:47 +00:00
Merge branch 'trunk' into document-only-exposed-values
This commit is contained in:
commit
64aba9ed46
25 changed files with 1248 additions and 254 deletions
9
Cargo.lock
generated
9
Cargo.lock
generated
|
@ -1763,9 +1763,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.95"
|
version = "0.2.96"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "789da6d93f1b866ffe175afc5322a4d76c038605a1c3319bb57b06967ca98a36"
|
checksum = "5600b4e6efc5421841a2138a6b082e07fe12f9aaa12783d50e5d13325b26b4fc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libloading"
|
name = "libloading"
|
||||||
|
@ -2545,9 +2545,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "profiling"
|
name = "profiling"
|
||||||
version = "1.0.1"
|
version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c3a66d5e88679f2720126c11ee29da07a08f094eac52306b066edd7d393752d6"
|
checksum = "0a7c000c0ce9d9bb94c0fbacdf20e5087fbe652c556ffb2c9387d980e17d51fb"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pulldown-cmark"
|
name = "pulldown-cmark"
|
||||||
|
@ -3219,6 +3219,7 @@ dependencies = [
|
||||||
"inlinable_string",
|
"inlinable_string",
|
||||||
"libc",
|
"libc",
|
||||||
"maplit",
|
"maplit",
|
||||||
|
"morphic_lib",
|
||||||
"pretty_assertions 0.5.1",
|
"pretty_assertions 0.5.1",
|
||||||
"quickcheck 0.8.5",
|
"quickcheck 0.8.5",
|
||||||
"quickcheck_macros 0.8.0",
|
"quickcheck_macros 0.8.0",
|
||||||
|
|
|
@ -2298,7 +2298,7 @@ fn dict_get(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
|
|
||||||
let make_err = tag(
|
let make_err = tag(
|
||||||
"Err",
|
"Err",
|
||||||
vec![tag("OutOfBounds", Vec::new(), var_store)],
|
vec![tag("KeyNotFound", Vec::new(), var_store)],
|
||||||
var_store,
|
var_store,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,6 @@ pub enum Expr {
|
||||||
Float(Variable, Variable, f64),
|
Float(Variable, Variable, f64),
|
||||||
Str(InlinableString),
|
Str(InlinableString),
|
||||||
List {
|
List {
|
||||||
list_var: Variable, // required for uniqueness of the list
|
|
||||||
elem_var: Variable,
|
elem_var: Variable,
|
||||||
loc_elems: Vec<Located<Expr>>,
|
loc_elems: Vec<Located<Expr>>,
|
||||||
},
|
},
|
||||||
|
@ -304,7 +303,6 @@ pub fn canonicalize_expr<'a>(
|
||||||
if loc_elems.is_empty() {
|
if loc_elems.is_empty() {
|
||||||
(
|
(
|
||||||
List {
|
List {
|
||||||
list_var: var_store.fresh(),
|
|
||||||
elem_var: var_store.fresh(),
|
elem_var: var_store.fresh(),
|
||||||
loc_elems: Vec::new(),
|
loc_elems: Vec::new(),
|
||||||
},
|
},
|
||||||
|
@ -331,7 +329,6 @@ pub fn canonicalize_expr<'a>(
|
||||||
|
|
||||||
(
|
(
|
||||||
List {
|
List {
|
||||||
list_var: var_store.fresh(),
|
|
||||||
elem_var: var_store.fresh(),
|
elem_var: var_store.fresh(),
|
||||||
loc_elems: can_elems,
|
loc_elems: can_elems,
|
||||||
},
|
},
|
||||||
|
@ -1234,7 +1231,6 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
|
||||||
| other @ ForeignCall { .. } => other,
|
| other @ ForeignCall { .. } => other,
|
||||||
|
|
||||||
List {
|
List {
|
||||||
list_var,
|
|
||||||
elem_var,
|
elem_var,
|
||||||
loc_elems,
|
loc_elems,
|
||||||
} => {
|
} => {
|
||||||
|
@ -1250,7 +1246,6 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
|
||||||
}
|
}
|
||||||
|
|
||||||
List {
|
List {
|
||||||
list_var,
|
|
||||||
elem_var,
|
elem_var,
|
||||||
loc_elems: new_elems,
|
loc_elems: new_elems,
|
||||||
}
|
}
|
||||||
|
|
|
@ -220,7 +220,6 @@ pub fn constrain_expr(
|
||||||
List {
|
List {
|
||||||
elem_var,
|
elem_var,
|
||||||
loc_elems,
|
loc_elems,
|
||||||
list_var: _unused,
|
|
||||||
} => {
|
} => {
|
||||||
if loc_elems.is_empty() {
|
if loc_elems.is_empty() {
|
||||||
exists(
|
exists(
|
||||||
|
|
|
@ -16,6 +16,7 @@ roc_builtins = { path = "../builtins" }
|
||||||
roc_unify = { path = "../unify" }
|
roc_unify = { path = "../unify" }
|
||||||
roc_solve = { path = "../solve" }
|
roc_solve = { path = "../solve" }
|
||||||
roc_mono = { path = "../mono" }
|
roc_mono = { path = "../mono" }
|
||||||
|
morphic_lib = { path = "../../vendor/morphic_lib" }
|
||||||
im = "14" # im and im-rc should always have the same version!
|
im = "14" # im and im-rc should always have the same version!
|
||||||
im-rc = "14" # im and im-rc should always have the same version!
|
im-rc = "14" # im and im-rc should always have the same version!
|
||||||
bumpalo = { version = "3.6.1", features = ["collections"] }
|
bumpalo = { version = "3.6.1", features = ["collections"] }
|
||||||
|
|
|
@ -42,6 +42,7 @@ use inkwell::values::{
|
||||||
};
|
};
|
||||||
use inkwell::OptimizationLevel;
|
use inkwell::OptimizationLevel;
|
||||||
use inkwell::{AddressSpace, IntPredicate};
|
use inkwell::{AddressSpace, IntPredicate};
|
||||||
|
use morphic_lib::{CalleeSpecVar, FuncName, FuncSpec, FuncSpecSolutions, ModSolutions};
|
||||||
use roc_builtins::bitcode;
|
use roc_builtins::bitcode;
|
||||||
use roc_collections::all::{ImMap, MutMap, MutSet};
|
use roc_collections::all::{ImMap, MutMap, MutSet};
|
||||||
use roc_module::ident::TagName;
|
use roc_module::ident::TagName;
|
||||||
|
@ -583,22 +584,35 @@ pub fn construct_optimization_passes<'a>(
|
||||||
(mpm, fpm)
|
(mpm, fpm)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn promote_to_main_function<'a, 'ctx, 'env>(
|
fn promote_to_main_function<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
mod_solutions: &'a ModSolutions,
|
||||||
symbol: Symbol,
|
symbol: Symbol,
|
||||||
layout: TopLevelFunctionLayout<'a>,
|
top_level: TopLevelFunctionLayout<'a>,
|
||||||
) -> (&'static str, FunctionValue<'ctx>) {
|
) -> (&'static str, FunctionValue<'ctx>) {
|
||||||
let fn_name = layout_ids
|
let it = top_level.arguments.iter().copied();
|
||||||
.get(symbol, &(env.arena.alloc(layout).full()))
|
let bytes = roc_mono::alias_analysis::func_name_bytes_help(symbol, it, top_level.result);
|
||||||
.to_symbol_string(symbol, &env.interns);
|
let func_name = FuncName(&bytes);
|
||||||
|
let func_solutions = mod_solutions.func_solutions(func_name).unwrap();
|
||||||
|
|
||||||
let roc_main_fn = env.module.get_function(&fn_name).unwrap();
|
let mut it = func_solutions.specs();
|
||||||
|
let func_spec = it.next().unwrap();
|
||||||
|
debug_assert!(
|
||||||
|
it.next().is_none(),
|
||||||
|
"we expect only one specialization of this symbol"
|
||||||
|
);
|
||||||
|
|
||||||
|
let roc_main_fn = function_value_by_func_spec(env, *func_spec, symbol, Layout::Struct(&[]));
|
||||||
|
|
||||||
let main_fn_name = "$Test.main";
|
let main_fn_name = "$Test.main";
|
||||||
|
|
||||||
// Add main to the module.
|
// Add main to the module.
|
||||||
let main_fn = expose_function_to_host_help(env, roc_main_fn, main_fn_name);
|
let main_fn = expose_function_to_host_help(
|
||||||
|
env,
|
||||||
|
&inlinable_string::InlinableString::from(main_fn_name),
|
||||||
|
roc_main_fn,
|
||||||
|
main_fn_name,
|
||||||
|
);
|
||||||
|
|
||||||
(main_fn_name, main_fn)
|
(main_fn_name, main_fn)
|
||||||
}
|
}
|
||||||
|
@ -772,6 +786,7 @@ pub fn build_exp_literal<'a, 'ctx, 'env>(
|
||||||
pub fn build_exp_call<'a, 'ctx, 'env>(
|
pub fn build_exp_call<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
|
func_spec_solutions: &FuncSpecSolutions,
|
||||||
scope: &mut Scope<'a, 'ctx>,
|
scope: &mut Scope<'a, 'ctx>,
|
||||||
parent: FunctionValue<'ctx>,
|
parent: FunctionValue<'ctx>,
|
||||||
layout: &Layout<'a>,
|
layout: &Layout<'a>,
|
||||||
|
@ -784,7 +799,10 @@ pub fn build_exp_call<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
match call_type {
|
match call_type {
|
||||||
CallType::ByName {
|
CallType::ByName {
|
||||||
name, full_layout, ..
|
name,
|
||||||
|
full_layout,
|
||||||
|
specialization_id,
|
||||||
|
..
|
||||||
} => {
|
} => {
|
||||||
let mut arg_tuples: Vec<BasicValueEnum> =
|
let mut arg_tuples: Vec<BasicValueEnum> =
|
||||||
Vec::with_capacity_in(arguments.len(), env.arena);
|
Vec::with_capacity_in(arguments.len(), env.arena);
|
||||||
|
@ -793,12 +811,15 @@ pub fn build_exp_call<'a, 'ctx, 'env>(
|
||||||
arg_tuples.push(load_symbol(scope, symbol));
|
arg_tuples.push(load_symbol(scope, symbol));
|
||||||
}
|
}
|
||||||
|
|
||||||
call_with_args(
|
let bytes = specialization_id.to_bytes();
|
||||||
|
let callee_var = CalleeSpecVar(&bytes);
|
||||||
|
let func_spec = func_spec_solutions.callee_spec(callee_var).unwrap();
|
||||||
|
|
||||||
|
roc_call_with_args(
|
||||||
env,
|
env,
|
||||||
layout_ids,
|
|
||||||
&full_layout,
|
&full_layout,
|
||||||
*name,
|
*name,
|
||||||
parent,
|
func_spec,
|
||||||
arg_tuples.into_bump_slice(),
|
arg_tuples.into_bump_slice(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -811,16 +832,26 @@ pub fn build_exp_call<'a, 'ctx, 'env>(
|
||||||
op,
|
op,
|
||||||
closure_layout,
|
closure_layout,
|
||||||
function_owns_closure_data,
|
function_owns_closure_data,
|
||||||
} => run_higher_order_low_level(
|
specialization_id,
|
||||||
env,
|
..
|
||||||
layout_ids,
|
} => {
|
||||||
scope,
|
let bytes = specialization_id.to_bytes();
|
||||||
layout,
|
let callee_var = CalleeSpecVar(&bytes);
|
||||||
*op,
|
let func_spec = func_spec_solutions.callee_spec(callee_var).unwrap();
|
||||||
*closure_layout,
|
// let fn_val = function_value_by_func_spec(env, func_spec, symbol, *layout);
|
||||||
*function_owns_closure_data,
|
|
||||||
arguments,
|
run_higher_order_low_level(
|
||||||
),
|
env,
|
||||||
|
layout_ids,
|
||||||
|
scope,
|
||||||
|
layout,
|
||||||
|
*op,
|
||||||
|
*closure_layout,
|
||||||
|
func_spec,
|
||||||
|
*function_owns_closure_data,
|
||||||
|
arguments,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
CallType::Foreign {
|
CallType::Foreign {
|
||||||
foreign_symbol,
|
foreign_symbol,
|
||||||
|
@ -831,6 +862,7 @@ pub fn build_exp_call<'a, 'ctx, 'env>(
|
||||||
build_foreign_symbol(
|
build_foreign_symbol(
|
||||||
env,
|
env,
|
||||||
layout_ids,
|
layout_ids,
|
||||||
|
func_spec_solutions,
|
||||||
scope,
|
scope,
|
||||||
parent,
|
parent,
|
||||||
foreign_symbol,
|
foreign_symbol,
|
||||||
|
@ -845,6 +877,7 @@ pub fn build_exp_call<'a, 'ctx, 'env>(
|
||||||
pub fn build_exp_expr<'a, 'ctx, 'env>(
|
pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
|
func_spec_solutions: &FuncSpecSolutions,
|
||||||
scope: &mut Scope<'a, 'ctx>,
|
scope: &mut Scope<'a, 'ctx>,
|
||||||
parent: FunctionValue<'ctx>,
|
parent: FunctionValue<'ctx>,
|
||||||
layout: &Layout<'a>,
|
layout: &Layout<'a>,
|
||||||
|
@ -855,7 +888,15 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
match expr {
|
match expr {
|
||||||
Literal(literal) => build_exp_literal(env, layout, literal),
|
Literal(literal) => build_exp_literal(env, layout, literal),
|
||||||
|
|
||||||
Call(call) => build_exp_call(env, layout_ids, scope, parent, layout, call),
|
Call(call) => build_exp_call(
|
||||||
|
env,
|
||||||
|
layout_ids,
|
||||||
|
func_spec_solutions,
|
||||||
|
scope,
|
||||||
|
parent,
|
||||||
|
layout,
|
||||||
|
call,
|
||||||
|
),
|
||||||
|
|
||||||
Struct(sorted_fields) => {
|
Struct(sorted_fields) => {
|
||||||
let ctx = env.context;
|
let ctx = env.context;
|
||||||
|
@ -1802,6 +1843,7 @@ fn list_literal<'a, 'ctx, 'env>(
|
||||||
fn invoke_roc_function<'a, 'ctx, 'env>(
|
fn invoke_roc_function<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
|
func_spec_solutions: &FuncSpecSolutions,
|
||||||
scope: &mut Scope<'a, 'ctx>,
|
scope: &mut Scope<'a, 'ctx>,
|
||||||
parent: FunctionValue<'ctx>,
|
parent: FunctionValue<'ctx>,
|
||||||
symbol: Symbol,
|
symbol: Symbol,
|
||||||
|
@ -1846,7 +1888,7 @@ fn invoke_roc_function<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
scope.insert(symbol, (layout, call_result));
|
scope.insert(symbol, (layout, call_result));
|
||||||
|
|
||||||
build_exp_stmt(env, layout_ids, scope, parent, pass);
|
build_exp_stmt(env, layout_ids, func_spec_solutions, scope, parent, pass);
|
||||||
|
|
||||||
scope.remove(&symbol);
|
scope.remove(&symbol);
|
||||||
}
|
}
|
||||||
|
@ -1876,7 +1918,7 @@ fn invoke_roc_function<'a, 'ctx, 'env>(
|
||||||
(Layout::Struct(&[]), exception_object),
|
(Layout::Struct(&[]), exception_object),
|
||||||
);
|
);
|
||||||
|
|
||||||
build_exp_stmt(env, layout_ids, scope, parent, fail);
|
build_exp_stmt(env, layout_ids, func_spec_solutions, scope, parent, fail);
|
||||||
}
|
}
|
||||||
|
|
||||||
call_result
|
call_result
|
||||||
|
@ -1911,6 +1953,7 @@ fn decrement_with_size_check<'a, 'ctx, 'env>(
|
||||||
pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
|
func_spec_solutions: &FuncSpecSolutions,
|
||||||
scope: &mut Scope<'a, 'ctx>,
|
scope: &mut Scope<'a, 'ctx>,
|
||||||
parent: FunctionValue<'ctx>,
|
parent: FunctionValue<'ctx>,
|
||||||
stmt: &roc_mono::ir::Stmt<'a>,
|
stmt: &roc_mono::ir::Stmt<'a>,
|
||||||
|
@ -1935,7 +1978,15 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||||
for (symbol, expr, layout) in queue {
|
for (symbol, expr, layout) in queue {
|
||||||
debug_assert!(layout != &Layout::RecursivePointer);
|
debug_assert!(layout != &Layout::RecursivePointer);
|
||||||
|
|
||||||
let val = build_exp_expr(env, layout_ids, scope, parent, layout, &expr);
|
let val = build_exp_expr(
|
||||||
|
env,
|
||||||
|
layout_ids,
|
||||||
|
func_spec_solutions,
|
||||||
|
scope,
|
||||||
|
parent,
|
||||||
|
layout,
|
||||||
|
&expr,
|
||||||
|
);
|
||||||
|
|
||||||
// Make a new scope which includes the binding we just encountered.
|
// Make a new scope which includes the binding we just encountered.
|
||||||
// This should be done *after* compiling the bound expr, since any
|
// This should be done *after* compiling the bound expr, since any
|
||||||
|
@ -1948,7 +1999,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||||
stack.push(*symbol);
|
stack.push(*symbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = build_exp_stmt(env, layout_ids, scope, parent, cont);
|
let result = build_exp_stmt(env, layout_ids, func_spec_solutions, scope, parent, cont);
|
||||||
|
|
||||||
for symbol in stack {
|
for symbol in stack {
|
||||||
scope.remove(&symbol);
|
scope.remove(&symbol);
|
||||||
|
@ -1979,7 +2030,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||||
// when the fail case is just Rethrow, there is no cleanup work to do
|
// when the fail case is just Rethrow, there is no cleanup work to do
|
||||||
// so we can just treat this invoke as a normal call
|
// so we can just treat this invoke as a normal call
|
||||||
let stmt = roc_mono::ir::Stmt::Let(*symbol, Expr::Call(call.clone()), *layout, pass);
|
let stmt = roc_mono::ir::Stmt::Let(*symbol, Expr::Call(call.clone()), *layout, pass);
|
||||||
build_exp_stmt(env, layout_ids, scope, parent, &stmt)
|
build_exp_stmt(env, layout_ids, func_spec_solutions, scope, parent, &stmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
Invoke {
|
Invoke {
|
||||||
|
@ -1993,13 +2044,20 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||||
CallType::ByName {
|
CallType::ByName {
|
||||||
name,
|
name,
|
||||||
ref full_layout,
|
ref full_layout,
|
||||||
|
specialization_id,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let function_value = function_value_by_name(env, layout_ids, *full_layout, name);
|
let bytes = specialization_id.to_bytes();
|
||||||
|
let callee_var = CalleeSpecVar(&bytes);
|
||||||
|
let func_spec = func_spec_solutions.callee_spec(callee_var).unwrap();
|
||||||
|
|
||||||
|
let function_value =
|
||||||
|
function_value_by_func_spec(env, func_spec, name, *full_layout);
|
||||||
|
|
||||||
invoke_roc_function(
|
invoke_roc_function(
|
||||||
env,
|
env,
|
||||||
layout_ids,
|
layout_ids,
|
||||||
|
func_spec_solutions,
|
||||||
scope,
|
scope,
|
||||||
parent,
|
parent,
|
||||||
*symbol,
|
*symbol,
|
||||||
|
@ -2019,6 +2077,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||||
} => build_foreign_symbol(
|
} => build_foreign_symbol(
|
||||||
env,
|
env,
|
||||||
layout_ids,
|
layout_ids,
|
||||||
|
func_spec_solutions,
|
||||||
scope,
|
scope,
|
||||||
parent,
|
parent,
|
||||||
foreign_symbol,
|
foreign_symbol,
|
||||||
|
@ -2065,7 +2124,14 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||||
ret_type,
|
ret_type,
|
||||||
};
|
};
|
||||||
|
|
||||||
build_switch_ir(env, layout_ids, scope, parent, switch_args)
|
build_switch_ir(
|
||||||
|
env,
|
||||||
|
layout_ids,
|
||||||
|
func_spec_solutions,
|
||||||
|
scope,
|
||||||
|
parent,
|
||||||
|
switch_args,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Join {
|
Join {
|
||||||
id,
|
id,
|
||||||
|
@ -2096,7 +2162,14 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||||
scope.join_points.insert(*id, (cont_block, joinpoint_args));
|
scope.join_points.insert(*id, (cont_block, joinpoint_args));
|
||||||
|
|
||||||
// construct the blocks that may jump to this join point
|
// construct the blocks that may jump to this join point
|
||||||
build_exp_stmt(env, layout_ids, scope, parent, remainder);
|
build_exp_stmt(
|
||||||
|
env,
|
||||||
|
layout_ids,
|
||||||
|
func_spec_solutions,
|
||||||
|
scope,
|
||||||
|
parent,
|
||||||
|
remainder,
|
||||||
|
);
|
||||||
|
|
||||||
let phi_block = builder.get_insert_block().unwrap();
|
let phi_block = builder.get_insert_block().unwrap();
|
||||||
|
|
||||||
|
@ -2109,7 +2182,14 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
|
|
||||||
// put the continuation in
|
// put the continuation in
|
||||||
let result = build_exp_stmt(env, layout_ids, scope, parent, continuation);
|
let result = build_exp_stmt(
|
||||||
|
env,
|
||||||
|
layout_ids,
|
||||||
|
func_spec_solutions,
|
||||||
|
scope,
|
||||||
|
parent,
|
||||||
|
continuation,
|
||||||
|
);
|
||||||
|
|
||||||
// remove this join point again
|
// remove this join point again
|
||||||
scope.join_points.remove(&id);
|
scope.join_points.remove(&id);
|
||||||
|
@ -2153,7 +2233,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
build_exp_stmt(env, layout_ids, scope, parent, cont)
|
build_exp_stmt(env, layout_ids, func_spec_solutions, scope, parent, cont)
|
||||||
}
|
}
|
||||||
Dec(symbol) => {
|
Dec(symbol) => {
|
||||||
let (value, layout) = load_symbol_and_layout(scope, symbol);
|
let (value, layout) = load_symbol_and_layout(scope, symbol);
|
||||||
|
@ -2162,7 +2242,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||||
decrement_refcount_layout(env, parent, layout_ids, value, layout);
|
decrement_refcount_layout(env, parent, layout_ids, value, layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
build_exp_stmt(env, layout_ids, scope, parent, cont)
|
build_exp_stmt(env, layout_ids, func_spec_solutions, scope, parent, cont)
|
||||||
}
|
}
|
||||||
DecRef(symbol) => {
|
DecRef(symbol) => {
|
||||||
let (value, layout) = load_symbol_and_layout(scope, symbol);
|
let (value, layout) = load_symbol_and_layout(scope, symbol);
|
||||||
|
@ -2214,7 +2294,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
build_exp_stmt(env, layout_ids, scope, parent, cont)
|
build_exp_stmt(env, layout_ids, func_spec_solutions, scope, parent, cont)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2404,6 +2484,7 @@ fn const_i128<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>, value: i128) -> IntValu
|
||||||
fn build_switch_ir<'a, 'ctx, 'env>(
|
fn build_switch_ir<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
|
func_spec_solutions: &FuncSpecSolutions,
|
||||||
scope: &Scope<'a, 'ctx>,
|
scope: &Scope<'a, 'ctx>,
|
||||||
parent: FunctionValue<'ctx>,
|
parent: FunctionValue<'ctx>,
|
||||||
switch_args: SwitchArgsIr<'a, 'ctx>,
|
switch_args: SwitchArgsIr<'a, 'ctx>,
|
||||||
|
@ -2526,7 +2607,14 @@ fn build_switch_ir<'a, 'ctx, 'env>(
|
||||||
{
|
{
|
||||||
builder.position_at_end(then_block);
|
builder.position_at_end(then_block);
|
||||||
|
|
||||||
let branch_val = build_exp_stmt(env, layout_ids, scope, parent, true_branch);
|
let branch_val = build_exp_stmt(
|
||||||
|
env,
|
||||||
|
layout_ids,
|
||||||
|
func_spec_solutions,
|
||||||
|
scope,
|
||||||
|
parent,
|
||||||
|
true_branch,
|
||||||
|
);
|
||||||
|
|
||||||
if then_block.get_terminator().is_none() {
|
if then_block.get_terminator().is_none() {
|
||||||
builder.build_unconditional_branch(cont_block);
|
builder.build_unconditional_branch(cont_block);
|
||||||
|
@ -2537,7 +2625,14 @@ fn build_switch_ir<'a, 'ctx, 'env>(
|
||||||
{
|
{
|
||||||
builder.position_at_end(else_block);
|
builder.position_at_end(else_block);
|
||||||
|
|
||||||
let branch_val = build_exp_stmt(env, layout_ids, scope, parent, false_branch);
|
let branch_val = build_exp_stmt(
|
||||||
|
env,
|
||||||
|
layout_ids,
|
||||||
|
func_spec_solutions,
|
||||||
|
scope,
|
||||||
|
parent,
|
||||||
|
false_branch,
|
||||||
|
);
|
||||||
|
|
||||||
if else_block.get_terminator().is_none() {
|
if else_block.get_terminator().is_none() {
|
||||||
builder.build_unconditional_branch(cont_block);
|
builder.build_unconditional_branch(cont_block);
|
||||||
|
@ -2587,7 +2682,14 @@ fn build_switch_ir<'a, 'ctx, 'env>(
|
||||||
for ((_, _, branch_expr), (_, block)) in branches.iter().zip(cases) {
|
for ((_, _, branch_expr), (_, block)) in branches.iter().zip(cases) {
|
||||||
builder.position_at_end(block);
|
builder.position_at_end(block);
|
||||||
|
|
||||||
let branch_val = build_exp_stmt(env, layout_ids, scope, parent, branch_expr);
|
let branch_val = build_exp_stmt(
|
||||||
|
env,
|
||||||
|
layout_ids,
|
||||||
|
func_spec_solutions,
|
||||||
|
scope,
|
||||||
|
parent,
|
||||||
|
branch_expr,
|
||||||
|
);
|
||||||
|
|
||||||
if block.get_terminator().is_none() {
|
if block.get_terminator().is_none() {
|
||||||
builder.build_unconditional_branch(cont_block);
|
builder.build_unconditional_branch(cont_block);
|
||||||
|
@ -2598,7 +2700,14 @@ fn build_switch_ir<'a, 'ctx, 'env>(
|
||||||
// The block for the conditional's default branch.
|
// The block for the conditional's default branch.
|
||||||
builder.position_at_end(default_block);
|
builder.position_at_end(default_block);
|
||||||
|
|
||||||
let default_val = build_exp_stmt(env, layout_ids, scope, parent, default_branch);
|
let default_val = build_exp_stmt(
|
||||||
|
env,
|
||||||
|
layout_ids,
|
||||||
|
func_spec_solutions,
|
||||||
|
scope,
|
||||||
|
parent,
|
||||||
|
default_branch,
|
||||||
|
);
|
||||||
|
|
||||||
if default_block.get_terminator().is_none() {
|
if default_block.get_terminator().is_none() {
|
||||||
builder.build_unconditional_branch(cont_block);
|
builder.build_unconditional_branch(cont_block);
|
||||||
|
@ -2646,16 +2755,19 @@ pub fn create_entry_block_alloca<'a, 'ctx>(
|
||||||
|
|
||||||
fn expose_function_to_host<'a, 'ctx, 'env>(
|
fn expose_function_to_host<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
symbol: Symbol,
|
||||||
roc_function: FunctionValue<'ctx>,
|
roc_function: FunctionValue<'ctx>,
|
||||||
) {
|
) {
|
||||||
let c_function_name: String =
|
// Assumption: there is only one specialization of a host-exposed function
|
||||||
format!("roc_{}_exposed", roc_function.get_name().to_str().unwrap());
|
let ident_string = symbol.ident_string(&env.interns);
|
||||||
|
let c_function_name: String = format!("roc__{}_1_exposed", ident_string);
|
||||||
|
|
||||||
expose_function_to_host_help(env, roc_function, &c_function_name);
|
expose_function_to_host_help(env, ident_string, roc_function, &c_function_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expose_function_to_host_help<'a, 'ctx, 'env>(
|
fn expose_function_to_host_help<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
ident_string: &inlinable_string::InlinableString,
|
||||||
roc_function: FunctionValue<'ctx>,
|
roc_function: FunctionValue<'ctx>,
|
||||||
c_function_name: &str,
|
c_function_name: &str,
|
||||||
) -> FunctionValue<'ctx> {
|
) -> FunctionValue<'ctx> {
|
||||||
|
@ -2716,8 +2828,7 @@ fn expose_function_to_host_help<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
// STEP 3: build a {} -> u64 function that gives the size of the return type
|
// STEP 3: build a {} -> u64 function that gives the size of the return type
|
||||||
let size_function_type = env.context.i64_type().fn_type(&[], false);
|
let size_function_type = env.context.i64_type().fn_type(&[], false);
|
||||||
let size_function_name: String =
|
let size_function_name: String = format!("roc__{}_size", ident_string);
|
||||||
format!("roc_{}_size", roc_function.get_name().to_str().unwrap());
|
|
||||||
|
|
||||||
let size_function = add_func(
|
let size_function = add_func(
|
||||||
env.module,
|
env.module,
|
||||||
|
@ -2978,23 +3089,41 @@ fn make_exception_catching_wrapper<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
pub fn build_proc_headers<'a, 'ctx, 'env>(
|
pub fn build_proc_headers<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
mod_solutions: &'a ModSolutions,
|
||||||
procedures: MutMap<(Symbol, TopLevelFunctionLayout<'a>), roc_mono::ir::Proc<'a>>,
|
procedures: MutMap<(Symbol, TopLevelFunctionLayout<'a>), roc_mono::ir::Proc<'a>>,
|
||||||
scope: &mut Scope<'a, 'ctx>,
|
scope: &mut Scope<'a, 'ctx>,
|
||||||
// alias_analysis_solutions: AliasAnalysisSolutions,
|
// alias_analysis_solutions: AliasAnalysisSolutions,
|
||||||
) -> std::vec::Vec<(roc_mono::ir::Proc<'a>, FunctionValue<'ctx>)> {
|
) -> Vec<
|
||||||
|
'a,
|
||||||
|
(
|
||||||
|
roc_mono::ir::Proc<'a>,
|
||||||
|
&'a [(&'a FuncSpecSolutions, FunctionValue<'ctx>)],
|
||||||
|
),
|
||||||
|
> {
|
||||||
// Populate Procs further and get the low-level Expr from the canonical Expr
|
// Populate Procs further and get the low-level Expr from the canonical Expr
|
||||||
let mut headers = std::vec::Vec::with_capacity(procedures.len());
|
let mut headers = Vec::with_capacity_in(procedures.len(), env.arena);
|
||||||
for ((symbol, layout), proc) in procedures {
|
for ((symbol, layout), proc) in procedures {
|
||||||
let fn_val = build_proc_header(env, layout_ids, symbol, layout, &proc);
|
let name_bytes = roc_mono::alias_analysis::func_name_bytes(&proc);
|
||||||
|
let func_name = FuncName(&name_bytes);
|
||||||
|
|
||||||
if proc.args.is_empty() {
|
let func_solutions = mod_solutions.func_solutions(func_name).unwrap();
|
||||||
// this is a 0-argument thunk, i.e. a top-level constant definition
|
|
||||||
// it must be in-scope everywhere in the module!
|
let it = func_solutions.specs();
|
||||||
scope.insert_top_level_thunk(symbol, env.arena.alloc(layout), fn_val);
|
let mut function_values = Vec::with_capacity_in(it.size_hint().0, env.arena);
|
||||||
|
for specialization in it {
|
||||||
|
let fn_val = build_proc_header(env, *specialization, symbol, &proc);
|
||||||
|
|
||||||
|
if proc.args.is_empty() {
|
||||||
|
// this is a 0-argument thunk, i.e. a top-level constant definition
|
||||||
|
// it must be in-scope everywhere in the module!
|
||||||
|
scope.insert_top_level_thunk(symbol, env.arena.alloc(layout), fn_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
let func_spec_solutions = func_solutions.spec(specialization).unwrap();
|
||||||
|
|
||||||
|
function_values.push((func_spec_solutions, fn_val));
|
||||||
}
|
}
|
||||||
|
headers.push((proc, function_values.into_bump_slice()));
|
||||||
headers.push((proc, fn_val));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
headers
|
headers
|
||||||
|
@ -3026,18 +3155,6 @@ pub fn build_procedures_return_main<'a, 'ctx, 'env>(
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Coming soon
|
|
||||||
// pub enum AliasAnalysisSolutions {
|
|
||||||
// NotAvailable,
|
|
||||||
// Available(morphic_lib::Solutions),
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// impl std::fmt::Debug for AliasAnalysisSolutions {
|
|
||||||
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
// write!(f, "AliasAnalysisSolutions {{}}")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
fn build_procedures_help<'a, 'ctx, 'env>(
|
fn build_procedures_help<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
opt_level: OptLevel,
|
opt_level: OptLevel,
|
||||||
|
@ -3047,82 +3164,107 @@ fn build_procedures_help<'a, 'ctx, 'env>(
|
||||||
let mut layout_ids = roc_mono::layout::LayoutIds::default();
|
let mut layout_ids = roc_mono::layout::LayoutIds::default();
|
||||||
let mut scope = Scope::default();
|
let mut scope = Scope::default();
|
||||||
|
|
||||||
// Coming Soon
|
let it = procedures.iter().map(|x| x.1);
|
||||||
//
|
|
||||||
// if false {
|
let solutions = match roc_mono::alias_analysis::spec_program(it) {
|
||||||
// let it = state.procedures.iter().map(|x| x.1);
|
Err(e) => panic!("Error in alias analysis: {:?}", e),
|
||||||
//
|
Ok(solutions) => solutions,
|
||||||
// match roc_mono::alias_analysis::spec_program(it) {
|
};
|
||||||
// Err(e) => panic!("Error in alias analysis: {:?}", e),
|
|
||||||
// Ok(solutions) => {
|
let solutions = env.arena.alloc(solutions);
|
||||||
// state.alias_analysis_solutions =
|
|
||||||
// AliasAnalysisSolutions::Available(solutions)
|
let mod_solutions = solutions
|
||||||
// }
|
.mod_solutions(roc_mono::alias_analysis::MOD_APP)
|
||||||
// }
|
.unwrap();
|
||||||
// }
|
|
||||||
|
|
||||||
// Add all the Proc headers to the module.
|
// Add all the Proc headers to the module.
|
||||||
// We have to do this in a separate pass first,
|
// We have to do this in a separate pass first,
|
||||||
// because their bodies may reference each other.
|
// because their bodies may reference each other.
|
||||||
let headers = build_proc_headers(env, &mut layout_ids, procedures, &mut scope);
|
let headers = build_proc_headers(env, &mod_solutions, procedures, &mut scope);
|
||||||
|
|
||||||
let (_, function_pass) = construct_optimization_passes(env.module, opt_level);
|
let (_, function_pass) = construct_optimization_passes(env.module, opt_level);
|
||||||
|
|
||||||
for (proc, fn_val) in headers {
|
for (proc, fn_vals) in headers {
|
||||||
let mut current_scope = scope.clone();
|
for (func_spec_solutions, fn_val) in fn_vals {
|
||||||
|
let mut current_scope = scope.clone();
|
||||||
|
|
||||||
// only have top-level thunks for this proc's module in scope
|
// only have top-level thunks for this proc's module in scope
|
||||||
// this retain is not needed for correctness, but will cause less confusion when debugging
|
// this retain is not needed for correctness, but will cause less confusion when debugging
|
||||||
let home = proc.name.module_id();
|
let home = proc.name.module_id();
|
||||||
current_scope.retain_top_level_thunks_for_module(home);
|
current_scope.retain_top_level_thunks_for_module(home);
|
||||||
|
|
||||||
build_proc(&env, &mut layout_ids, scope.clone(), proc, fn_val);
|
build_proc(
|
||||||
|
&env,
|
||||||
// call finalize() before any code generation/verification
|
mod_solutions,
|
||||||
env.dibuilder.finalize();
|
&mut layout_ids,
|
||||||
|
func_spec_solutions,
|
||||||
if fn_val.verify(true) {
|
scope.clone(),
|
||||||
function_pass.run_on(&fn_val);
|
&proc,
|
||||||
} else {
|
*fn_val,
|
||||||
let mode = "NON-OPTIMIZED";
|
|
||||||
|
|
||||||
eprintln!(
|
|
||||||
"\n\nFunction {:?} failed LLVM verification in {} build. Its content was:\n",
|
|
||||||
fn_val.get_name().to_str().unwrap(),
|
|
||||||
mode,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
fn_val.print_to_stderr();
|
// call finalize() before any code generation/verification
|
||||||
// module.print_to_stderr();
|
env.dibuilder.finalize();
|
||||||
|
|
||||||
panic!(
|
if fn_val.verify(true) {
|
||||||
"The preceding code was from {:?}, which failed LLVM verification in {} build.",
|
function_pass.run_on(&fn_val);
|
||||||
fn_val.get_name().to_str().unwrap(),
|
} else {
|
||||||
mode,
|
let mode = "NON-OPTIMIZED";
|
||||||
);
|
|
||||||
|
eprintln!(
|
||||||
|
"\n\nFunction {:?} failed LLVM verification in {} build. Its content was:\n",
|
||||||
|
fn_val.get_name().to_str().unwrap(),
|
||||||
|
mode,
|
||||||
|
);
|
||||||
|
|
||||||
|
fn_val.print_to_stderr();
|
||||||
|
// module.print_to_stderr();
|
||||||
|
|
||||||
|
panic!(
|
||||||
|
"The preceding code was from {:?}, which failed LLVM verification in {} build.",
|
||||||
|
fn_val.get_name().to_str().unwrap(),
|
||||||
|
mode,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
main_data.map(|(main_fn_symbol, main_fn_layout)| {
|
main_data.map(|(main_fn_symbol, main_fn_layout)| {
|
||||||
promote_to_main_function(env, &mut layout_ids, main_fn_symbol, main_fn_layout)
|
promote_to_main_function(env, mod_solutions, main_fn_symbol, main_fn_layout)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn func_spec_name<'a>(
|
||||||
|
arena: &'a Bump,
|
||||||
|
interns: &Interns,
|
||||||
|
symbol: Symbol,
|
||||||
|
func_spec: FuncSpec,
|
||||||
|
) -> bumpalo::collections::String<'a> {
|
||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
let mut buf = bumpalo::collections::String::with_capacity_in(1, arena);
|
||||||
|
|
||||||
|
let ident_string = symbol.ident_string(interns);
|
||||||
|
let module_string = interns.module_ids.get_name(symbol.module_id()).unwrap();
|
||||||
|
write!(buf, "{}_{}_", module_string, ident_string).unwrap();
|
||||||
|
|
||||||
|
for byte in func_spec.0.iter() {
|
||||||
|
write!(buf, "{:x?}", byte).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
buf
|
||||||
|
}
|
||||||
|
|
||||||
fn build_proc_header<'a, 'ctx, 'env>(
|
fn build_proc_header<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
func_spec: FuncSpec,
|
||||||
symbol: Symbol,
|
symbol: Symbol,
|
||||||
top_level: TopLevelFunctionLayout<'a>,
|
|
||||||
proc: &roc_mono::ir::Proc<'a>,
|
proc: &roc_mono::ir::Proc<'a>,
|
||||||
) -> FunctionValue<'ctx> {
|
) -> FunctionValue<'ctx> {
|
||||||
let layout = env.arena.alloc(top_level).full();
|
|
||||||
|
|
||||||
let args = proc.args;
|
let args = proc.args;
|
||||||
let arena = env.arena;
|
let arena = env.arena;
|
||||||
|
|
||||||
let fn_name = layout_ids
|
let fn_name = func_spec_name(env.arena, &env.interns, symbol, func_spec);
|
||||||
.get(symbol, &layout)
|
|
||||||
.to_symbol_string(symbol, &env.interns);
|
|
||||||
|
|
||||||
let ret_type = basic_type_from_layout(env, &proc.ret_layout);
|
let ret_type = basic_type_from_layout(env, &proc.ret_layout);
|
||||||
let mut arg_basic_types = Vec::with_capacity_in(args.len(), arena);
|
let mut arg_basic_types = Vec::with_capacity_in(args.len(), arena);
|
||||||
|
@ -3147,7 +3289,7 @@ fn build_proc_header<'a, 'ctx, 'env>(
|
||||||
fn_val.set_subprogram(subprogram);
|
fn_val.set_subprogram(subprogram);
|
||||||
|
|
||||||
if env.exposed_to_host.contains(&symbol) {
|
if env.exposed_to_host.contains(&symbol) {
|
||||||
expose_function_to_host(env, fn_val);
|
expose_function_to_host(env, symbol, fn_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn_val
|
fn_val
|
||||||
|
@ -3169,7 +3311,7 @@ pub fn build_closure_caller<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
// e.g. `roc__main_1_Fx_caller`
|
// e.g. `roc__main_1_Fx_caller`
|
||||||
let function_name = format!(
|
let function_name = format!(
|
||||||
"roc_{}_{}_caller",
|
"roc__{}_{}_caller",
|
||||||
def_name,
|
def_name,
|
||||||
alias_symbol.ident_string(&env.interns)
|
alias_symbol.ident_string(&env.interns)
|
||||||
);
|
);
|
||||||
|
@ -3403,14 +3545,14 @@ fn build_host_exposed_alias_size_help<'a, 'ctx, 'env>(
|
||||||
let size_function_type = env.context.i64_type().fn_type(&[], false);
|
let size_function_type = env.context.i64_type().fn_type(&[], false);
|
||||||
let size_function_name: String = if let Some(label) = opt_label {
|
let size_function_name: String = if let Some(label) = opt_label {
|
||||||
format!(
|
format!(
|
||||||
"roc_{}_{}_{}_size",
|
"roc__{}_{}_{}_size",
|
||||||
def_name,
|
def_name,
|
||||||
alias_symbol.ident_string(&env.interns),
|
alias_symbol.ident_string(&env.interns),
|
||||||
label
|
label
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
format!(
|
format!(
|
||||||
"roc_{}_{}_size",
|
"roc__{}_{}_size",
|
||||||
def_name,
|
def_name,
|
||||||
alias_symbol.ident_string(&env.interns)
|
alias_symbol.ident_string(&env.interns)
|
||||||
)
|
)
|
||||||
|
@ -3434,9 +3576,11 @@ fn build_host_exposed_alias_size_help<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
pub fn build_proc<'a, 'ctx, 'env>(
|
pub fn build_proc<'a, 'ctx, 'env>(
|
||||||
env: &'a Env<'a, 'ctx, 'env>,
|
env: &'a Env<'a, 'ctx, 'env>,
|
||||||
|
mod_solutions: &'a ModSolutions,
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
|
func_spec_solutions: &FuncSpecSolutions,
|
||||||
mut scope: Scope<'a, 'ctx>,
|
mut scope: Scope<'a, 'ctx>,
|
||||||
proc: roc_mono::ir::Proc<'a>,
|
proc: &roc_mono::ir::Proc<'a>,
|
||||||
fn_val: FunctionValue<'ctx>,
|
fn_val: FunctionValue<'ctx>,
|
||||||
) {
|
) {
|
||||||
use roc_mono::ir::HostExposedLayouts;
|
use roc_mono::ir::HostExposedLayouts;
|
||||||
|
@ -3454,16 +3598,28 @@ pub fn build_proc<'a, 'ctx, 'env>(
|
||||||
// * roc__mainForHost_1_Update_result_size() -> i64
|
// * roc__mainForHost_1_Update_result_size() -> i64
|
||||||
|
|
||||||
let evaluator_layout = env.arena.alloc(top_level).full();
|
let evaluator_layout = env.arena.alloc(top_level).full();
|
||||||
let evaluator_name = layout_ids
|
|
||||||
.get(symbol, &evaluator_layout)
|
|
||||||
.to_symbol_string(symbol, &env.interns);
|
|
||||||
|
|
||||||
let evaluator = function_value_by_name_help(
|
let it = top_level.arguments.iter().copied();
|
||||||
env,
|
let bytes = roc_mono::alias_analysis::func_name_bytes_help(
|
||||||
evaluator_layout,
|
|
||||||
symbol,
|
symbol,
|
||||||
&evaluator_name,
|
it,
|
||||||
|
top_level.result,
|
||||||
);
|
);
|
||||||
|
let func_name = FuncName(&bytes);
|
||||||
|
let func_solutions = mod_solutions.func_solutions(func_name).unwrap();
|
||||||
|
|
||||||
|
let mut it = func_solutions.specs();
|
||||||
|
let func_spec = it.next().unwrap();
|
||||||
|
debug_assert!(
|
||||||
|
it.next().is_none(),
|
||||||
|
"we expect only one specialization of this symbol"
|
||||||
|
);
|
||||||
|
|
||||||
|
let evaluator =
|
||||||
|
function_value_by_func_spec(env, *func_spec, symbol, evaluator_layout);
|
||||||
|
|
||||||
|
let ident_string = proc.name.ident_string(&env.interns);
|
||||||
|
let fn_name: String = format!("{}_1", ident_string);
|
||||||
|
|
||||||
build_closure_caller(
|
build_closure_caller(
|
||||||
env, &fn_name, evaluator, name, arguments, closure, result,
|
env, &fn_name, evaluator, name, arguments, closure, result,
|
||||||
|
@ -3503,7 +3659,14 @@ pub fn build_proc<'a, 'ctx, 'env>(
|
||||||
scope.insert(*arg_symbol, (*layout, arg_val));
|
scope.insert(*arg_symbol, (*layout, arg_val));
|
||||||
}
|
}
|
||||||
|
|
||||||
let body = build_exp_stmt(env, layout_ids, &mut scope, fn_val, &proc.body);
|
let body = build_exp_stmt(
|
||||||
|
env,
|
||||||
|
layout_ids,
|
||||||
|
func_spec_solutions,
|
||||||
|
&mut scope,
|
||||||
|
fn_val,
|
||||||
|
&proc.body,
|
||||||
|
);
|
||||||
|
|
||||||
// only add a return if codegen did not already add one
|
// only add a return if codegen did not already add one
|
||||||
if let Some(block) = builder.get_insert_block() {
|
if let Some(block) = builder.get_insert_block() {
|
||||||
|
@ -3523,15 +3686,13 @@ pub fn verify_fn(fn_val: FunctionValue<'_>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn function_value_by_name<'a, 'ctx, 'env>(
|
fn function_value_by_func_spec<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
func_spec: FuncSpec,
|
||||||
layout: Layout<'a>,
|
|
||||||
symbol: Symbol,
|
symbol: Symbol,
|
||||||
|
layout: Layout<'a>,
|
||||||
) -> FunctionValue<'ctx> {
|
) -> FunctionValue<'ctx> {
|
||||||
let fn_name = layout_ids
|
let fn_name = func_spec_name(env.arena, &env.interns, symbol, func_spec);
|
||||||
.get(symbol, &layout)
|
|
||||||
.to_symbol_string(symbol, &env.interns);
|
|
||||||
let fn_name = fn_name.as_str();
|
let fn_name = fn_name.as_str();
|
||||||
|
|
||||||
function_value_by_name_help(env, layout, symbol, fn_name)
|
function_value_by_name_help(env, layout, symbol, fn_name)
|
||||||
|
@ -3573,15 +3734,14 @@ fn function_value_by_name_help<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
// #[allow(clippy::cognitive_complexity)]
|
// #[allow(clippy::cognitive_complexity)]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn call_with_args<'a, 'ctx, 'env>(
|
fn roc_call_with_args<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
|
||||||
layout: &Layout<'a>,
|
layout: &Layout<'a>,
|
||||||
symbol: Symbol,
|
symbol: Symbol,
|
||||||
_parent: FunctionValue<'ctx>,
|
func_spec: FuncSpec,
|
||||||
args: &[BasicValueEnum<'ctx>],
|
args: &[BasicValueEnum<'ctx>],
|
||||||
) -> BasicValueEnum<'ctx> {
|
) -> BasicValueEnum<'ctx> {
|
||||||
let fn_val = function_value_by_name(env, layout_ids, *layout, symbol);
|
let fn_val = function_value_by_func_spec(env, func_spec, symbol, *layout);
|
||||||
|
|
||||||
let call = env.builder.build_call(fn_val, args, "call");
|
let call = env.builder.build_call(fn_val, args, "call");
|
||||||
|
|
||||||
|
@ -3664,6 +3824,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
|
||||||
return_layout: &Layout<'a>,
|
return_layout: &Layout<'a>,
|
||||||
op: LowLevel,
|
op: LowLevel,
|
||||||
function_layout: Layout<'a>,
|
function_layout: Layout<'a>,
|
||||||
|
func_spec: FuncSpec,
|
||||||
function_owns_closure_data: bool,
|
function_owns_closure_data: bool,
|
||||||
args: &[Symbol],
|
args: &[Symbol],
|
||||||
) -> BasicValueEnum<'ctx> {
|
) -> BasicValueEnum<'ctx> {
|
||||||
|
@ -3676,18 +3837,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
|
||||||
($index:expr) => {{
|
($index:expr) => {{
|
||||||
let function_symbol = args[$index];
|
let function_symbol = args[$index];
|
||||||
|
|
||||||
let fn_name = layout_ids
|
function_value_by_func_spec(env, func_spec, function_symbol, function_layout)
|
||||||
.get(function_symbol, &function_layout)
|
|
||||||
.to_symbol_string(function_symbol, &env.interns);
|
|
||||||
|
|
||||||
env.module
|
|
||||||
.get_function(fn_name.as_str())
|
|
||||||
.unwrap_or_else(|| {
|
|
||||||
panic!(
|
|
||||||
"Could not get pointer to unknown function {:?} {:?}",
|
|
||||||
fn_name, function_layout
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4892,6 +5042,7 @@ fn build_foreign_symbol_write_result_into_ptr<'a, 'ctx, 'env>(
|
||||||
fn build_foreign_symbol<'a, 'ctx, 'env>(
|
fn build_foreign_symbol<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
|
func_spec_solutions: &FuncSpecSolutions,
|
||||||
scope: &mut Scope<'a, 'ctx>,
|
scope: &mut Scope<'a, 'ctx>,
|
||||||
parent: FunctionValue<'ctx>,
|
parent: FunctionValue<'ctx>,
|
||||||
foreign: &roc_module::ident::ForeignSymbol,
|
foreign: &roc_module::ident::ForeignSymbol,
|
||||||
|
@ -4955,7 +5106,7 @@ fn build_foreign_symbol<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
scope.insert(symbol, (*ret_layout, call_result));
|
scope.insert(symbol, (*ret_layout, call_result));
|
||||||
|
|
||||||
build_exp_stmt(env, layout_ids, scope, parent, pass);
|
build_exp_stmt(env, layout_ids, func_spec_solutions, scope, parent, pass);
|
||||||
|
|
||||||
scope.remove(&symbol);
|
scope.remove(&symbol);
|
||||||
}
|
}
|
||||||
|
@ -4987,7 +5138,7 @@ fn build_foreign_symbol<'a, 'ctx, 'env>(
|
||||||
(Layout::Struct(&[]), exception_object),
|
(Layout::Struct(&[]), exception_object),
|
||||||
);
|
);
|
||||||
|
|
||||||
build_exp_stmt(env, layout_ids, scope, parent, fail);
|
build_exp_stmt(env, layout_ids, func_spec_solutions, scope, parent, fail);
|
||||||
}
|
}
|
||||||
|
|
||||||
call_result
|
call_result
|
||||||
|
|
|
@ -17,6 +17,65 @@ pub const MOD_APP: ModName = ModName(b"UserApp");
|
||||||
|
|
||||||
pub const STATIC_STR_NAME: ConstName = ConstName(&Symbol::STR_ALIAS_ANALYSIS_STATIC.to_ne_bytes());
|
pub const STATIC_STR_NAME: ConstName = ConstName(&Symbol::STR_ALIAS_ANALYSIS_STATIC.to_ne_bytes());
|
||||||
|
|
||||||
|
pub fn func_name_bytes(proc: &Proc) -> [u8; 16] {
|
||||||
|
func_name_bytes_help(proc.name, proc.args.iter().map(|x| x.0), proc.ret_layout)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn func_name_bytes_help<'a, I>(
|
||||||
|
symbol: Symbol,
|
||||||
|
argument_layouts: I,
|
||||||
|
return_layout: Layout<'a>,
|
||||||
|
) -> [u8; 16]
|
||||||
|
where
|
||||||
|
I: Iterator<Item = Layout<'a>>,
|
||||||
|
{
|
||||||
|
let mut name_bytes = [0u8; 16];
|
||||||
|
|
||||||
|
use std::collections::hash_map::DefaultHasher;
|
||||||
|
use std::hash::Hash;
|
||||||
|
use std::hash::Hasher;
|
||||||
|
|
||||||
|
let layout_hash = {
|
||||||
|
let mut hasher = DefaultHasher::new();
|
||||||
|
|
||||||
|
for layout in argument_layouts {
|
||||||
|
match layout {
|
||||||
|
Layout::Closure(_, lambda_set, _) => {
|
||||||
|
lambda_set.runtime_representation().hash(&mut hasher);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
layout.hash(&mut hasher);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match return_layout {
|
||||||
|
Layout::Closure(_, lambda_set, _) => {
|
||||||
|
lambda_set.runtime_representation().hash(&mut hasher);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return_layout.hash(&mut hasher);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hasher.finish()
|
||||||
|
};
|
||||||
|
|
||||||
|
let sbytes = symbol.to_ne_bytes();
|
||||||
|
let lbytes = layout_hash.to_ne_bytes();
|
||||||
|
|
||||||
|
let it = sbytes
|
||||||
|
.iter()
|
||||||
|
.chain(lbytes.iter())
|
||||||
|
.zip(name_bytes.iter_mut());
|
||||||
|
|
||||||
|
for (source, target) in it {
|
||||||
|
*target = *source;
|
||||||
|
}
|
||||||
|
|
||||||
|
name_bytes
|
||||||
|
}
|
||||||
|
|
||||||
pub fn spec_program<'a, I>(procs: I) -> Result<morphic_lib::Solutions>
|
pub fn spec_program<'a, I>(procs: I) -> Result<morphic_lib::Solutions>
|
||||||
where
|
where
|
||||||
I: Iterator<Item = &'a Proc<'a>>,
|
I: Iterator<Item = &'a Proc<'a>>,
|
||||||
|
@ -40,36 +99,7 @@ where
|
||||||
for proc in procs {
|
for proc in procs {
|
||||||
let spec = proc_spec(proc)?;
|
let spec = proc_spec(proc)?;
|
||||||
|
|
||||||
let mut name_bytes = [0u8; 16];
|
m.add_func(FuncName(&func_name_bytes(proc)), spec)?;
|
||||||
|
|
||||||
use std::collections::hash_map::DefaultHasher;
|
|
||||||
use std::hash::Hash;
|
|
||||||
use std::hash::Hasher;
|
|
||||||
|
|
||||||
let layout_hash = {
|
|
||||||
let mut hasher = DefaultHasher::new();
|
|
||||||
|
|
||||||
for (layout, _) in proc.args.iter() {
|
|
||||||
layout.hash(&mut hasher);
|
|
||||||
}
|
|
||||||
proc.ret_layout.hash(&mut hasher);
|
|
||||||
|
|
||||||
hasher.finish()
|
|
||||||
};
|
|
||||||
|
|
||||||
let sbytes = proc.name.to_ne_bytes();
|
|
||||||
let lbytes = layout_hash.to_ne_bytes();
|
|
||||||
|
|
||||||
let it = sbytes
|
|
||||||
.iter()
|
|
||||||
.chain(lbytes.iter())
|
|
||||||
.zip(name_bytes.iter_mut());
|
|
||||||
|
|
||||||
for (source, target) in it {
|
|
||||||
*target = *source;
|
|
||||||
}
|
|
||||||
|
|
||||||
m.add_func(FuncName(&name_bytes), spec)?;
|
|
||||||
|
|
||||||
if format!("{:?}", proc.name).contains("mainForHost") {
|
if format!("{:?}", proc.name).contains("mainForHost") {
|
||||||
main_function = Some(proc.name);
|
main_function = Some(proc.name);
|
||||||
|
@ -112,6 +142,8 @@ where
|
||||||
p.build()?
|
p.build()?
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// eprintln!("{}", program.to_source_string());
|
||||||
|
|
||||||
morphic_lib::solve(program)
|
morphic_lib::solve(program)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -345,16 +377,17 @@ fn call_spec(
|
||||||
ByName {
|
ByName {
|
||||||
name: symbol,
|
name: symbol,
|
||||||
full_layout: _,
|
full_layout: _,
|
||||||
ret_layout: _,
|
ret_layout,
|
||||||
arg_layouts: _,
|
arg_layouts,
|
||||||
specialization_id,
|
specialization_id,
|
||||||
} => {
|
} => {
|
||||||
let array = specialization_id.to_bytes();
|
let array = specialization_id.to_bytes();
|
||||||
let spec_var = CalleeSpecVar(&array);
|
let spec_var = CalleeSpecVar(&array);
|
||||||
|
|
||||||
let arg_value_id = build_tuple_value(builder, env, block, call.arguments)?;
|
let arg_value_id = build_tuple_value(builder, env, block, call.arguments)?;
|
||||||
let slice = &symbol.to_ne_bytes();
|
let it = arg_layouts.iter().copied();
|
||||||
let name = FuncName(slice);
|
let bytes = func_name_bytes_help(*symbol, it, *ret_layout);
|
||||||
|
let name = FuncName(&bytes);
|
||||||
let module = MOD_APP;
|
let module = MOD_APP;
|
||||||
builder.add_call(block, spec_var, module, name, arg_value_id)
|
builder.add_call(block, spec_var, module, name, arg_value_id)
|
||||||
}
|
}
|
||||||
|
@ -381,7 +414,64 @@ fn call_spec(
|
||||||
*update_mode,
|
*update_mode,
|
||||||
call.arguments,
|
call.arguments,
|
||||||
),
|
),
|
||||||
HigherOrderLowLevel { .. } => {
|
HigherOrderLowLevel {
|
||||||
|
specialization_id,
|
||||||
|
closure_layout: _,
|
||||||
|
op,
|
||||||
|
arg_layouts,
|
||||||
|
ret_layout,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let array = specialization_id.to_bytes();
|
||||||
|
let spec_var = CalleeSpecVar(&array);
|
||||||
|
|
||||||
|
let symbol = {
|
||||||
|
use roc_module::low_level::LowLevel::*;
|
||||||
|
|
||||||
|
match op {
|
||||||
|
ListMap | ListMapWithIndex => call.arguments[1],
|
||||||
|
ListMap2 => call.arguments[2],
|
||||||
|
ListMap3 => call.arguments[3],
|
||||||
|
ListWalk | ListWalkUntil | ListWalkBackwards | DictWalk => call.arguments[2],
|
||||||
|
ListKeepIf | ListKeepOks | ListKeepErrs => call.arguments[1],
|
||||||
|
ListSortWith => call.arguments[1],
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let it = arg_layouts.iter().copied();
|
||||||
|
let bytes = func_name_bytes_help(symbol, it, *ret_layout);
|
||||||
|
let name = FuncName(&bytes);
|
||||||
|
let module = MOD_APP;
|
||||||
|
|
||||||
|
{
|
||||||
|
use roc_module::low_level::LowLevel::*;
|
||||||
|
|
||||||
|
match op {
|
||||||
|
DictWalk => {
|
||||||
|
let dict = env.symbols[&call.arguments[0]];
|
||||||
|
let default = env.symbols[&call.arguments[1]];
|
||||||
|
|
||||||
|
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])?;
|
||||||
|
builder.add_call(block, spec_var, module, name, argument)?;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// fake a call to the function argument
|
||||||
|
// to make sure the function is specialized
|
||||||
|
|
||||||
|
// very invalid
|
||||||
|
let arg_value_id = build_tuple_value(builder, env, block, &[])?;
|
||||||
|
|
||||||
|
builder.add_call(block, spec_var, module, name, arg_value_id)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO overly pessimstic
|
// TODO overly pessimstic
|
||||||
// filter_map because one of the arguments is a function name, which
|
// filter_map because one of the arguments is a function name, which
|
||||||
// is not defined in the env
|
// is not defined in the env
|
||||||
|
@ -740,6 +830,9 @@ fn str_type<TC: TypeContext>(builder: &mut TC) -> Result<TypeId> {
|
||||||
const LIST_CELL_INDEX: u32 = 0;
|
const LIST_CELL_INDEX: u32 = 0;
|
||||||
const LIST_BAG_INDEX: u32 = 1;
|
const LIST_BAG_INDEX: u32 = 1;
|
||||||
|
|
||||||
|
const DICT_CELL_INDEX: u32 = LIST_CELL_INDEX;
|
||||||
|
const DICT_BAG_INDEX: u32 = LIST_BAG_INDEX;
|
||||||
|
|
||||||
fn new_list(builder: &mut FuncDefBuilder, block: BlockId, element_type: TypeId) -> Result<ValueId> {
|
fn new_list(builder: &mut FuncDefBuilder, block: BlockId, element_type: TypeId) -> Result<ValueId> {
|
||||||
let cell = builder.add_new_heap_cell(block)?;
|
let cell = builder.add_new_heap_cell(block)?;
|
||||||
let bag = builder.add_empty_bag(block, element_type)?;
|
let bag = builder.add_empty_bag(block, element_type)?;
|
||||||
|
|
|
@ -454,7 +454,12 @@ impl<'a> Context<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
HigherOrderLowLevel {
|
HigherOrderLowLevel {
|
||||||
op, closure_layout, ..
|
op,
|
||||||
|
closure_layout,
|
||||||
|
specialization_id,
|
||||||
|
arg_layouts,
|
||||||
|
ret_layout,
|
||||||
|
..
|
||||||
} => {
|
} => {
|
||||||
macro_rules! create_call {
|
macro_rules! create_call {
|
||||||
($borrows:expr) => {
|
($borrows:expr) => {
|
||||||
|
@ -464,6 +469,9 @@ impl<'a> Context<'a> {
|
||||||
op: *op,
|
op: *op,
|
||||||
closure_layout: *closure_layout,
|
closure_layout: *closure_layout,
|
||||||
function_owns_closure_data: true,
|
function_owns_closure_data: true,
|
||||||
|
specialization_id: *specialization_id,
|
||||||
|
arg_layouts,
|
||||||
|
ret_layout: *ret_layout,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
call_type
|
call_type
|
||||||
|
|
|
@ -1146,8 +1146,13 @@ pub enum CallType<'a> {
|
||||||
op: LowLevel,
|
op: LowLevel,
|
||||||
/// the layout of the closure argument, if any
|
/// the layout of the closure argument, if any
|
||||||
closure_layout: Layout<'a>,
|
closure_layout: Layout<'a>,
|
||||||
|
/// specialization id of the function argument
|
||||||
|
specialization_id: CallSpecId,
|
||||||
/// does the function need to own the closure data
|
/// does the function need to own the closure data
|
||||||
function_owns_closure_data: bool,
|
function_owns_closure_data: bool,
|
||||||
|
/// function layout
|
||||||
|
arg_layouts: &'a [Layout<'a>],
|
||||||
|
ret_layout: Layout<'a>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2575,8 +2580,8 @@ fn cleanup_attempted_type<'a>(
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct TopLevelFunctionLayout<'a> {
|
pub struct TopLevelFunctionLayout<'a> {
|
||||||
arguments: &'a [Layout<'a>],
|
pub arguments: &'a [Layout<'a>],
|
||||||
result: Layout<'a>,
|
pub result: Layout<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TopLevelFunctionLayout<'a> {
|
impl<'a> TopLevelFunctionLayout<'a> {
|
||||||
|
@ -2701,21 +2706,29 @@ macro_rules! match_on_closure_argument {
|
||||||
|
|
||||||
let arena = $env.arena;
|
let arena = $env.arena;
|
||||||
|
|
||||||
|
let function_layout = arena.alloc(top_level).full();
|
||||||
|
|
||||||
|
let arg_layouts = top_level.arguments;
|
||||||
|
let ret_layout = top_level.result;
|
||||||
|
|
||||||
match closure_data_layout {
|
match closure_data_layout {
|
||||||
Layout::Closure(_, lambda_set, _) => {
|
Layout::Closure(_, lambda_set, _) => {
|
||||||
lowlevel_match_on_lambda_set(
|
lowlevel_match_on_lambda_set(
|
||||||
$env,
|
$env,
|
||||||
lambda_set,
|
lambda_set,
|
||||||
$closure_data_symbol,
|
$closure_data_symbol,
|
||||||
|top_level_function, closure_data, function_layout| self::Call {
|
|top_level_function, closure_data, function_layout, specialization_id| self::Call {
|
||||||
call_type: CallType::HigherOrderLowLevel {
|
call_type: CallType::HigherOrderLowLevel {
|
||||||
op: $op,
|
op: $op,
|
||||||
closure_layout: function_layout,
|
closure_layout: function_layout,
|
||||||
function_owns_closure_data: false
|
specialization_id,
|
||||||
|
function_owns_closure_data: false,
|
||||||
|
arg_layouts,
|
||||||
|
ret_layout,
|
||||||
},
|
},
|
||||||
arguments: arena.alloc([$($x,)* top_level_function, closure_data]),
|
arguments: arena.alloc([$($x,)* top_level_function, closure_data]),
|
||||||
},
|
},
|
||||||
arena.alloc(top_level).full(),
|
function_layout,
|
||||||
$layout,
|
$layout,
|
||||||
$assigned,
|
$assigned,
|
||||||
$hole,
|
$hole,
|
||||||
|
@ -3393,7 +3406,6 @@ pub fn with_hole<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
List {
|
List {
|
||||||
list_var: _,
|
|
||||||
elem_var,
|
elem_var,
|
||||||
loc_elems,
|
loc_elems,
|
||||||
} => {
|
} => {
|
||||||
|
@ -7696,7 +7708,7 @@ fn lowlevel_match_on_lambda_set<'a, ToLowLevelCall>(
|
||||||
hole: &'a Stmt<'a>,
|
hole: &'a Stmt<'a>,
|
||||||
) -> Stmt<'a>
|
) -> Stmt<'a>
|
||||||
where
|
where
|
||||||
ToLowLevelCall: Fn(Symbol, Symbol, Layout<'a>) -> Call<'a> + Copy,
|
ToLowLevelCall: Fn(Symbol, Symbol, Layout<'a>, CallSpecId) -> Call<'a> + Copy,
|
||||||
{
|
{
|
||||||
match lambda_set.runtime_representation() {
|
match lambda_set.runtime_representation() {
|
||||||
Layout::Union(_) => {
|
Layout::Union(_) => {
|
||||||
|
@ -7733,7 +7745,13 @@ where
|
||||||
Layout::Struct(_) => {
|
Layout::Struct(_) => {
|
||||||
let function_symbol = lambda_set.set[0].0;
|
let function_symbol = lambda_set.set[0].0;
|
||||||
|
|
||||||
let call = to_lowlevel_call(function_symbol, closure_data_symbol, function_layout);
|
let call_spec_id = env.next_call_specialization_id();
|
||||||
|
let call = to_lowlevel_call(
|
||||||
|
function_symbol,
|
||||||
|
closure_data_symbol,
|
||||||
|
function_layout,
|
||||||
|
call_spec_id,
|
||||||
|
);
|
||||||
|
|
||||||
build_call(env, call, assigned, return_layout, env.arena.alloc(hole))
|
build_call(env, call, assigned, return_layout, env.arena.alloc(hole))
|
||||||
}
|
}
|
||||||
|
@ -7787,7 +7805,7 @@ fn lowlevel_union_lambda_set_to_switch<'a, ToLowLevelCall>(
|
||||||
hole: &'a Stmt<'a>,
|
hole: &'a Stmt<'a>,
|
||||||
) -> Stmt<'a>
|
) -> Stmt<'a>
|
||||||
where
|
where
|
||||||
ToLowLevelCall: Fn(Symbol, Symbol, Layout<'a>) -> Call<'a> + Copy,
|
ToLowLevelCall: Fn(Symbol, Symbol, Layout<'a>, CallSpecId) -> Call<'a> + Copy,
|
||||||
{
|
{
|
||||||
debug_assert!(!lambda_set.is_empty());
|
debug_assert!(!lambda_set.is_empty());
|
||||||
|
|
||||||
|
@ -7800,7 +7818,13 @@ where
|
||||||
|
|
||||||
let hole = Stmt::Jump(join_point_id, env.arena.alloc([assigned]));
|
let hole = Stmt::Jump(join_point_id, env.arena.alloc([assigned]));
|
||||||
|
|
||||||
let call = to_lowlevel_call(*function_symbol, closure_data_symbol, function_layout);
|
let call_spec_id = env.next_call_specialization_id();
|
||||||
|
let call = to_lowlevel_call(
|
||||||
|
*function_symbol,
|
||||||
|
closure_data_symbol,
|
||||||
|
function_layout,
|
||||||
|
call_spec_id,
|
||||||
|
);
|
||||||
let stmt = build_call(env, call, assigned, return_layout, env.arena.alloc(hole));
|
let stmt = build_call(env, call, assigned, return_layout, env.arena.alloc(hole));
|
||||||
|
|
||||||
branches.push((i as u64, BranchInfo::None, stmt));
|
branches.push((i as u64, BranchInfo::None, stmt));
|
||||||
|
@ -8206,7 +8230,7 @@ fn lowlevel_enum_lambda_set_to_switch<'a, ToLowLevelCall>(
|
||||||
hole: &'a Stmt<'a>,
|
hole: &'a Stmt<'a>,
|
||||||
) -> Stmt<'a>
|
) -> Stmt<'a>
|
||||||
where
|
where
|
||||||
ToLowLevelCall: Fn(Symbol, Symbol, Layout<'a>) -> Call<'a> + Copy,
|
ToLowLevelCall: Fn(Symbol, Symbol, Layout<'a>, CallSpecId) -> Call<'a> + Copy,
|
||||||
{
|
{
|
||||||
debug_assert!(!lambda_set.is_empty());
|
debug_assert!(!lambda_set.is_empty());
|
||||||
|
|
||||||
|
@ -8219,7 +8243,13 @@ where
|
||||||
|
|
||||||
let hole = Stmt::Jump(join_point_id, env.arena.alloc([result_symbol]));
|
let hole = Stmt::Jump(join_point_id, env.arena.alloc([result_symbol]));
|
||||||
|
|
||||||
let call = to_lowlevel_call(*function_symbol, closure_data_symbol, function_layout);
|
let call_spec_id = env.next_call_specialization_id();
|
||||||
|
let call = to_lowlevel_call(
|
||||||
|
*function_symbol,
|
||||||
|
closure_data_symbol,
|
||||||
|
function_layout,
|
||||||
|
call_spec_id,
|
||||||
|
);
|
||||||
let stmt = build_call(
|
let stmt = build_call(
|
||||||
env,
|
env,
|
||||||
call,
|
call,
|
||||||
|
|
|
@ -43,6 +43,7 @@ pub enum Layout<'a> {
|
||||||
Struct(&'a [Layout<'a>]),
|
Struct(&'a [Layout<'a>]),
|
||||||
Union(UnionLayout<'a>),
|
Union(UnionLayout<'a>),
|
||||||
RecursivePointer,
|
RecursivePointer,
|
||||||
|
|
||||||
/// A function. The types of its arguments, then the type of its return value.
|
/// A function. The types of its arguments, then the type of its return value.
|
||||||
FunctionPointer(&'a [Layout<'a>], &'a Layout<'a>),
|
FunctionPointer(&'a [Layout<'a>], &'a Layout<'a>),
|
||||||
Closure(&'a [Layout<'a>], LambdaSet<'a>, &'a Layout<'a>),
|
Closure(&'a [Layout<'a>], LambdaSet<'a>, &'a Layout<'a>),
|
||||||
|
|
|
@ -114,7 +114,6 @@ pub enum Expr2 {
|
||||||
InvalidLookup(PoolStr), // 8B
|
InvalidLookup(PoolStr), // 8B
|
||||||
|
|
||||||
List {
|
List {
|
||||||
list_var: Variable, // 4B - required for uniqueness of the list
|
|
||||||
elem_var: Variable, // 4B
|
elem_var: Variable, // 4B
|
||||||
elems: PoolVec<Expr2>, // 8B
|
elems: PoolVec<Expr2>, // 8B
|
||||||
},
|
},
|
||||||
|
|
|
@ -346,7 +346,6 @@ pub fn to_expr2<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
let expr = Expr2::List {
|
let expr = Expr2::List {
|
||||||
list_var: env.var_store.fresh(),
|
|
||||||
elem_var: env.var_store.fresh(),
|
elem_var: env.var_store.fresh(),
|
||||||
elems,
|
elems,
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,9 +7,9 @@ app "closure"
|
||||||
|
|
||||||
main : Task.Task {} []
|
main : Task.Task {} []
|
||||||
main = closure1 {}
|
main = closure1 {}
|
||||||
|> Task.after (\_ -> closure2 {})
|
# |> Task.after (\_ -> closure2 {})
|
||||||
|> Task.after (\_ -> closure3 {})
|
# |> Task.after (\_ -> closure3 {})
|
||||||
|> Task.after (\_ -> closure4 {})
|
# |> Task.after (\_ -> closure4 {})
|
||||||
|
|
||||||
# ---
|
# ---
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ const mem = std.mem;
|
||||||
const Allocator = mem.Allocator;
|
const Allocator = mem.Allocator;
|
||||||
|
|
||||||
extern fn roc__mainForHost_1_exposed([*]u8) void;
|
extern fn roc__mainForHost_1_exposed([*]u8) void;
|
||||||
extern fn roc__mainForHost_1_size() i64;
|
extern fn roc__mainForHost_size() i64;
|
||||||
extern fn roc__mainForHost_1_Fx_caller(*const u8, [*]u8, [*]u8) void;
|
extern fn roc__mainForHost_1_Fx_caller(*const u8, [*]u8, [*]u8) void;
|
||||||
extern fn roc__mainForHost_1_Fx_size() i64;
|
extern fn roc__mainForHost_1_Fx_size() i64;
|
||||||
extern fn roc__mainForHost_1_Fx_result_size() i64;
|
extern fn roc__mainForHost_1_Fx_result_size() i64;
|
||||||
|
@ -51,7 +51,7 @@ pub export fn main() u8 {
|
||||||
const stdout = std.io.getStdOut().writer();
|
const stdout = std.io.getStdOut().writer();
|
||||||
const stderr = std.io.getStdErr().writer();
|
const stderr = std.io.getStdErr().writer();
|
||||||
|
|
||||||
const size = @intCast(usize, roc__mainForHost_1_size());
|
const size = @intCast(usize, roc__mainForHost_size());
|
||||||
const raw_output = std.heap.c_allocator.alloc(u8, size) catch unreachable;
|
const raw_output = std.heap.c_allocator.alloc(u8, size) catch unreachable;
|
||||||
var output = @ptrCast([*]u8, raw_output);
|
var output = @ptrCast([*]u8, raw_output);
|
||||||
|
|
||||||
|
|
76
examples/hello-world/platform/host.c
Normal file
76
examples/hello-world/platform/host.c
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
void* roc_alloc(size_t size, unsigned int alignment) {
|
||||||
|
return malloc(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* roc_realloc(void* ptr, size_t old_size, size_t new_size, unsigned int alignment) {
|
||||||
|
return realloc(ptr, new_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void roc_dealloc(void* ptr, unsigned int alignment) {
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RocStr {
|
||||||
|
char* bytes;
|
||||||
|
size_t len;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RocCallResult {
|
||||||
|
size_t flag;
|
||||||
|
struct RocStr content;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern void roc__mainForHost_1_exposed(struct RocCallResult *re);
|
||||||
|
|
||||||
|
const size_t MAX_STACK_STR_BYTES = 1024;
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
// make space for the result
|
||||||
|
struct RocCallResult callresult;
|
||||||
|
|
||||||
|
// call roc to populate the callresult
|
||||||
|
roc__mainForHost_1_exposed(&callresult);
|
||||||
|
struct RocStr str = callresult.content;
|
||||||
|
|
||||||
|
// Convert from RocStr to C string (null-terminated char*)
|
||||||
|
size_t len = str.len;
|
||||||
|
char* c_str;
|
||||||
|
|
||||||
|
// Allocate on the stack unless the string is particularly big.
|
||||||
|
// (Don't want a stack overflow!)
|
||||||
|
if (len <= MAX_STACK_STR_BYTES) {
|
||||||
|
c_str = (char*)alloca(len + 1);
|
||||||
|
} else {
|
||||||
|
c_str = (char*)malloc(len + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(c_str, str.bytes, len);
|
||||||
|
|
||||||
|
// null-terminate
|
||||||
|
c_str[len] = 0;
|
||||||
|
|
||||||
|
// Print the string to stdout
|
||||||
|
printf("%s\n", c_str);
|
||||||
|
|
||||||
|
// Pointer to the beginning of the RocStr's actual allocation, which is
|
||||||
|
// the size_t immediately preceding the first stored byte.
|
||||||
|
size_t* str_base_ptr = (size_t*)str.bytes - 1;
|
||||||
|
|
||||||
|
// If *str_base_ptr is equal to 0, then the string is in the
|
||||||
|
// read-only data section of the binary, and can't be freed!
|
||||||
|
if (*str_base_ptr != 0) {
|
||||||
|
roc_dealloc(str_base_ptr, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we malloc'd c_str, free it.
|
||||||
|
if (len > MAX_STACK_STR_BYTES) {
|
||||||
|
free(c_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
1
examples/hello-zig/.gitignore
vendored
Normal file
1
examples/hello-zig/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
hello-world
|
12
examples/hello-zig/Hello.roc
Normal file
12
examples/hello-zig/Hello.roc
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
app "hello-world"
|
||||||
|
packages { base: "platform" }
|
||||||
|
imports []
|
||||||
|
provides [ main ] to base
|
||||||
|
|
||||||
|
greeting =
|
||||||
|
hi = "Hello"
|
||||||
|
name = "World"
|
||||||
|
|
||||||
|
"\(hi), \(name)!!!!!!!!!!!!!"
|
||||||
|
|
||||||
|
main = greeting
|
48
examples/hello-zig/README.md
Normal file
48
examples/hello-zig/README.md
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
# Hello, World!
|
||||||
|
|
||||||
|
To run, `cd` into this directory and run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ cargo run run Hello.roc
|
||||||
|
```
|
||||||
|
|
||||||
|
To run in release mode instead, do:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ cargo run --release run Hello.roc
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
If you encounter `cannot find -lc++`, run the following for ubuntu `sudo apt install libc++-dev`.
|
||||||
|
|
||||||
|
## Design Notes
|
||||||
|
|
||||||
|
This demonstrates the basic design of hosts: Roc code gets compiled into a pure
|
||||||
|
function (in this case, a thunk that always returns `"Hello, World!"`) and
|
||||||
|
then the host calls that function. Fundamentally, that's the whole idea! The host
|
||||||
|
might not even have a `main` - it could be a library, a plugin, anything.
|
||||||
|
Everything else is built on this basic "hosts calling linked pure functions" design.
|
||||||
|
|
||||||
|
For example, things get more interesting when the compiled Roc function returns
|
||||||
|
a `Task` - that is, a tagged union data structure containing function pointers
|
||||||
|
to callback closures. This lets the Roc pure function describe arbitrary
|
||||||
|
chainable effects, which the host can interpret to perform I/O as requested by
|
||||||
|
the Roc program. (The tagged union `Task` would have a variant for each supported
|
||||||
|
I/O operation.)
|
||||||
|
|
||||||
|
In this trivial example, it's very easy to line up the API between the host and
|
||||||
|
the Roc program. In a more involved host, this would be much trickier - especially
|
||||||
|
if the API were changing frequently during development.
|
||||||
|
|
||||||
|
The idea there is to have a first-class concept of "glue code" which host authors
|
||||||
|
can write (it would be plain Roc code, but with some extra keywords that aren't
|
||||||
|
available in normal modules - kinda like `port module` in Elm), and which
|
||||||
|
describe both the Roc-host/C boundary as well as the Roc-host/Roc-app boundary.
|
||||||
|
Roc application authors only care about the Roc-host/Roc-app portion, and the
|
||||||
|
host author only cares about the Roc-host/C bounary when implementing the host.
|
||||||
|
|
||||||
|
Using this glue code, the Roc compiler can generate C header files describing the
|
||||||
|
boundary. This not only gets us host compatibility with C compilers, but also
|
||||||
|
Rust FFI for free, because [`rust-bindgen`](https://github.com/rust-lang/rust-bindgen)
|
||||||
|
generates correct Rust FFI bindings from C headers.
|
10
examples/hello-zig/platform/Package-Config.roc
Normal file
10
examples/hello-zig/platform/Package-Config.roc
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
platform examples/hello-world
|
||||||
|
requires {}{ main : Str }
|
||||||
|
exposes []
|
||||||
|
packages {}
|
||||||
|
imports []
|
||||||
|
provides [ mainForHost ]
|
||||||
|
effects fx.Effect {}
|
||||||
|
|
||||||
|
mainForHost : Str
|
||||||
|
mainForHost = main
|
|
@ -6,5 +6,7 @@ platform examples/quicksort
|
||||||
provides [ mainForHost ]
|
provides [ mainForHost ]
|
||||||
effects fx.Effect {}
|
effects fx.Effect {}
|
||||||
|
|
||||||
|
update : Model -> Model
|
||||||
|
|
||||||
mainForHost : List I64 -> List I64
|
mainForHost : List I64 -> List I64
|
||||||
mainForHost = \list -> quicksort list
|
mainForHost = \list -> quicksort list
|
||||||
|
|
87
vendor/morphic_lib/src/api.rs
vendored
87
vendor/morphic_lib/src/api.rs
vendored
|
@ -1,7 +1,8 @@
|
||||||
use sha2::{digest::Digest, Sha256};
|
use sha2::{digest::Digest, Sha256};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::collections::{btree_map::Entry, BTreeMap, BTreeSet};
|
use std::collections::{btree_map::Entry, BTreeMap};
|
||||||
|
|
||||||
|
use crate::render_api_ir;
|
||||||
use crate::util::blocks::Blocks;
|
use crate::util::blocks::Blocks;
|
||||||
use crate::util::id_bi_map::IdBiMap;
|
use crate::util::id_bi_map::IdBiMap;
|
||||||
use crate::util::id_type::Count;
|
use crate::util::id_type::Count;
|
||||||
|
@ -554,11 +555,14 @@ pub(crate) enum TypeOp {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A `TypeDef` defines the content type of a named type.
|
/// A `TypeDef` defines the content type of a named type.
|
||||||
pub struct TypeDef;
|
pub struct TypeDef {
|
||||||
|
pub(crate) builder: TypeBuilder,
|
||||||
|
pub(crate) root: TypeId,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct TypeBuilder {
|
pub(crate) struct TypeBuilder {
|
||||||
types: OpGraph<TypeId, TypeOp>,
|
pub(crate) types: OpGraph<TypeId, TypeOp>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TypeBuilder {
|
impl Default for TypeBuilder {
|
||||||
|
@ -637,7 +641,10 @@ impl TypeDefBuilder {
|
||||||
/// Create a `TypeDef` using the given type node as the root.
|
/// Create a `TypeDef` using the given type node as the root.
|
||||||
pub fn build(self, root: TypeId) -> Result<TypeDef> {
|
pub fn build(self, root: TypeId) -> Result<TypeDef> {
|
||||||
self.inner.check(root)?;
|
self.inner.check(root)?;
|
||||||
Ok(TypeDef)
|
Ok(TypeDef {
|
||||||
|
builder: self.inner,
|
||||||
|
root,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -743,27 +750,27 @@ pub(crate) enum Op {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
struct ContinuationInfo {
|
pub(crate) struct ContinuationInfo {
|
||||||
arg_type: TypeId,
|
pub(crate) arg_type: TypeId,
|
||||||
ret_type: TypeId,
|
pub(crate) ret_type: TypeId,
|
||||||
arg: ValueId,
|
pub(crate) arg: ValueId,
|
||||||
body: Option<BlockExpr>,
|
pub(crate) body: Option<BlockExpr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
enum BlockState {
|
pub(crate) enum BlockState {
|
||||||
Detached,
|
Detached,
|
||||||
Attached,
|
Attached,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct ExprBuilder {
|
pub(crate) struct ExprBuilder {
|
||||||
type_builder: TypeBuilder,
|
pub(crate) type_builder: TypeBuilder,
|
||||||
blocks: Blocks<BlockId, ValueId, BlockState>,
|
pub(crate) blocks: Blocks<BlockId, ValueId, BlockState>,
|
||||||
vals: OpGraph<ValueId, Op>,
|
pub(crate) vals: OpGraph<ValueId, Op>,
|
||||||
continuations: IdVec<ContinuationId, ContinuationInfo>,
|
pub(crate) continuations: IdVec<ContinuationId, ContinuationInfo>,
|
||||||
callee_spec_vars: IdBiMap<CalleeSpecVarId, CalleeSpecBuf>,
|
pub(crate) callee_spec_vars: IdBiMap<CalleeSpecVarId, CalleeSpecBuf>,
|
||||||
update_mode_vars: IdBiMap<UpdateModeVarId, UpdateModeBuf>,
|
pub(crate) update_mode_vars: IdBiMap<UpdateModeVarId, UpdateModeBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ExprBuilder {
|
impl Default for ExprBuilder {
|
||||||
|
@ -1134,15 +1141,15 @@ impl ExprContext for ExprBuilder {
|
||||||
|
|
||||||
/// A `FuncDef` defines the signature and body of a function.
|
/// A `FuncDef` defines the signature and body of a function.
|
||||||
pub struct FuncDef {
|
pub struct FuncDef {
|
||||||
builder: FuncDefBuilder,
|
pub(crate) builder: FuncDefBuilder,
|
||||||
arg_type: TypeId,
|
pub(crate) arg_type: TypeId,
|
||||||
ret_type: TypeId,
|
pub(crate) ret_type: TypeId,
|
||||||
root: BlockExpr,
|
pub(crate) root: BlockExpr,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FuncDefBuilder {
|
pub struct FuncDefBuilder {
|
||||||
expr_builder: ExprBuilder,
|
pub(crate) expr_builder: ExprBuilder,
|
||||||
argument: ValueId,
|
pub(crate) argument: ValueId,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for FuncDefBuilder {
|
impl Default for FuncDefBuilder {
|
||||||
|
@ -1187,13 +1194,13 @@ impl FuncDefBuilder {
|
||||||
|
|
||||||
/// A `ConstDef` defines the type and initializer expression for a global constant.
|
/// A `ConstDef` defines the type and initializer expression for a global constant.
|
||||||
pub struct ConstDef {
|
pub struct ConstDef {
|
||||||
builder: ConstDefBuilder,
|
pub(crate) builder: ConstDefBuilder,
|
||||||
type_: TypeId,
|
pub(crate) type_: TypeId,
|
||||||
root: BlockExpr,
|
pub(crate) root: BlockExpr,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ConstDefBuilder {
|
pub struct ConstDefBuilder {
|
||||||
expr_builder: ExprBuilder,
|
pub(crate) expr_builder: ExprBuilder,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ConstDefBuilder {
|
impl Default for ConstDefBuilder {
|
||||||
|
@ -1225,12 +1232,13 @@ impl ConstDefBuilder {
|
||||||
|
|
||||||
/// A `ModDef` defines a module, which is a collection of named types, functions, and constants.
|
/// A `ModDef` defines a module, which is a collection of named types, functions, and constants.
|
||||||
pub struct ModDef {
|
pub struct ModDef {
|
||||||
func_defs: BTreeMap<FuncNameBuf, FuncDef>,
|
pub(crate) type_defs: BTreeMap<TypeNameBuf, TypeDef>,
|
||||||
const_defs: BTreeMap<ConstNameBuf, ConstDef>,
|
pub(crate) func_defs: BTreeMap<FuncNameBuf, FuncDef>,
|
||||||
|
pub(crate) const_defs: BTreeMap<ConstNameBuf, ConstDef>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ModDefBuilder {
|
pub struct ModDefBuilder {
|
||||||
type_names: BTreeSet<TypeNameBuf>,
|
type_defs: BTreeMap<TypeNameBuf, TypeDef>,
|
||||||
func_defs: BTreeMap<FuncNameBuf, FuncDef>,
|
func_defs: BTreeMap<FuncNameBuf, FuncDef>,
|
||||||
const_defs: BTreeMap<ConstNameBuf, ConstDef>,
|
const_defs: BTreeMap<ConstNameBuf, ConstDef>,
|
||||||
}
|
}
|
||||||
|
@ -1244,7 +1252,7 @@ impl Default for ModDefBuilder {
|
||||||
impl ModDefBuilder {
|
impl ModDefBuilder {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
type_names: BTreeSet::new(),
|
type_defs: BTreeMap::new(),
|
||||||
func_defs: BTreeMap::new(),
|
func_defs: BTreeMap::new(),
|
||||||
const_defs: BTreeMap::new(),
|
const_defs: BTreeMap::new(),
|
||||||
}
|
}
|
||||||
|
@ -1252,13 +1260,14 @@ impl ModDefBuilder {
|
||||||
|
|
||||||
pub fn build(self) -> Result<ModDef> {
|
pub fn build(self) -> Result<ModDef> {
|
||||||
Ok(ModDef {
|
Ok(ModDef {
|
||||||
|
type_defs: self.type_defs,
|
||||||
func_defs: self.func_defs,
|
func_defs: self.func_defs,
|
||||||
const_defs: self.const_defs,
|
const_defs: self.const_defs,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_named_type(&mut self, name: TypeName, _type_def: TypeDef) -> Result<()> {
|
pub fn add_named_type(&mut self, name: TypeName, type_def: TypeDef) -> Result<()> {
|
||||||
if !self.type_names.insert(name.into()) {
|
if self.type_defs.insert(name.into(), type_def).is_some() {
|
||||||
return Err(ErrorKind::DuplicateTypeName(name.into()).into());
|
return Err(ErrorKind::DuplicateTypeName(name.into()).into());
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -1284,8 +1293,14 @@ impl ModDefBuilder {
|
||||||
///
|
///
|
||||||
/// Each entry point has an associated main function, which must have signature `() -> ()`.
|
/// Each entry point has an associated main function, which must have signature `() -> ()`.
|
||||||
pub struct Program {
|
pub struct Program {
|
||||||
mods: BTreeMap<ModNameBuf, ModDef>,
|
pub(crate) mods: BTreeMap<ModNameBuf, ModDef>,
|
||||||
entry_points: BTreeMap<EntryPointNameBuf, (ModNameBuf, FuncNameBuf)>,
|
pub(crate) entry_points: BTreeMap<EntryPointNameBuf, (ModNameBuf, FuncNameBuf)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Program {
|
||||||
|
pub fn to_source_string(&self) -> String {
|
||||||
|
render_api_ir::render_program_to_string(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ProgramBuilder {
|
pub struct ProgramBuilder {
|
||||||
|
|
2
vendor/morphic_lib/src/bindings.rs
vendored
2
vendor/morphic_lib/src/bindings.rs
vendored
|
@ -1,6 +1,8 @@
|
||||||
// TODO: These bindings are incomplete
|
// TODO: These bindings are incomplete
|
||||||
// TODO: Add test for compatibility with `include/morphic.h`
|
// TODO: Add test for compatibility with `include/morphic.h`
|
||||||
|
|
||||||
|
#![allow(clippy::boxed_local)]
|
||||||
|
|
||||||
use crate::api::*;
|
use crate::api::*;
|
||||||
use std::{ffi::CString, os::raw::c_char, ptr, slice};
|
use std::{ffi::CString, os::raw::c_char, ptr, slice};
|
||||||
|
|
||||||
|
|
1
vendor/morphic_lib/src/lib.rs
vendored
1
vendor/morphic_lib/src/lib.rs
vendored
|
@ -5,5 +5,6 @@ mod util;
|
||||||
|
|
||||||
mod api;
|
mod api;
|
||||||
mod bindings;
|
mod bindings;
|
||||||
|
mod render_api_ir;
|
||||||
|
|
||||||
pub use api::*;
|
pub use api::*;
|
||||||
|
|
550
vendor/morphic_lib/src/render_api_ir.rs
vendored
Normal file
550
vendor/morphic_lib/src/render_api_ir.rs
vendored
Normal file
|
@ -0,0 +1,550 @@
|
||||||
|
use std::fmt::{Display, Formatter, Write};
|
||||||
|
|
||||||
|
use crate::api::{
|
||||||
|
BlockExpr, CalleeSpecBuf, ConstDef, ConstNameBuf, ContinuationId, EntryPointNameBuf,
|
||||||
|
ExprBuilder, FuncDef, FuncNameBuf, ModDef, ModNameBuf, Op, Program, TypeDef, TypeId,
|
||||||
|
TypeNameBuf, TypeOp, UpdateModeBuf, ValueId,
|
||||||
|
};
|
||||||
|
use crate::util::id_type::Id;
|
||||||
|
use crate::util::op_graph::{Node, OpGraph};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
struct RenderContext {
|
||||||
|
content: String,
|
||||||
|
spaces_per_level: usize,
|
||||||
|
indent_level: usize,
|
||||||
|
pending_indent: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenderContext {
|
||||||
|
fn new(spaces_per_level: usize) -> RenderContext {
|
||||||
|
RenderContext {
|
||||||
|
content: String::new(),
|
||||||
|
spaces_per_level,
|
||||||
|
indent_level: 0,
|
||||||
|
pending_indent: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_indent<R>(&mut self, body: impl for<'a> FnOnce(&'a mut RenderContext) -> R) -> R {
|
||||||
|
self.indent_level += 1;
|
||||||
|
let result = body(self);
|
||||||
|
self.indent_level -= 1;
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&mut self, to_write: impl std::fmt::Display) {
|
||||||
|
if self.pending_indent {
|
||||||
|
self.content
|
||||||
|
.extend((0..self.indent_level * self.spaces_per_level).map(|_| ' '));
|
||||||
|
self.pending_indent = false;
|
||||||
|
}
|
||||||
|
write!(&mut self.content, "{}", to_write).expect("writing to string failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn writeln(&mut self, to_write: impl std::fmt::Display) {
|
||||||
|
self.write(to_write);
|
||||||
|
self.content.push('\n');
|
||||||
|
self.pending_indent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish(self) -> String {
|
||||||
|
self.content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
struct Ident {
|
||||||
|
prefix: &'static str,
|
||||||
|
id: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Ident {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}_{}", self.prefix, self.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn val_ident(value: ValueId) -> Ident {
|
||||||
|
Ident {
|
||||||
|
prefix: "val",
|
||||||
|
id: value.to_index(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn type_ident(type_: TypeId) -> Ident {
|
||||||
|
Ident {
|
||||||
|
prefix: "type",
|
||||||
|
id: type_.to_index(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn continuation_ident(continuation: ContinuationId) -> Ident {
|
||||||
|
Ident {
|
||||||
|
prefix: "cont",
|
||||||
|
id: continuation.to_index(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
struct ByteString<'a>(&'a [u8]);
|
||||||
|
|
||||||
|
impl<'a> Display for ByteString<'a> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "\"")?;
|
||||||
|
for &byte in self.0 {
|
||||||
|
write!(f, "{}", std::ascii::escape_default(byte))?;
|
||||||
|
}
|
||||||
|
write!(f, "\"")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_mode_string(buf: &UpdateModeBuf) -> ByteString {
|
||||||
|
ByteString(&buf.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn callee_spec_string(buf: &CalleeSpecBuf) -> ByteString {
|
||||||
|
ByteString(&buf.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mod_name_string(mod_name: &ModNameBuf) -> ByteString {
|
||||||
|
ByteString(&mod_name.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn func_name_string(func_name: &FuncNameBuf) -> ByteString {
|
||||||
|
ByteString(&func_name.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn type_name_string(type_name: &TypeNameBuf) -> ByteString {
|
||||||
|
ByteString(&type_name.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn const_name_string(const_name: &ConstNameBuf) -> ByteString {
|
||||||
|
ByteString(&const_name.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn entry_point_name_string(entry_point: &EntryPointNameBuf) -> ByteString {
|
||||||
|
ByteString(&entry_point.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn delimit<T>(
|
||||||
|
ctx: &mut RenderContext,
|
||||||
|
items: impl Iterator<Item = T>,
|
||||||
|
mut render_delim: impl for<'a> FnMut(&'a mut RenderContext),
|
||||||
|
mut render_item: impl for<'a> FnMut(&'a mut RenderContext, T),
|
||||||
|
) {
|
||||||
|
for (i, item) in items.enumerate() {
|
||||||
|
if i != 0 {
|
||||||
|
render_delim(ctx);
|
||||||
|
}
|
||||||
|
render_item(ctx, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_op(builder: &ExprBuilder, ctx: &mut RenderContext, op: &Op) {
|
||||||
|
match op {
|
||||||
|
Op::Arg | Op::ContinuationArg | Op::DeclareContinuation { .. } => {
|
||||||
|
ctx.write(format_args!(
|
||||||
|
"/* internal error: {:?} should not be rendered as a value */",
|
||||||
|
op
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Op::Jump {
|
||||||
|
continuation,
|
||||||
|
unreachable_result_type,
|
||||||
|
} => {
|
||||||
|
ctx.write(format_args!(
|
||||||
|
"jump<{}> {}",
|
||||||
|
type_ident(*unreachable_result_type),
|
||||||
|
continuation_ident(*continuation),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Op::UnknownWith { result_type } => {
|
||||||
|
ctx.write(format_args!("unknown_with<{}>", type_ident(*result_type)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Op::Call {
|
||||||
|
callee_spec_var,
|
||||||
|
callee_mod,
|
||||||
|
callee,
|
||||||
|
} => {
|
||||||
|
ctx.write(format_args!(
|
||||||
|
"call[{}] {}::{}",
|
||||||
|
callee_spec_string(&builder.callee_spec_vars[callee_spec_var]),
|
||||||
|
mod_name_string(callee_mod),
|
||||||
|
func_name_string(callee),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Op::ConstRef { const_mod, const_ } => {
|
||||||
|
ctx.write(format_args!(
|
||||||
|
"const_ref {}::{}",
|
||||||
|
mod_name_string(const_mod),
|
||||||
|
const_name_string(const_),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Op::Choice { cases } => {
|
||||||
|
ctx.writeln("choice {");
|
||||||
|
ctx.with_indent(|ctx| {
|
||||||
|
for &case in cases {
|
||||||
|
ctx.write("case ");
|
||||||
|
render_block_expr(builder, ctx, case);
|
||||||
|
ctx.writeln(",");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ctx.write("}");
|
||||||
|
}
|
||||||
|
|
||||||
|
Op::SubBlock { sub_block } => {
|
||||||
|
render_block_expr(builder, ctx, *sub_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
Op::Terminate {
|
||||||
|
unreachable_result_type,
|
||||||
|
} => {
|
||||||
|
ctx.write(format_args!(
|
||||||
|
"terminate<{}>",
|
||||||
|
type_ident(*unreachable_result_type),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Op::NewHeapCell => {
|
||||||
|
ctx.write("new_heap_cell");
|
||||||
|
}
|
||||||
|
|
||||||
|
Op::Touch => {
|
||||||
|
ctx.write("touch");
|
||||||
|
}
|
||||||
|
|
||||||
|
Op::RecursiveTouch => {
|
||||||
|
ctx.write("recursive_touch");
|
||||||
|
}
|
||||||
|
|
||||||
|
Op::UpdateWriteOnly { update_mode_var } => {
|
||||||
|
ctx.write(format_args!(
|
||||||
|
"update_write_only[{}]",
|
||||||
|
update_mode_string(&builder.update_mode_vars[update_mode_var]),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Op::EmptyBag { item_type } => {
|
||||||
|
ctx.write(format_args!("empty_bag<{}>", type_ident(*item_type)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Op::BagInsert => {
|
||||||
|
ctx.write("bag_insert");
|
||||||
|
}
|
||||||
|
|
||||||
|
Op::BagGet => {
|
||||||
|
ctx.write("bag_get");
|
||||||
|
}
|
||||||
|
|
||||||
|
Op::BagRemove => {
|
||||||
|
ctx.write("bag_remove");
|
||||||
|
}
|
||||||
|
|
||||||
|
Op::MakeTuple => {
|
||||||
|
ctx.write("make_tuple");
|
||||||
|
}
|
||||||
|
|
||||||
|
Op::GetTupleField { field_idx } => {
|
||||||
|
ctx.write(format_args!("get_tuple_field {}", field_idx));
|
||||||
|
}
|
||||||
|
|
||||||
|
Op::MakeUnion {
|
||||||
|
variant_types,
|
||||||
|
variant_idx,
|
||||||
|
} => {
|
||||||
|
ctx.write("make_union<");
|
||||||
|
delimit(
|
||||||
|
ctx,
|
||||||
|
variant_types.iter(),
|
||||||
|
|ctx| {
|
||||||
|
ctx.write(", ");
|
||||||
|
},
|
||||||
|
|ctx, variant_type| {
|
||||||
|
ctx.write(type_ident(*variant_type));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
ctx.write(format_args!("> {}", variant_idx));
|
||||||
|
}
|
||||||
|
|
||||||
|
Op::UnwrapUnion { variant_idx } => {
|
||||||
|
ctx.write(format_args!("unwrap_union {}", variant_idx));
|
||||||
|
}
|
||||||
|
|
||||||
|
Op::MakeNamed { named_mod, named } => {
|
||||||
|
ctx.write(format_args!(
|
||||||
|
"make_named {}::{}",
|
||||||
|
mod_name_string(named_mod),
|
||||||
|
type_name_string(named),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Op::UnwrapNamed { named_mod, named } => {
|
||||||
|
ctx.write(format_args!(
|
||||||
|
"unwrap_named {}::{}",
|
||||||
|
mod_name_string(named_mod),
|
||||||
|
type_name_string(named),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_statement(builder: &ExprBuilder, ctx: &mut RenderContext, value_id: ValueId) {
|
||||||
|
match builder.vals.node(value_id) {
|
||||||
|
Node {
|
||||||
|
op: Op::DeclareContinuation { continuation },
|
||||||
|
inputs: [],
|
||||||
|
} => {
|
||||||
|
render_continuation_def(builder, ctx, *continuation);
|
||||||
|
}
|
||||||
|
|
||||||
|
Node { op, inputs } => {
|
||||||
|
ctx.write(format_args!("let {} = ", val_ident(value_id)));
|
||||||
|
render_op(builder, ctx, op);
|
||||||
|
ctx.write(" (");
|
||||||
|
delimit(
|
||||||
|
ctx,
|
||||||
|
inputs.iter(),
|
||||||
|
|ctx| {
|
||||||
|
ctx.write(", ");
|
||||||
|
},
|
||||||
|
|ctx, input| {
|
||||||
|
ctx.write(val_ident(*input));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
ctx.writeln(");");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_continuation_def(
|
||||||
|
builder: &ExprBuilder,
|
||||||
|
ctx: &mut RenderContext,
|
||||||
|
continuation: ContinuationId,
|
||||||
|
) {
|
||||||
|
let info = builder.continuations[continuation];
|
||||||
|
ctx.write(format_args!(
|
||||||
|
"continuation {} ({}: {}) -> {} ",
|
||||||
|
continuation_ident(continuation),
|
||||||
|
val_ident(info.arg),
|
||||||
|
type_ident(info.arg_type),
|
||||||
|
type_ident(info.ret_type),
|
||||||
|
));
|
||||||
|
render_block_expr(
|
||||||
|
builder,
|
||||||
|
ctx,
|
||||||
|
info.body
|
||||||
|
.expect("builder API should have verified that continuation body is defined"),
|
||||||
|
);
|
||||||
|
ctx.writeln("");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_block_expr(builder: &ExprBuilder, ctx: &mut RenderContext, block_expr: BlockExpr) {
|
||||||
|
let BlockExpr(block, ret_val) = block_expr;
|
||||||
|
ctx.writeln("{");
|
||||||
|
ctx.with_indent(|ctx| {
|
||||||
|
for value in builder.blocks.block_values(block) {
|
||||||
|
render_statement(builder, ctx, value);
|
||||||
|
}
|
||||||
|
ctx.writeln(val_ident(ret_val))
|
||||||
|
});
|
||||||
|
ctx.write("}");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_type_node(ctx: &mut RenderContext, node: Node<TypeId, TypeOp>) {
|
||||||
|
match node {
|
||||||
|
Node {
|
||||||
|
op: TypeOp::Named { named_mod, named },
|
||||||
|
inputs,
|
||||||
|
} => {
|
||||||
|
debug_assert!(inputs.is_empty());
|
||||||
|
ctx.write(format_args!(
|
||||||
|
"{}::{}",
|
||||||
|
mod_name_string(named_mod),
|
||||||
|
type_name_string(named)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Node {
|
||||||
|
op: TypeOp::Tuple,
|
||||||
|
inputs,
|
||||||
|
} => {
|
||||||
|
ctx.write("(");
|
||||||
|
delimit(
|
||||||
|
ctx,
|
||||||
|
inputs.iter(),
|
||||||
|
|ctx| {
|
||||||
|
ctx.write(", ");
|
||||||
|
},
|
||||||
|
|ctx, input| {
|
||||||
|
ctx.write(type_ident(*input));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
ctx.write(")");
|
||||||
|
}
|
||||||
|
|
||||||
|
Node {
|
||||||
|
op: TypeOp::Union,
|
||||||
|
inputs,
|
||||||
|
} => {
|
||||||
|
ctx.write("union { ");
|
||||||
|
delimit(
|
||||||
|
ctx,
|
||||||
|
inputs.iter(),
|
||||||
|
|ctx| {
|
||||||
|
ctx.write(", ");
|
||||||
|
},
|
||||||
|
|ctx, input| {
|
||||||
|
ctx.write(type_ident(*input));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if !inputs.is_empty() {
|
||||||
|
ctx.write(" ");
|
||||||
|
}
|
||||||
|
ctx.write("}");
|
||||||
|
}
|
||||||
|
|
||||||
|
Node {
|
||||||
|
op: TypeOp::HeapCell,
|
||||||
|
inputs,
|
||||||
|
} => {
|
||||||
|
debug_assert!(inputs.is_empty());
|
||||||
|
ctx.write("heap_cell");
|
||||||
|
}
|
||||||
|
|
||||||
|
Node {
|
||||||
|
op: TypeOp::Bag,
|
||||||
|
inputs,
|
||||||
|
} => {
|
||||||
|
debug_assert_eq!(inputs.len(), 1);
|
||||||
|
ctx.write(format_args!("bag<{}>", type_ident(inputs[0])));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_type_bindings(ctx: &mut RenderContext, types: &OpGraph<TypeId, TypeOp>) {
|
||||||
|
ctx.writeln("{");
|
||||||
|
ctx.with_indent(|ctx| {
|
||||||
|
for type_id in types.count().iter() {
|
||||||
|
ctx.write(format_args!("type {} = ", type_ident(type_id)));
|
||||||
|
render_type_node(ctx, types.node(type_id));
|
||||||
|
ctx.writeln(";");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ctx.write("}");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_func_def(ctx: &mut RenderContext, name: &FuncNameBuf, func_def: &FuncDef) {
|
||||||
|
ctx.write(format_args!(
|
||||||
|
"fn {} ({}: {}) -> {} ",
|
||||||
|
func_name_string(name),
|
||||||
|
val_ident(func_def.builder.argument),
|
||||||
|
type_ident(func_def.arg_type),
|
||||||
|
type_ident(func_def.ret_type),
|
||||||
|
));
|
||||||
|
render_block_expr(&func_def.builder.expr_builder, ctx, func_def.root);
|
||||||
|
ctx.write(" where ");
|
||||||
|
render_type_bindings(ctx, &func_def.builder.expr_builder.type_builder.types);
|
||||||
|
ctx.writeln("");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_const_def(ctx: &mut RenderContext, name: &ConstNameBuf, const_def: &ConstDef) {
|
||||||
|
ctx.write(format_args!(
|
||||||
|
"const {}: {} = ",
|
||||||
|
const_name_string(name),
|
||||||
|
type_ident(const_def.type_),
|
||||||
|
));
|
||||||
|
render_block_expr(&const_def.builder.expr_builder, ctx, const_def.root);
|
||||||
|
ctx.write(" where ");
|
||||||
|
render_type_bindings(ctx, &const_def.builder.expr_builder.type_builder.types);
|
||||||
|
ctx.writeln("");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_type_def(ctx: &mut RenderContext, name: &TypeNameBuf, type_def: &TypeDef) {
|
||||||
|
ctx.write(format_args!(
|
||||||
|
"type {} = {} where ",
|
||||||
|
type_name_string(name),
|
||||||
|
type_ident(type_def.root),
|
||||||
|
));
|
||||||
|
render_type_bindings(ctx, &type_def.builder.types);
|
||||||
|
ctx.writeln("");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_mod_def(ctx: &mut RenderContext, name: &ModNameBuf, mod_def: &ModDef) {
|
||||||
|
ctx.writeln(format_args!("mod {} {{", mod_name_string(name)));
|
||||||
|
ctx.with_indent(|ctx| {
|
||||||
|
let mut first_item = true;
|
||||||
|
let mut write_spacing = |ctx: &mut RenderContext| {
|
||||||
|
if !first_item {
|
||||||
|
ctx.writeln("");
|
||||||
|
}
|
||||||
|
first_item = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (type_name, type_def) in &mod_def.type_defs {
|
||||||
|
write_spacing(ctx);
|
||||||
|
render_type_def(ctx, type_name, type_def);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const_name, const_def) in &mod_def.const_defs {
|
||||||
|
write_spacing(ctx);
|
||||||
|
render_const_def(ctx, const_name, const_def);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (func_name, func_def) in &mod_def.func_defs {
|
||||||
|
write_spacing(ctx);
|
||||||
|
render_func_def(ctx, func_name, func_def);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ctx.writeln("}");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_entry_point(
|
||||||
|
ctx: &mut RenderContext,
|
||||||
|
name: &EntryPointNameBuf,
|
||||||
|
entry_point: &(ModNameBuf, FuncNameBuf),
|
||||||
|
) {
|
||||||
|
let (mod_, func) = entry_point;
|
||||||
|
ctx.writeln(format_args!(
|
||||||
|
"entry_point {} = {}::{};",
|
||||||
|
entry_point_name_string(name),
|
||||||
|
mod_name_string(mod_),
|
||||||
|
func_name_string(func)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_program(ctx: &mut RenderContext, program: &Program) {
|
||||||
|
ctx.writeln("program {");
|
||||||
|
ctx.with_indent(|ctx| {
|
||||||
|
let mut first_item = true;
|
||||||
|
let mut write_spacing = |ctx: &mut RenderContext| {
|
||||||
|
if !first_item {
|
||||||
|
ctx.writeln("");
|
||||||
|
}
|
||||||
|
first_item = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (mod_name, mod_def) in &program.mods {
|
||||||
|
write_spacing(ctx);
|
||||||
|
render_mod_def(ctx, mod_name, mod_def);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (entry_point_name, entry_point) in &program.entry_points {
|
||||||
|
write_spacing(ctx);
|
||||||
|
render_entry_point(ctx, entry_point_name, entry_point);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ctx.write("}");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_program_to_string(program: &Program) -> String {
|
||||||
|
let mut ctx = RenderContext::new(2);
|
||||||
|
render_program(&mut ctx, program);
|
||||||
|
ctx.finish()
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue