mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-20 07:17:50 +03:00
Merge pull request #5576 from roc-lang/spike-erasure
Implement function erasure
This commit is contained in:
commit
b36ad76cdd
5
Cargo.lock
generated
5
Cargo.lock
generated
@ -3351,6 +3351,7 @@ dependencies = [
|
||||
"roc_parse",
|
||||
"roc_region",
|
||||
"roc_reporting",
|
||||
"roc_solve",
|
||||
"roc_target",
|
||||
"roc_types",
|
||||
"snafu",
|
||||
@ -3591,6 +3592,7 @@ dependencies = [
|
||||
"roc_mono",
|
||||
"roc_packaging",
|
||||
"roc_reporting",
|
||||
"roc_solve",
|
||||
"roc_target",
|
||||
"serde",
|
||||
"serial_test",
|
||||
@ -3611,6 +3613,7 @@ dependencies = [
|
||||
"roc_module",
|
||||
"roc_packaging",
|
||||
"roc_reporting",
|
||||
"roc_solve",
|
||||
"roc_target",
|
||||
"roc_types",
|
||||
]
|
||||
@ -3794,6 +3797,7 @@ dependencies = [
|
||||
"roc_problem",
|
||||
"roc_region",
|
||||
"roc_reporting",
|
||||
"roc_solve",
|
||||
"roc_std",
|
||||
"roc_target",
|
||||
"roc_types",
|
||||
@ -3849,6 +3853,7 @@ dependencies = [
|
||||
"roc_parse",
|
||||
"roc_repl_eval",
|
||||
"roc_reporting",
|
||||
"roc_solve",
|
||||
"roc_target",
|
||||
"roc_types",
|
||||
"tempfile",
|
||||
|
@ -12,6 +12,7 @@ pub fn load_module(
|
||||
) -> LoadedModule {
|
||||
let load_config = LoadConfig {
|
||||
target_info: TargetInfo::default_x86_64(), // editor only needs type info, so this is unused
|
||||
function_kind: roc_solve::FunctionKind::LambdaSet, // TODO the editor may need to dynamically change this
|
||||
render: roc_reporting::report::RenderTarget::ColorTerminal,
|
||||
palette: DEFAULT_PALETTE,
|
||||
threading,
|
||||
|
@ -1489,6 +1489,8 @@ fn adjust_rank_content(
|
||||
rank
|
||||
}
|
||||
|
||||
ErasedLambda => group_rank,
|
||||
|
||||
RangedNumber(_vars) => group_rank,
|
||||
}
|
||||
}
|
||||
@ -1669,6 +1671,7 @@ fn instantiate_rigids_help(
|
||||
}
|
||||
}
|
||||
|
||||
ErasedLambda => {}
|
||||
RangedNumber(_vars) => {}
|
||||
}
|
||||
|
||||
@ -1981,6 +1984,12 @@ fn deep_copy_var_help(
|
||||
copy
|
||||
}
|
||||
|
||||
ErasedLambda => {
|
||||
subs.set(copy, make_descriptor(ErasedLambda));
|
||||
|
||||
copy
|
||||
}
|
||||
|
||||
RangedNumber(vars) => {
|
||||
let new_content = RangedNumber(vars);
|
||||
|
||||
|
@ -385,7 +385,7 @@ pub fn test(_matches: &ArgMatches, _triple: Triple) -> io::Result<i32> {
|
||||
#[cfg(not(windows))]
|
||||
pub fn test(matches: &ArgMatches, triple: Triple) -> io::Result<i32> {
|
||||
use roc_build::program::report_problems_monomorphized;
|
||||
use roc_load::{ExecutionMode, LoadConfig, LoadMonomorphizedError};
|
||||
use roc_load::{ExecutionMode, FunctionKind, LoadConfig, LoadMonomorphizedError};
|
||||
use roc_packaging::cache;
|
||||
use roc_target::TargetInfo;
|
||||
|
||||
@ -427,10 +427,13 @@ pub fn test(matches: &ArgMatches, triple: Triple) -> io::Result<i32> {
|
||||
let target = &triple;
|
||||
let opt_level = opt_level;
|
||||
let target_info = TargetInfo::from(target);
|
||||
// TODO may need to determine this dynamically based on dev builds.
|
||||
let function_kind = FunctionKind::LambdaSet;
|
||||
|
||||
// Step 1: compile the app and generate the .o file
|
||||
let load_config = LoadConfig {
|
||||
target_info,
|
||||
function_kind,
|
||||
// TODO: expose this from CLI?
|
||||
render: roc_reporting::report::RenderTarget::ColorTerminal,
|
||||
palette: roc_reporting::report::DEFAULT_PALETTE,
|
||||
|
@ -11,7 +11,7 @@ use roc_docs::generate_docs_html;
|
||||
use roc_error_macros::user_error;
|
||||
use roc_gen_dev::AssemblyBackendMode;
|
||||
use roc_gen_llvm::llvm::build::LlvmBackendMode;
|
||||
use roc_load::{LoadingProblem, Threading};
|
||||
use roc_load::{FunctionKind, LoadingProblem, Threading};
|
||||
use roc_packaging::cache::{self, RocCacheDir};
|
||||
use roc_target::Target;
|
||||
use std::fs::{self, FileType};
|
||||
@ -123,10 +123,12 @@ fn main() -> io::Result<()> {
|
||||
.get_one::<String>(FLAG_TARGET)
|
||||
.and_then(|s| Target::from_str(s).ok())
|
||||
.unwrap_or_default();
|
||||
let function_kind = FunctionKind::LambdaSet;
|
||||
roc_linker::generate_stub_lib(
|
||||
input_path,
|
||||
RocCacheDir::Persistent(cache::roc_cache_dir().as_path()),
|
||||
&target.to_triple(),
|
||||
function_kind,
|
||||
)
|
||||
}
|
||||
Some((CMD_BUILD, matches)) => {
|
||||
|
@ -14,8 +14,8 @@ use roc_module::low_level::LowLevel;
|
||||
use roc_module::symbol::Symbol;
|
||||
|
||||
use roc_mono::ir::{
|
||||
Call, CallType, EntryPoint, Expr, HigherOrderLowLevel, HostExposedLayouts, ListLiteralElement,
|
||||
Literal, ModifyRc, OptLevel, Proc, ProcLayout, SingleEntryPoint, Stmt,
|
||||
Call, CallType, EntryPoint, ErasedField, Expr, HigherOrderLowLevel, HostExposedLayouts,
|
||||
ListLiteralElement, Literal, ModifyRc, OptLevel, Proc, ProcLayout, SingleEntryPoint, Stmt,
|
||||
};
|
||||
use roc_mono::layout::{
|
||||
Builtin, InLayout, Layout, LayoutInterner, LayoutRepr, Niche, RawFunctionLayout,
|
||||
@ -181,6 +181,7 @@ where
|
||||
|
||||
let mut type_definitions = MutSet::default();
|
||||
let mut host_exposed_functions = Vec::new();
|
||||
let mut erased_functions = Vec::new();
|
||||
|
||||
// all other functions
|
||||
for proc in procs {
|
||||
@ -201,6 +202,17 @@ where
|
||||
|
||||
host_exposed_functions.push((bytes, hels.proc_layout.arguments));
|
||||
}
|
||||
RawFunctionLayout::ErasedFunction(..) => {
|
||||
let it = hels.proc_layout.arguments.iter().copied();
|
||||
let bytes = func_name_bytes_help(
|
||||
hels.symbol,
|
||||
it,
|
||||
Niche::NONE,
|
||||
hels.proc_layout.result,
|
||||
);
|
||||
|
||||
host_exposed_functions.push((bytes, hels.proc_layout.arguments));
|
||||
}
|
||||
RawFunctionLayout::ZeroArgumentThunk(_) => {
|
||||
let bytes = func_name_bytes_help(
|
||||
hels.symbol,
|
||||
@ -226,6 +238,11 @@ where
|
||||
|
||||
let (spec, type_names) = proc_spec(arena, interner, proc)?;
|
||||
|
||||
if proc.is_erased {
|
||||
let args = &*arena.alloc_slice_fill_iter(proc.args.iter().map(|(lay, _)| *lay));
|
||||
erased_functions.push((bytes, args));
|
||||
}
|
||||
|
||||
type_definitions.extend(type_names);
|
||||
|
||||
m.add_func(func_name, spec)?;
|
||||
@ -253,6 +270,7 @@ where
|
||||
entry_point_layout,
|
||||
Some(roc_main),
|
||||
&host_exposed_functions,
|
||||
&erased_functions,
|
||||
)?;
|
||||
|
||||
type_definitions.extend(env.type_names);
|
||||
@ -279,8 +297,14 @@ where
|
||||
.collect();
|
||||
|
||||
let mut env = Env::new();
|
||||
let entry_point_function =
|
||||
build_entry_point(&mut env, interner, layout, None, &host_exposed)?;
|
||||
let entry_point_function = build_entry_point(
|
||||
&mut env,
|
||||
interner,
|
||||
layout,
|
||||
None,
|
||||
&host_exposed,
|
||||
&erased_functions,
|
||||
)?;
|
||||
|
||||
type_definitions.extend(env.type_names);
|
||||
|
||||
@ -364,6 +388,7 @@ fn build_entry_point<'a>(
|
||||
layout: roc_mono::ir::ProcLayout<'a>,
|
||||
entry_point_function: Option<FuncName>,
|
||||
host_exposed_functions: &[([u8; SIZE], &'a [InLayout<'a>])],
|
||||
erased_functions: &[([u8; SIZE], &'a [InLayout<'a>])],
|
||||
) -> Result<FuncDef> {
|
||||
let mut builder = FuncDefBuilder::new();
|
||||
let outer_block = builder.add_block();
|
||||
@ -394,7 +419,7 @@ fn build_entry_point<'a>(
|
||||
}
|
||||
|
||||
// add fake calls to host-exposed functions so they are specialized
|
||||
for (name_bytes, layouts) in host_exposed_functions {
|
||||
for (name_bytes, layouts) in host_exposed_functions.iter().chain(erased_functions) {
|
||||
let host_exposed_func_name = FuncName(name_bytes);
|
||||
|
||||
if Some(host_exposed_func_name) == entry_point_function {
|
||||
@ -788,6 +813,16 @@ fn call_spec<'a>(
|
||||
let module = MOD_APP;
|
||||
builder.add_call(block, spec_var, module, name, arg_value_id)
|
||||
}
|
||||
ByPointer {
|
||||
pointer,
|
||||
ret_layout,
|
||||
arg_layouts: _,
|
||||
} => {
|
||||
let result_type = layout_spec(env, builder, interner, interner.get_repr(*ret_layout))?;
|
||||
let fnptr = env.symbols[pointer];
|
||||
let arg_value_id = build_tuple_value(builder, env, block, call.arguments)?;
|
||||
builder.add_unknown_with(block, &[fnptr, arg_value_id], result_type)
|
||||
}
|
||||
Foreign {
|
||||
foreign_symbol: _,
|
||||
ret_layout,
|
||||
@ -1517,6 +1552,28 @@ fn expr_spec<'a>(
|
||||
let value = with_new_heap_cell(builder, block, union_data)?;
|
||||
builder.add_make_named(block, MOD_APP, type_name, value)
|
||||
}
|
||||
FunctionPointer { .. } => {
|
||||
let pointer_type = layout_spec(env, builder, interner, interner.get_repr(layout))?;
|
||||
|
||||
builder.add_unknown_with(block, &[], pointer_type)
|
||||
}
|
||||
ErasedMake { callee, value } => {
|
||||
let value = match value {
|
||||
Some(v) => box_erasure_value_unknown(builder, block, env.symbols[v]),
|
||||
// model nullptr
|
||||
None => box_erasure_value_unknown_nullptr(builder, block),
|
||||
}?;
|
||||
|
||||
let callee = env.symbols[callee];
|
||||
|
||||
erasure_make(builder, block, value, callee)
|
||||
}
|
||||
ErasedLoad { symbol, field } => {
|
||||
let value = env.symbols[symbol];
|
||||
let loaded_type = layout_spec(env, builder, interner, interner.get_repr(layout))?;
|
||||
|
||||
erasure_load(builder, block, value, *field, loaded_type)
|
||||
}
|
||||
RuntimeErrorFunction(_) => {
|
||||
let type_id = layout_spec(env, builder, interner, interner.get_repr(layout))?;
|
||||
|
||||
@ -1631,6 +1688,9 @@ fn layout_spec_help<'a>(
|
||||
}
|
||||
_ => internal_error!("somehow, a non-recursive layout is under a recursive pointer"),
|
||||
},
|
||||
|
||||
FunctionPointer(_) => function_pointer_type(builder),
|
||||
Erased(_) => erasure_type(builder),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1707,3 +1767,78 @@ fn new_num(builder: &mut FuncDefBuilder, block: BlockId) -> Result<ValueId> {
|
||||
// we model all our numbers as unit values
|
||||
builder.add_make_tuple(block, &[])
|
||||
}
|
||||
|
||||
fn function_pointer_type<TC: TypeContext>(builder: &mut TC) -> Result<TypeId> {
|
||||
builder.add_tuple_type(&[])
|
||||
}
|
||||
|
||||
const ERASURE_CALEE_INDEX: u32 = 0;
|
||||
const ERASURE_VALUE_INDEX: u32 = 1;
|
||||
|
||||
/// Erasure type modeled as
|
||||
///
|
||||
/// ```text
|
||||
/// Tuple(callee: FnPtr, value: HeapCell)
|
||||
/// ```
|
||||
fn erasure_type<TC: TypeContext>(builder: &mut TC) -> Result<TypeId> {
|
||||
let value_cell_id = builder.add_heap_cell_type();
|
||||
let callee_id = function_pointer_type(builder)?;
|
||||
builder.add_tuple_type(&[value_cell_id, callee_id])
|
||||
}
|
||||
|
||||
fn erasure_box_value_type<TC: TypeContext>(builder: &mut TC) -> TypeId {
|
||||
builder.add_heap_cell_type()
|
||||
}
|
||||
|
||||
fn box_erasure_value_unknown(
|
||||
builder: &mut FuncDefBuilder,
|
||||
block: BlockId,
|
||||
value: ValueId,
|
||||
) -> Result<ValueId> {
|
||||
let heap_cell = erasure_box_value_type(builder);
|
||||
builder.add_unknown_with(block, &[value], heap_cell)
|
||||
}
|
||||
|
||||
fn box_erasure_value_unknown_nullptr(
|
||||
builder: &mut FuncDefBuilder,
|
||||
block: BlockId,
|
||||
) -> Result<ValueId> {
|
||||
let heap_cell = erasure_box_value_type(builder);
|
||||
builder.add_unknown_with(block, &[], heap_cell)
|
||||
}
|
||||
|
||||
/// Erasure value modeled as
|
||||
///
|
||||
/// ```text
|
||||
/// callee = make_tuple(&[])
|
||||
/// value = unknown(make_tuple(...captures))
|
||||
///
|
||||
/// x : Tuple(callee: FnPtr, value: HeapCell)
|
||||
/// x = make_tuple(callee, value)
|
||||
/// ```
|
||||
fn erasure_make(
|
||||
builder: &mut FuncDefBuilder,
|
||||
block: BlockId,
|
||||
value: ValueId,
|
||||
callee: ValueId,
|
||||
) -> Result<ValueId> {
|
||||
builder.add_make_tuple(block, &[value, callee])
|
||||
}
|
||||
|
||||
fn erasure_load(
|
||||
builder: &mut FuncDefBuilder,
|
||||
block: BlockId,
|
||||
value: ValueId,
|
||||
field: ErasedField,
|
||||
loaded_type: TypeId,
|
||||
) -> Result<ValueId> {
|
||||
match field {
|
||||
ErasedField::Callee => builder.add_get_tuple_field(block, value, ERASURE_CALEE_INDEX),
|
||||
ErasedField::Value | ErasedField::ValuePtr => {
|
||||
let unknown_heap_cell_value =
|
||||
builder.add_get_tuple_field(block, value, ERASURE_VALUE_INDEX)?;
|
||||
// Cast the unknown cell to the wanted type
|
||||
builder.add_unknown_with(block, &[unknown_heap_cell_value], loaded_type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,8 +8,8 @@ use roc_gen_dev::AssemblyBackendMode;
|
||||
use roc_gen_llvm::llvm::build::{module_from_builtins, LlvmBackendMode};
|
||||
use roc_gen_llvm::llvm::externs::add_default_roc_externs;
|
||||
use roc_load::{
|
||||
EntryPoint, ExecutionMode, ExpectMetadata, LoadConfig, LoadMonomorphizedError, LoadedModule,
|
||||
LoadingProblem, MonomorphizedModule, Threading,
|
||||
EntryPoint, ExecutionMode, ExpectMetadata, FunctionKind, LoadConfig, LoadMonomorphizedError,
|
||||
LoadedModule, LoadingProblem, MonomorphizedModule, Threading,
|
||||
};
|
||||
use roc_mono::ir::{OptLevel, SingleEntryPoint};
|
||||
use roc_packaging::cache::RocCacheDir;
|
||||
@ -740,8 +740,20 @@ pub fn standard_load_config(
|
||||
BuildOrdering::AlwaysBuild => ExecutionMode::Executable,
|
||||
};
|
||||
|
||||
// UNSTABLE(lambda-erasure)
|
||||
let function_kind = if cfg!(debug_assertions) {
|
||||
if std::env::var("EXPERIMENTAL_ROC_ERASE").is_ok() {
|
||||
FunctionKind::Erased
|
||||
} else {
|
||||
FunctionKind::LambdaSet
|
||||
}
|
||||
} else {
|
||||
FunctionKind::LambdaSet
|
||||
};
|
||||
|
||||
LoadConfig {
|
||||
target_info,
|
||||
function_kind,
|
||||
render: RenderTarget::ColorTerminal,
|
||||
palette: DEFAULT_PALETTE,
|
||||
threading,
|
||||
@ -1205,6 +1217,8 @@ pub fn check_file<'a>(
|
||||
|
||||
let load_config = LoadConfig {
|
||||
target_info,
|
||||
// TODO: we may not want this for just checking.
|
||||
function_kind: FunctionKind::LambdaSet,
|
||||
// TODO: expose this from CLI?
|
||||
render: RenderTarget::ColorTerminal,
|
||||
palette: DEFAULT_PALETTE,
|
||||
|
@ -64,26 +64,6 @@ pub type PExpectedTypeIndex = Index<PExpected<TypeOrVar>>;
|
||||
pub type TypeOrVar = EitherIndex<TypeTag, Variable>;
|
||||
|
||||
impl Constraints {
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
constraints: Default::default(),
|
||||
type_slices: Default::default(),
|
||||
variables: Default::default(),
|
||||
loc_symbols: Default::default(),
|
||||
let_constraints: Default::default(),
|
||||
categories: Default::default(),
|
||||
pattern_categories: Default::default(),
|
||||
expectations: Default::default(),
|
||||
pattern_expectations: Default::default(),
|
||||
includes_tags: Default::default(),
|
||||
strings: Default::default(),
|
||||
sketched_rows: Default::default(),
|
||||
eq: Default::default(),
|
||||
pattern_eq: Default::default(),
|
||||
cycles: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
let constraints = Vec::new();
|
||||
let type_slices = Vec::with_capacity(16);
|
||||
|
@ -1148,6 +1148,7 @@ fn deep_copy_type_vars<C: CopyEnv>(
|
||||
})
|
||||
})
|
||||
}
|
||||
ErasedLambda => ErasedLambda,
|
||||
|
||||
RangedNumber(range) => {
|
||||
perform_clone!(RangedNumber(range))
|
||||
|
@ -140,6 +140,7 @@ fn index_var(
|
||||
| Content::FlexAbleVar(_, _)
|
||||
| Content::RigidAbleVar(_, _)
|
||||
| Content::LambdaSet(_)
|
||||
| Content::ErasedLambda
|
||||
| Content::RangedNumber(..) => return Err(TypeError),
|
||||
Content::Error => return Err(TypeError),
|
||||
Content::RecursionVar {
|
||||
|
@ -103,7 +103,7 @@ impl FlatDecodable {
|
||||
| Content::RigidVar(_)
|
||||
| Content::FlexAbleVar(_, _)
|
||||
| Content::RigidAbleVar(_, _) => Err(UnboundVar),
|
||||
Content::LambdaSet(_) => Err(Underivable),
|
||||
Content::LambdaSet(_) | Content::ErasedLambda => Err(Underivable),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,7 +137,7 @@ impl FlatEncodable {
|
||||
| Content::RigidVar(_)
|
||||
| Content::FlexAbleVar(_, _)
|
||||
| Content::RigidAbleVar(_, _) => Err(UnboundVar),
|
||||
Content::LambdaSet(_) => Err(Underivable),
|
||||
Content::LambdaSet(_) | Content::ErasedLambda => Err(Underivable),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -148,7 +148,7 @@ impl FlatHash {
|
||||
| Content::RigidVar(_)
|
||||
| Content::FlexAbleVar(_, _)
|
||||
| Content::RigidAbleVar(_, _) => Err(UnboundVar),
|
||||
Content::LambdaSet(_) => Err(Underivable),
|
||||
Content::LambdaSet(_) | Content::ErasedLambda => Err(Underivable),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ use crate::{
|
||||
use bumpalo::collections::{CollectIn, Vec};
|
||||
use roc_builtins::bitcode::{self, FloatWidth, IntWidth};
|
||||
use roc_collections::all::MutMap;
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_error_macros::{internal_error, todo_lambda_erasure};
|
||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||
use roc_mono::code_gen_help::{CallerProc, CodeGenHelp, HelperOp};
|
||||
use roc_mono::ir::{
|
||||
@ -3630,7 +3630,8 @@ impl<
|
||||
}
|
||||
LayoutRepr::Union(UnionLayout::NonRecursive(_))
|
||||
| LayoutRepr::Builtin(_)
|
||||
| LayoutRepr::Struct(_) => {
|
||||
| LayoutRepr::Struct(_)
|
||||
| LayoutRepr::Erased(_) => {
|
||||
internal_error!("All primitive values should fit in a single register");
|
||||
}
|
||||
}
|
||||
@ -4306,6 +4307,8 @@ impl<
|
||||
dst,
|
||||
);
|
||||
}
|
||||
|
||||
LayoutRepr::Erased(_) => todo_lambda_erasure!(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -4527,5 +4530,6 @@ macro_rules! pointer_layouts {
|
||||
| UnionLayout::NullableWrapped { .. }
|
||||
| UnionLayout::NullableUnwrapped { .. },
|
||||
)
|
||||
| LayoutRepr::FunctionPointer(_)
|
||||
};
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ use crate::{
|
||||
use bumpalo::collections::Vec;
|
||||
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
||||
use roc_collections::all::{MutMap, MutSet};
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_error_macros::{internal_error, todo_lambda_erasure};
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_mono::{
|
||||
ir::{JoinPointId, Param},
|
||||
@ -830,6 +830,7 @@ impl<
|
||||
|
||||
self.copy_to_stack_offset(buf, size, from_offset, to_offset)
|
||||
}
|
||||
LayoutRepr::Erased(_) => todo_lambda_erasure!(),
|
||||
pointer_layouts!() => {
|
||||
// like a 64-bit integer
|
||||
debug_assert_eq!(to_offset % 8, 0);
|
||||
|
@ -10,7 +10,7 @@ use std::collections::hash_map::Entry;
|
||||
use bumpalo::{collections::Vec, Bump};
|
||||
use roc_builtins::bitcode::{self, FloatWidth, IntWidth};
|
||||
use roc_collections::all::{MutMap, MutSet};
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_error_macros::{internal_error, todo_lambda_erasure};
|
||||
use roc_module::ident::ModuleName;
|
||||
use roc_module::low_level::{LowLevel, LowLevelWrapperType};
|
||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||
@ -149,6 +149,13 @@ impl<'a> LastSeenMap<'a> {
|
||||
self.set_last_seen(*sym, stmt);
|
||||
}
|
||||
}
|
||||
Expr::ErasedMake { value, callee } => {
|
||||
value.map(|v| self.set_last_seen(v, stmt));
|
||||
self.set_last_seen(*callee, stmt);
|
||||
}
|
||||
Expr::ErasedLoad { symbol, field: _ } => {
|
||||
self.set_last_seen(*symbol, stmt);
|
||||
}
|
||||
Expr::Struct(syms) => {
|
||||
for sym in *syms {
|
||||
self.set_last_seen(*sym, stmt);
|
||||
@ -176,6 +183,7 @@ impl<'a> LastSeenMap<'a> {
|
||||
Expr::Reset { symbol, .. } | Expr::ResetRef { symbol, .. } => {
|
||||
self.set_last_seen(*symbol, stmt);
|
||||
}
|
||||
Expr::FunctionPointer { .. } => todo_lambda_erasure!(),
|
||||
Expr::EmptyArray => {}
|
||||
Expr::RuntimeErrorFunction(_) => {}
|
||||
}
|
||||
@ -265,6 +273,7 @@ impl<'a> LastSeenMap<'a> {
|
||||
|
||||
match call_type {
|
||||
CallType::ByName { .. } => {}
|
||||
CallType::ByPointer { .. } => {}
|
||||
CallType::LowLevel { .. } => {}
|
||||
CallType::HigherOrder { .. } => {}
|
||||
CallType::Foreign { .. } => {}
|
||||
@ -729,6 +738,10 @@ trait Backend<'a> {
|
||||
self.build_fn_call(sym, fn_name, arguments, arg_layouts, ret_layout)
|
||||
}
|
||||
|
||||
CallType::ByPointer { .. } => {
|
||||
todo_lambda_erasure!()
|
||||
}
|
||||
|
||||
CallType::LowLevel { op: lowlevel, .. } => {
|
||||
let mut arg_layouts: bumpalo::collections::Vec<InLayout<'a>> =
|
||||
bumpalo::vec![in self.env().arena];
|
||||
@ -839,6 +852,9 @@ trait Backend<'a> {
|
||||
Expr::NullPointer => {
|
||||
self.load_literal_i64(sym, 0);
|
||||
}
|
||||
Expr::FunctionPointer { .. } => todo_lambda_erasure!(),
|
||||
Expr::ErasedMake { .. } => todo_lambda_erasure!(),
|
||||
Expr::ErasedLoad { .. } => todo_lambda_erasure!(),
|
||||
Expr::Reset { symbol, .. } => {
|
||||
let layout = *self.layout_map().get(symbol).unwrap();
|
||||
|
||||
|
@ -444,6 +444,7 @@ fn build_exposed_proc<'a, B: Backend<'a>>(backend: &mut B, proc: &Proc<'a>) -> P
|
||||
ret_layout: proc.ret_layout,
|
||||
is_self_recursive: roc_mono::ir::SelfRecursive::NotSelfRecursive,
|
||||
host_exposed_layouts: roc_mono::ir::HostExposedLayouts::NotHostExposed,
|
||||
is_erased: proc.is_erased,
|
||||
}
|
||||
}
|
||||
|
||||
@ -525,6 +526,7 @@ fn build_exposed_generic_proc<'a, B: Backend<'a>>(backend: &mut B, proc: &Proc<'
|
||||
ret_layout: roc_mono::layout::Layout::UNIT,
|
||||
is_self_recursive: roc_mono::ir::SelfRecursive::NotSelfRecursive,
|
||||
host_exposed_layouts: roc_mono::ir::HostExposedLayouts::NotHostExposed,
|
||||
is_erased: proc.is_erased,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -119,8 +119,9 @@ impl<'a> LlvmAlignment<'a> for LayoutRepr<'a> {
|
||||
.runtime_representation()
|
||||
.llvm_alignment_bytes(interner),
|
||||
Builtin(builtin) => builtin.llvm_alignment_bytes(interner),
|
||||
RecursivePointer(_) => interner.target_info().ptr_width() as u32,
|
||||
Ptr(_) => interner.target_info().ptr_width() as u32,
|
||||
RecursivePointer(_) | Ptr(_) | FunctionPointer(_) | Erased(_) => {
|
||||
interner.target_info().ptr_width() as u32
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -296,7 +296,7 @@ fn build_transform_caller_help<'a, 'ctx>(
|
||||
}
|
||||
}
|
||||
|
||||
let result = crate::llvm::build::call_roc_function(
|
||||
let result = crate::llvm::build::call_direct_roc_function(
|
||||
env,
|
||||
layout_interner,
|
||||
roc_function,
|
||||
|
@ -9,6 +9,7 @@ use crate::llvm::refcounting::{
|
||||
build_reset, decrement_refcount_layout, increment_refcount_layout, PointerToRefcount,
|
||||
};
|
||||
use crate::llvm::struct_::{struct_from_fields, RocStruct};
|
||||
use crate::llvm::{erased, fn_ptr};
|
||||
use bumpalo::collections::Vec;
|
||||
use bumpalo::Bump;
|
||||
use inkwell::attributes::{Attribute, AttributeLoc};
|
||||
@ -23,11 +24,11 @@ use inkwell::passes::{PassManager, PassManagerBuilder};
|
||||
use inkwell::types::{
|
||||
AnyType, BasicMetadataTypeEnum, BasicType, BasicTypeEnum, FunctionType, IntType, StructType,
|
||||
};
|
||||
use inkwell::values::BasicValueEnum;
|
||||
use inkwell::values::{
|
||||
BasicMetadataValueEnum, CallSiteValue, FunctionValue, InstructionValue, IntValue, PointerValue,
|
||||
StructValue,
|
||||
};
|
||||
use inkwell::values::{BasicValueEnum, CallableValue};
|
||||
use inkwell::OptimizationLevel;
|
||||
use inkwell::{AddressSpace, IntPredicate};
|
||||
use morphic_lib::{
|
||||
@ -38,6 +39,7 @@ use roc_collections::all::{MutMap, MutSet};
|
||||
use roc_debug_flags::dbg_do;
|
||||
#[cfg(debug_assertions)]
|
||||
use roc_debug_flags::ROC_PRINT_LLVM_FN_VERIFICATION;
|
||||
use roc_error_macros::{internal_error, todo_lambda_erasure};
|
||||
use roc_module::symbol::{Interns, Symbol};
|
||||
use roc_mono::ir::{
|
||||
BranchInfo, CallType, CrashTag, EntryPoint, GlueLayouts, HostExposedLambdaSet,
|
||||
@ -604,8 +606,7 @@ fn promote_to_main_function<'a, 'ctx>(
|
||||
);
|
||||
|
||||
// NOTE fake layout; it is only used for debug prints
|
||||
let roc_main_fn =
|
||||
function_value_by_func_spec(env, *func_spec, symbol, &[], Niche::NONE, Layout::UNIT);
|
||||
let roc_main_fn = function_value_by_func_spec(env, FuncBorrowSpec::Some(*func_spec), symbol);
|
||||
|
||||
let main_fn_name = "$Test.main";
|
||||
|
||||
@ -654,8 +655,7 @@ fn promote_to_wasm_test_wrapper<'a, 'ctx>(
|
||||
);
|
||||
|
||||
// NOTE fake layout; it is only used for debug prints
|
||||
let roc_main_fn =
|
||||
function_value_by_func_spec(env, *func_spec, symbol, &[], Niche::NONE, Layout::UNIT);
|
||||
let roc_main_fn = function_value_by_func_spec(env, FuncBorrowSpec::Some(*func_spec), symbol);
|
||||
|
||||
let output_type = match roc_main_fn.get_type().get_return_type() {
|
||||
Some(return_type) => {
|
||||
@ -690,7 +690,7 @@ fn promote_to_wasm_test_wrapper<'a, 'ctx>(
|
||||
let entry = context.append_basic_block(c_function, "entry");
|
||||
builder.position_at_end(entry);
|
||||
|
||||
let roc_main_fn_result = call_roc_function(
|
||||
let roc_main_fn_result = call_direct_roc_function(
|
||||
env,
|
||||
layout_interner,
|
||||
roc_main_fn,
|
||||
@ -912,7 +912,6 @@ pub(crate) fn build_exp_call<'a, 'ctx>(
|
||||
CallType::ByName {
|
||||
name,
|
||||
specialization_id,
|
||||
arg_layouts,
|
||||
ret_layout,
|
||||
..
|
||||
} => {
|
||||
@ -927,17 +926,39 @@ pub(crate) fn build_exp_call<'a, 'ctx>(
|
||||
let callee_var = CalleeSpecVar(&bytes);
|
||||
let func_spec = func_spec_solutions.callee_spec(callee_var).unwrap();
|
||||
|
||||
roc_call_with_args(
|
||||
roc_call_direct_with_args(
|
||||
env,
|
||||
layout_interner,
|
||||
arg_layouts,
|
||||
*ret_layout,
|
||||
*name,
|
||||
func_spec,
|
||||
FuncBorrowSpec::Some(func_spec),
|
||||
arg_tuples.into_bump_slice(),
|
||||
)
|
||||
}
|
||||
|
||||
CallType::ByPointer {
|
||||
pointer,
|
||||
arg_layouts,
|
||||
ret_layout,
|
||||
} => {
|
||||
let mut args: Vec<BasicValueEnum> = Vec::with_capacity_in(arguments.len(), env.arena);
|
||||
|
||||
for symbol in arguments.iter() {
|
||||
args.push(scope.load_symbol(symbol));
|
||||
}
|
||||
|
||||
let pointer = scope.load_symbol(pointer).into_pointer_value();
|
||||
|
||||
roc_call_erased_with_args(
|
||||
env,
|
||||
layout_interner,
|
||||
pointer,
|
||||
arg_layouts,
|
||||
*ret_layout,
|
||||
args.into_bump_slice(),
|
||||
)
|
||||
}
|
||||
|
||||
CallType::LowLevel { op, update_mode } => {
|
||||
let bytes = update_mode.to_bytes();
|
||||
let update_var = UpdateModeVar(&bytes);
|
||||
@ -1085,6 +1106,24 @@ pub(crate) fn build_exp_expr<'a, 'ctx>(
|
||||
)
|
||||
}
|
||||
|
||||
FunctionPointer { lambda_name } => {
|
||||
let alloca = fn_ptr::build(env, *lambda_name);
|
||||
alloca.into()
|
||||
}
|
||||
ErasedMake { value, callee } => {
|
||||
let value = value.map(|sym| scope.load_symbol(&sym).into_pointer_value());
|
||||
let callee = scope.load_symbol(callee).into_pointer_value();
|
||||
erased::build(env, value, callee).into()
|
||||
}
|
||||
ErasedLoad { symbol, field } => {
|
||||
let value = scope.load_symbol(symbol).into_struct_value();
|
||||
let wanted_llvm_type =
|
||||
basic_type_from_layout(env, layout_interner, layout_interner.get_repr(layout))
|
||||
.into_pointer_type();
|
||||
|
||||
erased::load(env, value, *field, wanted_llvm_type).into()
|
||||
}
|
||||
|
||||
Reset {
|
||||
symbol,
|
||||
update_mode,
|
||||
@ -3849,7 +3888,7 @@ fn expose_function_to_host_help_c_abi_generic<'a, 'ctx>(
|
||||
builder.position_at_end(entry);
|
||||
|
||||
let wrapped_layout = roc_call_result_layout(env.arena, return_layout);
|
||||
call_roc_function(
|
||||
call_direct_roc_function(
|
||||
env,
|
||||
layout_interner,
|
||||
roc_function,
|
||||
@ -3857,7 +3896,7 @@ fn expose_function_to_host_help_c_abi_generic<'a, 'ctx>(
|
||||
arguments_for_call,
|
||||
)
|
||||
} else {
|
||||
call_roc_function(
|
||||
call_direct_roc_function(
|
||||
env,
|
||||
layout_interner,
|
||||
roc_function,
|
||||
@ -3995,7 +4034,7 @@ fn expose_function_to_host_help_c_abi_gen_test<'a, 'ctx>(
|
||||
|
||||
let wrapper_result = roc_call_result_layout(env.arena, return_layout);
|
||||
|
||||
let roc_value = call_roc_function(
|
||||
let roc_value = call_direct_roc_function(
|
||||
env,
|
||||
layout_interner,
|
||||
roc_wrapper_function,
|
||||
@ -4248,7 +4287,7 @@ fn expose_function_to_host_help_c_abi_v2<'a, 'ctx>(
|
||||
|
||||
let arguments = Vec::from_iter_in(it, env.arena);
|
||||
|
||||
let value = call_roc_function(
|
||||
let value = call_direct_roc_function(
|
||||
env,
|
||||
layout_interner,
|
||||
roc_function,
|
||||
@ -4558,7 +4597,7 @@ fn set_jump_and_catch_long_jump<'a, 'ctx>(
|
||||
{
|
||||
builder.position_at_end(then_block);
|
||||
|
||||
let call_result = call_roc_function(
|
||||
let call_result = call_direct_roc_function(
|
||||
env,
|
||||
layout_interner,
|
||||
roc_function,
|
||||
@ -4826,15 +4865,19 @@ pub(crate) fn build_proc_headers<'a, 'r, 'ctx>(
|
||||
|
||||
let it = func_solutions.specs();
|
||||
let mut function_values = std::vec::Vec::with_capacity(it.size_hint().0);
|
||||
|
||||
let is_erased = proc.is_erased;
|
||||
debug_assert!(!is_erased || func_solutions.specs().count() == 1);
|
||||
|
||||
for specialization in it {
|
||||
let fn_val = build_proc_header(
|
||||
env,
|
||||
layout_interner,
|
||||
*specialization,
|
||||
symbol,
|
||||
&proc,
|
||||
layout_ids,
|
||||
);
|
||||
let func_spec = if is_erased {
|
||||
FuncBorrowSpec::Erased
|
||||
} else {
|
||||
FuncBorrowSpec::Some(*specialization)
|
||||
};
|
||||
|
||||
let fn_val =
|
||||
build_proc_header(env, layout_interner, func_spec, symbol, &proc, layout_ids);
|
||||
|
||||
if proc.args.is_empty() {
|
||||
// this is a 0-argument thunk, i.e. a top-level constant definition
|
||||
@ -4889,8 +4932,7 @@ pub fn build_procedures<'a>(
|
||||
);
|
||||
|
||||
// NOTE fake layout; it is only used for debug prints
|
||||
let getter_fn =
|
||||
function_value_by_func_spec(env, *func_spec, symbol, &[], niche, Layout::UNIT);
|
||||
let getter_fn = function_value_by_func_spec(env, FuncBorrowSpec::Some(*func_spec), symbol);
|
||||
|
||||
let name = getter_fn.get_name().to_str().unwrap();
|
||||
let getter_name = symbol.as_str(&env.interns);
|
||||
@ -5006,7 +5048,7 @@ pub fn build_procedures_expose_expects<'a>(
|
||||
|
||||
// NOTE fake layout; it is only used for debug prints
|
||||
let roc_main_fn =
|
||||
function_value_by_func_spec(env, *func_spec, symbol, &[], captures_niche, Layout::UNIT);
|
||||
function_value_by_func_spec(env, FuncBorrowSpec::Some(*func_spec), symbol);
|
||||
|
||||
let name = roc_main_fn.get_name().to_str().unwrap();
|
||||
|
||||
@ -5133,11 +5175,19 @@ fn build_procedures_help<'a>(
|
||||
mod_solutions
|
||||
}
|
||||
|
||||
pub enum FuncBorrowSpec {
|
||||
/// This function has an specialization due to alias analysis.
|
||||
Some(FuncSpec),
|
||||
/// This function does not have a specialization due to alias analysis,
|
||||
/// because it is type-erased, and thus has no statically determined AA specialization.
|
||||
Erased,
|
||||
}
|
||||
|
||||
fn func_spec_name<'a>(
|
||||
arena: &'a Bump,
|
||||
interns: &Interns,
|
||||
symbol: Symbol,
|
||||
func_spec: FuncSpec,
|
||||
func_spec: FuncBorrowSpec,
|
||||
) -> bumpalo::collections::String<'a> {
|
||||
use std::fmt::Write;
|
||||
|
||||
@ -5147,8 +5197,13 @@ fn func_spec_name<'a>(
|
||||
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, "{byte:x?}").unwrap();
|
||||
match func_spec {
|
||||
FuncBorrowSpec::Some(func_spec) => {
|
||||
for byte in func_spec.0.iter() {
|
||||
write!(buf, "{byte:x?}").unwrap();
|
||||
}
|
||||
}
|
||||
FuncBorrowSpec::Erased => write!(buf, "erased").unwrap(),
|
||||
}
|
||||
|
||||
buf
|
||||
@ -5157,7 +5212,7 @@ fn func_spec_name<'a>(
|
||||
fn build_proc_header<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
func_spec: FuncSpec,
|
||||
func_spec: FuncBorrowSpec,
|
||||
symbol: Symbol,
|
||||
proc: &roc_mono::ir::Proc<'a>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
@ -5260,14 +5315,7 @@ fn expose_alias_to_host<'a>(
|
||||
"we expect only one specialization of this symbol"
|
||||
);
|
||||
|
||||
function_value_by_func_spec(
|
||||
env,
|
||||
*func_spec,
|
||||
hels.symbol,
|
||||
hels.proc_layout.arguments,
|
||||
Niche::NONE,
|
||||
hels.proc_layout.result,
|
||||
)
|
||||
function_value_by_func_spec(env, FuncBorrowSpec::Some(*func_spec), hels.symbol)
|
||||
}
|
||||
None => {
|
||||
// morphic did not generate a specialization for this function,
|
||||
@ -5290,6 +5338,7 @@ fn expose_alias_to_host<'a>(
|
||||
)
|
||||
}
|
||||
|
||||
RawFunctionLayout::ErasedFunction(..) => todo_lambda_erasure!(),
|
||||
RawFunctionLayout::ZeroArgumentThunk(result) => {
|
||||
// Define only the return value size, since this is a thunk
|
||||
//
|
||||
@ -5400,7 +5449,7 @@ fn build_closure_caller<'a, 'ctx>(
|
||||
|
||||
builder.build_store(output, call_result);
|
||||
} else {
|
||||
let call_result = call_roc_function(
|
||||
let call_result = call_direct_roc_function(
|
||||
env,
|
||||
layout_interner,
|
||||
evaluator,
|
||||
@ -5571,73 +5620,43 @@ pub fn verify_fn(fn_val: FunctionValue<'_>) {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn function_value_by_func_spec<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
func_spec: FuncSpec,
|
||||
pub(crate) fn function_value_by_func_spec<'ctx>(
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
func_spec: FuncBorrowSpec,
|
||||
symbol: Symbol,
|
||||
arguments: &[InLayout<'a>],
|
||||
niche: Niche<'a>,
|
||||
result: InLayout<'a>,
|
||||
) -> FunctionValue<'ctx> {
|
||||
let fn_name = func_spec_name(env.arena, &env.interns, symbol, func_spec);
|
||||
let fn_name = fn_name.as_str();
|
||||
|
||||
function_value_by_name_help(env, arguments, niche, result, symbol, fn_name)
|
||||
function_value_by_name_help(env, symbol, fn_name)
|
||||
}
|
||||
|
||||
fn function_value_by_name_help<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
arguments: &[InLayout<'a>],
|
||||
_niche: Niche<'a>,
|
||||
result: InLayout<'a>,
|
||||
fn function_value_by_name_help<'ctx>(
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
symbol: Symbol,
|
||||
fn_name: &str,
|
||||
) -> FunctionValue<'ctx> {
|
||||
env.module.get_function(fn_name).unwrap_or_else(|| {
|
||||
if symbol.is_builtin() {
|
||||
eprintln!(
|
||||
"Unrecognized builtin function: {:?}\nLayout: {:?}\n",
|
||||
fn_name,
|
||||
(arguments, result)
|
||||
);
|
||||
eprintln!("Is the function defined? If so, maybe there is a problem with the layout");
|
||||
|
||||
panic!("Unrecognized builtin function: {fn_name:?} (symbol: {symbol:?})",)
|
||||
panic!("Unrecognized builtin function: {fn_name:?} (symbol: {symbol:?})")
|
||||
} else {
|
||||
// Unrecognized non-builtin function:
|
||||
eprintln!(
|
||||
"Unrecognized non-builtin function: {:?}\n\nSymbol: {:?}\nLayout: {:?}\n",
|
||||
fn_name,
|
||||
symbol,
|
||||
(arguments, result)
|
||||
);
|
||||
eprintln!("Is the function defined? If so, maybe there is a problem with the layout");
|
||||
|
||||
panic!("Unrecognized non-builtin function: {fn_name:?} (symbol: {symbol:?})",)
|
||||
panic!("Unrecognized non-builtin function: {fn_name:?} (symbol: {symbol:?})")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn roc_call_with_args<'a, 'ctx>(
|
||||
fn roc_call_direct_with_args<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
argument_layouts: &[InLayout<'a>],
|
||||
result_layout: InLayout<'a>,
|
||||
name: LambdaName<'a>,
|
||||
func_spec: FuncSpec,
|
||||
func_spec: FuncBorrowSpec,
|
||||
arguments: &[BasicValueEnum<'ctx>],
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let fn_val = function_value_by_func_spec(
|
||||
env,
|
||||
func_spec,
|
||||
name.name(),
|
||||
argument_layouts,
|
||||
name.niche(),
|
||||
result_layout,
|
||||
);
|
||||
let fn_val = function_value_by_func_spec(env, func_spec, name.name());
|
||||
|
||||
call_roc_function(
|
||||
call_direct_roc_function(
|
||||
env,
|
||||
layout_interner,
|
||||
fn_val,
|
||||
@ -5646,14 +5665,70 @@ fn roc_call_with_args<'a, 'ctx>(
|
||||
)
|
||||
}
|
||||
|
||||
pub fn call_roc_function<'a, 'ctx>(
|
||||
#[inline(always)]
|
||||
fn roc_call_erased_with_args<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
pointer: PointerValue<'ctx>,
|
||||
argument_layouts: &[InLayout<'a>],
|
||||
result_layout: InLayout<'a>,
|
||||
arguments: &[BasicValueEnum<'ctx>],
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let function_type =
|
||||
fn_ptr::function_type(env, layout_interner, argument_layouts, result_layout);
|
||||
let function_ptr_type = function_type.ptr_type(AddressSpace::default());
|
||||
|
||||
let function_pointer = fn_ptr::cast_to_function_ptr_type(env, pointer, function_ptr_type);
|
||||
let callable_function_pointer = CallableValue::try_from(function_pointer).unwrap();
|
||||
|
||||
let build_call = |arguments: &[BasicMetadataValueEnum<'ctx>]| {
|
||||
env.builder
|
||||
.build_call(callable_function_pointer, arguments, "call")
|
||||
};
|
||||
|
||||
call_roc_function_help(
|
||||
env,
|
||||
layout_interner,
|
||||
build_call,
|
||||
function_type,
|
||||
layout_interner.get_repr(result_layout),
|
||||
arguments,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn call_direct_roc_function<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
roc_function: FunctionValue<'ctx>,
|
||||
result_layout: LayoutRepr<'a>,
|
||||
arguments: &[BasicValueEnum<'ctx>],
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let pass_by_pointer = roc_function.get_type().get_param_types().len() == arguments.len() + 1;
|
||||
let function_type = roc_function.get_type();
|
||||
|
||||
let build_call = |arguments: &[BasicMetadataValueEnum<'ctx>]| {
|
||||
env.builder.build_call(roc_function, arguments, "call")
|
||||
};
|
||||
debug_assert_eq!(roc_function.get_call_conventions(), FAST_CALL_CONV);
|
||||
|
||||
call_roc_function_help(
|
||||
env,
|
||||
layout_interner,
|
||||
build_call,
|
||||
function_type,
|
||||
result_layout,
|
||||
arguments,
|
||||
)
|
||||
}
|
||||
|
||||
fn call_roc_function_help<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
build_call: impl FnOnce(&[BasicMetadataValueEnum<'ctx>]) -> CallSiteValue<'ctx>,
|
||||
roc_function_type: FunctionType<'ctx>,
|
||||
result_layout: LayoutRepr<'a>,
|
||||
arguments: &[BasicValueEnum<'ctx>],
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let pass_by_pointer = roc_function_type.get_param_types().len() == arguments.len() + 1;
|
||||
|
||||
match RocReturn::from_layout(layout_interner, result_layout) {
|
||||
RocReturn::ByPointer if !pass_by_pointer => {
|
||||
@ -5667,14 +5742,10 @@ pub fn call_roc_function<'a, 'ctx>(
|
||||
|
||||
arguments.push(result_alloca.into());
|
||||
|
||||
debug_assert_eq!(
|
||||
roc_function.get_type().get_param_types().len(),
|
||||
arguments.len()
|
||||
);
|
||||
let call = env.builder.build_call(roc_function, &arguments, "call");
|
||||
debug_assert_eq!(roc_function_type.get_param_types().len(), arguments.len());
|
||||
let call = build_call(&arguments);
|
||||
|
||||
// roc functions should have the fast calling convention
|
||||
debug_assert_eq!(roc_function.get_call_conventions(), FAST_CALL_CONV);
|
||||
call.set_call_convention(FAST_CALL_CONV);
|
||||
|
||||
env.builder
|
||||
@ -5689,14 +5760,10 @@ pub fn call_roc_function<'a, 'ctx>(
|
||||
|
||||
arguments.push(result_alloca.into());
|
||||
|
||||
debug_assert_eq!(
|
||||
roc_function.get_type().get_param_types().len(),
|
||||
arguments.len()
|
||||
);
|
||||
let call = env.builder.build_call(roc_function, &arguments, "call");
|
||||
debug_assert_eq!(roc_function_type.get_param_types().len(), arguments.len());
|
||||
let call = build_call(&arguments);
|
||||
|
||||
// roc functions should have the fast calling convention
|
||||
debug_assert_eq!(roc_function.get_call_conventions(), FAST_CALL_CONV);
|
||||
call.set_call_convention(FAST_CALL_CONV);
|
||||
|
||||
if result_layout.is_passed_by_reference(layout_interner) {
|
||||
@ -5710,25 +5777,18 @@ pub fn call_roc_function<'a, 'ctx>(
|
||||
}
|
||||
}
|
||||
RocReturn::Return => {
|
||||
debug_assert_eq!(
|
||||
roc_function.get_type().get_param_types().len(),
|
||||
arguments.len()
|
||||
);
|
||||
debug_assert_eq!(roc_function_type.get_param_types().len(), arguments.len());
|
||||
let it = arguments.iter().map(|x| (*x).into());
|
||||
let arguments = Vec::from_iter_in(it, env.arena);
|
||||
|
||||
let call = env.builder.build_call(roc_function, &arguments, "call");
|
||||
let call = build_call(&arguments);
|
||||
|
||||
// roc functions should have the fast calling convention
|
||||
debug_assert_eq!(roc_function.get_call_conventions(), FAST_CALL_CONV);
|
||||
call.set_call_convention(FAST_CALL_CONV);
|
||||
|
||||
call.try_as_basic_value().left().unwrap_or_else(|| {
|
||||
panic!(
|
||||
"LLVM error: Invalid call by name for name {:?}",
|
||||
roc_function.get_name()
|
||||
)
|
||||
})
|
||||
call.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap_or_else(|| internal_error!("LLVM error: Invalid call by name",))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5905,7 +5965,7 @@ pub enum CCReturn {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct FunctionSpec<'ctx> {
|
||||
pub(crate) struct FunctionSpec<'ctx> {
|
||||
/// The function type
|
||||
pub typ: FunctionType<'ctx>,
|
||||
call_conv: u32,
|
||||
@ -5975,7 +6035,7 @@ impl<'ctx> FunctionSpec<'ctx> {
|
||||
}
|
||||
|
||||
/// Fastcc calling convention
|
||||
fn fastcc<'a, 'env>(
|
||||
pub fn fastcc<'a, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
roc_return: RocReturn,
|
||||
return_type: BasicTypeEnum<'ctx>,
|
||||
@ -6223,7 +6283,7 @@ fn build_foreign_symbol<'a, 'ctx>(
|
||||
}
|
||||
};
|
||||
|
||||
call_roc_function(
|
||||
call_direct_roc_function(
|
||||
env,
|
||||
layout_interner,
|
||||
fastcc_function,
|
||||
@ -6366,7 +6426,7 @@ fn get_foreign_symbol<'ctx>(
|
||||
/// Add a function to a module, after asserting that the function is unique.
|
||||
/// We never want to define the same function twice in the same module!
|
||||
/// The result can be bugs that are difficult to track down.
|
||||
pub fn add_func<'ctx>(
|
||||
pub(crate) fn add_func<'ctx>(
|
||||
ctx: &Context,
|
||||
module: &Module<'ctx>,
|
||||
name: &str,
|
||||
|
@ -168,6 +168,8 @@ fn build_eq<'a, 'ctx>(
|
||||
),
|
||||
|
||||
LayoutRepr::LambdaSet(_) => unreachable!("cannot compare closures"),
|
||||
LayoutRepr::FunctionPointer(_) => unreachable!("cannot compare function pointers"),
|
||||
LayoutRepr::Erased(_) => unreachable!("cannot compare erased types"),
|
||||
|
||||
LayoutRepr::Union(union_layout) => build_tag_eq(
|
||||
env,
|
||||
@ -399,6 +401,8 @@ fn build_neq<'a, 'ctx>(
|
||||
unreachable!("recursion pointers should never be compared directly")
|
||||
}
|
||||
LayoutRepr::LambdaSet(_) => unreachable!("cannot compare closure"),
|
||||
LayoutRepr::FunctionPointer(_) => unreachable!("cannot compare function pointers"),
|
||||
LayoutRepr::Erased(_) => unreachable!("cannot compare erased types"),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1364,7 +1368,7 @@ fn build_box_eq<'a, 'ctx>(
|
||||
env.builder.set_current_debug_location(di_location);
|
||||
let call = env
|
||||
.builder
|
||||
.build_call(function, &[tag1.into(), tag2.into()], "tag_eq");
|
||||
.build_call(function, &[tag1.into(), tag2.into()], "box_eq");
|
||||
|
||||
call.set_call_convention(FAST_CALL_CONV);
|
||||
|
||||
@ -1429,12 +1433,47 @@ fn build_box_eq_help<'a, 'ctx>(
|
||||
"compare_pointers",
|
||||
);
|
||||
|
||||
let compare_inner_value = ctx.append_basic_block(parent, "compare_inner_value");
|
||||
let check_null_then_compare_inner_values =
|
||||
ctx.append_basic_block(parent, "check_null_then_compare_inner_values");
|
||||
|
||||
env.builder.build_conditional_branch(
|
||||
ptr_equal,
|
||||
return_true,
|
||||
check_null_then_compare_inner_values,
|
||||
);
|
||||
|
||||
env.builder
|
||||
.build_conditional_branch(ptr_equal, return_true, compare_inner_value);
|
||||
.position_at_end(check_null_then_compare_inner_values);
|
||||
|
||||
env.builder.position_at_end(compare_inner_value);
|
||||
// Check for nullability, then compare inner values
|
||||
|
||||
let box1_is_null = env
|
||||
.builder
|
||||
.build_is_null(box1.into_pointer_value(), "box1_is_null");
|
||||
let check_box2_is_null = ctx.append_basic_block(parent, "check_if_box2_is_null");
|
||||
let return_false = ctx.append_basic_block(parent, "return_false");
|
||||
let compare_inner_values = ctx.append_basic_block(parent, "compare_inner_values");
|
||||
|
||||
env.builder
|
||||
.build_conditional_branch(box1_is_null, return_false, check_box2_is_null);
|
||||
|
||||
{
|
||||
env.builder.position_at_end(check_box2_is_null);
|
||||
let box2_is_null = env
|
||||
.builder
|
||||
.build_is_null(box2.into_pointer_value(), "box2_is_null");
|
||||
env.builder
|
||||
.build_conditional_branch(box2_is_null, return_false, compare_inner_values);
|
||||
}
|
||||
|
||||
{
|
||||
env.builder.position_at_end(return_false);
|
||||
env.builder
|
||||
.build_return(Some(&env.context.bool_type().const_zero()));
|
||||
}
|
||||
|
||||
// Compare the inner values.
|
||||
env.builder.position_at_end(compare_inner_values);
|
||||
|
||||
// clear the tag_id so we get a pointer to the actual data
|
||||
let box1 = box1.into_pointer_value();
|
||||
|
@ -1,14 +1,15 @@
|
||||
use crate::llvm::build::{BuilderExt, Env};
|
||||
use crate::llvm::build::{BuilderExt, Env, FunctionSpec, RocReturn};
|
||||
use crate::llvm::erased;
|
||||
use crate::llvm::memcpy::build_memcpy;
|
||||
use bumpalo::collections::Vec as AVec;
|
||||
use bumpalo::collections::{CollectIn, Vec as AVec};
|
||||
use inkwell::context::Context;
|
||||
use inkwell::types::{BasicType, BasicTypeEnum, FloatType, IntType, StructType};
|
||||
use inkwell::values::PointerValue;
|
||||
use inkwell::AddressSpace;
|
||||
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
||||
use roc_mono::layout::{
|
||||
round_up_to_alignment, Builtin, InLayout, Layout, LayoutInterner, LayoutRepr, STLayoutInterner,
|
||||
UnionLayout,
|
||||
round_up_to_alignment, Builtin, FunctionPointer, InLayout, Layout, LayoutInterner, LayoutRepr,
|
||||
STLayoutInterner, UnionLayout,
|
||||
};
|
||||
use roc_target::TargetInfo;
|
||||
|
||||
@ -48,6 +49,22 @@ pub fn basic_type_from_layout<'a, 'ctx>(
|
||||
.ptr_type(AddressSpace::default())
|
||||
.as_basic_type_enum(),
|
||||
|
||||
FunctionPointer(self::FunctionPointer { args, ret }) => {
|
||||
let args = args.iter().map(|arg| {
|
||||
basic_type_from_layout(env, layout_interner, layout_interner.get_repr(*arg))
|
||||
});
|
||||
|
||||
let ret_repr = layout_interner.get_repr(ret);
|
||||
let ret = basic_type_from_layout(env, layout_interner, ret_repr);
|
||||
|
||||
let roc_return = RocReturn::from_layout(layout_interner, ret_repr);
|
||||
|
||||
let fn_spec = FunctionSpec::fastcc(env, roc_return, ret, args.collect_in(env.arena));
|
||||
|
||||
fn_spec.typ.ptr_type(AddressSpace::default()).into()
|
||||
}
|
||||
Erased(_) => erased::basic_type(env).into(),
|
||||
|
||||
Builtin(builtin) => basic_type_from_builtin(env, &builtin),
|
||||
}
|
||||
}
|
||||
|
131
crates/compiler/gen_llvm/src/llvm/erased.rs
Normal file
131
crates/compiler/gen_llvm/src/llvm/erased.rs
Normal file
@ -0,0 +1,131 @@
|
||||
use inkwell::{
|
||||
types::{PointerType, StructType},
|
||||
values::{PointerValue, StructValue},
|
||||
AddressSpace,
|
||||
};
|
||||
use roc_mono::ir::ErasedField;
|
||||
|
||||
use super::build::Env;
|
||||
|
||||
pub fn opaque_ptr_type<'ctx>(env: &Env<'_, 'ctx, '_>) -> PointerType<'ctx> {
|
||||
env.context.i8_type().ptr_type(AddressSpace::default())
|
||||
}
|
||||
|
||||
fn refcounter_type<'ctx>(env: &Env<'_, 'ctx, '_>) -> PointerType<'ctx> {
|
||||
let return_void = env.context.void_type();
|
||||
let arg_ty = opaque_ptr_type(env);
|
||||
|
||||
return_void
|
||||
.fn_type(&[arg_ty.into()], false)
|
||||
.ptr_type(AddressSpace::default())
|
||||
}
|
||||
|
||||
/// Erased is laid out like
|
||||
///
|
||||
/// ```text
|
||||
/// struct Erased {
|
||||
/// value: void*,
|
||||
/// callee: void*,
|
||||
/// refcounter_inc: (void* -> void) *,
|
||||
/// refcounter_dec: (void* -> void) *,
|
||||
/// }
|
||||
/// ```
|
||||
pub fn basic_type<'ctx>(env: &Env<'_, 'ctx, '_>) -> StructType<'ctx> {
|
||||
let opaque_ptr_ty = opaque_ptr_type(env);
|
||||
let refcounter_ptr_ty = refcounter_type(env);
|
||||
|
||||
env.context.struct_type(
|
||||
&[
|
||||
opaque_ptr_ty.into(),
|
||||
opaque_ptr_ty.into(),
|
||||
refcounter_ptr_ty.into(),
|
||||
refcounter_ptr_ty.into(),
|
||||
],
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
fn bitcast_to_opaque_ptr<'ctx>(
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
value: PointerValue<'ctx>,
|
||||
) -> PointerValue<'ctx> {
|
||||
env.builder
|
||||
.build_bitcast(
|
||||
value,
|
||||
env.context.i8_type().ptr_type(AddressSpace::default()),
|
||||
"to_opaque_ptr",
|
||||
)
|
||||
.into_pointer_value()
|
||||
}
|
||||
|
||||
pub fn build<'ctx>(
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
value: Option<PointerValue<'ctx>>,
|
||||
callee: PointerValue<'ctx>,
|
||||
) -> StructValue<'ctx> {
|
||||
let struct_type = basic_type(env);
|
||||
|
||||
let struct_value = struct_type.const_zero().into();
|
||||
|
||||
let struct_value = match value {
|
||||
Some(value) => {
|
||||
let value = bitcast_to_opaque_ptr(env, value);
|
||||
env.builder
|
||||
.build_insert_value(struct_value, value, 0, "insert_value")
|
||||
.unwrap()
|
||||
}
|
||||
None => struct_value,
|
||||
};
|
||||
|
||||
let callee = bitcast_to_opaque_ptr(env, callee);
|
||||
let struct_value = env
|
||||
.builder
|
||||
.build_insert_value(struct_value, callee, 1, "insert_callee")
|
||||
.unwrap();
|
||||
|
||||
// TODO: insert refcounter
|
||||
|
||||
struct_value.into_struct_value()
|
||||
}
|
||||
|
||||
pub fn load<'ctx>(
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
erasure: StructValue<'ctx>,
|
||||
field: ErasedField,
|
||||
as_type: PointerType<'ctx>,
|
||||
) -> PointerValue<'ctx> {
|
||||
let index = match field {
|
||||
ErasedField::Value => 0,
|
||||
ErasedField::ValuePtr => 0,
|
||||
ErasedField::Callee => 1,
|
||||
};
|
||||
|
||||
let value = env
|
||||
.builder
|
||||
.build_extract_value(erasure, index, "extract_erased_value")
|
||||
.unwrap()
|
||||
.into_pointer_value();
|
||||
|
||||
let value = env
|
||||
.builder
|
||||
.build_bitcast(value, as_type, "bitcast_to_type")
|
||||
.into_pointer_value();
|
||||
|
||||
value
|
||||
}
|
||||
|
||||
pub fn load_refcounter<'ctx>(
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
erasure: StructValue<'ctx>,
|
||||
mode: super::refcounting::Mode,
|
||||
) -> PointerValue<'ctx> {
|
||||
let index = match mode {
|
||||
super::refcounting::Mode::Inc => 2,
|
||||
super::refcounting::Mode::Dec => 3,
|
||||
};
|
||||
|
||||
env.builder
|
||||
.build_extract_value(erasure, index, "extract_refcounter")
|
||||
.unwrap()
|
||||
.into_pointer_value()
|
||||
}
|
@ -9,7 +9,7 @@ use inkwell::types::{BasicMetadataTypeEnum, BasicType, BasicTypeEnum};
|
||||
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue};
|
||||
use inkwell::AddressSpace;
|
||||
use roc_builtins::bitcode;
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_error_macros::{internal_error, todo_lambda_erasure};
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_mono::ir::LookupType;
|
||||
use roc_mono::layout::{
|
||||
@ -387,6 +387,8 @@ fn build_clone<'a, 'ctx>(
|
||||
union_layout,
|
||||
)
|
||||
}
|
||||
LayoutRepr::FunctionPointer(_) => todo_lambda_erasure!(),
|
||||
LayoutRepr::Erased(_) => todo_lambda_erasure!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
48
crates/compiler/gen_llvm/src/llvm/fn_ptr.rs
Normal file
48
crates/compiler/gen_llvm/src/llvm/fn_ptr.rs
Normal file
@ -0,0 +1,48 @@
|
||||
use bumpalo::collections::CollectIn;
|
||||
use inkwell::{
|
||||
types::{FunctionType, PointerType},
|
||||
values::{FunctionValue, PointerValue},
|
||||
};
|
||||
|
||||
use roc_mono::layout::{InLayout, LambdaName, LayoutInterner, STLayoutInterner};
|
||||
|
||||
use super::{
|
||||
build::{function_value_by_func_spec, Env, FuncBorrowSpec, FunctionSpec, RocReturn},
|
||||
convert::{argument_type_from_layout, basic_type_from_layout},
|
||||
};
|
||||
|
||||
pub fn function_type<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
arguments: &[InLayout<'a>],
|
||||
return_type: InLayout<'a>,
|
||||
) -> FunctionType<'ctx> {
|
||||
let args = arguments
|
||||
.iter()
|
||||
.map(|arg| argument_type_from_layout(env, layout_interner, layout_interner.get_repr(*arg)));
|
||||
|
||||
let ret_repr = layout_interner.get_repr(return_type);
|
||||
let ret = basic_type_from_layout(env, layout_interner, ret_repr);
|
||||
|
||||
let roc_return = RocReturn::from_layout(layout_interner, ret_repr);
|
||||
|
||||
let fn_spec = FunctionSpec::fastcc(env, roc_return, ret, args.collect_in(env.arena));
|
||||
|
||||
fn_spec.typ
|
||||
}
|
||||
|
||||
pub fn build<'a, 'ctx>(env: &Env<'a, 'ctx, '_>, lambda_name: LambdaName<'a>) -> PointerValue<'ctx> {
|
||||
let func_value: FunctionValue<'ctx> =
|
||||
function_value_by_func_spec(env, FuncBorrowSpec::Erased, lambda_name.name());
|
||||
func_value.as_global_value().as_pointer_value()
|
||||
}
|
||||
|
||||
pub fn cast_to_function_ptr_type<'ctx>(
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
pointer: PointerValue<'ctx>,
|
||||
function_pointer_type: PointerType<'ctx>,
|
||||
) -> PointerValue<'ctx> {
|
||||
env.builder
|
||||
.build_bitcast(pointer, function_pointer_type, "cast_to_function_ptr")
|
||||
.into_pointer_value()
|
||||
}
|
@ -31,7 +31,7 @@ use crate::llvm::{
|
||||
build::{
|
||||
cast_basic_basic, complex_bitcast_check_size, create_entry_block_alloca,
|
||||
entry_block_alloca_zerofill, function_value_by_func_spec, load_roc_value,
|
||||
roc_function_call, tag_pointer_clear_tag_id, BuilderExt, RocReturn,
|
||||
roc_function_call, tag_pointer_clear_tag_id, BuilderExt, FuncBorrowSpec, RocReturn,
|
||||
},
|
||||
build_list::{
|
||||
list_append_unsafe, list_concat, list_drop_at, list_get_unsafe, list_len, list_map,
|
||||
@ -2611,7 +2611,7 @@ pub(crate) fn run_higher_order_low_level<'a, 'ctx>(
|
||||
} = higher_order;
|
||||
|
||||
let PassedFunction {
|
||||
argument_layouts,
|
||||
argument_layouts: _,
|
||||
return_layout: result_layout,
|
||||
owns_captured_environment: function_owns_closure_data,
|
||||
name: function_name,
|
||||
@ -2624,11 +2624,8 @@ pub(crate) fn run_higher_order_low_level<'a, 'ctx>(
|
||||
() => {{
|
||||
let function = function_value_by_func_spec(
|
||||
env,
|
||||
func_spec,
|
||||
FuncBorrowSpec::Some(func_spec),
|
||||
function_name.name(),
|
||||
argument_layouts,
|
||||
function_name.niche(),
|
||||
return_layout,
|
||||
);
|
||||
|
||||
let (closure, closure_layout) =
|
||||
|
@ -11,6 +11,8 @@ mod lowlevel;
|
||||
pub mod refcounting;
|
||||
|
||||
mod align;
|
||||
mod erased;
|
||||
mod fn_ptr;
|
||||
mod memcpy;
|
||||
mod scope;
|
||||
mod struct_;
|
||||
|
@ -14,16 +14,20 @@ use bumpalo::collections::Vec;
|
||||
use inkwell::basic_block::BasicBlock;
|
||||
use inkwell::module::Linkage;
|
||||
use inkwell::types::{AnyTypeEnum, BasicMetadataTypeEnum, BasicType, BasicTypeEnum};
|
||||
use inkwell::values::{BasicValueEnum, FunctionValue, InstructionValue, IntValue, PointerValue};
|
||||
use inkwell::values::{
|
||||
BasicValueEnum, CallableValue, FunctionValue, InstructionValue, IntValue, PointerValue,
|
||||
};
|
||||
use inkwell::{AddressSpace, IntPredicate};
|
||||
use roc_module::symbol::Interns;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_mono::ir::ErasedField;
|
||||
use roc_mono::layout::{
|
||||
Builtin, InLayout, LayoutIds, LayoutInterner, LayoutRepr, STLayoutInterner, UnionLayout,
|
||||
Builtin, InLayout, Layout, LayoutIds, LayoutInterner, LayoutRepr, STLayoutInterner, UnionLayout,
|
||||
};
|
||||
|
||||
use super::build::{cast_if_necessary_for_opaque_recursive_pointers, load_roc_value, FunctionSpec};
|
||||
use super::convert::{argument_type_from_layout, argument_type_from_union_layout};
|
||||
use super::erased;
|
||||
|
||||
pub struct PointerToRefcount<'ctx> {
|
||||
value: PointerValue<'ctx>,
|
||||
@ -388,6 +392,94 @@ fn modify_refcount_struct_help<'a, 'ctx>(
|
||||
builder.build_return(None);
|
||||
}
|
||||
|
||||
fn modify_refcount_erased<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
mode: Mode,
|
||||
) -> FunctionValue<'ctx> {
|
||||
let block = env.builder.get_insert_block().expect("to be in a function");
|
||||
let di_location = env.builder.get_current_debug_location().unwrap();
|
||||
|
||||
let (_, fn_name) = function_name_from_mode(
|
||||
layout_ids,
|
||||
&env.interns,
|
||||
"increment_erased",
|
||||
"decrement_erased",
|
||||
layout_interner.get_repr(Layout::ERASED),
|
||||
mode,
|
||||
);
|
||||
|
||||
let function = match env.module.get_function(fn_name.as_str()) {
|
||||
Some(function_value) => function_value,
|
||||
None => {
|
||||
let arg_type = erased::basic_type(env);
|
||||
let function_value = build_header(env, arg_type.into(), mode, &fn_name);
|
||||
|
||||
modify_refcount_erased_help(env, mode, function_value);
|
||||
|
||||
function_value
|
||||
}
|
||||
};
|
||||
|
||||
env.builder.position_at_end(block);
|
||||
env.builder.set_current_debug_location(di_location);
|
||||
|
||||
function
|
||||
}
|
||||
|
||||
fn modify_refcount_erased_help<'ctx>(
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
mode: Mode,
|
||||
fn_val: FunctionValue<'ctx>,
|
||||
) {
|
||||
let builder = env.builder;
|
||||
let ctx = env.context;
|
||||
|
||||
// Add a basic block for the entry point
|
||||
let entry = ctx.append_basic_block(fn_val, "entry");
|
||||
|
||||
builder.position_at_end(entry);
|
||||
|
||||
debug_info_init!(env, fn_val);
|
||||
|
||||
// Add args to scope
|
||||
let arg_symbol = Symbol::ARG_1;
|
||||
let arg_val = fn_val.get_param_iter().next().unwrap().into_struct_value();
|
||||
|
||||
arg_val.set_name(arg_symbol.as_str(&env.interns));
|
||||
|
||||
let refcounter = erased::load_refcounter(env, arg_val, mode);
|
||||
let refcounter_is_null = env.builder.build_is_null(refcounter, "refcounter_unset");
|
||||
|
||||
let call_refcounter_block = ctx.append_basic_block(fn_val, "call_refcounter");
|
||||
let noop_block = ctx.append_basic_block(fn_val, "noop");
|
||||
|
||||
builder.build_conditional_branch(refcounter_is_null, noop_block, call_refcounter_block);
|
||||
{
|
||||
builder.position_at_end(call_refcounter_block);
|
||||
let value = erased::load(
|
||||
env,
|
||||
arg_val,
|
||||
ErasedField::Value,
|
||||
erased::opaque_ptr_type(env),
|
||||
);
|
||||
|
||||
builder.build_call(
|
||||
CallableValue::try_from(refcounter).unwrap(),
|
||||
&[value.into()],
|
||||
"call_refcounter",
|
||||
);
|
||||
|
||||
builder.build_return(None);
|
||||
}
|
||||
|
||||
{
|
||||
builder.position_at_end(noop_block);
|
||||
builder.build_return(None);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn increment_refcount_layout<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
@ -624,6 +716,12 @@ fn modify_refcount_layout_build_function<'a, 'ctx>(
|
||||
mode,
|
||||
lambda_set.runtime_representation(),
|
||||
),
|
||||
FunctionPointer(_) => None,
|
||||
Erased(_) => {
|
||||
let function = modify_refcount_erased(env, layout_interner, layout_ids, mode);
|
||||
|
||||
Some(function)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ use bumpalo::collections::{String, Vec};
|
||||
|
||||
use roc_builtins::bitcode::{self, FloatWidth, IntWidth};
|
||||
use roc_collections::all::MutMap;
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_error_macros::{internal_error, todo_lambda_erasure};
|
||||
use roc_module::low_level::{LowLevel, LowLevelWrapperType};
|
||||
use roc_module::symbol::{Interns, Symbol};
|
||||
use roc_mono::code_gen_help::{CodeGenHelp, HelperOp, REFCOUNT_MAX};
|
||||
@ -1136,6 +1136,10 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
||||
storage,
|
||||
),
|
||||
|
||||
Expr::FunctionPointer { .. } => todo_lambda_erasure!(),
|
||||
Expr::ErasedMake { .. } => todo_lambda_erasure!(),
|
||||
Expr::ErasedLoad { .. } => todo_lambda_erasure!(),
|
||||
|
||||
Expr::Reset { symbol: arg, .. } => self.expr_reset(*arg, sym, storage),
|
||||
|
||||
Expr::ResetRef { symbol: arg, .. } => self.expr_resetref(*arg, sym, storage),
|
||||
@ -1317,6 +1321,10 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
||||
)
|
||||
}
|
||||
|
||||
CallType::ByPointer { .. } => {
|
||||
todo_lambda_erasure!()
|
||||
}
|
||||
|
||||
CallType::LowLevel { op: lowlevel, .. } => {
|
||||
self.expr_call_low_level(*lowlevel, arguments, ret_sym, ret_layout, ret_storage)
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
||||
use roc_error_macros::todo_lambda_erasure;
|
||||
use roc_mono::layout::{InLayout, LayoutInterner, LayoutRepr, STLayoutInterner, UnionLayout};
|
||||
|
||||
use crate::{PTR_SIZE, PTR_TYPE};
|
||||
@ -99,6 +100,8 @@ impl WasmLayout {
|
||||
)
|
||||
| LayoutRepr::Ptr(_)
|
||||
| LayoutRepr::RecursivePointer(_) => Self::Primitive(PTR_TYPE, PTR_SIZE),
|
||||
LayoutRepr::FunctionPointer(_) => todo_lambda_erasure!(),
|
||||
LayoutRepr::Erased(_) => todo_lambda_erasure!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
use bumpalo::collections::Vec;
|
||||
use bumpalo::Bump;
|
||||
use roc_builtins::bitcode::{self, FloatWidth, IntWidth};
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_error_macros::{internal_error, todo_lambda_erasure};
|
||||
use roc_module::low_level::LowLevel;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_mono::code_gen_help::HelperOp;
|
||||
@ -2120,6 +2120,9 @@ impl<'a> LowLevelCall<'a> {
|
||||
self.arguments,
|
||||
)
|
||||
}
|
||||
|
||||
LayoutRepr::FunctionPointer(_) => todo_lambda_erasure!(),
|
||||
LayoutRepr::Erased(_) => todo_lambda_erasure!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,6 @@ use std::sync::{Arc, RwLock};
|
||||
|
||||
use bumpalo::Bump;
|
||||
use roc_can::abilities::AbilitiesStore;
|
||||
use roc_can::constraint::Constraints;
|
||||
use roc_can::module::ExposedByModule;
|
||||
use roc_collections::MutMap;
|
||||
use roc_derive::SharedDerivedModule;
|
||||
@ -15,7 +14,7 @@ use roc_module::symbol::Symbol;
|
||||
use roc_solve::ability::AbilityResolver;
|
||||
use roc_solve::specialize::{compact_lambda_sets_of_vars, Phase};
|
||||
use roc_solve::Pools;
|
||||
use roc_solve::{DerivedEnv, Env};
|
||||
use roc_solve::{DerivedEnv, SolveEnv};
|
||||
use roc_types::subs::{get_member_lambda_sets_at_region, Content, FlatType, LambdaSet};
|
||||
use roc_types::subs::{ExposedTypesStorageSubs, Subs, Variable};
|
||||
use roc_types::types::Polarity;
|
||||
@ -342,19 +341,6 @@ impl MetaCollector for ChangedVariableCollector {
|
||||
}
|
||||
}
|
||||
|
||||
std::thread_local! {
|
||||
static SCRATCHPAD_FOR_OCCURS: std::cell::RefCell<Option<Constraints>> = std::cell::RefCell::new(Some(Constraints::empty()));
|
||||
}
|
||||
|
||||
fn with_empty_solve_constraints<T>(f: impl FnOnce(&Constraints) -> T) -> T {
|
||||
SCRATCHPAD_FOR_OCCURS.with(|cell| {
|
||||
let constr = cell.take().unwrap();
|
||||
let result = f(&constr);
|
||||
cell.replace(Some(constr));
|
||||
result
|
||||
})
|
||||
}
|
||||
|
||||
/// Unifies two variables and performs lambda set compaction.
|
||||
/// Ranks and other ability demands are disregarded.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
@ -396,9 +382,8 @@ pub fn unify(
|
||||
exposed_types: exposed_by_module,
|
||||
};
|
||||
|
||||
let must_implement_constraints = with_empty_solve_constraints(|c| {
|
||||
let mut env = Env {
|
||||
constraints: c,
|
||||
let must_implement_constraints = {
|
||||
let mut env = SolveEnv {
|
||||
subs,
|
||||
derived_env: &derived_env,
|
||||
arena,
|
||||
@ -406,7 +391,8 @@ pub fn unify(
|
||||
};
|
||||
|
||||
compact_lambda_sets_of_vars(&mut env, lambda_sets_to_specialize, &late_phase)
|
||||
});
|
||||
};
|
||||
|
||||
// At this point we can't do anything with must-implement constraints, since we're no
|
||||
// longer solving. We must assume that they were totally caught during solving.
|
||||
// After we land https://github.com/roc-lang/roc/issues/3207 this concern should totally
|
||||
|
@ -14,6 +14,7 @@ roc_load_internal = { path = "../load_internal" }
|
||||
roc_module = { path = "../module" }
|
||||
roc_packaging = { path = "../../packaging" }
|
||||
roc_reporting = { path = "../../reporting" }
|
||||
roc_solve = { path = "../solve" }
|
||||
roc_target = { path = "../roc_target" }
|
||||
roc_types = { path = "../types" }
|
||||
|
||||
@ -25,6 +26,7 @@ roc_can = { path = "../can" }
|
||||
roc_module = { path = "../module" }
|
||||
roc_packaging = { path = "../../packaging" }
|
||||
roc_reporting = { path = "../../reporting" }
|
||||
roc_solve = { path = "../solve" }
|
||||
roc_target = { path = "../roc_target" }
|
||||
roc_error_macros = { path = "../../error_macros" }
|
||||
|
||||
|
@ -77,6 +77,7 @@ fn write_types_for_module_real(module_id: ModuleId, filename: &str, output_path:
|
||||
let cwd = std::env::current_dir().unwrap();
|
||||
let source = roc_builtins::roc::module_source(module_id);
|
||||
let target_info = roc_target::TargetInfo::default_x86_64();
|
||||
let function_kind = roc_solve::FunctionKind::LambdaSet;
|
||||
|
||||
let res_module = roc_load_internal::file::load_and_typecheck_str(
|
||||
&arena,
|
||||
@ -85,6 +86,7 @@ fn write_types_for_module_real(module_id: ModuleId, filename: &str, output_path:
|
||||
cwd,
|
||||
Default::default(),
|
||||
target_info,
|
||||
function_kind,
|
||||
roc_reporting::report::RenderTarget::ColorTerminal,
|
||||
roc_reporting::report::DEFAULT_PALETTE,
|
||||
RocCacheDir::Disallowed,
|
||||
|
@ -24,6 +24,7 @@ pub use roc_load_internal::file::{
|
||||
pub use roc_load_internal::module::{
|
||||
EntryPoint, Expectations, ExposedToHost, LoadedModule, MonomorphizedModule,
|
||||
};
|
||||
pub use roc_solve::FunctionKind;
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn load<'a>(
|
||||
@ -51,6 +52,7 @@ pub fn load_single_threaded<'a>(
|
||||
arena: &'a Bump,
|
||||
load_start: LoadStart<'a>,
|
||||
target_info: TargetInfo,
|
||||
function_kind: FunctionKind,
|
||||
render: RenderTarget,
|
||||
palette: Palette,
|
||||
roc_cache_dir: RocCacheDir<'_>,
|
||||
@ -64,6 +66,7 @@ pub fn load_single_threaded<'a>(
|
||||
load_start,
|
||||
exposed_types,
|
||||
target_info,
|
||||
function_kind,
|
||||
cached_subs,
|
||||
render,
|
||||
palette,
|
||||
@ -170,6 +173,7 @@ pub fn load_and_typecheck_str<'a>(
|
||||
source: &'a str,
|
||||
src_dir: PathBuf,
|
||||
target_info: TargetInfo,
|
||||
function_kind: FunctionKind,
|
||||
render: RenderTarget,
|
||||
roc_cache_dir: RocCacheDir<'_>,
|
||||
palette: Palette,
|
||||
@ -185,6 +189,7 @@ pub fn load_and_typecheck_str<'a>(
|
||||
arena,
|
||||
load_start,
|
||||
target_info,
|
||||
function_kind,
|
||||
render,
|
||||
palette,
|
||||
roc_cache_dir,
|
||||
|
@ -63,6 +63,7 @@ use roc_region::all::{LineInfo, Loc, Region};
|
||||
use roc_reporting::report::to_https_problem_report_string;
|
||||
use roc_reporting::report::{to_file_problem_report_string, Palette, RenderTarget};
|
||||
use roc_solve::module::{extract_module_owned_implementations, SolveConfig, Solved, SolvedModule};
|
||||
use roc_solve::FunctionKind;
|
||||
use roc_solve_problem::TypeError;
|
||||
use roc_target::TargetInfo;
|
||||
use roc_types::subs::{CopiedImport, ExposedTypesStorageSubs, Subs, VarStore, Variable};
|
||||
@ -113,6 +114,7 @@ pub struct LoadConfig {
|
||||
pub palette: Palette,
|
||||
pub threading: Threading,
|
||||
pub exec_mode: ExecutionMode,
|
||||
pub function_kind: FunctionKind,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
@ -336,6 +338,7 @@ fn start_phase<'a>(
|
||||
types,
|
||||
constraints,
|
||||
constraint,
|
||||
state.function_kind,
|
||||
pending_derives,
|
||||
var_store,
|
||||
imported_modules,
|
||||
@ -679,6 +682,7 @@ struct State<'a> {
|
||||
pub output_path: Option<&'a str>,
|
||||
pub platform_path: PlatformPath<'a>,
|
||||
pub target_info: TargetInfo,
|
||||
pub(self) function_kind: FunctionKind,
|
||||
|
||||
/// Note: only packages and platforms actually expose any modules;
|
||||
/// for all others, this will be empty.
|
||||
@ -740,6 +744,7 @@ impl<'a> State<'a> {
|
||||
root_id: ModuleId,
|
||||
opt_platform_shorthand: Option<&'a str>,
|
||||
target_info: TargetInfo,
|
||||
function_kind: FunctionKind,
|
||||
exposed_types: ExposedByModule,
|
||||
arc_modules: Arc<Mutex<PackageModuleIds<'a>>>,
|
||||
ident_ids_by_module: SharedIdentIdsByModule,
|
||||
@ -759,6 +764,7 @@ impl<'a> State<'a> {
|
||||
opt_platform_shorthand,
|
||||
cache_dir,
|
||||
target_info,
|
||||
function_kind,
|
||||
platform_data: None,
|
||||
output_path: None,
|
||||
platform_path: PlatformPath::NotSpecified,
|
||||
@ -865,6 +871,7 @@ enum BuildTask<'a> {
|
||||
types: Types,
|
||||
constraints: Constraints,
|
||||
constraint: ConstraintSoa,
|
||||
function_kind: FunctionKind,
|
||||
pending_derives: PendingDerives,
|
||||
var_store: VarStore,
|
||||
declarations: Declarations,
|
||||
@ -969,6 +976,7 @@ pub fn load_and_typecheck_str<'a>(
|
||||
src_dir: PathBuf,
|
||||
exposed_types: ExposedByModule,
|
||||
target_info: TargetInfo,
|
||||
function_kind: FunctionKind,
|
||||
render: RenderTarget,
|
||||
palette: Palette,
|
||||
roc_cache_dir: RocCacheDir<'_>,
|
||||
@ -988,6 +996,7 @@ pub fn load_and_typecheck_str<'a>(
|
||||
palette,
|
||||
threading,
|
||||
exec_mode: ExecutionMode::Check,
|
||||
function_kind,
|
||||
};
|
||||
|
||||
match load(
|
||||
@ -1243,6 +1252,7 @@ pub fn load<'a>(
|
||||
load_start,
|
||||
exposed_types,
|
||||
load_config.target_info,
|
||||
load_config.function_kind,
|
||||
cached_types,
|
||||
load_config.render,
|
||||
load_config.palette,
|
||||
@ -1254,6 +1264,7 @@ pub fn load<'a>(
|
||||
load_start,
|
||||
exposed_types,
|
||||
load_config.target_info,
|
||||
load_config.function_kind,
|
||||
cached_types,
|
||||
load_config.render,
|
||||
load_config.palette,
|
||||
@ -1270,6 +1281,7 @@ pub fn load_single_threaded<'a>(
|
||||
load_start: LoadStart<'a>,
|
||||
exposed_types: ExposedByModule,
|
||||
target_info: TargetInfo,
|
||||
function_kind: FunctionKind,
|
||||
cached_types: MutMap<ModuleId, TypeState>,
|
||||
render: RenderTarget,
|
||||
palette: Palette,
|
||||
@ -1297,6 +1309,7 @@ pub fn load_single_threaded<'a>(
|
||||
root_id,
|
||||
opt_platform_shorthand,
|
||||
target_info,
|
||||
function_kind,
|
||||
exposed_types,
|
||||
arc_modules,
|
||||
ident_ids_by_module,
|
||||
@ -1584,6 +1597,7 @@ fn load_multi_threaded<'a>(
|
||||
load_start: LoadStart<'a>,
|
||||
exposed_types: ExposedByModule,
|
||||
target_info: TargetInfo,
|
||||
function_kind: FunctionKind,
|
||||
cached_types: MutMap<ModuleId, TypeState>,
|
||||
render: RenderTarget,
|
||||
palette: Palette,
|
||||
@ -1627,6 +1641,7 @@ fn load_multi_threaded<'a>(
|
||||
root_id,
|
||||
opt_platform_shorthand,
|
||||
target_info,
|
||||
function_kind,
|
||||
exposed_types,
|
||||
arc_modules,
|
||||
ident_ids_by_module,
|
||||
@ -4417,6 +4432,7 @@ impl<'a> BuildTask<'a> {
|
||||
types: Types,
|
||||
constraints: Constraints,
|
||||
constraint: ConstraintSoa,
|
||||
function_kind: FunctionKind,
|
||||
pending_derives: PendingDerives,
|
||||
var_store: VarStore,
|
||||
imported_modules: MutMap<ModuleId, Region>,
|
||||
@ -4439,6 +4455,7 @@ impl<'a> BuildTask<'a> {
|
||||
types,
|
||||
constraints,
|
||||
constraint,
|
||||
function_kind,
|
||||
pending_derives,
|
||||
var_store,
|
||||
declarations,
|
||||
@ -4709,6 +4726,7 @@ fn run_solve_solve(
|
||||
mut types: Types,
|
||||
mut constraints: Constraints,
|
||||
constraint: ConstraintSoa,
|
||||
function_kind: FunctionKind,
|
||||
pending_derives: PendingDerives,
|
||||
var_store: VarStore,
|
||||
module: Module,
|
||||
@ -4760,6 +4778,7 @@ fn run_solve_solve(
|
||||
types,
|
||||
constraints: &constraints,
|
||||
root_constraint: actual_constraint,
|
||||
function_kind,
|
||||
pending_derives,
|
||||
exposed_by_module: &exposed_for_module.exposed_by_module,
|
||||
derived_module,
|
||||
@ -4824,6 +4843,7 @@ fn run_solve<'a>(
|
||||
types: Types,
|
||||
constraints: Constraints,
|
||||
constraint: ConstraintSoa,
|
||||
function_kind: FunctionKind,
|
||||
pending_derives: PendingDerives,
|
||||
var_store: VarStore,
|
||||
decls: Declarations,
|
||||
@ -4851,6 +4871,7 @@ fn run_solve<'a>(
|
||||
types,
|
||||
constraints,
|
||||
constraint,
|
||||
function_kind,
|
||||
pending_derives,
|
||||
var_store,
|
||||
module,
|
||||
@ -4875,6 +4896,7 @@ fn run_solve<'a>(
|
||||
types,
|
||||
constraints,
|
||||
constraint,
|
||||
function_kind,
|
||||
pending_derives,
|
||||
var_store,
|
||||
module,
|
||||
@ -6130,6 +6152,7 @@ fn run_task<'a>(
|
||||
types,
|
||||
constraints,
|
||||
constraint,
|
||||
function_kind,
|
||||
pending_derives,
|
||||
var_store,
|
||||
ident_ids,
|
||||
@ -6145,6 +6168,7 @@ fn run_task<'a>(
|
||||
types,
|
||||
constraints,
|
||||
constraint,
|
||||
function_kind,
|
||||
pending_derives,
|
||||
var_store,
|
||||
declarations,
|
||||
|
@ -29,6 +29,7 @@ use roc_region::all::LineInfo;
|
||||
use roc_reporting::report::RenderTarget;
|
||||
use roc_reporting::report::RocDocAllocator;
|
||||
use roc_reporting::report::{can_problem, DEFAULT_PALETTE};
|
||||
use roc_solve::FunctionKind;
|
||||
use roc_target::TargetInfo;
|
||||
use roc_types::pretty_print::name_and_print_var;
|
||||
use roc_types::pretty_print::DebugPrint;
|
||||
@ -40,6 +41,7 @@ fn load_and_typecheck(
|
||||
filename: PathBuf,
|
||||
exposed_types: ExposedByModule,
|
||||
target_info: TargetInfo,
|
||||
function_kind: FunctionKind,
|
||||
) -> Result<LoadedModule, LoadingProblem> {
|
||||
use LoadResult::*;
|
||||
|
||||
@ -52,6 +54,7 @@ fn load_and_typecheck(
|
||||
)?;
|
||||
let load_config = LoadConfig {
|
||||
target_info,
|
||||
function_kind,
|
||||
render: RenderTarget::Generic,
|
||||
palette: DEFAULT_PALETTE,
|
||||
threading: Threading::Single,
|
||||
@ -176,7 +179,13 @@ fn multiple_modules_help<'a>(
|
||||
writeln!(file, "{source}")?;
|
||||
file_handles.push(file);
|
||||
|
||||
load_and_typecheck(arena, full_file_path, Default::default(), TARGET_INFO)
|
||||
load_and_typecheck(
|
||||
arena,
|
||||
full_file_path,
|
||||
Default::default(),
|
||||
TARGET_INFO,
|
||||
FunctionKind::LambdaSet,
|
||||
)
|
||||
};
|
||||
|
||||
Ok(result)
|
||||
@ -190,7 +199,13 @@ fn load_fixture(
|
||||
let src_dir = fixtures_dir().join(dir_name);
|
||||
let filename = src_dir.join(format!("{module_name}.roc"));
|
||||
let arena = Bump::new();
|
||||
let loaded = load_and_typecheck(&arena, filename, subs_by_module, TARGET_INFO);
|
||||
let loaded = load_and_typecheck(
|
||||
&arena,
|
||||
filename,
|
||||
subs_by_module,
|
||||
TARGET_INFO,
|
||||
FunctionKind::LambdaSet,
|
||||
);
|
||||
let mut loaded_module = match loaded {
|
||||
Ok(x) => x,
|
||||
Err(roc_load_internal::file::LoadingProblem::FormattedReport(report)) => {
|
||||
@ -347,7 +362,13 @@ fn interface_with_deps() {
|
||||
let src_dir = fixtures_dir().join("interface_with_deps");
|
||||
let filename = src_dir.join("Primary.roc");
|
||||
let arena = Bump::new();
|
||||
let loaded = load_and_typecheck(&arena, filename, subs_by_module, TARGET_INFO);
|
||||
let loaded = load_and_typecheck(
|
||||
&arena,
|
||||
filename,
|
||||
subs_by_module,
|
||||
TARGET_INFO,
|
||||
FunctionKind::LambdaSet,
|
||||
);
|
||||
|
||||
let mut loaded_module = loaded.expect("Test module failed to load");
|
||||
let home = loaded_module.module_id;
|
||||
|
@ -800,6 +800,7 @@ macro_rules! define_builtins {
|
||||
$(
|
||||
$module_id:literal $module_const:ident: $module_name:literal => {
|
||||
$(
|
||||
$(#[$ident_meta:meta])*
|
||||
$ident_id:literal $ident_const:ident: $ident_name:literal
|
||||
$(exposed_apply_type=$exposed_apply_type:literal)?
|
||||
$(exposed_type=$exposed_type:literal)?
|
||||
@ -951,6 +952,7 @@ macro_rules! define_builtins {
|
||||
impl Symbol {
|
||||
$(
|
||||
$(
|
||||
$(#[$ident_meta])*
|
||||
pub const $ident_const: Symbol = Symbol::new(ModuleId::$module_const, IdentId($ident_id));
|
||||
)*
|
||||
$(
|
||||
|
@ -9,6 +9,7 @@ use bumpalo::collections::Vec;
|
||||
use bumpalo::Bump;
|
||||
use roc_collections::all::{MutMap, MutSet};
|
||||
use roc_collections::ReferenceMatrix;
|
||||
use roc_error_macros::todo_lambda_erasure;
|
||||
use roc_module::low_level::LowLevel;
|
||||
use roc_module::symbol::Symbol;
|
||||
|
||||
@ -560,6 +561,10 @@ impl<'a> BorrowInfState<'a> {
|
||||
self.own_args_using_params(arguments, ps);
|
||||
}
|
||||
|
||||
ByPointer { .. } => {
|
||||
todo_lambda_erasure!()
|
||||
}
|
||||
|
||||
LowLevel { op, .. } => {
|
||||
debug_assert!(!op.is_higher_order());
|
||||
|
||||
@ -703,6 +708,27 @@ impl<'a> BorrowInfState<'a> {
|
||||
self.own_args_if_param(xs);
|
||||
}
|
||||
|
||||
ErasedMake { value, callee } => {
|
||||
value.map(|v| {
|
||||
// if the value is owned, the erasure is
|
||||
self.if_is_owned_then_own(v, z);
|
||||
|
||||
// if the erasure is owned, the value is
|
||||
self.if_is_owned_then_own(z, v);
|
||||
});
|
||||
|
||||
// the erasure owns the callee (which should always be a stack value)
|
||||
self.own_var(*callee);
|
||||
}
|
||||
|
||||
ErasedLoad { symbol, field: _ } => {
|
||||
// if the extracted value is owned, the erasure is too
|
||||
self.if_is_owned_then_own(*symbol, z);
|
||||
|
||||
// if the erasure is owned, so is the extracted value
|
||||
self.if_is_owned_then_own(z, *symbol);
|
||||
}
|
||||
|
||||
Reset { symbol: x, .. } | ResetRef { symbol: x, .. } => {
|
||||
self.own_var(z);
|
||||
self.own_var(*x);
|
||||
@ -714,7 +740,7 @@ impl<'a> BorrowInfState<'a> {
|
||||
|
||||
Call(call) => self.collect_call(interner, param_map, z, call),
|
||||
|
||||
Literal(_) | NullPointer | RuntimeErrorFunction(_) => {}
|
||||
Literal(_) | NullPointer | RuntimeErrorFunction(_) | FunctionPointer { .. } => {}
|
||||
|
||||
StructAtIndex { structure: x, .. } => {
|
||||
// if the structure (record/tag/array) is owned, the extracted value is
|
||||
@ -1036,9 +1062,10 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[Ownership] {
|
||||
|
||||
PtrStore => arena.alloc_slice_copy(&[owned, owned]),
|
||||
PtrLoad => arena.alloc_slice_copy(&[owned]),
|
||||
PtrCast => arena.alloc_slice_copy(&[owned]),
|
||||
Alloca => arena.alloc_slice_copy(&[owned]),
|
||||
|
||||
PtrClearTagId | PtrCast | RefCountIncRcPtr | RefCountDecRcPtr | RefCountIncDataPtr
|
||||
PtrClearTagId | RefCountIncRcPtr | RefCountDecRcPtr | RefCountIncDataPtr
|
||||
| RefCountDecDataPtr | RefCountIsUnique => {
|
||||
unreachable!("Only inserted *after* borrow checking: {:?}", op);
|
||||
}
|
||||
@ -1056,6 +1083,9 @@ fn call_info_call<'a>(call: &crate::ir::Call<'a>, info: &mut CallInfo<'a>) {
|
||||
ByName { name, .. } => {
|
||||
info.keys.push(name.name());
|
||||
}
|
||||
ByPointer { .. } => {
|
||||
todo_lambda_erasure!()
|
||||
}
|
||||
Foreign { .. } => {}
|
||||
LowLevel { .. } => {}
|
||||
HigherOrder(HigherOrderLowLevel {
|
||||
|
@ -25,7 +25,7 @@ pub fn eq_generic<'a>(
|
||||
use crate::layout::Builtin::*;
|
||||
use LayoutRepr::*;
|
||||
let main_body = match layout_interner.get_repr(layout) {
|
||||
Builtin(Int(_) | Float(_) | Bool | Decimal) => {
|
||||
Builtin(Int(_) | Float(_) | Bool | Decimal) | FunctionPointer(_) => {
|
||||
unreachable!(
|
||||
"No generated proc for `==`. Use direct code gen for {:?}",
|
||||
layout
|
||||
@ -39,6 +39,7 @@ pub fn eq_generic<'a>(
|
||||
Union(union_layout) => eq_tag_union(root, ident_ids, ctx, layout_interner, union_layout),
|
||||
Ptr(inner_layout) => eq_boxed(root, ident_ids, ctx, layout_interner, inner_layout),
|
||||
LambdaSet(_) => unreachable!("`==` is not defined on functions"),
|
||||
Erased(_) => unreachable!("`==` is not defined on erased types"),
|
||||
RecursivePointer(_) => {
|
||||
unreachable!(
|
||||
"Can't perform `==` on RecursivePointer. Should have been replaced by a tag union."
|
||||
|
@ -453,6 +453,7 @@ impl<'a> CodeGenHelp<'a> {
|
||||
ret_layout,
|
||||
is_self_recursive: SelfRecursive::NotSelfRecursive,
|
||||
host_exposed_layouts: HostExposedLayouts::NotHostExposed,
|
||||
is_erased: false,
|
||||
});
|
||||
|
||||
proc_symbol
|
||||
@ -582,6 +583,10 @@ impl<'a> CodeGenHelp<'a> {
|
||||
|
||||
// This line is the whole point of the function
|
||||
LayoutRepr::RecursivePointer(_) => LayoutRepr::Union(ctx.recursive_union.unwrap()),
|
||||
|
||||
LayoutRepr::FunctionPointer(_) => return layout,
|
||||
|
||||
LayoutRepr::Erased(_) => return layout,
|
||||
};
|
||||
|
||||
layout_interner.insert(Layout::new(LayoutWrapper::Direct(repr), semantic))
|
||||
@ -768,6 +773,7 @@ impl<'a> CallerProc<'a> {
|
||||
ret_layout: Layout::UNIT,
|
||||
is_self_recursive: SelfRecursive::NotSelfRecursive,
|
||||
host_exposed_layouts: HostExposedLayouts::NotHostExposed,
|
||||
is_erased: false,
|
||||
};
|
||||
|
||||
if false {
|
||||
@ -838,5 +844,7 @@ fn layout_needs_helper_proc<'a>(
|
||||
LayoutRepr::LambdaSet(_) => true,
|
||||
LayoutRepr::RecursivePointer(_) => false,
|
||||
LayoutRepr::Ptr(_) => false,
|
||||
LayoutRepr::FunctionPointer(_) => false,
|
||||
LayoutRepr::Erased(_) => true,
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
use bumpalo::collections::vec::Vec;
|
||||
use bumpalo::collections::CollectIn;
|
||||
use roc_error_macros::todo_lambda_erasure;
|
||||
use roc_module::low_level::{LowLevel, LowLevel::*};
|
||||
use roc_module::symbol::{IdentIds, Symbol};
|
||||
use roc_target::PtrWidth;
|
||||
@ -188,7 +189,8 @@ pub fn refcount_generic<'a>(
|
||||
match layout_interner.get_repr(layout) {
|
||||
LayoutRepr::Builtin(
|
||||
Builtin::Int(_) | Builtin::Float(_) | Builtin::Bool | Builtin::Decimal,
|
||||
) => {
|
||||
)
|
||||
| LayoutRepr::FunctionPointer(_) => {
|
||||
// Generate a dummy function that immediately returns Unit
|
||||
// Some higher-order Zig builtins *always* call an RC function on List elements.
|
||||
rc_return_stmt(root, ident_ids, ctx)
|
||||
@ -230,6 +232,9 @@ pub fn refcount_generic<'a>(
|
||||
structure,
|
||||
)
|
||||
}
|
||||
LayoutRepr::Erased(_) => {
|
||||
todo_lambda_erasure!()
|
||||
}
|
||||
LayoutRepr::RecursivePointer(_) => unreachable!(
|
||||
"We should never call a refcounting helper on a RecursivePointer layout directly"
|
||||
),
|
||||
|
@ -6,12 +6,12 @@ use roc_module::symbol::Symbol;
|
||||
|
||||
use crate::{
|
||||
ir::{
|
||||
Call, CallSpecId, CallType, Expr, HigherOrderLowLevel, JoinPointId, ListLiteralElement,
|
||||
ModifyRc, Param, Proc, ProcLayout, Stmt,
|
||||
Call, CallSpecId, CallType, ErasedField, Expr, HigherOrderLowLevel, JoinPointId,
|
||||
ListLiteralElement, ModifyRc, Param, Proc, ProcLayout, Stmt,
|
||||
},
|
||||
layout::{
|
||||
Builtin, InLayout, Layout, LayoutInterner, LayoutRepr, STLayoutInterner, TagIdIntType,
|
||||
UnionLayout,
|
||||
Builtin, FunctionPointer, InLayout, Layout, LayoutInterner, LayoutRepr, STLayoutInterner,
|
||||
TagIdIntType, UnionLayout,
|
||||
},
|
||||
};
|
||||
|
||||
@ -27,6 +27,9 @@ pub enum UseKind {
|
||||
SwitchCond,
|
||||
ExpectCond,
|
||||
ExpectLookup,
|
||||
ErasedMake(ErasedField),
|
||||
Erased,
|
||||
FunctionPointer,
|
||||
}
|
||||
|
||||
pub enum ProblemKind<'a> {
|
||||
@ -70,6 +73,9 @@ pub enum ProblemKind<'a> {
|
||||
proc_layout: ProcLayout<'a>,
|
||||
similar: Vec<ProcLayout<'a>>,
|
||||
},
|
||||
PtrToUndefinedProc {
|
||||
symbol: Symbol,
|
||||
},
|
||||
DuplicateCallSpecId {
|
||||
old_call_line: usize,
|
||||
},
|
||||
@ -114,6 +120,24 @@ pub enum ProblemKind<'a> {
|
||||
num_needed: usize,
|
||||
num_given: usize,
|
||||
},
|
||||
ErasedMakeValueNotBoxed {
|
||||
symbol: Symbol,
|
||||
def_layout: InLayout<'a>,
|
||||
def_line: usize,
|
||||
},
|
||||
ErasedMakeCalleeNotFunctionPointer {
|
||||
symbol: Symbol,
|
||||
def_layout: InLayout<'a>,
|
||||
def_line: usize,
|
||||
},
|
||||
ErasedLoadValueNotBoxed {
|
||||
symbol: Symbol,
|
||||
target_layout: InLayout<'a>,
|
||||
},
|
||||
ErasedLoadCalleeNotFunctionPointer {
|
||||
symbol: Symbol,
|
||||
target_layout: InLayout<'a>,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct Problem<'a> {
|
||||
@ -271,7 +295,7 @@ impl<'a, 'r> Ctx<'a, 'r> {
|
||||
|
||||
match body {
|
||||
Stmt::Let(x, e, x_layout, rest) => {
|
||||
if let Some(e_layout) = self.check_expr(e) {
|
||||
if let Some(e_layout) = self.check_expr(e, *x_layout) {
|
||||
if self.not_equiv(e_layout, *x_layout) {
|
||||
self.problem(ProblemKind::SymbolDefMismatch {
|
||||
symbol: *x,
|
||||
@ -388,7 +412,7 @@ impl<'a, 'r> Ctx<'a, 'r> {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, e: &Expr<'a>) -> Option<InLayout<'a>> {
|
||||
fn check_expr(&mut self, e: &Expr<'a>, target_layout: InLayout<'a>) -> Option<InLayout<'a>> {
|
||||
match e {
|
||||
Expr::Literal(_) => None,
|
||||
Expr::NullPointer => None,
|
||||
@ -464,6 +488,40 @@ impl<'a, 'r> Ctx<'a, 'r> {
|
||||
// TODO don't know what the element layout is
|
||||
None
|
||||
}
|
||||
&Expr::ErasedMake { value, callee } => Some(self.check_erased_make(value, callee)),
|
||||
&Expr::ErasedLoad { symbol, field } => {
|
||||
Some(self.check_erased_load(symbol, field, target_layout))
|
||||
}
|
||||
&Expr::FunctionPointer { lambda_name } => {
|
||||
let lambda_symbol = lambda_name.name();
|
||||
let proc = self.procs.iter().find(|((name, proc), _)| {
|
||||
*name == lambda_symbol && proc.niche == lambda_name.niche()
|
||||
});
|
||||
match proc {
|
||||
None => {
|
||||
self.problem(ProblemKind::PtrToUndefinedProc {
|
||||
symbol: lambda_symbol,
|
||||
});
|
||||
Some(target_layout)
|
||||
}
|
||||
Some(((_, proc_layout), _)) => {
|
||||
let ProcLayout {
|
||||
arguments, result, ..
|
||||
} = proc_layout;
|
||||
|
||||
let fn_ptr =
|
||||
self.interner
|
||||
.insert_direct_no_semantic(LayoutRepr::FunctionPointer(
|
||||
FunctionPointer {
|
||||
args: arguments,
|
||||
ret: *result,
|
||||
},
|
||||
));
|
||||
|
||||
Some(fn_ptr)
|
||||
}
|
||||
}
|
||||
}
|
||||
&Expr::Reset {
|
||||
symbol,
|
||||
update_mode: _,
|
||||
@ -642,6 +700,23 @@ impl<'a, 'r> Ctx<'a, 'r> {
|
||||
}
|
||||
Some(*ret_layout)
|
||||
}
|
||||
CallType::ByPointer {
|
||||
pointer,
|
||||
ret_layout,
|
||||
arg_layouts,
|
||||
} => {
|
||||
let expected_layout =
|
||||
self.interner
|
||||
.insert_direct_no_semantic(LayoutRepr::FunctionPointer(FunctionPointer {
|
||||
args: arg_layouts,
|
||||
ret: *ret_layout,
|
||||
}));
|
||||
self.check_sym_layout(*pointer, expected_layout, UseKind::FunctionPointer);
|
||||
for (arg, wanted_layout) in arguments.iter().zip(arg_layouts.iter()) {
|
||||
self.check_sym_layout(*arg, *wanted_layout, UseKind::CallArg);
|
||||
}
|
||||
Some(*ret_layout)
|
||||
}
|
||||
CallType::HigherOrder(HigherOrderLowLevel {
|
||||
op: _,
|
||||
closure_env_layout: _,
|
||||
@ -700,6 +775,84 @@ impl<'a, 'r> Ctx<'a, 'r> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_erased_make(&mut self, value: Option<Symbol>, callee: Symbol) -> InLayout<'a> {
|
||||
if let Some(value) = value {
|
||||
self.with_sym_layout(value, |this, def_line, layout| {
|
||||
let repr = this.interner.get_repr(layout);
|
||||
if !matches!(
|
||||
repr,
|
||||
LayoutRepr::Union(UnionLayout::NullableUnwrapped { .. })
|
||||
) {
|
||||
this.problem(ProblemKind::ErasedMakeValueNotBoxed {
|
||||
symbol: value,
|
||||
def_layout: layout,
|
||||
def_line,
|
||||
});
|
||||
}
|
||||
|
||||
Option::<()>::None
|
||||
});
|
||||
}
|
||||
self.with_sym_layout(callee, |this, def_line, layout| {
|
||||
let repr = this.interner.get_repr(layout);
|
||||
if !matches!(repr, LayoutRepr::FunctionPointer(_)) {
|
||||
this.problem(ProblemKind::ErasedMakeCalleeNotFunctionPointer {
|
||||
symbol: callee,
|
||||
def_layout: layout,
|
||||
def_line,
|
||||
});
|
||||
}
|
||||
|
||||
Option::<()>::None
|
||||
});
|
||||
|
||||
Layout::ERASED
|
||||
}
|
||||
|
||||
fn check_erased_load(
|
||||
&mut self,
|
||||
symbol: Symbol,
|
||||
field: ErasedField,
|
||||
target_layout: InLayout<'a>,
|
||||
) -> InLayout<'a> {
|
||||
self.check_sym_layout(symbol, Layout::ERASED, UseKind::Erased);
|
||||
|
||||
match field {
|
||||
ErasedField::Value => {
|
||||
let repr = self.interner.get_repr(target_layout);
|
||||
if !matches!(
|
||||
repr,
|
||||
LayoutRepr::Union(UnionLayout::NullableUnwrapped { .. })
|
||||
) {
|
||||
self.problem(ProblemKind::ErasedLoadValueNotBoxed {
|
||||
symbol,
|
||||
target_layout,
|
||||
});
|
||||
}
|
||||
}
|
||||
ErasedField::ValuePtr => {
|
||||
let repr = self.interner.get_repr(target_layout);
|
||||
if !matches!(repr, LayoutRepr::Ptr(_)) {
|
||||
self.problem(ProblemKind::ErasedLoadValueNotBoxed {
|
||||
symbol,
|
||||
target_layout,
|
||||
});
|
||||
}
|
||||
}
|
||||
ErasedField::Callee => {
|
||||
let repr = self.interner.get_repr(target_layout);
|
||||
if !matches!(repr, LayoutRepr::FunctionPointer(_)) {
|
||||
self.problem(ProblemKind::ErasedLoadCalleeNotFunctionPointer {
|
||||
symbol,
|
||||
target_layout,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
target_layout
|
||||
}
|
||||
}
|
||||
|
||||
enum TagPayloads<'a> {
|
||||
|
@ -4,7 +4,7 @@ use roc_module::symbol::{Interns, Symbol};
|
||||
use ven_pretty::{text, Arena, DocAllocator, DocBuilder};
|
||||
|
||||
use crate::{
|
||||
ir::{Parens, ProcLayout},
|
||||
ir::{ErasedField, Parens, ProcLayout},
|
||||
layout::LayoutInterner,
|
||||
};
|
||||
|
||||
@ -265,6 +265,15 @@ where
|
||||
};
|
||||
stack(f, [no_spec_doc, similar_doc])
|
||||
}
|
||||
ProblemKind::PtrToUndefinedProc { symbol } => {
|
||||
title = "PROC SPECIALIZATION NOT DEFINED";
|
||||
docs_before = vec![];
|
||||
f.concat([
|
||||
f.reflow("The proc "),
|
||||
format_symbol(f, interns, symbol),
|
||||
f.reflow(" is not defined"),
|
||||
])
|
||||
}
|
||||
ProblemKind::DuplicateCallSpecId { old_call_line } => {
|
||||
title = "DUPLICATE CALL SPEC ID";
|
||||
docs_before = vec![(old_call_line, f.reflow("This call has a specialization ID"))];
|
||||
@ -406,6 +415,74 @@ where
|
||||
f.as_string(num_given),
|
||||
])
|
||||
}
|
||||
ProblemKind::ErasedMakeValueNotBoxed {
|
||||
symbol,
|
||||
def_layout,
|
||||
def_line,
|
||||
} => {
|
||||
title = "ERASED VALUE IS NOT BOXED";
|
||||
docs_before = vec![(
|
||||
def_line,
|
||||
f.concat([
|
||||
f.reflow("The value "),
|
||||
format_symbol(f, interns, symbol),
|
||||
f.reflow(" defined here"),
|
||||
]),
|
||||
)];
|
||||
f.concat([
|
||||
f.reflow("must be boxed in order to be erased, but has layout "),
|
||||
interner.to_doc_top(def_layout, f),
|
||||
])
|
||||
}
|
||||
ProblemKind::ErasedMakeCalleeNotFunctionPointer {
|
||||
symbol,
|
||||
def_layout,
|
||||
def_line,
|
||||
} => {
|
||||
title = "ERASED CALLEE IS NOT A FUNCTION POINTER";
|
||||
docs_before = vec![(
|
||||
def_line,
|
||||
f.concat([
|
||||
f.reflow("The value "),
|
||||
format_symbol(f, interns, symbol),
|
||||
f.reflow(" defined here"),
|
||||
]),
|
||||
)];
|
||||
f.concat([
|
||||
f.reflow(
|
||||
"must be a function pointer in order to be an erasure callee, but has layout ",
|
||||
),
|
||||
interner.to_doc_top(def_layout, f),
|
||||
])
|
||||
}
|
||||
ProblemKind::ErasedLoadValueNotBoxed {
|
||||
symbol,
|
||||
target_layout,
|
||||
} => {
|
||||
title = "ERASED VALUE IS NOT BOXED";
|
||||
docs_before = vec![];
|
||||
f.concat([
|
||||
f.reflow("The erased value load "),
|
||||
format_symbol(f, interns, symbol),
|
||||
f.reflow(" has layout "),
|
||||
interner.to_doc_top(target_layout, f),
|
||||
f.reflow(", but should be boxed!"),
|
||||
])
|
||||
}
|
||||
ProblemKind::ErasedLoadCalleeNotFunctionPointer {
|
||||
symbol,
|
||||
target_layout,
|
||||
} => {
|
||||
title = "ERASED CALLEE IS NOT A FUNCTION POINTER";
|
||||
docs_before = vec![];
|
||||
f.concat([
|
||||
f.reflow("The erased callee load "),
|
||||
format_symbol(f, interns, symbol),
|
||||
f.reflow(" has layout "),
|
||||
interner.to_doc_top(target_layout, f),
|
||||
f.reflow(", but should be a function pointer!"),
|
||||
])
|
||||
}
|
||||
};
|
||||
(title, docs_before, doc)
|
||||
}
|
||||
@ -429,6 +506,13 @@ fn format_use_kind(use_kind: UseKind) -> &'static str {
|
||||
UseKind::SwitchCond => "switch condition",
|
||||
UseKind::ExpectCond => "expect condition",
|
||||
UseKind::ExpectLookup => "lookup for an expect",
|
||||
UseKind::ErasedMake(kind) => match kind {
|
||||
ErasedField::Value => "erased value field",
|
||||
ErasedField::ValuePtr => "erased value pointer",
|
||||
ErasedField::Callee => "erased callee field",
|
||||
},
|
||||
UseKind::Erased => "erasure",
|
||||
UseKind::FunctionPointer => "function pointer",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,8 +17,8 @@ use roc_module::low_level::LowLevel;
|
||||
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
|
||||
|
||||
use crate::ir::{
|
||||
BranchInfo, Call, CallType, Expr, JoinPointId, ListLiteralElement, Literal, ModifyRc, Proc,
|
||||
ProcLayout, Stmt, UpdateModeId,
|
||||
BranchInfo, Call, CallType, ErasedField, Expr, JoinPointId, ListLiteralElement, Literal,
|
||||
ModifyRc, Proc, ProcLayout, Stmt, UpdateModeId,
|
||||
};
|
||||
use crate::layout::{
|
||||
Builtin, InLayout, Layout, LayoutInterner, LayoutRepr, STLayoutInterner, UnionLayout,
|
||||
@ -228,9 +228,27 @@ fn specialize_drops_stmt<'a, 'i>(
|
||||
environment.add_list_child(*binding, *child, index as u64);
|
||||
}
|
||||
}
|
||||
Reset { .. } | Expr::ResetRef { .. } => { /* do nothing */ }
|
||||
RuntimeErrorFunction(_) | GetTagId { .. } | EmptyArray | NullPointer => { /* do nothing */
|
||||
ErasedMake { value, callee: _ } => {
|
||||
if let Some(value) = value {
|
||||
environment.add_struct_child(*binding, *value, 0);
|
||||
}
|
||||
}
|
||||
ErasedLoad { symbol, field } => {
|
||||
match field {
|
||||
ErasedField::Value => {
|
||||
environment.add_struct_child(*symbol, *binding, 0);
|
||||
}
|
||||
ErasedField::Callee | ErasedField::ValuePtr => {
|
||||
// nothing to own
|
||||
}
|
||||
}
|
||||
}
|
||||
Reset { .. } | Expr::ResetRef { .. } => { /* do nothing */ }
|
||||
RuntimeErrorFunction(_)
|
||||
| FunctionPointer { .. }
|
||||
| GetTagId { .. }
|
||||
| EmptyArray
|
||||
| NullPointer => { /* do nothing */ }
|
||||
}
|
||||
|
||||
// now store the let binding for later
|
||||
@ -1595,9 +1613,10 @@ fn low_level_no_rc(lowlevel: &LowLevel) -> RC {
|
||||
// only inserted for internal purposes. RC should not touch it
|
||||
PtrStore => RC::NoRc,
|
||||
PtrLoad => RC::NoRc,
|
||||
PtrCast => RC::NoRc,
|
||||
Alloca => RC::NoRc,
|
||||
|
||||
PtrClearTagId | PtrCast | RefCountIncRcPtr | RefCountDecRcPtr | RefCountIncDataPtr
|
||||
PtrClearTagId | RefCountIncRcPtr | RefCountDecRcPtr | RefCountIncDataPtr
|
||||
| RefCountDecDataPtr | RefCountIsUnique => {
|
||||
unreachable!("Only inserted *after* borrow checking: {:?}", lowlevel);
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ use roc_error_macros::internal_error;
|
||||
use roc_module::low_level::LowLevel;
|
||||
use roc_module::{low_level::LowLevelWrapperType, symbol::Symbol};
|
||||
|
||||
use crate::ir::ErasedField;
|
||||
use crate::{
|
||||
borrow::{lowlevel_borrow_signature, Ownership},
|
||||
ir::{
|
||||
@ -872,7 +873,11 @@ fn insert_refcount_operations_binding<'a>(
|
||||
}
|
||||
|
||||
match expr {
|
||||
Expr::Literal(_) | Expr::NullPointer | Expr::EmptyArray | Expr::RuntimeErrorFunction(_) => {
|
||||
Expr::Literal(_)
|
||||
| Expr::NullPointer
|
||||
| Expr::FunctionPointer { .. }
|
||||
| Expr::EmptyArray
|
||||
| Expr::RuntimeErrorFunction(_) => {
|
||||
// Literals, empty arrays, and runtime errors are not (and have nothing) reference counted.
|
||||
new_let!(stmt)
|
||||
}
|
||||
@ -883,6 +888,25 @@ fn insert_refcount_operations_binding<'a>(
|
||||
inc_owned!(arguments.iter().copied(), new_let)
|
||||
}
|
||||
|
||||
Expr::ErasedMake { value, callee: _ } => {
|
||||
let new_let = new_let!(stmt);
|
||||
|
||||
if let Some(value) = value {
|
||||
inc_owned!([*value], new_let)
|
||||
} else {
|
||||
new_let
|
||||
}
|
||||
}
|
||||
|
||||
Expr::ErasedLoad { symbol, field } => {
|
||||
let new_let = new_let!(stmt);
|
||||
|
||||
match field {
|
||||
ErasedField::Value => inc_owned!([*symbol], new_let),
|
||||
ErasedField::Callee | ErasedField::ValuePtr => new_let,
|
||||
}
|
||||
}
|
||||
|
||||
Expr::GetTagId { structure, .. }
|
||||
| Expr::StructAtIndex { structure, .. }
|
||||
| Expr::UnionAtIndex { structure, .. }
|
||||
@ -942,6 +966,14 @@ fn insert_refcount_operations_binding<'a>(
|
||||
|
||||
inc_owned!(arguments.iter().copied(), new_let)
|
||||
}
|
||||
// A normal Roc function call, but we don't actually know where its target is.
|
||||
// As such, we assume that it takes all parameters as owned, as will the function
|
||||
// itself.
|
||||
CallType::ByPointer { .. } => {
|
||||
let new_let = new_let!(stmt);
|
||||
|
||||
inc_owned!(arguments.iter().copied(), new_let)
|
||||
}
|
||||
CallType::Foreign { .. } => {
|
||||
// Foreign functions should be responsible for their own memory management.
|
||||
// But previously they were assumed to be called with borrowed parameters, so we do the same now.
|
||||
|
@ -1,11 +1,13 @@
|
||||
#![allow(clippy::manual_map)]
|
||||
|
||||
use crate::borrow::Ownership;
|
||||
use crate::ir::erased::{build_erased_function, ResolvedErasedLambda};
|
||||
use crate::ir::literal::{make_num_literal, IntOrFloatValue};
|
||||
use crate::layout::{
|
||||
self, Builtin, ClosureCallOptions, ClosureRepresentation, EnumDispatch, InLayout, LambdaName,
|
||||
LambdaSet, Layout, LayoutCache, LayoutInterner, LayoutProblem, LayoutRepr, Niche,
|
||||
RawFunctionLayout, TLLayoutInterner, TagIdIntType, UnionLayout, WrappedVariant,
|
||||
self, Builtin, ClosureCallOptions, ClosureDataKind, ClosureRepresentation, EnumDispatch,
|
||||
InLayout, LambdaName, LambdaSet, Layout, LayoutCache, LayoutInterner, LayoutProblem,
|
||||
LayoutRepr, Niche, RawFunctionLayout, TLLayoutInterner, TagIdIntType, UnionLayout,
|
||||
WrappedVariant,
|
||||
};
|
||||
use bumpalo::collections::{CollectIn, Vec};
|
||||
use bumpalo::Bump;
|
||||
@ -21,7 +23,7 @@ use roc_debug_flags::{
|
||||
ROC_PRINT_IR_AFTER_RESET_REUSE, ROC_PRINT_IR_AFTER_SPECIALIZATION, ROC_PRINT_RUNTIME_ERROR_GEN,
|
||||
};
|
||||
use roc_derive::SharedDerivedModule;
|
||||
use roc_error_macros::{internal_error, todo_abilities};
|
||||
use roc_error_macros::{internal_error, todo_abilities, todo_lambda_erasure};
|
||||
use roc_late_solve::storage::{ExternalModuleStorage, ExternalModuleStorageSnapshot};
|
||||
use roc_late_solve::{resolve_ability_specialization, AbilitiesView, Resolved, UnificationFailed};
|
||||
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
|
||||
@ -42,7 +44,9 @@ use pattern::{from_can_pattern, store_pattern, Pattern};
|
||||
|
||||
pub use literal::{ListLiteralElement, Literal};
|
||||
|
||||
mod boxed;
|
||||
mod decision_tree;
|
||||
mod erased;
|
||||
mod literal;
|
||||
mod pattern;
|
||||
|
||||
@ -305,6 +309,7 @@ pub struct Proc<'a> {
|
||||
pub ret_layout: InLayout<'a>,
|
||||
pub is_self_recursive: SelfRecursive,
|
||||
pub host_exposed_layouts: HostExposedLayouts<'a>,
|
||||
pub is_erased: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
@ -1682,6 +1687,13 @@ impl<'a> Call<'a> {
|
||||
|
||||
alloc.text("CallByName ").append(alloc.intersperse(it, " "))
|
||||
}
|
||||
CallType::ByPointer { pointer, .. } => {
|
||||
let it = std::iter::once(pointer)
|
||||
.chain(arguments.iter().copied())
|
||||
.map(|s| symbol_to_doc(alloc, s, pretty));
|
||||
|
||||
alloc.text("CallByPtr ").append(alloc.intersperse(it, " "))
|
||||
}
|
||||
LowLevel { op: lowlevel, .. } => {
|
||||
let it = arguments.iter().map(|s| symbol_to_doc(alloc, *s, pretty));
|
||||
|
||||
@ -1759,6 +1771,11 @@ pub enum CallType<'a> {
|
||||
arg_layouts: &'a [InLayout<'a>],
|
||||
specialization_id: CallSpecId,
|
||||
},
|
||||
ByPointer {
|
||||
pointer: Symbol,
|
||||
ret_layout: InLayout<'a>,
|
||||
arg_layouts: &'a [InLayout<'a>],
|
||||
},
|
||||
Foreign {
|
||||
foreign_symbol: ForeignSymbol,
|
||||
ret_layout: InLayout<'a>,
|
||||
@ -1826,6 +1843,15 @@ pub struct ReuseToken {
|
||||
pub update_mode: UpdateModeId,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum ErasedField {
|
||||
/// Load a dereferenceable pointer to the value.
|
||||
Value,
|
||||
/// Load a non-dereferenceable pointer to the value.
|
||||
ValuePtr,
|
||||
Callee,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Expr<'a> {
|
||||
Literal(Literal<'a>),
|
||||
@ -1872,6 +1898,28 @@ pub enum Expr<'a> {
|
||||
},
|
||||
EmptyArray,
|
||||
|
||||
/// Creates a type-erased value.
|
||||
ErasedMake {
|
||||
/// The erased value. If this is an erased function, the value are the function captures,
|
||||
/// or `None` if the function is not a closure.
|
||||
value: Option<Symbol>,
|
||||
/// The function pointer of the erased value, if it's an erased function.
|
||||
callee: Symbol,
|
||||
},
|
||||
|
||||
/// Loads a field from a type-erased value.
|
||||
ErasedLoad {
|
||||
/// The erased symbol.
|
||||
symbol: Symbol,
|
||||
/// The field to load.
|
||||
field: ErasedField,
|
||||
},
|
||||
|
||||
/// Returns a pointer to the given function.
|
||||
FunctionPointer {
|
||||
lambda_name: LambdaName<'a>,
|
||||
},
|
||||
|
||||
Reset {
|
||||
symbol: Symbol,
|
||||
update_mode: UpdateModeId,
|
||||
@ -2053,6 +2101,38 @@ impl<'a> Expr<'a> {
|
||||
.text("GetTagId ")
|
||||
.append(symbol_to_doc(alloc, *structure, pretty)),
|
||||
|
||||
ErasedMake { value, callee } => {
|
||||
let value = match value {
|
||||
Some(v) => symbol_to_doc(alloc, *v, pretty),
|
||||
None => alloc.text("<null>"),
|
||||
};
|
||||
let callee = symbol_to_doc(alloc, *callee, pretty);
|
||||
alloc
|
||||
.text("ErasedMake { value: ")
|
||||
.append(value)
|
||||
.append(", callee: ")
|
||||
.append(callee)
|
||||
.append(" }")
|
||||
}
|
||||
|
||||
ErasedLoad { symbol, field } => {
|
||||
let field = match field {
|
||||
ErasedField::Value => ".Value",
|
||||
ErasedField::ValuePtr => ".ValuePtr",
|
||||
ErasedField::Callee => ".Callee",
|
||||
};
|
||||
|
||||
alloc
|
||||
.text("ErasedLoad ")
|
||||
.append(symbol_to_doc(alloc, *symbol, pretty))
|
||||
.append(alloc.text(" "))
|
||||
.append(field)
|
||||
}
|
||||
|
||||
FunctionPointer { lambda_name } => alloc
|
||||
.text("FunctionPointer ")
|
||||
.append(symbol_to_doc(alloc, lambda_name.name(), pretty)),
|
||||
|
||||
UnionAtIndex {
|
||||
tag_id,
|
||||
structure,
|
||||
@ -2096,24 +2176,6 @@ impl<'a> Expr<'a> {
|
||||
arguments: std::slice::from_ref(symbol),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn expr_box(symbol: &'a Symbol, element_layout: &'a InLayout<'a>) -> Expr<'a> {
|
||||
Expr::Tag {
|
||||
tag_layout: UnionLayout::NonNullableUnwrapped(std::slice::from_ref(element_layout)),
|
||||
tag_id: 0,
|
||||
arguments: std::slice::from_ref(symbol),
|
||||
reuse: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn expr_unbox(symbol: Symbol, element_layout: &'a InLayout<'a>) -> Expr<'a> {
|
||||
Expr::UnionAtIndex {
|
||||
structure: symbol,
|
||||
tag_id: 0,
|
||||
union_layout: UnionLayout::NonNullableUnwrapped(std::slice::from_ref(element_layout)),
|
||||
index: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Stmt<'a> {
|
||||
@ -3179,6 +3241,7 @@ fn generate_runtime_error_function<'a>(
|
||||
|
||||
let runtime_error = runtime_error(env, msg.into_bump_str());
|
||||
|
||||
let is_erased = layout.is_erased_function();
|
||||
let (args, ret_layout) = match layout {
|
||||
RawFunctionLayout::Function(arg_layouts, lambda_set, ret_layout) => {
|
||||
let real_arg_layouts =
|
||||
@ -3195,6 +3258,9 @@ fn generate_runtime_error_function<'a>(
|
||||
|
||||
(args.into_bump_slice(), ret_layout)
|
||||
}
|
||||
RawFunctionLayout::ErasedFunction(..) => {
|
||||
todo_lambda_erasure!()
|
||||
}
|
||||
RawFunctionLayout::ZeroArgumentThunk(ret_layout) => (&[] as &[_], ret_layout),
|
||||
};
|
||||
|
||||
@ -3206,6 +3272,7 @@ fn generate_runtime_error_function<'a>(
|
||||
ret_layout,
|
||||
is_self_recursive: SelfRecursive::NotSelfRecursive,
|
||||
host_exposed_layouts: HostExposedLayouts::NotHostExposed,
|
||||
is_erased,
|
||||
}
|
||||
}
|
||||
|
||||
@ -3284,6 +3351,9 @@ fn generate_host_exposed_function<'a>(
|
||||
|
||||
(function_name, (top_level, proc))
|
||||
}
|
||||
RawFunctionLayout::ErasedFunction(..) => {
|
||||
todo_lambda_erasure!()
|
||||
}
|
||||
RawFunctionLayout::ZeroArgumentThunk(result) => {
|
||||
let assigned = env.unique_symbol();
|
||||
let hole = env.arena.alloc(Stmt::Ret(assigned));
|
||||
@ -3298,6 +3368,7 @@ fn generate_host_exposed_function<'a>(
|
||||
ret_layout: result,
|
||||
is_self_recursive: SelfRecursive::NotSelfRecursive,
|
||||
host_exposed_layouts: HostExposedLayouts::NotHostExposed,
|
||||
is_erased: false,
|
||||
};
|
||||
|
||||
let top_level = ProcLayout::from_raw_named(env.arena, lambda_name, layout);
|
||||
@ -3362,6 +3433,7 @@ fn generate_host_exposed_lambda_set<'a>(
|
||||
ret_layout: return_layout,
|
||||
is_self_recursive: SelfRecursive::NotSelfRecursive,
|
||||
host_exposed_layouts: HostExposedLayouts::NotHostExposed,
|
||||
is_erased: false,
|
||||
};
|
||||
|
||||
let top_level = ProcLayout::new(
|
||||
@ -3433,6 +3505,7 @@ fn specialize_proc_help<'a>(
|
||||
SpecializedLayout::FunctionPointerBody {
|
||||
ret_layout,
|
||||
closure: opt_closure_layout,
|
||||
is_erased,
|
||||
} => {
|
||||
// this is a function body like
|
||||
//
|
||||
@ -3458,18 +3531,23 @@ fn specialize_proc_help<'a>(
|
||||
ret_layout,
|
||||
is_self_recursive: recursivity,
|
||||
host_exposed_layouts,
|
||||
is_erased,
|
||||
}
|
||||
}
|
||||
SpecializedLayout::FunctionBody {
|
||||
arguments: proc_args,
|
||||
closure: opt_closure_layout,
|
||||
ret_layout,
|
||||
is_erased,
|
||||
} => {
|
||||
let mut proc_args = Vec::from_iter_in(proc_args.iter().copied(), env.arena);
|
||||
|
||||
// unpack the closure symbols, if any
|
||||
match (opt_closure_layout, captured_symbols) {
|
||||
(Some(closure_layout), CapturedSymbols::Captured(captured)) => {
|
||||
(
|
||||
Some(ClosureDataKind::LambdaSet(closure_layout)),
|
||||
CapturedSymbols::Captured(captured),
|
||||
) => {
|
||||
// debug_assert!(!captured.is_empty());
|
||||
|
||||
// An argument from the closure list may have taken on a specialized symbol
|
||||
@ -3626,6 +3704,15 @@ fn specialize_proc_help<'a>(
|
||||
}
|
||||
}
|
||||
}
|
||||
(Some(ClosureDataKind::Erased), CapturedSymbols::Captured(captured)) => {
|
||||
specialized_body = erased::unpack_closure_data(
|
||||
env,
|
||||
layout_cache,
|
||||
Symbol::ARG_CLOSURE,
|
||||
captured,
|
||||
specialized_body,
|
||||
);
|
||||
}
|
||||
(None, CapturedSymbols::None) | (None, CapturedSymbols::Captured([])) => {}
|
||||
_ => unreachable!("to closure or not to closure?"),
|
||||
}
|
||||
@ -3637,13 +3724,7 @@ fn specialize_proc_help<'a>(
|
||||
.maybe_get_specialized(*symbol, *layout)
|
||||
});
|
||||
|
||||
let closure_data_layout = match opt_closure_layout {
|
||||
Some(lambda_set) => {
|
||||
let lambda_set = lambda_set.full_layout;
|
||||
Some(lambda_set)
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
let closure_data_layout = opt_closure_layout.map(|clos| clos.data_layout());
|
||||
|
||||
Proc {
|
||||
name: lambda_name,
|
||||
@ -3653,6 +3734,7 @@ fn specialize_proc_help<'a>(
|
||||
ret_layout,
|
||||
is_self_recursive: recursivity,
|
||||
host_exposed_layouts,
|
||||
is_erased,
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -3665,13 +3747,15 @@ enum SpecializedLayout<'a> {
|
||||
/// A body like `foo = \a,b,c -> ...`
|
||||
FunctionBody {
|
||||
arguments: &'a [(InLayout<'a>, Symbol)],
|
||||
closure: Option<LambdaSet<'a>>,
|
||||
closure: Option<ClosureDataKind<'a>>,
|
||||
ret_layout: InLayout<'a>,
|
||||
is_erased: bool,
|
||||
},
|
||||
/// A body like `foo = Num.add`
|
||||
FunctionPointerBody {
|
||||
closure: Option<LambdaSet<'a>>,
|
||||
ret_layout: InLayout<'a>,
|
||||
is_erased: bool,
|
||||
},
|
||||
}
|
||||
|
||||
@ -3693,7 +3777,20 @@ fn build_specialized_proc_from_var<'a>(
|
||||
lambda_name,
|
||||
pattern_symbols,
|
||||
pattern_layouts_vec,
|
||||
Some(closure_layout),
|
||||
Some(ClosureDataKind::LambdaSet(closure_layout)),
|
||||
ret_layout,
|
||||
)
|
||||
}
|
||||
RawFunctionLayout::ErasedFunction(pattern_layouts, ret_layout) => {
|
||||
let mut pattern_layouts_vec = Vec::with_capacity_in(pattern_layouts.len(), env.arena);
|
||||
pattern_layouts_vec.extend_from_slice(pattern_layouts);
|
||||
|
||||
build_specialized_proc(
|
||||
env.arena,
|
||||
lambda_name,
|
||||
pattern_symbols,
|
||||
pattern_layouts_vec,
|
||||
Some(ClosureDataKind::Erased),
|
||||
ret_layout,
|
||||
)
|
||||
}
|
||||
@ -3717,7 +3814,7 @@ fn build_specialized_proc<'a>(
|
||||
lambda_name: LambdaName<'a>,
|
||||
pattern_symbols: &[Symbol],
|
||||
pattern_layouts: Vec<'a, InLayout<'a>>,
|
||||
lambda_set: Option<LambdaSet<'a>>,
|
||||
closure_data: Option<ClosureDataKind<'a>>,
|
||||
ret_layout: InLayout<'a>,
|
||||
) -> Result<SpecializedLayout<'a>, LayoutProblem> {
|
||||
use SpecializedLayout::*;
|
||||
@ -3730,6 +3827,8 @@ fn build_specialized_proc<'a>(
|
||||
proc_args.push((arg_layout, *arg_name));
|
||||
}
|
||||
|
||||
let is_erased = matches!(closure_data, Some(ClosureDataKind::Erased));
|
||||
|
||||
// Given
|
||||
//
|
||||
// foo =
|
||||
@ -3749,12 +3848,12 @@ fn build_specialized_proc<'a>(
|
||||
// then
|
||||
|
||||
let proc_name = lambda_name.name();
|
||||
match lambda_set {
|
||||
Some(lambda_set) if pattern_symbols.last() == Some(&Symbol::ARG_CLOSURE) => {
|
||||
match closure_data {
|
||||
Some(closure_data) if pattern_symbols.last() == Some(&Symbol::ARG_CLOSURE) => {
|
||||
// here we define the lifted (now top-level) f function. Its final argument is `Symbol::ARG_CLOSURE`,
|
||||
// it stores the closure structure (just an integer in this case)
|
||||
let lambda_set_layout = lambda_set.full_layout;
|
||||
proc_args.push((lambda_set_layout, Symbol::ARG_CLOSURE));
|
||||
let closure_data_layout = closure_data.data_layout();
|
||||
proc_args.push((closure_data_layout, Symbol::ARG_CLOSURE));
|
||||
|
||||
debug_assert_eq!(
|
||||
pattern_layouts_len + 1,
|
||||
@ -3766,11 +3865,12 @@ fn build_specialized_proc<'a>(
|
||||
|
||||
Ok(FunctionBody {
|
||||
arguments: proc_args,
|
||||
closure: Some(lambda_set),
|
||||
closure: Some(closure_data),
|
||||
ret_layout,
|
||||
is_erased,
|
||||
})
|
||||
}
|
||||
Some(lambda_set) => {
|
||||
Some(closure_data) => {
|
||||
// a function that returns a function, but is not itself a closure
|
||||
// e.g. f = Num.add
|
||||
|
||||
@ -3786,14 +3886,16 @@ fn build_specialized_proc<'a>(
|
||||
arguments: proc_args,
|
||||
closure: None,
|
||||
ret_layout,
|
||||
is_erased,
|
||||
})
|
||||
}
|
||||
Ordering::Greater => {
|
||||
if pattern_symbols.is_empty() {
|
||||
let ret_layout = lambda_set.full_layout;
|
||||
let ret_layout = closure_data.data_layout();
|
||||
Ok(FunctionPointerBody {
|
||||
closure: None,
|
||||
ret_layout,
|
||||
is_erased,
|
||||
})
|
||||
} else {
|
||||
// so far, the problem when hitting this branch was always somewhere else
|
||||
@ -3824,6 +3926,7 @@ fn build_specialized_proc<'a>(
|
||||
arguments: proc_args,
|
||||
closure: None,
|
||||
ret_layout,
|
||||
is_erased,
|
||||
})
|
||||
}
|
||||
Ordering::Greater => {
|
||||
@ -3831,6 +3934,7 @@ fn build_specialized_proc<'a>(
|
||||
Ok(FunctionPointerBody {
|
||||
closure: None,
|
||||
ret_layout,
|
||||
is_erased,
|
||||
})
|
||||
} else {
|
||||
// so far, the problem when hitting this branch was always somewhere else
|
||||
@ -3971,6 +4075,17 @@ impl<'a> ProcLayout<'a> {
|
||||
lambda_set.extend_argument_list_for_named(arena, lambda_name, arguments);
|
||||
ProcLayout::new(arena, arguments, lambda_name.niche(), result)
|
||||
}
|
||||
RawFunctionLayout::ErasedFunction(arguments, result) => {
|
||||
let arguments = if lambda_name.no_captures() {
|
||||
arguments
|
||||
} else {
|
||||
let mut extended_args = Vec::with_capacity_in(arguments.len(), arena);
|
||||
extended_args.extend(arguments.iter().chain(&[Layout::ERASED]).copied());
|
||||
extended_args.into_bump_slice()
|
||||
};
|
||||
|
||||
ProcLayout::new(arena, arguments, lambda_name.niche(), result)
|
||||
}
|
||||
RawFunctionLayout::ZeroArgumentThunk(result) => {
|
||||
ProcLayout::new(arena, &[], Niche::NONE, result)
|
||||
}
|
||||
@ -4824,6 +4939,7 @@ pub fn with_hole<'a>(
|
||||
hole,
|
||||
)
|
||||
}
|
||||
RawFunctionLayout::ErasedFunction(_, _) => todo_lambda_erasure!(),
|
||||
RawFunctionLayout::ZeroArgumentThunk(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
@ -4925,6 +5041,7 @@ pub fn with_hole<'a>(
|
||||
hole,
|
||||
)
|
||||
}
|
||||
RawFunctionLayout::ErasedFunction(..) => todo_lambda_erasure!(),
|
||||
RawFunctionLayout::ZeroArgumentThunk(_) => {
|
||||
internal_error!("should not be a thunk!")
|
||||
}
|
||||
@ -5140,6 +5257,44 @@ pub fn with_hole<'a>(
|
||||
RawFunctionLayout::ZeroArgumentThunk(_) => {
|
||||
unreachable!("a closure syntactically always must have at least one argument")
|
||||
}
|
||||
RawFunctionLayout::ErasedFunction(argument_layouts, ret_layout) => {
|
||||
let captured_symbols = if captured_symbols.is_empty() {
|
||||
CapturedSymbols::None
|
||||
} else {
|
||||
let captured_symbols = Vec::from_iter_in(captured_symbols, env.arena);
|
||||
let captured_symbols = captured_symbols.into_bump_slice();
|
||||
CapturedSymbols::Captured(captured_symbols)
|
||||
};
|
||||
let resolved_erased_lambda = ResolvedErasedLambda::new(
|
||||
env,
|
||||
layout_cache,
|
||||
name,
|
||||
captured_symbols,
|
||||
argument_layouts,
|
||||
ret_layout,
|
||||
);
|
||||
|
||||
let inserted = procs.insert_anonymous(
|
||||
env,
|
||||
resolved_erased_lambda.lambda_name(),
|
||||
function_type,
|
||||
arguments,
|
||||
loc_body,
|
||||
captured_symbols,
|
||||
return_type,
|
||||
layout_cache,
|
||||
);
|
||||
|
||||
if let Err(e) = inserted {
|
||||
return runtime_error(
|
||||
env,
|
||||
env.arena.alloc(format!("RuntimeError: {:?}", e,)),
|
||||
);
|
||||
}
|
||||
drop(inserted);
|
||||
|
||||
build_erased_function(env, layout_cache, resolved_erased_lambda, assigned, hole)
|
||||
}
|
||||
RawFunctionLayout::Function(_argument_layouts, lambda_set, _ret_layout) => {
|
||||
let mut captured_symbols = Vec::from_iter_in(captured_symbols, env.arena);
|
||||
captured_symbols.sort();
|
||||
@ -5172,9 +5327,8 @@ pub fn with_hole<'a>(
|
||||
env,
|
||||
env.arena.alloc(format!("RuntimeError: {e:?}",)),
|
||||
);
|
||||
} else {
|
||||
drop(inserted);
|
||||
}
|
||||
drop(inserted);
|
||||
|
||||
// define the closure data
|
||||
|
||||
@ -5323,6 +5477,7 @@ pub fn with_hole<'a>(
|
||||
env.arena.alloc(result),
|
||||
);
|
||||
}
|
||||
RawFunctionLayout::ErasedFunction(..) => todo_lambda_erasure!(),
|
||||
RawFunctionLayout::ZeroArgumentThunk(_) => {
|
||||
unreachable!("calling a non-closure layout")
|
||||
}
|
||||
@ -5357,6 +5512,7 @@ pub fn with_hole<'a>(
|
||||
hole,
|
||||
);
|
||||
}
|
||||
RawFunctionLayout::ErasedFunction(..) => todo_lambda_erasure!(),
|
||||
RawFunctionLayout::ZeroArgumentThunk(_) => {
|
||||
unreachable!("calling a non-closure layout")
|
||||
}
|
||||
@ -5424,6 +5580,22 @@ pub fn with_hole<'a>(
|
||||
env.arena.alloc(result),
|
||||
);
|
||||
}
|
||||
RawFunctionLayout::ErasedFunction(arg_layouts, ret_layout) => {
|
||||
let hole_layout =
|
||||
layout_cache.from_var(env.arena, fn_var, env.subs).unwrap();
|
||||
result = erased::call_erased_function(
|
||||
env,
|
||||
layout_cache,
|
||||
procs,
|
||||
loc_expr.value,
|
||||
fn_var,
|
||||
(arg_layouts, ret_layout),
|
||||
arg_symbols,
|
||||
assigned,
|
||||
hole,
|
||||
hole_layout,
|
||||
);
|
||||
}
|
||||
RawFunctionLayout::ZeroArgumentThunk(_) => {
|
||||
unreachable!(
|
||||
"{:?} cannot be called in the source language",
|
||||
@ -5557,6 +5729,7 @@ pub fn with_hole<'a>(
|
||||
hole,
|
||||
)
|
||||
}
|
||||
RawFunctionLayout::ErasedFunction(..) => todo_lambda_erasure!(),
|
||||
RawFunctionLayout::ZeroArgumentThunk(_) => unreachable!("match_on_closure_argument received a zero-argument thunk"),
|
||||
}
|
||||
}};
|
||||
@ -5610,7 +5783,7 @@ pub fn with_hole<'a>(
|
||||
_ => unreachable!("invalid layout for a box expression"),
|
||||
};
|
||||
|
||||
let expr = Expr::expr_box(arena.alloc(x), element_layout);
|
||||
let expr = boxed::box_(arena.alloc(x), element_layout);
|
||||
|
||||
Stmt::Let(assigned, expr, layout, hole)
|
||||
}
|
||||
@ -5618,7 +5791,7 @@ pub fn with_hole<'a>(
|
||||
debug_assert_eq!(arg_symbols.len(), 1);
|
||||
let x = arg_symbols[0];
|
||||
|
||||
let expr = Expr::expr_unbox(x, arena.alloc(layout));
|
||||
let expr = boxed::unbox(x, arena.alloc(layout));
|
||||
|
||||
Stmt::Let(assigned, expr, layout, hole)
|
||||
}
|
||||
@ -6478,6 +6651,7 @@ fn tag_union_to_function<'a>(
|
||||
hole,
|
||||
)
|
||||
}
|
||||
RawFunctionLayout::ErasedFunction(..) => todo_lambda_erasure!(),
|
||||
RawFunctionLayout::ZeroArgumentThunk(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
@ -7502,6 +7676,15 @@ fn substitute_in_call<'a>(
|
||||
ret_layout: *ret_layout,
|
||||
specialization_id: *specialization_id,
|
||||
}),
|
||||
CallType::ByPointer {
|
||||
pointer,
|
||||
arg_layouts,
|
||||
ret_layout,
|
||||
} => substitute(subs, *pointer).map(|new| CallType::ByPointer {
|
||||
pointer: new,
|
||||
arg_layouts,
|
||||
ret_layout: *ret_layout,
|
||||
}),
|
||||
CallType::Foreign { .. } => None,
|
||||
CallType::LowLevel { .. } => None,
|
||||
CallType::HigherOrder { .. } => None,
|
||||
@ -7651,6 +7834,34 @@ fn substitute_in_expr<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
ErasedMake { value, callee } => {
|
||||
match (
|
||||
value.and_then(|v| substitute(subs, v)),
|
||||
substitute(subs, *callee),
|
||||
) {
|
||||
(None, None) => None,
|
||||
(Some(value), None) => Some(ErasedMake {
|
||||
value: Some(value),
|
||||
callee: *callee,
|
||||
}),
|
||||
(None, Some(callee)) => Some(ErasedMake {
|
||||
value: *value,
|
||||
callee,
|
||||
}),
|
||||
(Some(value), Some(callee)) => Some(ErasedMake {
|
||||
value: Some(value),
|
||||
callee,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
ErasedLoad { symbol, field } => substitute(subs, *symbol).map(|new_symbol| ErasedLoad {
|
||||
symbol: new_symbol,
|
||||
field: *field,
|
||||
}),
|
||||
|
||||
FunctionPointer { .. } => None,
|
||||
|
||||
StructAtIndex {
|
||||
index,
|
||||
structure,
|
||||
@ -7968,6 +8179,7 @@ fn specialize_symbol<'a>(
|
||||
// data for a lambda set.
|
||||
let layout = match raw {
|
||||
RawFunctionLayout::ZeroArgumentThunk(layout) => layout,
|
||||
RawFunctionLayout::ErasedFunction(..) => todo_lambda_erasure!(),
|
||||
RawFunctionLayout::Function(_, lambda_set, _) => layout_cache
|
||||
.put_in_direct_no_semantic(LayoutRepr::LambdaSet(lambda_set)),
|
||||
};
|
||||
@ -8119,6 +8331,36 @@ fn specialize_symbol<'a>(
|
||||
)
|
||||
}
|
||||
}
|
||||
RawFunctionLayout::ErasedFunction(argument_layouts, ret_layout) => {
|
||||
let erased_lambda = erased::ResolvedErasedLambda::new(
|
||||
env,
|
||||
layout_cache,
|
||||
original,
|
||||
captured,
|
||||
argument_layouts,
|
||||
ret_layout,
|
||||
);
|
||||
let lambda_name = erased_lambda.lambda_name();
|
||||
|
||||
let proc_layout =
|
||||
ProcLayout::from_raw_named(env.arena, lambda_name, res_layout);
|
||||
|
||||
procs.insert_passed_by_name(
|
||||
env,
|
||||
arg_var,
|
||||
lambda_name,
|
||||
proc_layout,
|
||||
layout_cache,
|
||||
);
|
||||
|
||||
erased::build_erased_function(
|
||||
env,
|
||||
layout_cache,
|
||||
erased_lambda,
|
||||
assign_to,
|
||||
result,
|
||||
)
|
||||
}
|
||||
RawFunctionLayout::ZeroArgumentThunk(ret_layout) => {
|
||||
// this is a 0-argument thunk
|
||||
let top_level = ProcLayout::new(env.arena, &[], Niche::NONE, ret_layout);
|
||||
@ -8367,6 +8609,40 @@ fn call_by_name<'a>(
|
||||
)
|
||||
}
|
||||
}
|
||||
Ok(RawFunctionLayout::ErasedFunction(arg_layouts, ret_layout)) => {
|
||||
// TODO(erased-lambdas) call-by-name should never apply here
|
||||
let arena = env.arena;
|
||||
let arg_symbols = Vec::from_iter_in(
|
||||
loc_args.iter().map(|(arg_var, arg_expr)| {
|
||||
possible_reuse_symbol_or_specialize(
|
||||
env,
|
||||
procs,
|
||||
layout_cache,
|
||||
&arg_expr.value,
|
||||
*arg_var,
|
||||
)
|
||||
}),
|
||||
arena,
|
||||
)
|
||||
.into_bump_slice();
|
||||
|
||||
let result = erased::call_erased_function(
|
||||
env,
|
||||
layout_cache,
|
||||
procs,
|
||||
roc_can::expr::Expr::Var(proc_name, fn_var),
|
||||
fn_var,
|
||||
(arg_layouts, ret_layout),
|
||||
arg_symbols,
|
||||
assigned,
|
||||
hole,
|
||||
// TODO is this right??
|
||||
ret_layout,
|
||||
);
|
||||
|
||||
let iter = loc_args.into_iter().rev().zip(arg_symbols.iter().rev());
|
||||
assign_to_symbols(env, procs, layout_cache, iter, result)
|
||||
}
|
||||
Ok(RawFunctionLayout::ZeroArgumentThunk(ret_layout)) => {
|
||||
if procs.is_module_thunk(proc_name) {
|
||||
// here we turn a call to a module thunk into forcing of that thunk
|
||||
@ -8852,6 +9128,7 @@ fn call_specialized_proc<'a>(
|
||||
// but now we need to remove it because the `match_on_lambda_set` will add it again
|
||||
build_call(env, call, assigned, lambda_set.full_layout, hole)
|
||||
}
|
||||
RawFunctionLayout::ErasedFunction(..) => todo_lambda_erasure!(),
|
||||
RawFunctionLayout::ZeroArgumentThunk(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
@ -9805,6 +10082,7 @@ pub fn find_lambda_sets_help(
|
||||
stack.extend(subs.variables[subs_slice.indices()].iter());
|
||||
}
|
||||
}
|
||||
Content::ErasedLambda => {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -9943,6 +10221,8 @@ where
|
||||
LayoutRepr::RecursivePointer(_) => {
|
||||
/* do nothing, we've already generated for this type through the Union(_) */
|
||||
}
|
||||
LayoutRepr::FunctionPointer(_) => todo_lambda_erasure!(),
|
||||
LayoutRepr::Erased(_) => todo_lambda_erasure!(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -10004,7 +10284,7 @@ where
|
||||
|
||||
let field_get_stmt = Stmt::Let(result, field_get_expr, *field, ret_stmt);
|
||||
|
||||
let unbox_expr = Expr::expr_unbox(argument, arena.alloc(interned_unboxed_struct_layout));
|
||||
let unbox_expr = boxed::unbox(argument, arena.alloc(interned_unboxed_struct_layout));
|
||||
|
||||
let unbox_stmt = Stmt::Let(
|
||||
unboxed,
|
||||
@ -10021,6 +10301,7 @@ where
|
||||
ret_layout: *field,
|
||||
is_self_recursive: SelfRecursive::NotSelfRecursive,
|
||||
host_exposed_layouts: HostExposedLayouts::NotHostExposed,
|
||||
is_erased: false,
|
||||
};
|
||||
|
||||
answer.push(GlueProc {
|
||||
@ -10105,7 +10386,7 @@ where
|
||||
|
||||
let field_get_stmt = Stmt::Let(result, field_get_expr, *field, ret_stmt);
|
||||
|
||||
let unbox_expr = Expr::expr_unbox(argument, arena.alloc(interned));
|
||||
let unbox_expr = boxed::unbox(argument, arena.alloc(interned));
|
||||
let unbox_stmt = Stmt::Let(unboxed, unbox_expr, interned, arena.alloc(field_get_stmt));
|
||||
|
||||
let proc = Proc {
|
||||
@ -10116,6 +10397,7 @@ where
|
||||
ret_layout: *field,
|
||||
is_self_recursive: SelfRecursive::NotSelfRecursive,
|
||||
host_exposed_layouts: HostExposedLayouts::NotHostExposed,
|
||||
is_erased: false,
|
||||
};
|
||||
|
||||
answer.push(GlueProc {
|
||||
|
41
crates/compiler/mono/src/ir/boxed.rs
Normal file
41
crates/compiler/mono/src/ir/boxed.rs
Normal file
@ -0,0 +1,41 @@
|
||||
use roc_module::symbol::Symbol;
|
||||
|
||||
use crate::layout::{InLayout, UnionLayout};
|
||||
|
||||
use super::Expr;
|
||||
|
||||
pub fn box_<'a>(symbol: &'a Symbol, element_layout: &'a InLayout<'a>) -> Expr<'a> {
|
||||
Expr::Tag {
|
||||
tag_layout: UnionLayout::NonNullableUnwrapped(std::slice::from_ref(element_layout)),
|
||||
tag_id: 0,
|
||||
arguments: std::slice::from_ref(symbol),
|
||||
reuse: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unbox<'a>(symbol: Symbol, element_layout: &'a InLayout<'a>) -> Expr<'a> {
|
||||
Expr::UnionAtIndex {
|
||||
structure: symbol,
|
||||
tag_id: 0,
|
||||
union_layout: UnionLayout::NonNullableUnwrapped(std::slice::from_ref(element_layout)),
|
||||
index: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn box_nullable<'a>(symbol: &'a Symbol, element_layout: &'a InLayout<'a>) -> Expr<'a> {
|
||||
Expr::Tag {
|
||||
tag_layout: UnionLayout::boxed_erased_value(element_layout),
|
||||
tag_id: 0,
|
||||
arguments: std::slice::from_ref(symbol),
|
||||
reuse: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unbox_nullable<'a>(symbol: Symbol, element_layout: &'a InLayout<'a>) -> Expr<'a> {
|
||||
Expr::UnionAtIndex {
|
||||
structure: symbol,
|
||||
tag_id: 0,
|
||||
union_layout: UnionLayout::boxed_erased_value(element_layout),
|
||||
index: 0,
|
||||
}
|
||||
}
|
501
crates/compiler/mono/src/ir/erased.rs
Normal file
501
crates/compiler/mono/src/ir/erased.rs
Normal file
@ -0,0 +1,501 @@
|
||||
use bumpalo::{collections::Vec as AVec, Bump};
|
||||
use roc_module::{low_level::LowLevel, symbol::Symbol};
|
||||
use roc_types::subs::Variable;
|
||||
|
||||
use crate::{
|
||||
borrow::Ownership,
|
||||
layout::{FunctionPointer, InLayout, LambdaName, Layout, LayoutCache, LayoutRepr},
|
||||
};
|
||||
|
||||
use super::{
|
||||
boxed, with_hole, BranchInfo, Call, CallType, CapturedSymbols, Env, ErasedField, Expr,
|
||||
JoinPointId, Param, Procs, Stmt, UpdateModeId,
|
||||
};
|
||||
|
||||
fn index_erased_function<'a>(
|
||||
arena: &'a Bump,
|
||||
assign_to: Symbol,
|
||||
erased_function: Symbol,
|
||||
field: ErasedField,
|
||||
layout: InLayout<'a>,
|
||||
) -> impl FnOnce(Stmt<'a>) -> Stmt<'a> {
|
||||
move |rest| {
|
||||
Stmt::Let(
|
||||
assign_to,
|
||||
Expr::ErasedLoad {
|
||||
symbol: erased_function,
|
||||
field,
|
||||
},
|
||||
layout,
|
||||
arena.alloc(rest),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn call_callee<'a>(
|
||||
arena: &'a Bump,
|
||||
result_symbol: Symbol,
|
||||
result: InLayout<'a>,
|
||||
fn_ptr_symbol: Symbol,
|
||||
fn_arg_layouts: &'a [InLayout<'a>],
|
||||
fn_arguments: &'a [Symbol],
|
||||
) -> impl FnOnce(Stmt<'a>) -> Stmt<'a> {
|
||||
move |rest| {
|
||||
Stmt::Let(
|
||||
result_symbol,
|
||||
Expr::Call(Call {
|
||||
call_type: CallType::ByPointer {
|
||||
pointer: fn_ptr_symbol,
|
||||
ret_layout: result,
|
||||
arg_layouts: fn_arg_layouts,
|
||||
},
|
||||
arguments: fn_arguments,
|
||||
}),
|
||||
result,
|
||||
arena.alloc(rest),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn is_null<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
arena: &'a Bump,
|
||||
assign_to: Symbol,
|
||||
ptr_symbol: Symbol,
|
||||
layout: InLayout<'a>,
|
||||
) -> impl FnOnce(Stmt<'a>) -> Stmt<'a> {
|
||||
let null_symbol = env.unique_symbol();
|
||||
move |rest| {
|
||||
Stmt::Let(
|
||||
null_symbol,
|
||||
Expr::NullPointer,
|
||||
layout,
|
||||
arena.alloc(Stmt::Let(
|
||||
assign_to,
|
||||
Expr::Call(Call {
|
||||
call_type: CallType::LowLevel {
|
||||
op: LowLevel::Eq,
|
||||
update_mode: UpdateModeId::BACKEND_DUMMY,
|
||||
},
|
||||
arguments: arena.alloc([ptr_symbol, null_symbol]),
|
||||
}),
|
||||
Layout::BOOL,
|
||||
arena.alloc(rest),
|
||||
)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
struct BuiltFunctionPointer<'a> {
|
||||
function_pointer: InLayout<'a>,
|
||||
reified_arguments: &'a [InLayout<'a>],
|
||||
}
|
||||
|
||||
fn build_function_pointer<'a>(
|
||||
arena: &'a Bump,
|
||||
layout_cache: &mut LayoutCache<'a>,
|
||||
argument_layouts: &'a [InLayout<'a>],
|
||||
return_layout: InLayout<'a>,
|
||||
pass_closure: bool,
|
||||
) -> BuiltFunctionPointer<'a> {
|
||||
let reified_arguments = if pass_closure {
|
||||
let mut args = AVec::with_capacity_in(argument_layouts.len() + 1, arena);
|
||||
args.extend(argument_layouts.iter().chain(&[Layout::ERASED]).copied());
|
||||
args.into_bump_slice()
|
||||
} else {
|
||||
argument_layouts
|
||||
};
|
||||
|
||||
let fn_ptr_layout = LayoutRepr::FunctionPointer(FunctionPointer {
|
||||
args: reified_arguments,
|
||||
ret: return_layout,
|
||||
});
|
||||
|
||||
let function_pointer = layout_cache.put_in_direct_no_semantic(fn_ptr_layout);
|
||||
|
||||
BuiltFunctionPointer {
|
||||
function_pointer,
|
||||
reified_arguments,
|
||||
}
|
||||
}
|
||||
|
||||
/// Given
|
||||
///
|
||||
/// ```text
|
||||
/// Call(f, args)
|
||||
/// ```
|
||||
///
|
||||
/// We generate
|
||||
///
|
||||
/// ```text
|
||||
/// f = compile(f)
|
||||
/// joinpoint join result:
|
||||
/// <hole>
|
||||
/// f_value: Ptr<[]> = ErasedLoad(f, .value_ptr)
|
||||
/// f_callee: Ptr<[]> = ErasedLoad(f, .callee)
|
||||
/// if (f_value != nullptr) {
|
||||
/// f_callee = Cast(f_callee, (..params, Erased) -> ret);
|
||||
/// result = f_callee ..args f
|
||||
/// jump join result
|
||||
/// } else {
|
||||
/// f_callee = cast(f_callee, (..params) -> ret);
|
||||
/// result = f_callee ..args
|
||||
/// jump join result
|
||||
/// }
|
||||
/// ```
|
||||
pub fn call_erased_function<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
layout_cache: &mut LayoutCache<'a>,
|
||||
procs: &mut Procs<'a>,
|
||||
function_expr: roc_can::expr::Expr,
|
||||
function_var: Variable,
|
||||
function_signature: (&'a [InLayout<'a>], InLayout<'a>),
|
||||
function_argument_symbols: &'a [Symbol],
|
||||
call_result_symbol: Symbol,
|
||||
hole: &'a Stmt<'a>,
|
||||
hole_layout: InLayout<'a>,
|
||||
) -> Stmt<'a> {
|
||||
let arena = env.arena;
|
||||
let (f_args, f_ret) = function_signature;
|
||||
|
||||
let f = env.unique_symbol();
|
||||
|
||||
let join_point_id = JoinPointId(env.unique_symbol());
|
||||
|
||||
// f_value = ErasedLoad(f, .value)
|
||||
let f_value = env.unique_symbol();
|
||||
let let_f_value =
|
||||
index_erased_function(arena, f_value, f, ErasedField::ValuePtr, Layout::OPAQUE_PTR);
|
||||
|
||||
let mut build_closure_data_branch = |env: &mut Env, pass_closure| {
|
||||
// f_callee = Cast(f_callee, (..params) -> ret);
|
||||
// result = f_callee ..args
|
||||
// jump join result
|
||||
|
||||
let BuiltFunctionPointer {
|
||||
function_pointer,
|
||||
reified_arguments: f_args,
|
||||
} = build_function_pointer(arena, layout_cache, f_args, f_ret, pass_closure);
|
||||
|
||||
// f_callee = ErasedLoad(f, .callee)
|
||||
let f_callee = env.unique_symbol();
|
||||
let let_f_callee =
|
||||
index_erased_function(arena, f_callee, f, ErasedField::Callee, function_pointer);
|
||||
|
||||
let function_argument_symbols = if pass_closure {
|
||||
// function_argument_symbols = ...args, f.value
|
||||
let mut args = AVec::with_capacity_in(function_argument_symbols.len() + 1, arena);
|
||||
args.extend(function_argument_symbols.iter().chain(&[f]));
|
||||
args.into_bump_slice()
|
||||
} else {
|
||||
function_argument_symbols
|
||||
};
|
||||
|
||||
let result = env.unique_symbol();
|
||||
let let_result = call_callee(
|
||||
arena,
|
||||
result,
|
||||
f_ret,
|
||||
f_callee,
|
||||
f_args,
|
||||
function_argument_symbols,
|
||||
);
|
||||
|
||||
let_f_callee(
|
||||
//
|
||||
let_result(
|
||||
//
|
||||
Stmt::Jump(join_point_id, arena.alloc([result])),
|
||||
),
|
||||
)
|
||||
};
|
||||
|
||||
let value_is_null = env.unique_symbol();
|
||||
let let_value_is_null = is_null(env, arena, value_is_null, f_value, Layout::OPAQUE_PTR);
|
||||
|
||||
let call_and_jump_on_value = let_value_is_null(
|
||||
//
|
||||
Stmt::Switch {
|
||||
cond_symbol: value_is_null,
|
||||
cond_layout: Layout::BOOL,
|
||||
// value == null
|
||||
branches: arena.alloc([(1, BranchInfo::None, build_closure_data_branch(env, false))]),
|
||||
// value != null
|
||||
default_branch: (
|
||||
BranchInfo::None,
|
||||
arena.alloc(build_closure_data_branch(env, true)),
|
||||
),
|
||||
ret_layout: hole_layout,
|
||||
},
|
||||
);
|
||||
|
||||
let joinpoint = {
|
||||
let param = Param {
|
||||
symbol: call_result_symbol,
|
||||
layout: f_ret,
|
||||
ownership: Ownership::Owned,
|
||||
};
|
||||
|
||||
let remainder = let_f_value(
|
||||
// f_value = ErasedLoad(f, .value)
|
||||
// <rest>
|
||||
call_and_jump_on_value,
|
||||
);
|
||||
|
||||
Stmt::Join {
|
||||
id: join_point_id,
|
||||
parameters: env.arena.alloc([param]),
|
||||
body: hole,
|
||||
remainder: arena.alloc(remainder),
|
||||
}
|
||||
};
|
||||
|
||||
// Compile the function expression into f_val
|
||||
with_hole(
|
||||
env,
|
||||
function_expr,
|
||||
function_var,
|
||||
procs,
|
||||
layout_cache,
|
||||
f,
|
||||
env.arena.alloc(joinpoint),
|
||||
)
|
||||
}
|
||||
|
||||
/// Given
|
||||
///
|
||||
/// ```text
|
||||
/// f = \{} -> s
|
||||
/// ```
|
||||
///
|
||||
/// We generate
|
||||
///
|
||||
/// ```text
|
||||
/// value = Expr::Box({s})
|
||||
/// callee = Expr::FunctionPointer(f)
|
||||
/// f = Expr::ErasedMake({ value, callee })
|
||||
/// ```
|
||||
///
|
||||
/// Given
|
||||
///
|
||||
/// ```text
|
||||
/// f = \{} -> {}
|
||||
/// ```
|
||||
///
|
||||
/// We generate
|
||||
///
|
||||
/// ```text
|
||||
/// callee = Expr::FunctionPointer(f)
|
||||
/// f = Expr::ErasedMake({ value: nullptr, callee })
|
||||
/// ```
|
||||
pub fn build_erased_function<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
layout_cache: &mut LayoutCache<'a>,
|
||||
resolved_lambda: ResolvedErasedLambda<'a>,
|
||||
assigned: Symbol,
|
||||
hole: &'a Stmt<'a>,
|
||||
) -> Stmt<'a> {
|
||||
let ResolvedErasedLambda {
|
||||
captures,
|
||||
lambda_name,
|
||||
arguments,
|
||||
ret,
|
||||
} = resolved_lambda;
|
||||
|
||||
let value = match captures {
|
||||
None => None,
|
||||
Some(_) => Some(env.unique_symbol()),
|
||||
};
|
||||
|
||||
let callee = env.unique_symbol();
|
||||
|
||||
// assigned = Expr::ErasedMake({ value, callee })
|
||||
// hole <assigned>
|
||||
let result = Stmt::Let(
|
||||
assigned,
|
||||
Expr::ErasedMake { value, callee },
|
||||
Layout::ERASED,
|
||||
hole,
|
||||
);
|
||||
|
||||
let BuiltFunctionPointer {
|
||||
function_pointer,
|
||||
reified_arguments: _,
|
||||
} = build_function_pointer(env.arena, layout_cache, arguments, ret, captures.is_some());
|
||||
|
||||
// callee = Expr::FunctionPointer(f)
|
||||
let result = Stmt::Let(
|
||||
callee,
|
||||
Expr::FunctionPointer { lambda_name },
|
||||
function_pointer,
|
||||
env.arena.alloc(result),
|
||||
);
|
||||
|
||||
// value = Expr::Box({s})
|
||||
match captures {
|
||||
None => result,
|
||||
Some(ResolvedErasedCaptures { layouts, symbols }) => {
|
||||
// captures = {...captures}
|
||||
// captures = Box(captures)
|
||||
// value = Cast(captures, void*)
|
||||
// <hole>
|
||||
|
||||
let stack_captures = env.unique_symbol();
|
||||
let stack_captures_layout =
|
||||
layout_cache.put_in_direct_no_semantic(LayoutRepr::Struct(layouts));
|
||||
let stack_captures_layout = env.arena.alloc(stack_captures_layout);
|
||||
|
||||
let boxed_captures_layout = layout_cache
|
||||
.put_in_direct_no_semantic(LayoutRepr::boxed_erased_value(stack_captures_layout));
|
||||
|
||||
let result = Stmt::Let(
|
||||
value.unwrap(),
|
||||
boxed::box_nullable(env.arena.alloc(stack_captures), stack_captures_layout),
|
||||
boxed_captures_layout,
|
||||
env.arena.alloc(result),
|
||||
);
|
||||
|
||||
let result = Stmt::Let(
|
||||
stack_captures,
|
||||
Expr::Struct(symbols),
|
||||
*stack_captures_layout,
|
||||
env.arena.alloc(result),
|
||||
);
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ResolvedErasedCaptures<'a> {
|
||||
layouts: &'a [InLayout<'a>],
|
||||
symbols: &'a [Symbol],
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ResolvedErasedLambda<'a> {
|
||||
captures: Option<ResolvedErasedCaptures<'a>>,
|
||||
lambda_name: LambdaName<'a>,
|
||||
arguments: &'a [InLayout<'a>],
|
||||
ret: InLayout<'a>,
|
||||
}
|
||||
|
||||
impl<'a> ResolvedErasedLambda<'a> {
|
||||
pub fn new(
|
||||
env: &Env<'a, '_>,
|
||||
layout_cache: &mut LayoutCache<'a>,
|
||||
lambda_symbol: Symbol,
|
||||
captures: CapturedSymbols<'a>,
|
||||
arguments: &'a [InLayout<'a>],
|
||||
ret: InLayout<'a>,
|
||||
) -> Self {
|
||||
let resolved_captures;
|
||||
let lambda_name;
|
||||
match captures {
|
||||
CapturedSymbols::None => {
|
||||
resolved_captures = None;
|
||||
lambda_name = LambdaName::from_captures(lambda_symbol, &[]);
|
||||
}
|
||||
CapturedSymbols::Captured(captures) => {
|
||||
let layouts = {
|
||||
let layouts = captures
|
||||
.iter()
|
||||
.map(|(_, var)| layout_cache.from_var(env.arena, *var, env.subs).unwrap());
|
||||
env.arena.alloc_slice_fill_iter(layouts)
|
||||
};
|
||||
let symbols = {
|
||||
let symbols = captures.iter().map(|(sym, _)| *sym);
|
||||
env.arena.alloc_slice_fill_iter(symbols)
|
||||
};
|
||||
|
||||
resolved_captures = Some(ResolvedErasedCaptures { layouts, symbols });
|
||||
lambda_name = LambdaName::from_captures(lambda_symbol, layouts);
|
||||
}
|
||||
};
|
||||
|
||||
Self {
|
||||
captures: resolved_captures,
|
||||
lambda_name,
|
||||
arguments,
|
||||
ret,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lambda_name(&self) -> LambdaName<'a> {
|
||||
self.lambda_name
|
||||
}
|
||||
}
|
||||
|
||||
/// Given
|
||||
///
|
||||
/// ```text
|
||||
/// proc f(...args, captures_symbol: Erased):
|
||||
/// # captures = { a: A, b: B }
|
||||
/// ```
|
||||
///
|
||||
/// We generate
|
||||
///
|
||||
/// ```text
|
||||
/// loaded_captures: Ptr<[]> = ErasedLoad(captures_symbol, .value)
|
||||
/// heap_captures: Box<{ A, B }> = Expr::Call(Lowlevel { Cast, captures_symbol })
|
||||
/// stack_captures = Expr::Unbox(heap_captures)
|
||||
/// a = Expr::StructAtIndex(stack_captures, 0)
|
||||
/// b = Expr::StructAtIndex(stack_captures, 1)
|
||||
/// <hole>
|
||||
/// ```
|
||||
pub fn unpack_closure_data<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
layout_cache: &mut LayoutCache<'a>,
|
||||
captures_symbol: Symbol,
|
||||
captures: &[(Symbol, Variable)],
|
||||
mut hole: Stmt<'a>,
|
||||
) -> Stmt<'a> {
|
||||
let heap_captures = env.unique_symbol();
|
||||
let stack_captures = env.unique_symbol();
|
||||
|
||||
let captures_layouts = {
|
||||
let layouts = captures
|
||||
.iter()
|
||||
.map(|(_, var)| layout_cache.from_var(env.arena, *var, env.subs).unwrap());
|
||||
&*env.arena.alloc_slice_fill_iter(layouts)
|
||||
};
|
||||
|
||||
let stack_captures_layout =
|
||||
layout_cache.put_in_direct_no_semantic(LayoutRepr::Struct(captures_layouts));
|
||||
let stack_captures_layout = env.arena.alloc(stack_captures_layout);
|
||||
let heap_captures_layout = layout_cache
|
||||
.put_in_direct_no_semantic(LayoutRepr::boxed_erased_value(stack_captures_layout));
|
||||
|
||||
for (i, ((capture, _capture_var), &capture_layout)) in
|
||||
captures.iter().zip(captures_layouts).enumerate().rev()
|
||||
{
|
||||
hole = Stmt::Let(
|
||||
*capture,
|
||||
Expr::StructAtIndex {
|
||||
index: i as _,
|
||||
field_layouts: captures_layouts,
|
||||
structure: stack_captures,
|
||||
},
|
||||
capture_layout,
|
||||
env.arena.alloc(hole),
|
||||
);
|
||||
}
|
||||
|
||||
hole = Stmt::Let(
|
||||
stack_captures,
|
||||
boxed::unbox_nullable(heap_captures, stack_captures_layout),
|
||||
*stack_captures_layout,
|
||||
env.arena.alloc(hole),
|
||||
);
|
||||
|
||||
let let_loaded_captures = index_erased_function(
|
||||
env.arena,
|
||||
heap_captures,
|
||||
captures_symbol,
|
||||
ErasedField::Value,
|
||||
heap_captures_layout,
|
||||
);
|
||||
|
||||
let_loaded_captures(hole)
|
||||
}
|
@ -26,8 +26,11 @@ use std::collections::HashMap;
|
||||
use std::hash::Hash;
|
||||
use ven_pretty::{DocAllocator, DocBuilder};
|
||||
|
||||
mod erased;
|
||||
mod intern;
|
||||
mod semantic;
|
||||
|
||||
pub use erased::Erased;
|
||||
pub use intern::{
|
||||
GlobalLayoutInterner, InLayout, LayoutInterner, STLayoutInterner, TLLayoutInterner,
|
||||
};
|
||||
@ -484,6 +487,7 @@ impl From<LayoutProblem> for RuntimeError {
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum RawFunctionLayout<'a> {
|
||||
Function(&'a [InLayout<'a>], LambdaSet<'a>, InLayout<'a>),
|
||||
ErasedFunction(&'a [InLayout<'a>], InLayout<'a>),
|
||||
ZeroArgumentThunk(InLayout<'a>),
|
||||
}
|
||||
|
||||
@ -492,6 +496,10 @@ impl<'a> RawFunctionLayout<'a> {
|
||||
matches!(self, RawFunctionLayout::ZeroArgumentThunk(_))
|
||||
}
|
||||
|
||||
pub fn is_erased_function(&self) -> bool {
|
||||
matches!(self, RawFunctionLayout::ErasedFunction(_, _))
|
||||
}
|
||||
|
||||
fn new_help<'b>(
|
||||
env: &mut Env<'a, 'b>,
|
||||
var: Variable,
|
||||
@ -508,6 +516,7 @@ impl<'a> RawFunctionLayout<'a> {
|
||||
LambdaSet(_) => {
|
||||
internal_error!("lambda set should only appear under a function, where it's handled independently.");
|
||||
}
|
||||
ErasedLambda => internal_error!("erased lambda type should only appear under a function, where it's handled independently"),
|
||||
Structure(flat_type) => Self::layout_from_flat_type(env, flat_type),
|
||||
RangedNumber(..) => Layout::new_help(env, var, content).then(Self::ZeroArgumentThunk),
|
||||
|
||||
@ -604,13 +613,17 @@ impl<'a> RawFunctionLayout<'a> {
|
||||
|
||||
let fn_args = fn_args.into_bump_slice();
|
||||
|
||||
let lambda_set = cached!(
|
||||
LambdaSet::from_var(env, args, closure_var, ret_var),
|
||||
cache_criteria,
|
||||
env.subs
|
||||
);
|
||||
let closure_data = build_function_closure_data(env, args, closure_var, ret_var);
|
||||
let closure_data = cached!(closure_data, cache_criteria, env.subs);
|
||||
|
||||
Cacheable(Ok(Self::Function(fn_args, lambda_set, ret)), cache_criteria)
|
||||
let function_layout = match closure_data {
|
||||
ClosureDataKind::LambdaSet(lambda_set) => {
|
||||
Self::Function(fn_args, lambda_set, ret)
|
||||
}
|
||||
ClosureDataKind::Erased => Self::ErasedFunction(fn_args, ret),
|
||||
};
|
||||
|
||||
Cacheable(Ok(function_layout), cache_criteria)
|
||||
}
|
||||
TagUnion(tags, ext) if tags.is_newtype_wrapper(env.subs) => {
|
||||
debug_assert!(ext_var_is_empty_tag_union(env.subs, ext));
|
||||
@ -678,6 +691,47 @@ pub enum LayoutRepr<'a> {
|
||||
Union(UnionLayout<'a>),
|
||||
LambdaSet(LambdaSet<'a>),
|
||||
RecursivePointer(InLayout<'a>),
|
||||
/// Only used in erased functions.
|
||||
FunctionPointer(FunctionPointer<'a>),
|
||||
/// The layout of an erasure.
|
||||
Erased(Erased),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct FunctionPointer<'a> {
|
||||
pub args: &'a [InLayout<'a>],
|
||||
pub ret: InLayout<'a>,
|
||||
}
|
||||
|
||||
impl<'a> FunctionPointer<'a> {
|
||||
pub fn to_doc<'b, D, A, I>(
|
||||
self,
|
||||
alloc: &'b D,
|
||||
interner: &I,
|
||||
seen_rec: &mut SeenRecPtrs<'a>,
|
||||
parens: Parens,
|
||||
) -> DocBuilder<'b, D, A>
|
||||
where
|
||||
D: DocAllocator<'b, A>,
|
||||
D::Doc: Clone,
|
||||
A: Clone,
|
||||
I: LayoutInterner<'a>,
|
||||
{
|
||||
let Self { args, ret } = self;
|
||||
|
||||
let args = args
|
||||
.iter()
|
||||
.map(|arg| interner.to_doc(*arg, alloc, seen_rec, parens));
|
||||
let args = alloc.intersperse(args, alloc.text(", "));
|
||||
let ret = interner.to_doc(ret, alloc, seen_rec, parens);
|
||||
|
||||
alloc
|
||||
.text("FunPtr((")
|
||||
.append(args)
|
||||
.append(alloc.text(") -> "))
|
||||
.append(ret)
|
||||
.append(")")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
@ -1312,6 +1366,13 @@ pub struct LambdaName<'a> {
|
||||
}
|
||||
|
||||
impl<'a> LambdaName<'a> {
|
||||
pub(crate) fn from_captures(symbol: Symbol, captures: &'a [InLayout<'a>]) -> Self {
|
||||
Self {
|
||||
name: symbol,
|
||||
niche: Niche(NichePriv::Captures(captures)),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn name(&self) -> Symbol {
|
||||
self.name
|
||||
@ -1343,6 +1404,37 @@ impl<'a> LambdaName<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Closure data for a function
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub(crate) enum ClosureDataKind<'a> {
|
||||
/// The function is compiled with lambda sets.
|
||||
LambdaSet(LambdaSet<'a>),
|
||||
/// The function is compiled as type-erased.
|
||||
Erased,
|
||||
}
|
||||
|
||||
impl<'a> ClosureDataKind<'a> {
|
||||
pub fn data_layout(&self) -> InLayout<'a> {
|
||||
match self {
|
||||
Self::LambdaSet(lambda_set) => lambda_set.full_layout,
|
||||
Self::Erased => Layout::ERASED,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn build_function_closure_data<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
args: VariableSubsSlice,
|
||||
closure_var: Variable,
|
||||
ret_var: Variable,
|
||||
) -> Cacheable<Result<ClosureDataKind<'a>, LayoutProblem>> {
|
||||
match env.subs.get_content_without_compacting(closure_var) {
|
||||
Content::ErasedLambda => cacheable(Ok(ClosureDataKind::Erased)),
|
||||
_ => LambdaSet::from_var(env, args, closure_var, ret_var)
|
||||
.map(|result| result.map(ClosureDataKind::LambdaSet)),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct LambdaSet<'a> {
|
||||
pub(crate) args: &'a &'a [InLayout<'a>],
|
||||
@ -2113,7 +2205,8 @@ fn lambda_set_size(subs: &Subs, var: Variable) -> (usize, usize, usize) {
|
||||
| Content::FlexAbleVar(_, _)
|
||||
| Content::RigidAbleVar(_, _)
|
||||
| Content::RangedNumber(_)
|
||||
| Content::Error => {}
|
||||
| Content::Error
|
||||
| Content::ErasedLambda => {}
|
||||
}
|
||||
}
|
||||
(max_depth_any_ctor, max_depth_only_lset, total)
|
||||
@ -2393,6 +2486,9 @@ impl<'a> Layout<'a> {
|
||||
LambdaSet(_) => {
|
||||
internal_error!("lambda set should only appear under a function, where it's handled independently.");
|
||||
}
|
||||
ErasedLambda => {
|
||||
internal_error!("erased lambda type should only appear under a function, where it's handled independently.");
|
||||
}
|
||||
Structure(flat_type) => layout_from_flat_type(env, flat_type),
|
||||
|
||||
Alias(symbol, _args, actual_var, _) => {
|
||||
@ -2529,6 +2625,7 @@ impl<'a> LayoutRepr<'a> {
|
||||
pub const DEC: Self = LayoutRepr::Builtin(Builtin::Decimal);
|
||||
pub const STR: Self = LayoutRepr::Builtin(Builtin::Str);
|
||||
pub const OPAQUE_PTR: Self = LayoutRepr::Ptr(Layout::VOID);
|
||||
pub const ERASED: Self = LayoutRepr::Erased(Erased);
|
||||
|
||||
pub const fn struct_(field_layouts: &'a [InLayout<'a>]) -> Self {
|
||||
Self::Struct(field_layouts)
|
||||
@ -2574,6 +2671,8 @@ impl<'a> LayoutRepr<'a> {
|
||||
// We cannot memcpy pointers, because then we would have the same pointer in multiple places!
|
||||
false
|
||||
}
|
||||
Erased(e) => e.safe_to_memcpy(),
|
||||
FunctionPointer(..) => true,
|
||||
}
|
||||
}
|
||||
|
||||
@ -2659,8 +2758,10 @@ impl<'a> LayoutRepr<'a> {
|
||||
LambdaSet(lambda_set) => interner
|
||||
.get_repr(lambda_set.runtime_representation())
|
||||
.stack_size_without_alignment(interner),
|
||||
RecursivePointer(_) => interner.target_info().ptr_width() as u32,
|
||||
Ptr(_) => interner.target_info().ptr_width() as u32,
|
||||
RecursivePointer(_) | Ptr(_) | FunctionPointer(_) => {
|
||||
interner.target_info().ptr_width() as u32
|
||||
}
|
||||
Erased(e) => e.stack_size_without_alignment(interner.target_info()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -2712,8 +2813,10 @@ impl<'a> LayoutRepr<'a> {
|
||||
.get_repr(lambda_set.runtime_representation())
|
||||
.alignment_bytes(interner),
|
||||
Builtin(builtin) => builtin.alignment_bytes(interner.target_info()),
|
||||
RecursivePointer(_) => interner.target_info().ptr_width() as u32,
|
||||
Ptr(_) => interner.target_info().ptr_width() as u32,
|
||||
RecursivePointer(_) | Ptr(_) | FunctionPointer(_) => {
|
||||
interner.target_info().ptr_width() as u32
|
||||
}
|
||||
Erased(e) => e.alignment_bytes(interner.target_info()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -2735,6 +2838,8 @@ impl<'a> LayoutRepr<'a> {
|
||||
unreachable!("should be looked up to get an actual layout")
|
||||
}
|
||||
Ptr(inner) => interner.get_repr(*inner).alignment_bytes(interner),
|
||||
FunctionPointer(_) => ptr_width,
|
||||
Erased(e) => e.allocation_alignment_bytes(interner.target_info()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -2751,6 +2856,8 @@ impl<'a> LayoutRepr<'a> {
|
||||
|
||||
Builtin(List(_)) | Builtin(Str) => true,
|
||||
|
||||
Erased(_) => true,
|
||||
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -2808,6 +2915,8 @@ impl<'a> LayoutRepr<'a> {
|
||||
// author must make sure that invariants are upheld
|
||||
false
|
||||
}
|
||||
FunctionPointer(_) => false,
|
||||
Erased(e) => e.is_refcounted(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -2869,6 +2978,12 @@ impl<'a> LayoutRepr<'a> {
|
||||
RecursivePointer(_) => {
|
||||
/* do nothing, we've already generated for this type through the Union(_) */
|
||||
}
|
||||
FunctionPointer(_) => {
|
||||
// drop through
|
||||
}
|
||||
Erased(_) => {
|
||||
// erasures are just pointers, so they do not vary
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3219,14 +3334,15 @@ fn layout_from_flat_type<'a>(
|
||||
} else {
|
||||
let mut criteria = CACHEABLE;
|
||||
|
||||
let lambda_set = cached!(
|
||||
LambdaSet::from_var(env, args, closure_var, ret_var),
|
||||
criteria,
|
||||
env.subs
|
||||
);
|
||||
let lambda_set = lambda_set.full_layout;
|
||||
let closure_data = build_function_closure_data(env, args, closure_var, ret_var);
|
||||
let closure_data = cached!(closure_data, criteria, env.subs);
|
||||
|
||||
Cacheable(Ok(lambda_set), criteria)
|
||||
match closure_data {
|
||||
ClosureDataKind::LambdaSet(lambda_set) => {
|
||||
Cacheable(Ok(lambda_set.full_layout), criteria)
|
||||
}
|
||||
ClosureDataKind::Erased => Cacheable(Ok(Layout::ERASED), criteria),
|
||||
}
|
||||
}
|
||||
}
|
||||
Record(fields, ext_var) => {
|
||||
@ -4461,7 +4577,7 @@ fn layout_from_num_content<'a>(
|
||||
Alias(_, _, _, _) => {
|
||||
todo!("TODO recursively resolve type aliases in num_from_content");
|
||||
}
|
||||
Structure(_) | RangedNumber(..) | LambdaSet(_) => {
|
||||
Structure(_) | RangedNumber(..) | LambdaSet(_) | ErasedLambda => {
|
||||
panic!("Invalid Num.Num type application: {content:?}");
|
||||
}
|
||||
Error => Err(LayoutProblem::Erroneous),
|
||||
|
72
crates/compiler/mono/src/layout/erased.rs
Normal file
72
crates/compiler/mono/src/layout/erased.rs
Normal file
@ -0,0 +1,72 @@
|
||||
use roc_target::TargetInfo;
|
||||
|
||||
use super::{InLayout, LayoutRepr, UnionLayout};
|
||||
|
||||
/// The layout of an erasure.
|
||||
///
|
||||
/// A type-erased value consists of three fields at runtime:
|
||||
///
|
||||
/// ```text
|
||||
/// {
|
||||
/// // the material value being erased.
|
||||
/// // if the erasure is a function, this is the captured environment, or null.
|
||||
/// value: void*,
|
||||
///
|
||||
/// // if the erasure is a function, the function pointer, or null otherwise.
|
||||
/// callee: void*,
|
||||
///
|
||||
/// // the refcounter for the material value, or null if there is no material value.
|
||||
/// refcounter: void*,
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Erased;
|
||||
|
||||
impl Erased {
|
||||
pub fn safe_to_memcpy(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn stack_size_without_alignment(&self, target_info: TargetInfo) -> u32 {
|
||||
(target_info.ptr_width() as u32) * 3
|
||||
}
|
||||
|
||||
pub fn alignment_bytes(&self, target_info: TargetInfo) -> u32 {
|
||||
target_info.ptr_width() as u32
|
||||
}
|
||||
|
||||
pub fn allocation_alignment_bytes(&self, target_info: TargetInfo) -> u32 {
|
||||
target_info.ptr_width() as u32
|
||||
}
|
||||
|
||||
pub fn is_refcounted(&self) -> bool {
|
||||
// The refcounter may not be present, but we don't know that statically.
|
||||
// So assume we always refcount, and the implementor of the refcount function
|
||||
// can no-op if it's not needed.
|
||||
true
|
||||
}
|
||||
|
||||
pub fn to_doc<'b, D, A>(&self, alloc: &'b D) -> ven_pretty::DocBuilder<'b, D, A>
|
||||
where
|
||||
D: ven_pretty::DocAllocator<'b, A>,
|
||||
D::Doc: Clone,
|
||||
A: Clone,
|
||||
{
|
||||
alloc.text("?Erased")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> UnionLayout<'a> {
|
||||
pub(crate) fn boxed_erased_value(value: &'a InLayout<'a>) -> Self {
|
||||
UnionLayout::NullableUnwrapped {
|
||||
nullable_id: true,
|
||||
other_fields: std::slice::from_ref(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> LayoutRepr<'a> {
|
||||
pub(crate) fn boxed_erased_value(value: &'a InLayout<'a>) -> Self {
|
||||
Self::Union(UnionLayout::boxed_erased_value(value))
|
||||
}
|
||||
}
|
@ -75,11 +75,12 @@ cache_interned_layouts! {
|
||||
15, DEC, pub, nosema!(LayoutRepr::DEC)
|
||||
16, STR, pub, nosema!(LayoutRepr::STR)
|
||||
17, OPAQUE_PTR, pub, nosema!(LayoutRepr::OPAQUE_PTR)
|
||||
18, NAKED_RECURSIVE_PTR, pub(super), nosema!(LayoutRepr::RecursivePointer(Layout::VOID))
|
||||
19, STR_PTR, pub, nosema!(LayoutRepr::Ptr(Layout::STR))
|
||||
20, LIST_U8, pub, nosema!(LayoutRepr::Builtin(crate::layout::Builtin::List(Layout::U8)))
|
||||
18, ERASED, pub, nosema!(LayoutRepr::ERASED)
|
||||
19, NAKED_RECURSIVE_PTR, pub(super), nosema!(LayoutRepr::RecursivePointer(Layout::VOID))
|
||||
20, STR_PTR, pub, nosema!(LayoutRepr::Ptr(Layout::STR))
|
||||
21, LIST_U8, pub, nosema!(LayoutRepr::Builtin(crate::layout::Builtin::List(Layout::U8)))
|
||||
|
||||
; 21
|
||||
; 22
|
||||
}
|
||||
|
||||
macro_rules! impl_to_from_int_width {
|
||||
@ -354,6 +355,8 @@ pub trait LayoutInterner<'a>: Sized {
|
||||
.text("Ptr(")
|
||||
.append(self.to_doc(inner, alloc, seen_rec, parens))
|
||||
.append(")"),
|
||||
FunctionPointer(fp) => fp.to_doc(alloc, self, seen_rec, parens),
|
||||
Erased(e) => e.to_doc(alloc),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1076,7 +1079,9 @@ mod reify {
|
||||
use bumpalo::{collections::Vec, Bump};
|
||||
use roc_module::symbol::Symbol;
|
||||
|
||||
use crate::layout::{Builtin, LambdaSet, Layout, LayoutRepr, LayoutWrapper, UnionLayout};
|
||||
use crate::layout::{
|
||||
Builtin, FunctionPointer, LambdaSet, Layout, LayoutRepr, LayoutWrapper, UnionLayout,
|
||||
};
|
||||
|
||||
use super::{InLayout, LayoutInterner, NeedsRecursionPointerFixup};
|
||||
|
||||
@ -1121,6 +1126,13 @@ mod reify {
|
||||
// another recursive union's layout, do not change it.
|
||||
LayoutRepr::RecursivePointer(if l == Layout::VOID { slot } else { l })
|
||||
}
|
||||
LayoutRepr::FunctionPointer(FunctionPointer { args, ret }) => {
|
||||
LayoutRepr::FunctionPointer(FunctionPointer {
|
||||
args: reify_layout_slice(arena, interner, slot, args),
|
||||
ret: reify_layout(arena, interner, slot, ret),
|
||||
})
|
||||
}
|
||||
LayoutRepr::Erased(e) => LayoutRepr::Erased(e),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1394,7 +1406,7 @@ mod equiv {
|
||||
pub mod dbg_deep {
|
||||
use roc_module::symbol::Symbol;
|
||||
|
||||
use crate::layout::{Builtin, LambdaSet, LayoutRepr, UnionLayout};
|
||||
use crate::layout::{Builtin, Erased, LambdaSet, LayoutRepr, UnionLayout};
|
||||
|
||||
use super::{InLayout, LayoutInterner};
|
||||
|
||||
@ -1447,6 +1459,12 @@ pub mod dbg_deep {
|
||||
LayoutRepr::RecursivePointer(rp) => {
|
||||
f.debug_tuple("RecursivePointer").field(&rp.0).finish()
|
||||
}
|
||||
LayoutRepr::FunctionPointer(fp) => f
|
||||
.debug_struct("FunctionPointer")
|
||||
.field("args", &fp.args)
|
||||
.field("ret", &fp.ret)
|
||||
.finish(),
|
||||
LayoutRepr::Erased(Erased) => f.debug_struct("?Erased").finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1559,7 +1577,7 @@ pub mod dbg_deep {
|
||||
pub mod dbg_stable {
|
||||
use roc_module::symbol::Symbol;
|
||||
|
||||
use crate::layout::{Builtin, LambdaSet, LayoutRepr, SemanticRepr, UnionLayout};
|
||||
use crate::layout::{Builtin, Erased, LambdaSet, LayoutRepr, SemanticRepr, UnionLayout};
|
||||
|
||||
use super::{InLayout, LayoutInterner};
|
||||
|
||||
@ -1620,6 +1638,12 @@ pub mod dbg_stable {
|
||||
LayoutRepr::RecursivePointer(rp) => {
|
||||
f.debug_tuple("RecursivePointer").field(&rp.0).finish()
|
||||
}
|
||||
LayoutRepr::FunctionPointer(fp) => f
|
||||
.debug_struct("FunctionPointer")
|
||||
.field("args", &fp.args)
|
||||
.field("ret", &fp.ret)
|
||||
.finish(),
|
||||
LayoutRepr::Erased(Erased) => f.debug_struct("?Erased").finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -670,6 +670,7 @@ impl<'a> TrmcEnv<'a> {
|
||||
// because we do not allow polymorphic recursion, this is the only constraint
|
||||
name == lambda_name
|
||||
}
|
||||
CallType::ByPointer { .. } => false,
|
||||
CallType::Foreign { .. } | CallType::LowLevel { .. } | CallType::HigherOrder(_) => {
|
||||
false
|
||||
}
|
||||
@ -812,6 +813,7 @@ impl<'a> TrmcEnv<'a> {
|
||||
ret_layout: proc.ret_layout,
|
||||
is_self_recursive: SelfRecursive::NotSelfRecursive,
|
||||
host_exposed_layouts: proc.host_exposed_layouts.clone(),
|
||||
is_erased: proc.is_erased,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1096,7 +1098,7 @@ fn expr_contains_symbol(expr: &Expr, needle: Symbol) -> bool {
|
||||
Some(ru) => ru.symbol == needle || arguments.contains(&needle),
|
||||
},
|
||||
Expr::Struct(fields) => fields.contains(&needle),
|
||||
Expr::NullPointer => false,
|
||||
Expr::NullPointer | Expr::FunctionPointer { .. } => false,
|
||||
Expr::StructAtIndex { structure, .. }
|
||||
| Expr::GetTagId { structure, .. }
|
||||
| Expr::UnionAtIndex { structure, .. }
|
||||
@ -1108,6 +1110,10 @@ fn expr_contains_symbol(expr: &Expr, needle: Symbol) -> bool {
|
||||
Expr::EmptyArray => false,
|
||||
Expr::Reset { symbol, .. } | Expr::ResetRef { symbol, .. } => needle == *symbol,
|
||||
Expr::RuntimeErrorFunction(_) => false,
|
||||
Expr::ErasedMake { value, callee } => {
|
||||
value.map(|v| v == needle).unwrap_or(false) || needle == *callee
|
||||
}
|
||||
Expr::ErasedLoad { symbol, field: _ } => needle == *symbol,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ use roc_types::types::{AliasKind, Category, MemberImpl, PatternCategory, Polarit
|
||||
use roc_unify::unify::{Env as UEnv, MustImplementConstraints};
|
||||
use roc_unify::unify::{MustImplementAbility, Obligated};
|
||||
|
||||
use crate::env::Env;
|
||||
use crate::env::InferenceEnv;
|
||||
use crate::{aliases::Aliases, to_var::type_to_var};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -56,7 +56,7 @@ pub struct PendingDerivesTable(
|
||||
|
||||
impl PendingDerivesTable {
|
||||
pub fn new(
|
||||
env: &mut Env,
|
||||
env: &mut InferenceEnv,
|
||||
types: &mut Types,
|
||||
aliases: &mut Aliases,
|
||||
pending_derives: PendingDerives,
|
||||
@ -827,6 +827,12 @@ trait DerivableVisitor {
|
||||
context: NotDerivableContext::NoContext,
|
||||
})
|
||||
}
|
||||
ErasedLambda => {
|
||||
return Err(NotDerivable {
|
||||
var,
|
||||
context: NotDerivableContext::NoContext,
|
||||
})
|
||||
}
|
||||
Error => {
|
||||
return Err(NotDerivable {
|
||||
var,
|
||||
|
@ -9,7 +9,7 @@ use roc_types::{
|
||||
};
|
||||
|
||||
use crate::to_var::type_to_var_help;
|
||||
use crate::{ability::ObligationCache, env::Env};
|
||||
use crate::{ability::ObligationCache, env::InferenceEnv};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct DelayedAliasVariables {
|
||||
@ -121,7 +121,7 @@ impl Aliases {
|
||||
}
|
||||
|
||||
fn instantiate_result_result(
|
||||
env: &mut Env,
|
||||
env: &mut InferenceEnv,
|
||||
rank: Rank,
|
||||
alias_variables: AliasVariables,
|
||||
) -> Variable {
|
||||
@ -143,7 +143,7 @@ impl Aliases {
|
||||
|
||||
/// Build an alias of the form `Num range := range`
|
||||
fn build_num_opaque(
|
||||
env: &mut Env,
|
||||
env: &mut InferenceEnv,
|
||||
rank: Rank,
|
||||
symbol: Symbol,
|
||||
range_var: Variable,
|
||||
@ -160,7 +160,7 @@ impl Aliases {
|
||||
|
||||
fn instantiate_builtin_aliases_real_var(
|
||||
&mut self,
|
||||
env: &mut Env,
|
||||
env: &mut InferenceEnv,
|
||||
rank: Rank,
|
||||
symbol: Symbol,
|
||||
alias_variables: AliasVariables,
|
||||
@ -228,7 +228,7 @@ impl Aliases {
|
||||
|
||||
pub fn instantiate_real_var(
|
||||
&mut self,
|
||||
env: &mut Env,
|
||||
env: &mut InferenceEnv,
|
||||
rank: Rank,
|
||||
problems: &mut Vec<TypeError>,
|
||||
abilities_store: &AbilitiesStore,
|
||||
|
@ -10,10 +10,15 @@ use roc_types::{
|
||||
types::{RecordField, Uls},
|
||||
};
|
||||
|
||||
use crate::env::Env;
|
||||
use crate::env::SolveEnv;
|
||||
|
||||
// TODO: eventually, we could possibly use the arena in Env instead.
|
||||
pub(crate) fn deep_copy_var_in(env: &mut Env, rank: Rank, var: Variable, arena: &Bump) -> Variable {
|
||||
pub(crate) fn deep_copy_var_in(
|
||||
env: &mut SolveEnv,
|
||||
rank: Rank,
|
||||
var: Variable,
|
||||
arena: &Bump,
|
||||
) -> Variable {
|
||||
let mut visited = bumpalo::collections::Vec::with_capacity_in(256, arena);
|
||||
|
||||
let pool = env.pools.get_mut(rank);
|
||||
@ -257,7 +262,7 @@ fn deep_copy_var_help(
|
||||
subs.set_content_unchecked(copy, Structure(new_flat_type));
|
||||
}
|
||||
|
||||
FlexVar(_) | FlexAbleVar(_, _) | Error => {
|
||||
FlexVar(_) | FlexAbleVar(_, _) | Error | ErasedLambda => {
|
||||
subs.set_content_unchecked(copy, content);
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@ use roc_derive::SharedDerivedModule;
|
||||
use roc_types::subs::{Content, Descriptor, Mark, OptVariable, Rank, Subs, Variable};
|
||||
use roc_unify::unify::Env as UEnv;
|
||||
|
||||
use crate::Pools;
|
||||
use crate::{FunctionKind, Pools};
|
||||
|
||||
pub struct DerivedEnv<'a> {
|
||||
pub derived_module: &'a SharedDerivedModule,
|
||||
@ -12,15 +12,38 @@ pub struct DerivedEnv<'a> {
|
||||
pub exposed_types: &'a ExposedByModule,
|
||||
}
|
||||
|
||||
pub struct Env<'a> {
|
||||
/// Environment necessary for solving and specialization.
|
||||
pub struct SolveEnv<'a> {
|
||||
pub arena: &'a Bump,
|
||||
pub constraints: &'a Constraints,
|
||||
pub derived_env: &'a DerivedEnv<'a>,
|
||||
pub subs: &'a mut Subs,
|
||||
pub pools: &'a mut Pools,
|
||||
}
|
||||
|
||||
impl<'a> Env<'a> {
|
||||
/// Environment necessary for inference.
|
||||
pub struct InferenceEnv<'a> {
|
||||
pub constraints: &'a Constraints,
|
||||
pub function_kind: FunctionKind,
|
||||
pub arena: &'a Bump,
|
||||
pub derived_env: &'a DerivedEnv<'a>,
|
||||
pub subs: &'a mut Subs,
|
||||
pub pools: &'a mut Pools,
|
||||
}
|
||||
|
||||
impl<'a> SolveEnv<'a> {
|
||||
/// Introduce some variables to Pools at the given rank.
|
||||
/// Also, set each of their ranks in Subs to be the given rank.
|
||||
pub fn introduce(&mut self, rank: Rank, vars: &[Variable]) {
|
||||
introduce(self.subs, self.pools, rank, vars);
|
||||
}
|
||||
|
||||
/// Retrieves an environment for unification.
|
||||
pub fn uenv(&mut self) -> UEnv {
|
||||
UEnv::new(self.subs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> InferenceEnv<'a> {
|
||||
#[inline(always)]
|
||||
pub fn register(&mut self, rank: Rank, content: Content) -> Variable {
|
||||
let descriptor = Descriptor {
|
||||
@ -40,13 +63,7 @@ impl<'a> Env<'a> {
|
||||
/// Introduce some variables to Pools at the given rank.
|
||||
/// Also, set each of their ranks in Subs to be the given rank.
|
||||
pub fn introduce(&mut self, rank: Rank, vars: &[Variable]) {
|
||||
let pool: &mut Vec<Variable> = self.pools.get_mut(rank);
|
||||
|
||||
for &var in vars.iter() {
|
||||
self.subs.set_rank(var, rank);
|
||||
}
|
||||
|
||||
pool.extend(vars);
|
||||
introduce(self.subs, self.pools, rank, vars);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@ -78,4 +95,25 @@ impl<'a> Env<'a> {
|
||||
pub fn uenv(&mut self) -> UEnv {
|
||||
UEnv::new(self.subs)
|
||||
}
|
||||
|
||||
pub fn as_solve_env(&mut self) -> SolveEnv {
|
||||
SolveEnv {
|
||||
arena: self.arena,
|
||||
derived_env: self.derived_env,
|
||||
subs: self.subs,
|
||||
pools: self.pools,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Introduce some variables to Pools at the given rank.
|
||||
/// Also, set each of their ranks in Subs to be the given rank.
|
||||
fn introduce(subs: &mut Subs, pools: &mut Pools, rank: Rank, vars: &[Variable]) {
|
||||
let pool: &mut Vec<Variable> = pools.get_mut(rank);
|
||||
|
||||
for &var in vars.iter() {
|
||||
subs.set_rank(var, rank);
|
||||
}
|
||||
|
||||
pool.extend(vars);
|
||||
}
|
||||
|
8
crates/compiler/solve/src/kinds.rs
Normal file
8
crates/compiler/solve/src/kinds.rs
Normal file
@ -0,0 +1,8 @@
|
||||
/// How function kinds should be represented in the type system.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum FunctionKind {
|
||||
/// Function values are solved to lambda sets; lambda sets are the kind.
|
||||
LambdaSet,
|
||||
/// Function values are erased, no kind is introduced.
|
||||
Erased,
|
||||
}
|
@ -14,9 +14,11 @@ pub mod specialize;
|
||||
mod aliases;
|
||||
mod deep_copy;
|
||||
mod env;
|
||||
mod kinds;
|
||||
mod pools;
|
||||
mod to_var;
|
||||
|
||||
pub use aliases::Aliases;
|
||||
pub use env::{DerivedEnv, Env};
|
||||
pub use env::{DerivedEnv, SolveEnv};
|
||||
pub use kinds::FunctionKind;
|
||||
pub use pools::Pools;
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::FunctionKind;
|
||||
use crate::{aliases::Aliases, solve};
|
||||
use roc_can::abilities::{AbilitiesStore, ResolvedImpl};
|
||||
use roc_can::constraint::{Constraint, Constraints};
|
||||
@ -61,6 +62,8 @@ pub struct SolveConfig<'a> {
|
||||
/// All types introduced in the module. Canonicalized, but not necessarily yet associated with
|
||||
/// a variable substitution.
|
||||
pub types: Types,
|
||||
/// How functions should be kinded.
|
||||
pub function_kind: FunctionKind,
|
||||
/// Table of types introduced in this module that claim to derive an ability implementation.
|
||||
/// Due for checking and instantiation after the solver runs over the module.
|
||||
pub pending_derives: PendingDerives,
|
||||
|
@ -3,7 +3,7 @@ use crate::ability::{
|
||||
CheckedDerives, ObligationCache, PendingDerivesTable, Resolved,
|
||||
};
|
||||
use crate::deep_copy::deep_copy_var_in;
|
||||
use crate::env::{DerivedEnv, Env};
|
||||
use crate::env::{DerivedEnv, InferenceEnv};
|
||||
use crate::module::{SolveConfig, Solved};
|
||||
use crate::pools::Pools;
|
||||
use crate::specialize::{
|
||||
@ -122,6 +122,7 @@ fn run_in_place(
|
||||
pending_derives,
|
||||
exposed_by_module,
|
||||
derived_module,
|
||||
function_kind,
|
||||
} = config;
|
||||
|
||||
let mut pools = Pools::default();
|
||||
@ -141,9 +142,10 @@ fn run_in_place(
|
||||
exposed_types: exposed_by_module,
|
||||
};
|
||||
|
||||
let mut env = Env {
|
||||
let mut env = InferenceEnv {
|
||||
arena: &arena,
|
||||
constraints,
|
||||
function_kind,
|
||||
derived_env: &derived_env,
|
||||
subs,
|
||||
pools: &mut pools,
|
||||
@ -218,7 +220,7 @@ enum Work<'a> {
|
||||
}
|
||||
|
||||
fn solve(
|
||||
env: &mut Env,
|
||||
env: &mut InferenceEnv,
|
||||
mut can_types: Types,
|
||||
mut state: State,
|
||||
rank: Rank,
|
||||
@ -576,7 +578,11 @@ fn solve(
|
||||
// then we copy from that module's Subs into our own. If the value
|
||||
// is being looked up in this module, then we use our Subs as both
|
||||
// the source and destination.
|
||||
let actual = deep_copy_var_in(env, rank, var, env.arena);
|
||||
let actual = {
|
||||
let mut solve_env = env.as_solve_env();
|
||||
let solve_env = &mut solve_env;
|
||||
deep_copy_var_in(solve_env, rank, var, solve_env.arena)
|
||||
};
|
||||
let expectation = &env.constraints.expectations[expectation_index.index()];
|
||||
|
||||
let expected = either_type_index_to_var(
|
||||
@ -1390,7 +1396,7 @@ fn chase_alias_content(subs: &Subs, mut var: Variable) -> (Variable, &Content) {
|
||||
}
|
||||
|
||||
fn compact_lambdas_and_check_obligations(
|
||||
env: &mut Env,
|
||||
env: &mut InferenceEnv,
|
||||
problems: &mut Vec<TypeError>,
|
||||
abilities_store: &mut AbilitiesStore,
|
||||
obligation_cache: &mut ObligationCache,
|
||||
@ -1401,7 +1407,7 @@ fn compact_lambdas_and_check_obligations(
|
||||
obligations,
|
||||
awaiting_specialization: new_awaiting,
|
||||
} = compact_lambda_sets_of_vars(
|
||||
env,
|
||||
&mut env.as_solve_env(),
|
||||
lambda_sets_to_specialize,
|
||||
&SolvePhase { abilities_store },
|
||||
);
|
||||
@ -1414,7 +1420,7 @@ fn compact_lambdas_and_check_obligations(
|
||||
awaiting_specialization.union(new_awaiting);
|
||||
}
|
||||
|
||||
fn open_tag_union(env: &mut Env, var: Variable) {
|
||||
fn open_tag_union(env: &mut InferenceEnv, var: Variable) {
|
||||
let mut stack = vec![var];
|
||||
while let Some(var) = stack.pop() {
|
||||
use {Content::*, FlatType::*};
|
||||
@ -1546,7 +1552,7 @@ fn close_pattern_matched_tag_unions(subs: &mut Subs, var: Variable) {
|
||||
// Aggressive but necessary - there aren't many usages.
|
||||
#[inline(always)]
|
||||
fn check_ability_specialization(
|
||||
env: &mut Env,
|
||||
env: &mut InferenceEnv,
|
||||
rank: Rank,
|
||||
abilities_store: &mut AbilitiesStore,
|
||||
obligation_cache: &mut ObligationCache,
|
||||
@ -1570,8 +1576,16 @@ fn check_ability_specialization(
|
||||
|
||||
// We need to freshly instantiate the root signature so that all unifications are reflected
|
||||
// in the specialization type, but not the original signature type.
|
||||
let root_signature_var =
|
||||
deep_copy_var_in(env, Rank::toplevel(), root_signature_var, env.arena);
|
||||
let root_signature_var = {
|
||||
let mut solve_env = env.as_solve_env();
|
||||
let solve_env = &mut solve_env;
|
||||
deep_copy_var_in(
|
||||
solve_env,
|
||||
Rank::toplevel(),
|
||||
root_signature_var,
|
||||
solve_env.arena,
|
||||
)
|
||||
};
|
||||
let snapshot = env.subs.snapshot();
|
||||
let unified = unify_introduced_ability_specialization(
|
||||
&mut env.uenv(),
|
||||
@ -1777,7 +1791,7 @@ impl<T> LocalDefVarsVec<T> {
|
||||
|
||||
impl LocalDefVarsVec<(Symbol, Loc<Variable>)> {
|
||||
fn from_def_types(
|
||||
env: &mut Env,
|
||||
env: &mut InferenceEnv,
|
||||
rank: Rank,
|
||||
problems: &mut Vec<TypeError>,
|
||||
abilities_store: &mut AbilitiesStore,
|
||||
@ -1811,7 +1825,7 @@ impl LocalDefVarsVec<(Symbol, Loc<Variable>)> {
|
||||
}
|
||||
|
||||
fn check_for_infinite_type(
|
||||
env: &mut Env,
|
||||
env: &mut InferenceEnv,
|
||||
problems: &mut Vec<TypeError>,
|
||||
symbol: Symbol,
|
||||
loc_var: Loc<Variable>,
|
||||
@ -1874,7 +1888,7 @@ fn circular_error(
|
||||
/// Ensures that variables introduced at the `young_rank`, but that should be
|
||||
/// stuck at a lower level, are marked at that level and not generalized at the
|
||||
/// present `young_rank`. See [adjust_rank].
|
||||
fn generalize(env: &mut Env, young_mark: Mark, visit_mark: Mark, young_rank: Rank) {
|
||||
fn generalize(env: &mut InferenceEnv, young_mark: Mark, visit_mark: Mark, young_rank: Rank) {
|
||||
let subs = &mut env.subs;
|
||||
let pools = &mut env.pools;
|
||||
|
||||
@ -2302,6 +2316,8 @@ fn adjust_rank_content(
|
||||
rank
|
||||
}
|
||||
|
||||
ErasedLambda => group_rank,
|
||||
|
||||
RangedNumber(_) => group_rank,
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ use roc_unify::unify::{unify, Mode, MustImplementConstraints};
|
||||
use crate::{
|
||||
ability::builtin_module_with_unlisted_ability_impl,
|
||||
deep_copy::deep_copy_var_in,
|
||||
env::{DerivedEnv, Env},
|
||||
env::{DerivedEnv, SolveEnv},
|
||||
};
|
||||
|
||||
/// What phase in the compiler is reaching out to specialize lambda sets?
|
||||
@ -295,7 +295,7 @@ fn unique_unspecialized_lambda(subs: &Subs, c_a: Variable, uls: &[Uls]) -> Optio
|
||||
|
||||
#[must_use]
|
||||
pub fn compact_lambda_sets_of_vars<P: Phase>(
|
||||
env: &mut Env,
|
||||
env: &mut SolveEnv,
|
||||
uls_of_var: UlsOfVar,
|
||||
phase: &P,
|
||||
) -> CompactionResult {
|
||||
@ -464,7 +464,7 @@ enum OneCompactionResult {
|
||||
#[must_use]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn compact_lambda_set<P: Phase>(
|
||||
env: &mut Env,
|
||||
env: &mut SolveEnv,
|
||||
resolved_concrete: Variable,
|
||||
this_lambda_set: Variable,
|
||||
phase: &P,
|
||||
@ -690,6 +690,7 @@ fn make_specialization_decision<P: Phase>(
|
||||
| FlexVar(..)
|
||||
| RigidVar(..)
|
||||
| LambdaSet(..)
|
||||
| ErasedLambda
|
||||
| RangedNumber(..) => {
|
||||
internal_error!("unexpected")
|
||||
}
|
||||
|
@ -22,8 +22,8 @@ use roc_unify::unify::{unify, Mode, Unified};
|
||||
use crate::{
|
||||
ability::{AbilityImplError, ObligationCache},
|
||||
deep_copy::deep_copy_var_in,
|
||||
env::Env,
|
||||
Aliases,
|
||||
env::InferenceEnv,
|
||||
Aliases, FunctionKind,
|
||||
};
|
||||
|
||||
std::thread_local! {
|
||||
@ -42,7 +42,7 @@ fn put_scratchpad(scratchpad: bumpalo::Bump) {
|
||||
}
|
||||
|
||||
pub(crate) fn either_type_index_to_var(
|
||||
env: &mut Env,
|
||||
env: &mut InferenceEnv,
|
||||
rank: Rank,
|
||||
problems: &mut Vec<TypeError>,
|
||||
abilities_store: &mut AbilitiesStore,
|
||||
@ -70,7 +70,8 @@ pub(crate) fn either_type_index_to_var(
|
||||
|| matches!(
|
||||
types[type_index],
|
||||
TypeTag::EmptyRecord | TypeTag::EmptyTagUnion
|
||||
)
|
||||
),
|
||||
"different variable was returned for type index variable cell!"
|
||||
);
|
||||
var
|
||||
}
|
||||
@ -82,7 +83,7 @@ pub(crate) fn either_type_index_to_var(
|
||||
}
|
||||
|
||||
pub(crate) fn type_to_var(
|
||||
env: &mut Env,
|
||||
env: &mut InferenceEnv,
|
||||
rank: Rank,
|
||||
problems: &mut Vec<TypeError>,
|
||||
abilities_store: &mut AbilitiesStore,
|
||||
@ -126,7 +127,7 @@ enum RegisterVariable {
|
||||
|
||||
impl RegisterVariable {
|
||||
fn from_type(
|
||||
env: &mut Env,
|
||||
env: &mut InferenceEnv,
|
||||
rank: Rank,
|
||||
arena: &'_ bumpalo::Bump,
|
||||
types: &mut Types,
|
||||
@ -149,7 +150,7 @@ impl RegisterVariable {
|
||||
reserved
|
||||
} else {
|
||||
// for any other rank, we need to copy; it takes care of adjusting the rank
|
||||
deep_copy_var_in(env, rank, reserved, arena)
|
||||
deep_copy_var_in(&mut env.as_solve_env(), rank, reserved, arena)
|
||||
};
|
||||
// Safety: the `destination` will become the source-of-truth for the type index, since it
|
||||
// was not already transformed before (if it was, we'd be in the Variable branch!)
|
||||
@ -165,7 +166,7 @@ impl RegisterVariable {
|
||||
|
||||
#[inline(always)]
|
||||
fn with_stack(
|
||||
env: &mut Env,
|
||||
env: &mut InferenceEnv,
|
||||
rank: Rank,
|
||||
arena: &'_ bumpalo::Bump,
|
||||
types: &mut Types,
|
||||
@ -253,7 +254,7 @@ enum TypeToVar {
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn type_to_var_help(
|
||||
env: &mut Env,
|
||||
env: &mut InferenceEnv,
|
||||
rank: Rank,
|
||||
problems: &mut Vec<TypeError>,
|
||||
abilities_store: &AbilitiesStore,
|
||||
@ -346,20 +347,29 @@ pub(crate) fn type_to_var_help(
|
||||
name,
|
||||
ambient_function,
|
||||
} => {
|
||||
let captures = types.get_type_arguments(typ_index);
|
||||
let union_lambdas =
|
||||
create_union_lambda(env, rank, arena, types, name, captures, &mut stack);
|
||||
match env.function_kind {
|
||||
FunctionKind::LambdaSet => {
|
||||
let captures = types.get_type_arguments(typ_index);
|
||||
let union_lambdas = create_union_lambda(
|
||||
env, rank, arena, types, name, captures, &mut stack,
|
||||
);
|
||||
|
||||
let content = Content::LambdaSet(subs::LambdaSet {
|
||||
solved: union_lambdas,
|
||||
// We may figure out the lambda set is recursive during solving, but it never
|
||||
// is to begin with.
|
||||
recursion_var: OptVariable::NONE,
|
||||
unspecialized: SubsSlice::default(),
|
||||
ambient_function,
|
||||
});
|
||||
let content = Content::LambdaSet(subs::LambdaSet {
|
||||
solved: union_lambdas,
|
||||
// We may figure out the lambda set is recursive during solving, but it never
|
||||
// is to begin with.
|
||||
recursion_var: OptVariable::NONE,
|
||||
unspecialized: SubsSlice::default(),
|
||||
ambient_function,
|
||||
});
|
||||
|
||||
env.register_with_known_var(destination, rank, content)
|
||||
env.register_with_known_var(destination, rank, content)
|
||||
}
|
||||
FunctionKind::Erased => {
|
||||
// TODO(erased-lambda): can we merge in with Variable::ERASED_LAMBDA instead?
|
||||
env.register_with_known_var(destination, rank, Content::ErasedLambda)
|
||||
}
|
||||
}
|
||||
}
|
||||
UnspecializedLambdaSet { unspecialized } => {
|
||||
let unspecialized_slice = SubsSlice::extend_new(
|
||||
@ -911,7 +921,7 @@ pub(crate) fn type_to_var_help(
|
||||
|
||||
#[inline(always)]
|
||||
fn roc_result_to_var(
|
||||
env: &mut Env,
|
||||
env: &mut InferenceEnv,
|
||||
rank: Rank,
|
||||
arena: &'_ bumpalo::Bump,
|
||||
types: &mut Types,
|
||||
@ -1090,7 +1100,7 @@ fn find_tag_name_run(slice: &[TagName], subs: &mut Subs) -> Option<SubsSlice<Tag
|
||||
|
||||
#[inline(always)]
|
||||
fn register_tag_arguments(
|
||||
env: &mut Env,
|
||||
env: &mut InferenceEnv,
|
||||
rank: Rank,
|
||||
arena: &'_ bumpalo::Bump,
|
||||
types: &mut Types,
|
||||
@ -1114,7 +1124,7 @@ fn register_tag_arguments(
|
||||
|
||||
/// Assumes that the tags are sorted and there are no duplicates!
|
||||
fn insert_tags_fast_path(
|
||||
env: &mut Env,
|
||||
env: &mut InferenceEnv,
|
||||
rank: Rank,
|
||||
arena: &'_ bumpalo::Bump,
|
||||
types: &mut Types,
|
||||
@ -1185,7 +1195,7 @@ fn insert_tags_fast_path(
|
||||
}
|
||||
|
||||
fn insert_tags_slow_path(
|
||||
env: &mut Env,
|
||||
env: &mut InferenceEnv,
|
||||
rank: Rank,
|
||||
arena: &'_ bumpalo::Bump,
|
||||
types: &mut Types,
|
||||
@ -1215,7 +1225,7 @@ fn insert_tags_slow_path(
|
||||
}
|
||||
|
||||
fn type_to_union_tags(
|
||||
env: &mut Env,
|
||||
env: &mut InferenceEnv,
|
||||
rank: Rank,
|
||||
arena: &'_ bumpalo::Bump,
|
||||
types: &mut Types,
|
||||
@ -1274,7 +1284,7 @@ fn type_to_union_tags(
|
||||
}
|
||||
|
||||
fn create_union_lambda(
|
||||
env: &mut Env,
|
||||
env: &mut InferenceEnv,
|
||||
rank: Rank,
|
||||
arena: &'_ bumpalo::Bump,
|
||||
types: &mut Types,
|
||||
|
@ -8,6 +8,7 @@ extern crate bumpalo;
|
||||
#[cfg(test)]
|
||||
mod solve_expr {
|
||||
use roc_load::LoadedModule;
|
||||
use roc_solve::FunctionKind;
|
||||
use test_solve_helpers::{format_problems, run_load_and_infer};
|
||||
|
||||
use roc_types::pretty_print::{name_and_print_var, DebugPrint};
|
||||
@ -27,7 +28,7 @@ mod solve_expr {
|
||||
..
|
||||
},
|
||||
src,
|
||||
) = run_load_and_infer(src, [], false)?;
|
||||
) = run_load_and_infer(src, [], false, FunctionKind::LambdaSet)?;
|
||||
|
||||
let mut can_problems = can_problems.remove(&home).unwrap_or_default();
|
||||
let type_problems = type_problems.remove(&home).unwrap_or_default();
|
||||
|
@ -3,7 +3,10 @@ use std::path::PathBuf;
|
||||
|
||||
use bumpalo::Bump;
|
||||
use roc_packaging::cache::RocCacheDir;
|
||||
use roc_solve::module::{SolveConfig, SolveOutput};
|
||||
use roc_solve::{
|
||||
module::{SolveConfig, SolveOutput},
|
||||
FunctionKind,
|
||||
};
|
||||
use ven_pretty::DocAllocator;
|
||||
|
||||
use roc_can::{
|
||||
@ -426,6 +429,7 @@ fn check_derived_typechecks_and_golden(
|
||||
constraints: &constraints,
|
||||
root_constraint: constr,
|
||||
types,
|
||||
function_kind: FunctionKind::LambdaSet,
|
||||
pending_derives: Default::default(),
|
||||
exposed_by_module: &exposed_for_module.exposed_by_module,
|
||||
derived_module: Default::default(),
|
||||
@ -520,6 +524,7 @@ where
|
||||
path.parent().unwrap().to_path_buf(),
|
||||
Default::default(),
|
||||
target_info,
|
||||
FunctionKind::LambdaSet,
|
||||
roc_reporting::report::RenderTarget::ColorTerminal,
|
||||
roc_reporting::report::DEFAULT_PALETTE,
|
||||
RocCacheDir::Disallowed,
|
||||
|
44
crates/compiler/test_gen/src/gen_erased.rs
Normal file
44
crates/compiler/test_gen/src/gen_erased.rs
Normal file
@ -0,0 +1,44 @@
|
||||
#[cfg(feature = "gen-llvm")]
|
||||
use crate::helpers::llvm::assert_evals_to_erased;
|
||||
|
||||
use indoc::indoc;
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn capture_multiple() {
|
||||
assert_evals_to_erased!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
f = \n, m ->
|
||||
\{} -> n + m + 15u8
|
||||
|
||||
main = (f 10u8 20u8) {}
|
||||
"#
|
||||
),
|
||||
45,
|
||||
u8
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn multi_branch_capturing() {
|
||||
assert_evals_to_erased!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
f = \t, s ->
|
||||
if t
|
||||
then \{} -> 15nat
|
||||
else \{} -> Str.countGraphemes s
|
||||
|
||||
main = ((f Bool.true "abc") {}, (f Bool.false "abc") {})
|
||||
"#
|
||||
),
|
||||
(15, 3),
|
||||
(usize, usize)
|
||||
);
|
||||
}
|
@ -5,6 +5,7 @@ use roc_load::{EntryPoint, ExecutionMode, LoadConfig, Threading};
|
||||
use roc_mono::ir::SingleEntryPoint;
|
||||
use roc_packaging::cache::RocCacheDir;
|
||||
use roc_region::all::LineInfo;
|
||||
use roc_solve::FunctionKind;
|
||||
use tempfile::tempdir;
|
||||
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
@ -58,6 +59,7 @@ pub fn helper(
|
||||
palette: roc_reporting::report::DEFAULT_PALETTE,
|
||||
threading: Threading::Single,
|
||||
exec_mode: ExecutionMode::Executable,
|
||||
function_kind: FunctionKind::LambdaSet,
|
||||
};
|
||||
let loaded = roc_load::load_and_monomorphize_from_str(
|
||||
arena,
|
||||
|
@ -8,7 +8,9 @@ use roc_collections::all::MutSet;
|
||||
use roc_command_utils::zig;
|
||||
use roc_gen_llvm::llvm::externs::add_default_roc_externs;
|
||||
use roc_gen_llvm::{llvm::build::LlvmBackendMode, run_roc::RocCallResult};
|
||||
use roc_load::{EntryPoint, ExecutionMode, LoadConfig, LoadMonomorphizedError, Threading};
|
||||
use roc_load::{
|
||||
EntryPoint, ExecutionMode, FunctionKind, LoadConfig, LoadMonomorphizedError, Threading,
|
||||
};
|
||||
use roc_mono::ir::{CrashTag, OptLevel, SingleEntryPoint};
|
||||
use roc_packaging::cache::RocCacheDir;
|
||||
use roc_region::all::LineInfo;
|
||||
@ -44,13 +46,13 @@ fn promote_expr_to_module(src: &str) -> String {
|
||||
buffer
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn create_llvm_module<'a>(
|
||||
arena: &'a bumpalo::Bump,
|
||||
src: &str,
|
||||
config: HelperConfig,
|
||||
context: &'a inkwell::context::Context,
|
||||
target: &Triple,
|
||||
function_kind: FunctionKind,
|
||||
) -> (&'static str, String, &'a Module<'a>) {
|
||||
let target_info = roc_target::TargetInfo::from(target);
|
||||
|
||||
@ -70,6 +72,7 @@ fn create_llvm_module<'a>(
|
||||
|
||||
let load_config = LoadConfig {
|
||||
target_info,
|
||||
function_kind,
|
||||
render: RenderTarget::ColorTerminal,
|
||||
palette: DEFAULT_PALETTE,
|
||||
threading: Threading::Single,
|
||||
@ -330,11 +333,12 @@ pub fn helper<'a>(
|
||||
config: HelperConfig,
|
||||
src: &str,
|
||||
context: &'a inkwell::context::Context,
|
||||
function_kind: FunctionKind,
|
||||
) -> (&'static str, String, Library) {
|
||||
let target = target_lexicon::Triple::host();
|
||||
|
||||
let (main_fn_name, delayed_errors, module) =
|
||||
create_llvm_module(arena, src, config, context, &target);
|
||||
create_llvm_module(arena, src, config, context, &target, function_kind);
|
||||
|
||||
let res_lib = if config.add_debug_info {
|
||||
let module = annotate_with_debug_info(module, context);
|
||||
@ -420,11 +424,12 @@ fn compile_to_wasm_bytes<'a>(
|
||||
config: HelperConfig,
|
||||
src: &str,
|
||||
context: &'a inkwell::context::Context,
|
||||
function_kind: FunctionKind,
|
||||
) -> Vec<u8> {
|
||||
let target = wasm32_target_tripple();
|
||||
|
||||
let (_main_fn_name, _delayed_errors, llvm_module) =
|
||||
create_llvm_module(arena, src, config, context, &target);
|
||||
create_llvm_module(arena, src, config, context, &target, function_kind);
|
||||
|
||||
let content_hash = crate::helpers::src_hash(src);
|
||||
let wasm_file = llvm_module_to_wasm_file(&TEMP_DIR, content_hash, llvm_module);
|
||||
@ -512,7 +517,11 @@ fn fake_wasm_main_function(_: u32, _: u32) -> u32 {
|
||||
}
|
||||
|
||||
#[cfg(feature = "gen-llvm-wasm")]
|
||||
pub fn assert_wasm_evals_to_help<T>(src: &str, ignore_problems: bool) -> Result<T, String>
|
||||
pub fn assert_wasm_evals_to_help<T>(
|
||||
src: &str,
|
||||
ignore_problems: bool,
|
||||
function_kind: FunctionKind,
|
||||
) -> Result<T, String>
|
||||
where
|
||||
T: FromWasm32Memory + Wasm32Result,
|
||||
{
|
||||
@ -526,15 +535,19 @@ where
|
||||
opt_level: OPT_LEVEL,
|
||||
};
|
||||
|
||||
let wasm_bytes = compile_to_wasm_bytes(&arena, config, src, &context);
|
||||
let wasm_bytes = compile_to_wasm_bytes(&arena, config, src, &context, function_kind);
|
||||
|
||||
crate::helpers::wasm::run_wasm_test_bytes::<T>(TEST_WRAPPER_NAME, wasm_bytes)
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
#[cfg(feature = "gen-llvm-wasm")]
|
||||
macro_rules! assert_wasm_evals_to {
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr, $ignore_problems:expr) => {
|
||||
match $crate::helpers::llvm::assert_wasm_evals_to_help::<$ty>($src, $ignore_problems) {
|
||||
match $crate::helpers::llvm::assert_wasm_evals_to_help::<$ty>(
|
||||
$src,
|
||||
$ignore_problems,
|
||||
roc_load::FunctionKind::LambdaSet,
|
||||
) {
|
||||
Err(msg) => panic!("Wasm test failed: {}", msg),
|
||||
Ok(actual) => {
|
||||
assert_eq!($transform(actual), $expected, "Wasm test failed")
|
||||
@ -577,9 +590,13 @@ pub fn try_run_lib_function<T>(
|
||||
}
|
||||
|
||||
// only used in tests
|
||||
#[allow(unused)]
|
||||
pub(crate) fn llvm_evals_to<T, U, F>(src: &str, expected: U, transform: F, ignore_problems: bool)
|
||||
where
|
||||
pub(crate) fn llvm_evals_to<T, U, F>(
|
||||
src: &str,
|
||||
expected: U,
|
||||
transform: F,
|
||||
ignore_problems: bool,
|
||||
function_kind: FunctionKind,
|
||||
) where
|
||||
U: PartialEq + std::fmt::Debug,
|
||||
F: FnOnce(T) -> U,
|
||||
{
|
||||
@ -596,7 +613,8 @@ where
|
||||
opt_level: crate::helpers::llvm::OPT_LEVEL,
|
||||
};
|
||||
|
||||
let (main_fn_name, errors, lib) = crate::helpers::llvm::helper(&arena, config, src, &context);
|
||||
let (main_fn_name, errors, lib) =
|
||||
crate::helpers::llvm::helper(&arena, config, src, &context, function_kind);
|
||||
|
||||
let result = crate::helpers::llvm::try_run_lib_function::<T>(main_fn_name, &lib);
|
||||
|
||||
@ -620,7 +638,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! assert_llvm_evals_to {
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr, $ignore_problems:expr) => {
|
||||
crate::helpers::llvm::llvm_evals_to::<$ty, _, _>(
|
||||
@ -628,6 +645,7 @@ macro_rules! assert_llvm_evals_to {
|
||||
$expected,
|
||||
$transform,
|
||||
$ignore_problems,
|
||||
roc_load::FunctionKind::LambdaSet,
|
||||
);
|
||||
};
|
||||
|
||||
@ -653,8 +671,6 @@ macro_rules! assert_llvm_evals_to {
|
||||
//
|
||||
// let (_main_fn_name, _delayed_errors, _module) =
|
||||
// $crate::helpers::llvm::create_llvm_module(&arena, $src, config, &context, &target);
|
||||
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! assert_evals_to {
|
||||
($src:expr, $expected:expr, $ty:ty) => {{
|
||||
assert_evals_to!($src, $expected, $ty, $crate::helpers::llvm::identity, false);
|
||||
@ -685,14 +701,24 @@ macro_rules! assert_evals_to {
|
||||
}};
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
macro_rules! assert_evals_to_erased {
|
||||
($src:expr, $expected:expr, $ty:ty) => {{
|
||||
crate::helpers::llvm::llvm_evals_to::<$ty, _, _>(
|
||||
$src,
|
||||
$expected,
|
||||
$crate::helpers::llvm::identity,
|
||||
false,
|
||||
roc_load::FunctionKind::Erased,
|
||||
);
|
||||
}};
|
||||
}
|
||||
|
||||
pub fn identity<T>(value: T) -> T {
|
||||
value
|
||||
}
|
||||
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use assert_evals_to;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use assert_evals_to_erased;
|
||||
pub(crate) use assert_llvm_evals_to;
|
||||
#[allow(unused_imports)]
|
||||
#[cfg(feature = "gen-llvm-wasm")]
|
||||
pub(crate) use assert_wasm_evals_to;
|
||||
|
@ -7,6 +7,7 @@ use roc_gen_wasm::DEBUG_SETTINGS;
|
||||
use roc_load::{ExecutionMode, LoadConfig, Threading};
|
||||
use roc_packaging::cache::RocCacheDir;
|
||||
use roc_reporting::report::DEFAULT_PALETTE_HTML;
|
||||
use roc_solve::FunctionKind;
|
||||
use roc_std::RocStr;
|
||||
use roc_wasm_interp::{wasi, ImportDispatcher, Instance, WasiDispatcher};
|
||||
use roc_wasm_module::{Export, ExportType, Value, WasmModule};
|
||||
@ -92,6 +93,7 @@ fn compile_roc_to_wasm_bytes<'a, T: Wasm32Result>(
|
||||
palette: DEFAULT_PALETTE_HTML,
|
||||
threading: Threading::Single,
|
||||
exec_mode: ExecutionMode::Executable,
|
||||
function_kind: FunctionKind::LambdaSet,
|
||||
};
|
||||
let loaded = roc_load::load_and_monomorphize_from_str(
|
||||
arena,
|
||||
|
@ -1,4 +0,0 @@
|
||||
//! Contains all of Roc's [code generation](https://en.wikipedia.org/wiki/Code_generation_(compiler))
|
||||
//! tests.
|
||||
#[cfg(test)]
|
||||
pub mod helpers;
|
@ -8,6 +8,7 @@ pub mod gen_abilities;
|
||||
pub mod gen_compare;
|
||||
pub mod gen_definitions;
|
||||
pub mod gen_dict;
|
||||
pub mod gen_erased;
|
||||
pub mod gen_list;
|
||||
pub mod gen_num;
|
||||
pub mod gen_panic;
|
||||
|
@ -117,6 +117,7 @@ fn build_app_mono<'a>(
|
||||
ret_layout: int_layout,
|
||||
is_self_recursive: SelfRecursive::NotSelfRecursive,
|
||||
host_exposed_layouts: HostExposedLayouts::NotHostExposed,
|
||||
is_erased: false,
|
||||
};
|
||||
|
||||
let proc_layout = ProcLayout {
|
||||
|
@ -14,7 +14,7 @@ procedure List.66 (#Attr.2, #Attr.3):
|
||||
let List.537 : [] = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.537;
|
||||
|
||||
procedure List.80 (#Derived_gen.6, #Derived_gen.7, #Derived_gen.8, #Derived_gen.9, #Derived_gen.10):
|
||||
procedure List.80 (#Derived_gen.13, #Derived_gen.14, #Derived_gen.15, #Derived_gen.16, #Derived_gen.17):
|
||||
joinpoint List.527 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.529 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.529 then
|
||||
@ -27,7 +27,7 @@ procedure List.80 (#Derived_gen.6, #Derived_gen.7, #Derived_gen.8, #Derived_gen.
|
||||
dec List.439;
|
||||
ret List.440;
|
||||
in
|
||||
jump List.527 #Derived_gen.6 #Derived_gen.7 #Derived_gen.8 #Derived_gen.9 #Derived_gen.10;
|
||||
jump List.527 #Derived_gen.13 #Derived_gen.14 #Derived_gen.15 #Derived_gen.16 #Derived_gen.17;
|
||||
|
||||
procedure List.93 (List.436, List.437, List.438):
|
||||
let List.525 : U64 = 0i64;
|
||||
@ -45,8 +45,8 @@ procedure Num.22 (#Attr.2, #Attr.3):
|
||||
|
||||
procedure Test.10 (Test.66, #Attr.12):
|
||||
let Test.9 : {} = UnionAtIndex (Id 0) (Index 0) #Attr.12;
|
||||
let #Derived_gen.20 : Int1 = lowlevel RefCountIsUnique #Attr.12;
|
||||
if #Derived_gen.20 then
|
||||
let #Derived_gen.18 : Int1 = lowlevel RefCountIsUnique #Attr.12;
|
||||
if #Derived_gen.18 then
|
||||
free #Attr.12;
|
||||
ret Test.9;
|
||||
else
|
||||
@ -60,7 +60,7 @@ procedure Test.10 (Test.66, #Attr.12):
|
||||
procedure Test.14 (Test.45, #Attr.12):
|
||||
let Test.13 : {{}, []} = UnionAtIndex (Id 1) (Index 1) #Attr.12;
|
||||
let Test.12 : [<r>C {}, C *self {{}, []}] = UnionAtIndex (Id 1) (Index 0) #Attr.12;
|
||||
joinpoint #Derived_gen.18:
|
||||
joinpoint #Derived_gen.19:
|
||||
let Test.50 : {} = Struct {};
|
||||
let Test.51 : U8 = GetTagId Test.12;
|
||||
joinpoint Test.52 Test.15:
|
||||
@ -87,14 +87,14 @@ procedure Test.14 (Test.45, #Attr.12):
|
||||
jump Test.52 Test.53;
|
||||
|
||||
in
|
||||
let #Derived_gen.19 : Int1 = lowlevel RefCountIsUnique #Attr.12;
|
||||
if #Derived_gen.19 then
|
||||
let #Derived_gen.20 : Int1 = lowlevel RefCountIsUnique #Attr.12;
|
||||
if #Derived_gen.20 then
|
||||
free #Attr.12;
|
||||
jump #Derived_gen.18;
|
||||
jump #Derived_gen.19;
|
||||
else
|
||||
inc Test.12;
|
||||
decref #Attr.12;
|
||||
jump #Derived_gen.18;
|
||||
jump #Derived_gen.19;
|
||||
|
||||
procedure Test.20 (Test.21, Test.18):
|
||||
let Test.23 : [C {}, C []] = CallByName Test.32 Test.21 Test.18;
|
||||
|
@ -18,7 +18,7 @@ procedure List.66 (#Attr.2, #Attr.3):
|
||||
let List.537 : Int1 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.537;
|
||||
|
||||
procedure List.80 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4):
|
||||
procedure List.80 (#Derived_gen.3, #Derived_gen.4, #Derived_gen.5, #Derived_gen.6, #Derived_gen.7):
|
||||
joinpoint List.527 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.529 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.529 then
|
||||
@ -31,7 +31,7 @@ procedure List.80 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.
|
||||
dec List.439;
|
||||
ret List.440;
|
||||
in
|
||||
jump List.527 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4;
|
||||
jump List.527 #Derived_gen.3 #Derived_gen.4 #Derived_gen.5 #Derived_gen.6 #Derived_gen.7;
|
||||
|
||||
procedure List.93 (List.436, List.437, List.438):
|
||||
let List.525 : U64 = 0i64;
|
||||
@ -54,7 +54,7 @@ procedure Str.3 (#Attr.2, #Attr.3):
|
||||
procedure Test.1 (Test.5):
|
||||
ret Test.5;
|
||||
|
||||
procedure Test.11 (#Derived_gen.7, #Derived_gen.8):
|
||||
procedure Test.11 (#Derived_gen.10, #Derived_gen.11):
|
||||
joinpoint Test.27 Test.12 #Attr.12:
|
||||
let Test.8 : Int1 = UnionAtIndex (Id 2) (Index 1) #Attr.12;
|
||||
let Test.7 : [<rnw><null>, C *self Int1, C *self Int1] = UnionAtIndex (Id 2) (Index 0) #Attr.12;
|
||||
@ -94,7 +94,7 @@ procedure Test.11 (#Derived_gen.7, #Derived_gen.8):
|
||||
decref #Attr.12;
|
||||
jump #Derived_gen.14;
|
||||
in
|
||||
jump Test.27 #Derived_gen.7 #Derived_gen.8;
|
||||
jump Test.27 #Derived_gen.10 #Derived_gen.11;
|
||||
|
||||
procedure Test.2 (Test.13):
|
||||
ret Test.13;
|
||||
|
@ -51,20 +51,6 @@ procedure List.71 (#Attr.2, #Attr.3):
|
||||
ret List.541;
|
||||
|
||||
procedure List.83 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2):
|
||||
joinpoint List.535 List.123 List.124 List.125:
|
||||
let List.543 : U64 = 0i64;
|
||||
let List.537 : Int1 = CallByName Num.24 List.124 List.543;
|
||||
if List.537 then
|
||||
let List.542 : U64 = 1i64;
|
||||
let List.539 : U64 = CallByName Num.20 List.124 List.542;
|
||||
let List.540 : List U64 = CallByName List.71 List.125 List.123;
|
||||
jump List.535 List.123 List.539 List.540;
|
||||
else
|
||||
ret List.125;
|
||||
in
|
||||
jump List.535 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2;
|
||||
|
||||
procedure List.83 (#Derived_gen.3, #Derived_gen.4, #Derived_gen.5):
|
||||
joinpoint List.523 List.123 List.124 List.125:
|
||||
let List.531 : U64 = 0i64;
|
||||
let List.525 : Int1 = CallByName Num.24 List.124 List.531;
|
||||
@ -76,7 +62,21 @@ procedure List.83 (#Derived_gen.3, #Derived_gen.4, #Derived_gen.5):
|
||||
else
|
||||
ret List.125;
|
||||
in
|
||||
jump List.523 #Derived_gen.3 #Derived_gen.4 #Derived_gen.5;
|
||||
jump List.523 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2;
|
||||
|
||||
procedure List.83 (#Derived_gen.3, #Derived_gen.4, #Derived_gen.5):
|
||||
joinpoint List.535 List.123 List.124 List.125:
|
||||
let List.543 : U64 = 0i64;
|
||||
let List.537 : Int1 = CallByName Num.24 List.124 List.543;
|
||||
if List.537 then
|
||||
let List.542 : U64 = 1i64;
|
||||
let List.539 : U64 = CallByName Num.20 List.124 List.542;
|
||||
let List.540 : List U64 = CallByName List.71 List.125 List.123;
|
||||
jump List.535 List.123 List.539 List.540;
|
||||
else
|
||||
ret List.125;
|
||||
in
|
||||
jump List.535 #Derived_gen.3 #Derived_gen.4 #Derived_gen.5;
|
||||
|
||||
procedure Num.20 (#Attr.2, #Attr.3):
|
||||
let Num.293 : U64 = lowlevel NumSub #Attr.2 #Attr.3;
|
||||
|
@ -235,7 +235,7 @@ procedure List.8 (#Attr.2, #Attr.3):
|
||||
let List.668 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
|
||||
ret List.668;
|
||||
|
||||
procedure List.80 (#Derived_gen.23, #Derived_gen.24, #Derived_gen.25, #Derived_gen.26, #Derived_gen.27):
|
||||
procedure List.80 (#Derived_gen.30, #Derived_gen.31, #Derived_gen.32, #Derived_gen.33, #Derived_gen.34):
|
||||
joinpoint List.715 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.717 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.717 then
|
||||
@ -259,9 +259,9 @@ procedure List.80 (#Derived_gen.23, #Derived_gen.24, #Derived_gen.25, #Derived_g
|
||||
let List.716 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) List.440;
|
||||
ret List.716;
|
||||
in
|
||||
jump List.715 #Derived_gen.23 #Derived_gen.24 #Derived_gen.25 #Derived_gen.26 #Derived_gen.27;
|
||||
jump List.715 #Derived_gen.30 #Derived_gen.31 #Derived_gen.32 #Derived_gen.33 #Derived_gen.34;
|
||||
|
||||
procedure List.80 (#Derived_gen.31, #Derived_gen.32, #Derived_gen.33, #Derived_gen.34, #Derived_gen.35):
|
||||
procedure List.80 (#Derived_gen.36, #Derived_gen.37, #Derived_gen.38, #Derived_gen.39, #Derived_gen.40):
|
||||
joinpoint List.644 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.646 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.646 then
|
||||
@ -274,9 +274,9 @@ procedure List.80 (#Derived_gen.31, #Derived_gen.32, #Derived_gen.33, #Derived_g
|
||||
dec List.439;
|
||||
ret List.440;
|
||||
in
|
||||
jump List.644 #Derived_gen.31 #Derived_gen.32 #Derived_gen.33 #Derived_gen.34 #Derived_gen.35;
|
||||
jump List.644 #Derived_gen.36 #Derived_gen.37 #Derived_gen.38 #Derived_gen.39 #Derived_gen.40;
|
||||
|
||||
procedure List.80 (#Derived_gen.36, #Derived_gen.37, #Derived_gen.38, #Derived_gen.39, #Derived_gen.40):
|
||||
procedure List.80 (#Derived_gen.41, #Derived_gen.42, #Derived_gen.43, #Derived_gen.44, #Derived_gen.45):
|
||||
joinpoint List.624 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.626 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.626 then
|
||||
@ -290,9 +290,9 @@ procedure List.80 (#Derived_gen.36, #Derived_gen.37, #Derived_gen.38, #Derived_g
|
||||
dec List.439;
|
||||
ret List.440;
|
||||
in
|
||||
jump List.624 #Derived_gen.36 #Derived_gen.37 #Derived_gen.38 #Derived_gen.39 #Derived_gen.40;
|
||||
jump List.624 #Derived_gen.41 #Derived_gen.42 #Derived_gen.43 #Derived_gen.44 #Derived_gen.45;
|
||||
|
||||
procedure List.80 (#Derived_gen.42, #Derived_gen.43, #Derived_gen.44, #Derived_gen.45, #Derived_gen.46):
|
||||
procedure List.80 (#Derived_gen.52, #Derived_gen.53, #Derived_gen.54, #Derived_gen.55, #Derived_gen.56):
|
||||
joinpoint List.556 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.558 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.558 then
|
||||
@ -306,7 +306,7 @@ procedure List.80 (#Derived_gen.42, #Derived_gen.43, #Derived_gen.44, #Derived_g
|
||||
dec List.439;
|
||||
ret List.440;
|
||||
in
|
||||
jump List.556 #Derived_gen.42 #Derived_gen.43 #Derived_gen.44 #Derived_gen.45 #Derived_gen.46;
|
||||
jump List.556 #Derived_gen.52 #Derived_gen.53 #Derived_gen.54 #Derived_gen.55 #Derived_gen.56;
|
||||
|
||||
procedure List.93 (List.436, List.437, List.438):
|
||||
let List.554 : U64 = 0i64;
|
||||
@ -388,8 +388,8 @@ procedure Str.9 (Str.79):
|
||||
else
|
||||
let Str.300 : U8 = StructAtIndex 3 Str.80;
|
||||
let Str.301 : U64 = StructAtIndex 0 Str.80;
|
||||
let #Derived_gen.58 : Str = StructAtIndex 1 Str.80;
|
||||
dec #Derived_gen.58;
|
||||
let #Derived_gen.57 : Str = StructAtIndex 1 Str.80;
|
||||
dec #Derived_gen.57;
|
||||
let Str.299 : {U64, U8} = Struct {Str.301, Str.300};
|
||||
let Str.298 : [C {U64, U8}, C Str] = TagId(0) Str.299;
|
||||
ret Str.298;
|
||||
@ -1331,14 +1331,14 @@ procedure TotallyNotJson.82 (TotallyNotJson.802, TotallyNotJson.803):
|
||||
|
||||
procedure TotallyNotJson.832 (TotallyNotJson.1493):
|
||||
let TotallyNotJson.1845 : List Str = StructAtIndex 1 TotallyNotJson.1493;
|
||||
let #Derived_gen.59 : List Str = StructAtIndex 0 TotallyNotJson.1493;
|
||||
dec #Derived_gen.59;
|
||||
let #Derived_gen.58 : List Str = StructAtIndex 0 TotallyNotJson.1493;
|
||||
dec #Derived_gen.58;
|
||||
ret TotallyNotJson.1845;
|
||||
|
||||
procedure TotallyNotJson.840 (TotallyNotJson.1214):
|
||||
let TotallyNotJson.1566 : List Str = StructAtIndex 1 TotallyNotJson.1214;
|
||||
let #Derived_gen.57 : List Str = StructAtIndex 0 TotallyNotJson.1214;
|
||||
dec #Derived_gen.57;
|
||||
let #Derived_gen.59 : List Str = StructAtIndex 0 TotallyNotJson.1214;
|
||||
dec #Derived_gen.59;
|
||||
ret TotallyNotJson.1566;
|
||||
|
||||
procedure TotallyNotJson.87 (TotallyNotJson.809):
|
||||
@ -1393,7 +1393,7 @@ procedure TotallyNotJson.95 (TotallyNotJson.829):
|
||||
dec TotallyNotJson.1841;
|
||||
ret TotallyNotJson.1839;
|
||||
|
||||
procedure TotallyNotJson.96 (#Derived_gen.41):
|
||||
procedure TotallyNotJson.96 (#Derived_gen.29):
|
||||
joinpoint TotallyNotJson.1847 TotallyNotJson.1168:
|
||||
let TotallyNotJson.834 : List Str = StructAtIndex 0 TotallyNotJson.1168;
|
||||
let TotallyNotJson.833 : List Str = StructAtIndex 1 TotallyNotJson.1168;
|
||||
@ -1429,7 +1429,7 @@ procedure TotallyNotJson.96 (#Derived_gen.41):
|
||||
let TotallyNotJson.1848 : {List Str, List Str} = Struct {TotallyNotJson.834, TotallyNotJson.833};
|
||||
ret TotallyNotJson.1848;
|
||||
in
|
||||
jump TotallyNotJson.1847 #Derived_gen.41;
|
||||
jump TotallyNotJson.1847 #Derived_gen.29;
|
||||
|
||||
procedure TotallyNotJson.97 (TotallyNotJson.837):
|
||||
let TotallyNotJson.838 : List Str = CallByName Str.55 TotallyNotJson.837;
|
||||
@ -1446,7 +1446,7 @@ procedure TotallyNotJson.97 (TotallyNotJson.837):
|
||||
dec TotallyNotJson.1562;
|
||||
ret TotallyNotJson.1560;
|
||||
|
||||
procedure TotallyNotJson.98 (#Derived_gen.47):
|
||||
procedure TotallyNotJson.98 (#Derived_gen.35):
|
||||
joinpoint TotallyNotJson.1568 TotallyNotJson.1169:
|
||||
let TotallyNotJson.842 : List Str = StructAtIndex 0 TotallyNotJson.1169;
|
||||
let TotallyNotJson.841 : List Str = StructAtIndex 1 TotallyNotJson.1169;
|
||||
@ -1482,7 +1482,7 @@ procedure TotallyNotJson.98 (#Derived_gen.47):
|
||||
let TotallyNotJson.1569 : {List Str, List Str} = Struct {TotallyNotJson.842, TotallyNotJson.841};
|
||||
ret TotallyNotJson.1569;
|
||||
in
|
||||
jump TotallyNotJson.1568 #Derived_gen.47;
|
||||
jump TotallyNotJson.1568 #Derived_gen.35;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.12 : Str = "bar";
|
||||
|
@ -192,23 +192,7 @@ procedure List.8 (#Attr.2, #Attr.3):
|
||||
let List.600 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
|
||||
ret List.600;
|
||||
|
||||
procedure List.80 (#Derived_gen.11, #Derived_gen.12, #Derived_gen.13, #Derived_gen.14, #Derived_gen.15):
|
||||
joinpoint List.556 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.558 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.558 then
|
||||
let List.565 : {Str, Str} = CallByName List.66 List.439 List.442;
|
||||
inc List.565;
|
||||
let List.559 : {List U8, U64} = CallByName List.145 List.440 List.565 List.441;
|
||||
let List.562 : U64 = 1i64;
|
||||
let List.561 : U64 = CallByName Num.19 List.442 List.562;
|
||||
jump List.556 List.439 List.559 List.441 List.561 List.443;
|
||||
else
|
||||
dec List.439;
|
||||
ret List.440;
|
||||
in
|
||||
jump List.556 #Derived_gen.11 #Derived_gen.12 #Derived_gen.13 #Derived_gen.14 #Derived_gen.15;
|
||||
|
||||
procedure List.80 (#Derived_gen.16, #Derived_gen.17, #Derived_gen.18, #Derived_gen.19, #Derived_gen.20):
|
||||
procedure List.80 (#Derived_gen.10, #Derived_gen.11, #Derived_gen.12, #Derived_gen.13, #Derived_gen.14):
|
||||
joinpoint List.647 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.649 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.649 then
|
||||
@ -232,9 +216,9 @@ procedure List.80 (#Derived_gen.16, #Derived_gen.17, #Derived_gen.18, #Derived_g
|
||||
let List.648 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) List.440;
|
||||
ret List.648;
|
||||
in
|
||||
jump List.647 #Derived_gen.16 #Derived_gen.17 #Derived_gen.18 #Derived_gen.19 #Derived_gen.20;
|
||||
jump List.647 #Derived_gen.10 #Derived_gen.11 #Derived_gen.12 #Derived_gen.13 #Derived_gen.14;
|
||||
|
||||
procedure List.80 (#Derived_gen.21, #Derived_gen.22, #Derived_gen.23, #Derived_gen.24, #Derived_gen.25):
|
||||
procedure List.80 (#Derived_gen.18, #Derived_gen.19, #Derived_gen.20, #Derived_gen.21, #Derived_gen.22):
|
||||
joinpoint List.576 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.578 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.578 then
|
||||
@ -247,7 +231,23 @@ procedure List.80 (#Derived_gen.21, #Derived_gen.22, #Derived_gen.23, #Derived_g
|
||||
dec List.439;
|
||||
ret List.440;
|
||||
in
|
||||
jump List.576 #Derived_gen.21 #Derived_gen.22 #Derived_gen.23 #Derived_gen.24 #Derived_gen.25;
|
||||
jump List.576 #Derived_gen.18 #Derived_gen.19 #Derived_gen.20 #Derived_gen.21 #Derived_gen.22;
|
||||
|
||||
procedure List.80 (#Derived_gen.31, #Derived_gen.32, #Derived_gen.33, #Derived_gen.34, #Derived_gen.35):
|
||||
joinpoint List.556 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.558 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.558 then
|
||||
let List.565 : {Str, Str} = CallByName List.66 List.439 List.442;
|
||||
inc List.565;
|
||||
let List.559 : {List U8, U64} = CallByName List.145 List.440 List.565 List.441;
|
||||
let List.562 : U64 = 1i64;
|
||||
let List.561 : U64 = CallByName Num.19 List.442 List.562;
|
||||
jump List.556 List.439 List.559 List.441 List.561 List.443;
|
||||
else
|
||||
dec List.439;
|
||||
ret List.440;
|
||||
in
|
||||
jump List.556 #Derived_gen.31 #Derived_gen.32 #Derived_gen.33 #Derived_gen.34 #Derived_gen.35;
|
||||
|
||||
procedure List.93 (List.436, List.437, List.438):
|
||||
let List.554 : U64 = 0i64;
|
||||
@ -323,8 +323,8 @@ procedure Str.9 (Str.79):
|
||||
else
|
||||
let Str.300 : U8 = StructAtIndex 3 Str.80;
|
||||
let Str.301 : U64 = StructAtIndex 0 Str.80;
|
||||
let #Derived_gen.38 : Str = StructAtIndex 1 Str.80;
|
||||
dec #Derived_gen.38;
|
||||
let #Derived_gen.36 : Str = StructAtIndex 1 Str.80;
|
||||
dec #Derived_gen.36;
|
||||
let Str.299 : {U64, U8} = Struct {Str.301, Str.300};
|
||||
let Str.298 : [C {U64, U8}, C Str] = TagId(0) Str.299;
|
||||
ret Str.298;
|
||||
@ -1215,8 +1215,8 @@ procedure TotallyNotJson.82 (TotallyNotJson.802, TotallyNotJson.803):
|
||||
|
||||
procedure TotallyNotJson.832 (TotallyNotJson.1493):
|
||||
let TotallyNotJson.1494 : List Str = StructAtIndex 1 TotallyNotJson.1493;
|
||||
let #Derived_gen.36 : List Str = StructAtIndex 0 TotallyNotJson.1493;
|
||||
dec #Derived_gen.36;
|
||||
let #Derived_gen.38 : List Str = StructAtIndex 0 TotallyNotJson.1493;
|
||||
dec #Derived_gen.38;
|
||||
ret TotallyNotJson.1494;
|
||||
|
||||
procedure TotallyNotJson.840 (TotallyNotJson.1214):
|
||||
@ -1277,7 +1277,7 @@ procedure TotallyNotJson.95 (TotallyNotJson.829):
|
||||
dec TotallyNotJson.1489;
|
||||
ret TotallyNotJson.1488;
|
||||
|
||||
procedure TotallyNotJson.96 (#Derived_gen.32):
|
||||
procedure TotallyNotJson.96 (#Derived_gen.26):
|
||||
joinpoint TotallyNotJson.1496 TotallyNotJson.1168:
|
||||
let TotallyNotJson.834 : List Str = StructAtIndex 0 TotallyNotJson.1168;
|
||||
let TotallyNotJson.833 : List Str = StructAtIndex 1 TotallyNotJson.1168;
|
||||
@ -1313,7 +1313,7 @@ procedure TotallyNotJson.96 (#Derived_gen.32):
|
||||
let TotallyNotJson.1497 : {List Str, List Str} = Struct {TotallyNotJson.834, TotallyNotJson.833};
|
||||
ret TotallyNotJson.1497;
|
||||
in
|
||||
jump TotallyNotJson.1496 #Derived_gen.32;
|
||||
jump TotallyNotJson.1496 #Derived_gen.26;
|
||||
|
||||
procedure TotallyNotJson.97 (TotallyNotJson.837):
|
||||
let TotallyNotJson.838 : List Str = CallByName Str.55 TotallyNotJson.837;
|
||||
@ -1330,7 +1330,7 @@ procedure TotallyNotJson.97 (TotallyNotJson.837):
|
||||
dec TotallyNotJson.1210;
|
||||
ret TotallyNotJson.1209;
|
||||
|
||||
procedure TotallyNotJson.98 (#Derived_gen.10):
|
||||
procedure TotallyNotJson.98 (#Derived_gen.30):
|
||||
joinpoint TotallyNotJson.1217 TotallyNotJson.1169:
|
||||
let TotallyNotJson.842 : List Str = StructAtIndex 0 TotallyNotJson.1169;
|
||||
let TotallyNotJson.841 : List Str = StructAtIndex 1 TotallyNotJson.1169;
|
||||
@ -1366,7 +1366,7 @@ procedure TotallyNotJson.98 (#Derived_gen.10):
|
||||
let TotallyNotJson.1218 : {List Str, List Str} = Struct {TotallyNotJson.842, TotallyNotJson.841};
|
||||
ret TotallyNotJson.1218;
|
||||
in
|
||||
jump TotallyNotJson.1217 #Derived_gen.10;
|
||||
jump TotallyNotJson.1217 #Derived_gen.30;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.11 : Str = "foo";
|
||||
|
@ -199,23 +199,7 @@ procedure List.8 (#Attr.2, #Attr.3):
|
||||
let List.600 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
|
||||
ret List.600;
|
||||
|
||||
procedure List.80 (#Derived_gen.15, #Derived_gen.16, #Derived_gen.17, #Derived_gen.18, #Derived_gen.19):
|
||||
joinpoint List.556 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.558 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.558 then
|
||||
let List.565 : {Str, Str} = CallByName List.66 List.439 List.442;
|
||||
inc List.565;
|
||||
let List.559 : {List U8, U64} = CallByName List.145 List.440 List.565 List.441;
|
||||
let List.562 : U64 = 1i64;
|
||||
let List.561 : U64 = CallByName Num.19 List.442 List.562;
|
||||
jump List.556 List.439 List.559 List.441 List.561 List.443;
|
||||
else
|
||||
dec List.439;
|
||||
ret List.440;
|
||||
in
|
||||
jump List.556 #Derived_gen.15 #Derived_gen.16 #Derived_gen.17 #Derived_gen.18 #Derived_gen.19;
|
||||
|
||||
procedure List.80 (#Derived_gen.20, #Derived_gen.21, #Derived_gen.22, #Derived_gen.23, #Derived_gen.24):
|
||||
procedure List.80 (#Derived_gen.14, #Derived_gen.15, #Derived_gen.16, #Derived_gen.17, #Derived_gen.18):
|
||||
joinpoint List.647 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.649 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.649 then
|
||||
@ -239,9 +223,9 @@ procedure List.80 (#Derived_gen.20, #Derived_gen.21, #Derived_gen.22, #Derived_g
|
||||
let List.648 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) List.440;
|
||||
ret List.648;
|
||||
in
|
||||
jump List.647 #Derived_gen.20 #Derived_gen.21 #Derived_gen.22 #Derived_gen.23 #Derived_gen.24;
|
||||
jump List.647 #Derived_gen.14 #Derived_gen.15 #Derived_gen.16 #Derived_gen.17 #Derived_gen.18;
|
||||
|
||||
procedure List.80 (#Derived_gen.25, #Derived_gen.26, #Derived_gen.27, #Derived_gen.28, #Derived_gen.29):
|
||||
procedure List.80 (#Derived_gen.22, #Derived_gen.23, #Derived_gen.24, #Derived_gen.25, #Derived_gen.26):
|
||||
joinpoint List.576 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.578 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.578 then
|
||||
@ -254,7 +238,23 @@ procedure List.80 (#Derived_gen.25, #Derived_gen.26, #Derived_gen.27, #Derived_g
|
||||
dec List.439;
|
||||
ret List.440;
|
||||
in
|
||||
jump List.576 #Derived_gen.25 #Derived_gen.26 #Derived_gen.27 #Derived_gen.28 #Derived_gen.29;
|
||||
jump List.576 #Derived_gen.22 #Derived_gen.23 #Derived_gen.24 #Derived_gen.25 #Derived_gen.26;
|
||||
|
||||
procedure List.80 (#Derived_gen.35, #Derived_gen.36, #Derived_gen.37, #Derived_gen.38, #Derived_gen.39):
|
||||
joinpoint List.556 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.558 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.558 then
|
||||
let List.565 : {Str, Str} = CallByName List.66 List.439 List.442;
|
||||
inc List.565;
|
||||
let List.559 : {List U8, U64} = CallByName List.145 List.440 List.565 List.441;
|
||||
let List.562 : U64 = 1i64;
|
||||
let List.561 : U64 = CallByName Num.19 List.442 List.562;
|
||||
jump List.556 List.439 List.559 List.441 List.561 List.443;
|
||||
else
|
||||
dec List.439;
|
||||
ret List.440;
|
||||
in
|
||||
jump List.556 #Derived_gen.35 #Derived_gen.36 #Derived_gen.37 #Derived_gen.38 #Derived_gen.39;
|
||||
|
||||
procedure List.93 (List.436, List.437, List.438):
|
||||
let List.554 : U64 = 0i64;
|
||||
@ -330,8 +330,8 @@ procedure Str.9 (Str.79):
|
||||
else
|
||||
let Str.300 : U8 = StructAtIndex 3 Str.80;
|
||||
let Str.301 : U64 = StructAtIndex 0 Str.80;
|
||||
let #Derived_gen.42 : Str = StructAtIndex 1 Str.80;
|
||||
dec #Derived_gen.42;
|
||||
let #Derived_gen.40 : Str = StructAtIndex 1 Str.80;
|
||||
dec #Derived_gen.40;
|
||||
let Str.299 : {U64, U8} = Struct {Str.301, Str.300};
|
||||
let Str.298 : [C {U64, U8}, C Str] = TagId(0) Str.299;
|
||||
ret Str.298;
|
||||
@ -1222,8 +1222,8 @@ procedure TotallyNotJson.82 (TotallyNotJson.802, TotallyNotJson.803):
|
||||
|
||||
procedure TotallyNotJson.832 (TotallyNotJson.1493):
|
||||
let TotallyNotJson.1494 : List Str = StructAtIndex 1 TotallyNotJson.1493;
|
||||
let #Derived_gen.40 : List Str = StructAtIndex 0 TotallyNotJson.1493;
|
||||
dec #Derived_gen.40;
|
||||
let #Derived_gen.42 : List Str = StructAtIndex 0 TotallyNotJson.1493;
|
||||
dec #Derived_gen.42;
|
||||
ret TotallyNotJson.1494;
|
||||
|
||||
procedure TotallyNotJson.840 (TotallyNotJson.1214):
|
||||
@ -1284,7 +1284,7 @@ procedure TotallyNotJson.95 (TotallyNotJson.829):
|
||||
dec TotallyNotJson.1489;
|
||||
ret TotallyNotJson.1488;
|
||||
|
||||
procedure TotallyNotJson.96 (#Derived_gen.36):
|
||||
procedure TotallyNotJson.96 (#Derived_gen.30):
|
||||
joinpoint TotallyNotJson.1496 TotallyNotJson.1168:
|
||||
let TotallyNotJson.834 : List Str = StructAtIndex 0 TotallyNotJson.1168;
|
||||
let TotallyNotJson.833 : List Str = StructAtIndex 1 TotallyNotJson.1168;
|
||||
@ -1320,7 +1320,7 @@ procedure TotallyNotJson.96 (#Derived_gen.36):
|
||||
let TotallyNotJson.1497 : {List Str, List Str} = Struct {TotallyNotJson.834, TotallyNotJson.833};
|
||||
ret TotallyNotJson.1497;
|
||||
in
|
||||
jump TotallyNotJson.1496 #Derived_gen.36;
|
||||
jump TotallyNotJson.1496 #Derived_gen.30;
|
||||
|
||||
procedure TotallyNotJson.97 (TotallyNotJson.837):
|
||||
let TotallyNotJson.838 : List Str = CallByName Str.55 TotallyNotJson.837;
|
||||
@ -1337,7 +1337,7 @@ procedure TotallyNotJson.97 (TotallyNotJson.837):
|
||||
dec TotallyNotJson.1210;
|
||||
ret TotallyNotJson.1209;
|
||||
|
||||
procedure TotallyNotJson.98 (#Derived_gen.14):
|
||||
procedure TotallyNotJson.98 (#Derived_gen.34):
|
||||
joinpoint TotallyNotJson.1217 TotallyNotJson.1169:
|
||||
let TotallyNotJson.842 : List Str = StructAtIndex 0 TotallyNotJson.1169;
|
||||
let TotallyNotJson.841 : List Str = StructAtIndex 1 TotallyNotJson.1169;
|
||||
@ -1373,7 +1373,7 @@ procedure TotallyNotJson.98 (#Derived_gen.14):
|
||||
let TotallyNotJson.1218 : {List Str, List Str} = Struct {TotallyNotJson.842, TotallyNotJson.841};
|
||||
ret TotallyNotJson.1218;
|
||||
in
|
||||
jump TotallyNotJson.1217 #Derived_gen.14;
|
||||
jump TotallyNotJson.1217 #Derived_gen.34;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.11 : Str = "foo";
|
||||
|
@ -88,7 +88,22 @@ procedure List.8 (#Attr.2, #Attr.3):
|
||||
let List.529 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
|
||||
ret List.529;
|
||||
|
||||
procedure List.80 (#Derived_gen.3, #Derived_gen.4, #Derived_gen.5, #Derived_gen.6, #Derived_gen.7):
|
||||
procedure List.80 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4):
|
||||
joinpoint List.540 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.542 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.542 then
|
||||
let List.549 : U8 = CallByName List.66 List.439 List.442;
|
||||
let List.543 : List U8 = CallByName List.145 List.440 List.549 List.441;
|
||||
let List.546 : U64 = 1i64;
|
||||
let List.545 : U64 = CallByName Num.19 List.442 List.546;
|
||||
jump List.540 List.439 List.543 List.441 List.545 List.443;
|
||||
else
|
||||
dec List.439;
|
||||
ret List.440;
|
||||
in
|
||||
jump List.540 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4;
|
||||
|
||||
procedure List.80 (#Derived_gen.8, #Derived_gen.9, #Derived_gen.10, #Derived_gen.11, #Derived_gen.12):
|
||||
joinpoint List.578 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.580 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.580 then
|
||||
@ -112,22 +127,7 @@ procedure List.80 (#Derived_gen.3, #Derived_gen.4, #Derived_gen.5, #Derived_gen.
|
||||
let List.579 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) List.440;
|
||||
ret List.579;
|
||||
in
|
||||
jump List.578 #Derived_gen.3 #Derived_gen.4 #Derived_gen.5 #Derived_gen.6 #Derived_gen.7;
|
||||
|
||||
procedure List.80 (#Derived_gen.8, #Derived_gen.9, #Derived_gen.10, #Derived_gen.11, #Derived_gen.12):
|
||||
joinpoint List.540 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.542 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.542 then
|
||||
let List.549 : U8 = CallByName List.66 List.439 List.442;
|
||||
let List.543 : List U8 = CallByName List.145 List.440 List.549 List.441;
|
||||
let List.546 : U64 = 1i64;
|
||||
let List.545 : U64 = CallByName Num.19 List.442 List.546;
|
||||
jump List.540 List.439 List.543 List.441 List.545 List.443;
|
||||
else
|
||||
dec List.439;
|
||||
ret List.440;
|
||||
in
|
||||
jump List.540 #Derived_gen.8 #Derived_gen.9 #Derived_gen.10 #Derived_gen.11 #Derived_gen.12;
|
||||
jump List.578 #Derived_gen.8 #Derived_gen.9 #Derived_gen.10 #Derived_gen.11 #Derived_gen.12;
|
||||
|
||||
procedure List.93 (List.436, List.437, List.438):
|
||||
let List.538 : U64 = 0i64;
|
||||
|
@ -147,38 +147,7 @@ procedure List.8 (#Attr.2, #Attr.3):
|
||||
let List.599 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
|
||||
ret List.599;
|
||||
|
||||
procedure List.80 (#Derived_gen.10, #Derived_gen.11, #Derived_gen.12, #Derived_gen.13, #Derived_gen.14):
|
||||
joinpoint List.554 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.556 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.556 then
|
||||
let List.563 : Str = CallByName List.66 List.439 List.442;
|
||||
inc List.563;
|
||||
let List.557 : {List U8, U64} = CallByName List.145 List.440 List.563 List.441;
|
||||
let List.560 : U64 = 1i64;
|
||||
let List.559 : U64 = CallByName Num.19 List.442 List.560;
|
||||
jump List.554 List.439 List.557 List.441 List.559 List.443;
|
||||
else
|
||||
dec List.439;
|
||||
ret List.440;
|
||||
in
|
||||
jump List.554 #Derived_gen.10 #Derived_gen.11 #Derived_gen.12 #Derived_gen.13 #Derived_gen.14;
|
||||
|
||||
procedure List.80 (#Derived_gen.18, #Derived_gen.19, #Derived_gen.20, #Derived_gen.21, #Derived_gen.22):
|
||||
joinpoint List.574 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.576 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.576 then
|
||||
let List.583 : U8 = CallByName List.66 List.439 List.442;
|
||||
let List.577 : List U8 = CallByName List.145 List.440 List.583 List.441;
|
||||
let List.580 : U64 = 1i64;
|
||||
let List.579 : U64 = CallByName Num.19 List.442 List.580;
|
||||
jump List.574 List.439 List.577 List.441 List.579 List.443;
|
||||
else
|
||||
dec List.439;
|
||||
ret List.440;
|
||||
in
|
||||
jump List.574 #Derived_gen.18 #Derived_gen.19 #Derived_gen.20 #Derived_gen.21 #Derived_gen.22;
|
||||
|
||||
procedure List.80 (#Derived_gen.29, #Derived_gen.30, #Derived_gen.31, #Derived_gen.32, #Derived_gen.33):
|
||||
procedure List.80 (#Derived_gen.16, #Derived_gen.17, #Derived_gen.18, #Derived_gen.19, #Derived_gen.20):
|
||||
joinpoint List.627 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.629 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.629 then
|
||||
@ -202,7 +171,38 @@ procedure List.80 (#Derived_gen.29, #Derived_gen.30, #Derived_gen.31, #Derived_g
|
||||
let List.628 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) List.440;
|
||||
ret List.628;
|
||||
in
|
||||
jump List.627 #Derived_gen.29 #Derived_gen.30 #Derived_gen.31 #Derived_gen.32 #Derived_gen.33;
|
||||
jump List.627 #Derived_gen.16 #Derived_gen.17 #Derived_gen.18 #Derived_gen.19 #Derived_gen.20;
|
||||
|
||||
procedure List.80 (#Derived_gen.24, #Derived_gen.25, #Derived_gen.26, #Derived_gen.27, #Derived_gen.28):
|
||||
joinpoint List.574 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.576 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.576 then
|
||||
let List.583 : U8 = CallByName List.66 List.439 List.442;
|
||||
let List.577 : List U8 = CallByName List.145 List.440 List.583 List.441;
|
||||
let List.580 : U64 = 1i64;
|
||||
let List.579 : U64 = CallByName Num.19 List.442 List.580;
|
||||
jump List.574 List.439 List.577 List.441 List.579 List.443;
|
||||
else
|
||||
dec List.439;
|
||||
ret List.440;
|
||||
in
|
||||
jump List.574 #Derived_gen.24 #Derived_gen.25 #Derived_gen.26 #Derived_gen.27 #Derived_gen.28;
|
||||
|
||||
procedure List.80 (#Derived_gen.29, #Derived_gen.30, #Derived_gen.31, #Derived_gen.32, #Derived_gen.33):
|
||||
joinpoint List.554 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.556 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.556 then
|
||||
let List.563 : Str = CallByName List.66 List.439 List.442;
|
||||
inc List.563;
|
||||
let List.557 : {List U8, U64} = CallByName List.145 List.440 List.563 List.441;
|
||||
let List.560 : U64 = 1i64;
|
||||
let List.559 : U64 = CallByName Num.19 List.442 List.560;
|
||||
jump List.554 List.439 List.557 List.441 List.559 List.443;
|
||||
else
|
||||
dec List.439;
|
||||
ret List.440;
|
||||
in
|
||||
jump List.554 #Derived_gen.29 #Derived_gen.30 #Derived_gen.31 #Derived_gen.32 #Derived_gen.33;
|
||||
|
||||
procedure List.93 (List.436, List.437, List.438):
|
||||
let List.552 : U64 = 0i64;
|
||||
|
@ -150,22 +150,7 @@ procedure List.8 (#Attr.2, #Attr.3):
|
||||
let List.599 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
|
||||
ret List.599;
|
||||
|
||||
procedure List.80 (#Derived_gen.20, #Derived_gen.21, #Derived_gen.22, #Derived_gen.23, #Derived_gen.24):
|
||||
joinpoint List.574 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.576 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.576 then
|
||||
let List.583 : U8 = CallByName List.66 List.439 List.442;
|
||||
let List.577 : List U8 = CallByName List.145 List.440 List.583 List.441;
|
||||
let List.580 : U64 = 1i64;
|
||||
let List.579 : U64 = CallByName Num.19 List.442 List.580;
|
||||
jump List.574 List.439 List.577 List.441 List.579 List.443;
|
||||
else
|
||||
dec List.439;
|
||||
ret List.440;
|
||||
in
|
||||
jump List.574 #Derived_gen.20 #Derived_gen.21 #Derived_gen.22 #Derived_gen.23 #Derived_gen.24;
|
||||
|
||||
procedure List.80 (#Derived_gen.25, #Derived_gen.26, #Derived_gen.27, #Derived_gen.28, #Derived_gen.29):
|
||||
procedure List.80 (#Derived_gen.11, #Derived_gen.12, #Derived_gen.13, #Derived_gen.14, #Derived_gen.15):
|
||||
joinpoint List.627 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.629 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.629 then
|
||||
@ -189,9 +174,9 @@ procedure List.80 (#Derived_gen.25, #Derived_gen.26, #Derived_gen.27, #Derived_g
|
||||
let List.628 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) List.440;
|
||||
ret List.628;
|
||||
in
|
||||
jump List.627 #Derived_gen.25 #Derived_gen.26 #Derived_gen.27 #Derived_gen.28 #Derived_gen.29;
|
||||
jump List.627 #Derived_gen.11 #Derived_gen.12 #Derived_gen.13 #Derived_gen.14 #Derived_gen.15;
|
||||
|
||||
procedure List.80 (#Derived_gen.30, #Derived_gen.31, #Derived_gen.32, #Derived_gen.33, #Derived_gen.34):
|
||||
procedure List.80 (#Derived_gen.19, #Derived_gen.20, #Derived_gen.21, #Derived_gen.22, #Derived_gen.23):
|
||||
joinpoint List.554 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.556 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.556 then
|
||||
@ -205,7 +190,22 @@ procedure List.80 (#Derived_gen.30, #Derived_gen.31, #Derived_gen.32, #Derived_g
|
||||
dec List.439;
|
||||
ret List.440;
|
||||
in
|
||||
jump List.554 #Derived_gen.30 #Derived_gen.31 #Derived_gen.32 #Derived_gen.33 #Derived_gen.34;
|
||||
jump List.554 #Derived_gen.19 #Derived_gen.20 #Derived_gen.21 #Derived_gen.22 #Derived_gen.23;
|
||||
|
||||
procedure List.80 (#Derived_gen.24, #Derived_gen.25, #Derived_gen.26, #Derived_gen.27, #Derived_gen.28):
|
||||
joinpoint List.574 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.576 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.576 then
|
||||
let List.583 : U8 = CallByName List.66 List.439 List.442;
|
||||
let List.577 : List U8 = CallByName List.145 List.440 List.583 List.441;
|
||||
let List.580 : U64 = 1i64;
|
||||
let List.579 : U64 = CallByName Num.19 List.442 List.580;
|
||||
jump List.574 List.439 List.577 List.441 List.579 List.443;
|
||||
else
|
||||
dec List.439;
|
||||
ret List.440;
|
||||
in
|
||||
jump List.574 #Derived_gen.24 #Derived_gen.25 #Derived_gen.26 #Derived_gen.27 #Derived_gen.28;
|
||||
|
||||
procedure List.93 (List.436, List.437, List.438):
|
||||
let List.552 : U64 = 0i64;
|
||||
|
@ -247,8 +247,8 @@ procedure Str.9 (Str.79):
|
||||
else
|
||||
let Str.300 : U8 = StructAtIndex 3 Str.80;
|
||||
let Str.301 : U64 = StructAtIndex 0 Str.80;
|
||||
let #Derived_gen.7 : Str = StructAtIndex 1 Str.80;
|
||||
dec #Derived_gen.7;
|
||||
let #Derived_gen.6 : Str = StructAtIndex 1 Str.80;
|
||||
dec #Derived_gen.6;
|
||||
let Str.299 : {U64, U8} = Struct {Str.301, Str.300};
|
||||
let Str.298 : [C {U64, U8}, C Str] = TagId(0) Str.299;
|
||||
ret Str.298;
|
||||
@ -365,8 +365,8 @@ procedure TotallyNotJson.534 (TotallyNotJson.535):
|
||||
|
||||
procedure TotallyNotJson.536 (TotallyNotJson.1192):
|
||||
let TotallyNotJson.1193 : List U8 = StructAtIndex 1 TotallyNotJson.1192;
|
||||
let #Derived_gen.6 : List U8 = StructAtIndex 0 TotallyNotJson.1192;
|
||||
dec #Derived_gen.6;
|
||||
let #Derived_gen.7 : List U8 = StructAtIndex 0 TotallyNotJson.1192;
|
||||
dec #Derived_gen.7;
|
||||
ret TotallyNotJson.1193;
|
||||
|
||||
procedure TotallyNotJson.60 ():
|
||||
|
@ -248,8 +248,8 @@ procedure Str.9 (Str.79):
|
||||
else
|
||||
let Str.310 : U8 = StructAtIndex 3 Str.80;
|
||||
let Str.311 : U64 = StructAtIndex 0 Str.80;
|
||||
let #Derived_gen.6 : Str = StructAtIndex 1 Str.80;
|
||||
dec #Derived_gen.6;
|
||||
let #Derived_gen.7 : Str = StructAtIndex 1 Str.80;
|
||||
dec #Derived_gen.7;
|
||||
let Str.309 : {U64, U8} = Struct {Str.311, Str.310};
|
||||
let Str.308 : [C {U64, U8}, C Str] = TagId(0) Str.309;
|
||||
ret Str.308;
|
||||
@ -397,8 +397,8 @@ procedure TotallyNotJson.534 (TotallyNotJson.535):
|
||||
|
||||
procedure TotallyNotJson.536 (TotallyNotJson.1192):
|
||||
let TotallyNotJson.1193 : List U8 = StructAtIndex 1 TotallyNotJson.1192;
|
||||
let #Derived_gen.7 : List U8 = StructAtIndex 0 TotallyNotJson.1192;
|
||||
dec #Derived_gen.7;
|
||||
let #Derived_gen.6 : List U8 = StructAtIndex 0 TotallyNotJson.1192;
|
||||
dec #Derived_gen.6;
|
||||
ret TotallyNotJson.1193;
|
||||
|
||||
procedure TotallyNotJson.60 ():
|
||||
|
@ -6,10 +6,10 @@ procedure List.5 (#Attr.2, #Attr.3):
|
||||
procedure Test.2 (Test.5):
|
||||
let Test.6 : List [<rnnu>C List *self] = UnionAtIndex (Id 0) (Index 0) Test.5;
|
||||
inc Test.6;
|
||||
let #Derived_gen.1 : [<rnnu>C List *self] = Reset { symbol: Test.5, id: UpdateModeId { id: 0 } };
|
||||
let #Derived_gen.2 : [<rnnu>C List *self] = Reset { symbol: Test.5, id: UpdateModeId { id: 1 } };
|
||||
let Test.15 : {} = Struct {};
|
||||
let Test.7 : List [<rnnu>C List *self] = CallByName List.5 Test.6 Test.15;
|
||||
let Test.14 : [<rnnu>C List *self] = Reuse #Derived_gen.1 UpdateModeId { id: 0 } TagId(0) Test.7;
|
||||
let Test.14 : [<rnnu>C List *self] = Reuse #Derived_gen.2 UpdateModeId { id: 1 } TagId(0) Test.7;
|
||||
ret Test.14;
|
||||
|
||||
procedure Test.0 ():
|
||||
|
@ -19,7 +19,7 @@ procedure Test.11 (Test.29, #Attr.12):
|
||||
procedure Test.11 (Test.29, Test.10):
|
||||
ret Test.10;
|
||||
|
||||
procedure Test.14 (#Derived_gen.2, #Derived_gen.3):
|
||||
procedure Test.14 (#Derived_gen.7, #Derived_gen.8):
|
||||
joinpoint Test.37 Test.36 #Attr.12:
|
||||
let Test.12 : {} = UnionAtIndex (Id 1) (Index 1) #Attr.12;
|
||||
let Test.13 : I64 = UnionAtIndex (Id 1) (Index 0) #Attr.12;
|
||||
@ -46,7 +46,7 @@ procedure Test.14 (#Derived_gen.2, #Derived_gen.3):
|
||||
decref #Attr.12;
|
||||
jump #Derived_gen.10;
|
||||
in
|
||||
jump Test.37 #Derived_gen.2 #Derived_gen.3;
|
||||
jump Test.37 #Derived_gen.7 #Derived_gen.8;
|
||||
|
||||
procedure Test.2 ():
|
||||
let Test.6 : Str = "Hello";
|
||||
|
@ -136,7 +136,23 @@ procedure List.8 (#Attr.2, #Attr.3):
|
||||
let List.598 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
|
||||
ret List.598;
|
||||
|
||||
procedure List.80 (#Derived_gen.11, #Derived_gen.12, #Derived_gen.13, #Derived_gen.14, #Derived_gen.15):
|
||||
procedure List.80 (#Derived_gen.19, #Derived_gen.20, #Derived_gen.21, #Derived_gen.22, #Derived_gen.23):
|
||||
joinpoint List.553 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.555 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.555 then
|
||||
let List.562 : Str = CallByName List.66 List.439 List.442;
|
||||
inc List.562;
|
||||
let List.556 : {List U8, U64} = CallByName List.145 List.440 List.562 List.441;
|
||||
let List.559 : U64 = 1i64;
|
||||
let List.558 : U64 = CallByName Num.19 List.442 List.559;
|
||||
jump List.553 List.439 List.556 List.441 List.558 List.443;
|
||||
else
|
||||
dec List.439;
|
||||
ret List.440;
|
||||
in
|
||||
jump List.553 #Derived_gen.19 #Derived_gen.20 #Derived_gen.21 #Derived_gen.22 #Derived_gen.23;
|
||||
|
||||
procedure List.80 (#Derived_gen.3, #Derived_gen.4, #Derived_gen.5, #Derived_gen.6, #Derived_gen.7):
|
||||
joinpoint List.573 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.575 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.575 then
|
||||
@ -149,9 +165,9 @@ procedure List.80 (#Derived_gen.11, #Derived_gen.12, #Derived_gen.13, #Derived_g
|
||||
dec List.439;
|
||||
ret List.440;
|
||||
in
|
||||
jump List.573 #Derived_gen.11 #Derived_gen.12 #Derived_gen.13 #Derived_gen.14 #Derived_gen.15;
|
||||
jump List.573 #Derived_gen.3 #Derived_gen.4 #Derived_gen.5 #Derived_gen.6 #Derived_gen.7;
|
||||
|
||||
procedure List.80 (#Derived_gen.16, #Derived_gen.17, #Derived_gen.18, #Derived_gen.19, #Derived_gen.20):
|
||||
procedure List.80 (#Derived_gen.8, #Derived_gen.9, #Derived_gen.10, #Derived_gen.11, #Derived_gen.12):
|
||||
joinpoint List.626 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.628 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.628 then
|
||||
@ -175,23 +191,7 @@ procedure List.80 (#Derived_gen.16, #Derived_gen.17, #Derived_gen.18, #Derived_g
|
||||
let List.627 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) List.440;
|
||||
ret List.627;
|
||||
in
|
||||
jump List.626 #Derived_gen.16 #Derived_gen.17 #Derived_gen.18 #Derived_gen.19 #Derived_gen.20;
|
||||
|
||||
procedure List.80 (#Derived_gen.6, #Derived_gen.7, #Derived_gen.8, #Derived_gen.9, #Derived_gen.10):
|
||||
joinpoint List.553 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.555 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.555 then
|
||||
let List.562 : Str = CallByName List.66 List.439 List.442;
|
||||
inc List.562;
|
||||
let List.556 : {List U8, U64} = CallByName List.145 List.440 List.562 List.441;
|
||||
let List.559 : U64 = 1i64;
|
||||
let List.558 : U64 = CallByName Num.19 List.442 List.559;
|
||||
jump List.553 List.439 List.556 List.441 List.558 List.443;
|
||||
else
|
||||
dec List.439;
|
||||
ret List.440;
|
||||
in
|
||||
jump List.553 #Derived_gen.6 #Derived_gen.7 #Derived_gen.8 #Derived_gen.9 #Derived_gen.10;
|
||||
jump List.626 #Derived_gen.8 #Derived_gen.9 #Derived_gen.10 #Derived_gen.11 #Derived_gen.12;
|
||||
|
||||
procedure List.93 (List.436, List.437, List.438):
|
||||
let List.551 : U64 = 0i64;
|
||||
|
@ -131,7 +131,7 @@ procedure List.8 (#Attr.2, #Attr.3):
|
||||
let List.616 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
|
||||
ret List.616;
|
||||
|
||||
procedure List.80 (#Derived_gen.21, #Derived_gen.22, #Derived_gen.23, #Derived_gen.24, #Derived_gen.25):
|
||||
procedure List.80 (#Derived_gen.18, #Derived_gen.19, #Derived_gen.20, #Derived_gen.21, #Derived_gen.22):
|
||||
joinpoint List.601 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.603 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.603 then
|
||||
@ -144,9 +144,9 @@ procedure List.80 (#Derived_gen.21, #Derived_gen.22, #Derived_gen.23, #Derived_g
|
||||
dec List.439;
|
||||
ret List.440;
|
||||
in
|
||||
jump List.601 #Derived_gen.21 #Derived_gen.22 #Derived_gen.23 #Derived_gen.24 #Derived_gen.25;
|
||||
jump List.601 #Derived_gen.18 #Derived_gen.19 #Derived_gen.20 #Derived_gen.21 #Derived_gen.22;
|
||||
|
||||
procedure List.80 (#Derived_gen.29, #Derived_gen.30, #Derived_gen.31, #Derived_gen.32, #Derived_gen.33):
|
||||
procedure List.80 (#Derived_gen.38, #Derived_gen.39, #Derived_gen.40, #Derived_gen.41, #Derived_gen.42):
|
||||
joinpoint List.553 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.555 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.555 then
|
||||
@ -159,7 +159,7 @@ procedure List.80 (#Derived_gen.29, #Derived_gen.30, #Derived_gen.31, #Derived_g
|
||||
dec List.439;
|
||||
ret List.440;
|
||||
in
|
||||
jump List.553 #Derived_gen.29 #Derived_gen.30 #Derived_gen.31 #Derived_gen.32 #Derived_gen.33;
|
||||
jump List.553 #Derived_gen.38 #Derived_gen.39 #Derived_gen.40 #Derived_gen.41 #Derived_gen.42;
|
||||
|
||||
procedure List.93 (List.436, List.437, List.438):
|
||||
let List.551 : U64 = 0i64;
|
||||
|
@ -16,6 +16,7 @@ const EXPANDED_STACK_SIZE: usize = 8 * 1024 * 1024;
|
||||
use bumpalo::Bump;
|
||||
use roc_collections::all::MutMap;
|
||||
use roc_load::ExecutionMode;
|
||||
use roc_load::FunctionKind;
|
||||
use roc_load::LoadConfig;
|
||||
use roc_load::LoadMonomorphizedError;
|
||||
use roc_load::Threading;
|
||||
@ -104,6 +105,8 @@ fn compiles_to_ir(test_name: &str, src: &str, mode: &str, allow_type_errors: boo
|
||||
|
||||
let load_config = LoadConfig {
|
||||
target_info: TARGET_INFO,
|
||||
// TODO parameterize
|
||||
function_kind: FunctionKind::LambdaSet,
|
||||
threading: Threading::Single,
|
||||
render: roc_reporting::report::RenderTarget::Generic,
|
||||
palette: roc_reporting::report::DEFAULT_PALETTE,
|
||||
|
@ -11,7 +11,7 @@ use roc_can::{
|
||||
};
|
||||
use roc_derive::SharedDerivedModule;
|
||||
use roc_late_solve::AbilitiesView;
|
||||
use roc_load::LoadedModule;
|
||||
use roc_load::{FunctionKind, LoadedModule};
|
||||
use roc_module::symbol::{Interns, ModuleId};
|
||||
use roc_packaging::cache::RocCacheDir;
|
||||
use roc_problem::can::Problem;
|
||||
@ -48,6 +48,7 @@ pub fn run_load_and_infer<'a>(
|
||||
src: &str,
|
||||
dependencies: impl IntoIterator<Item = (&'a str, &'a str)>,
|
||||
no_promote: bool,
|
||||
function_kind: FunctionKind,
|
||||
) -> Result<(LoadedModule, String), std::io::Error> {
|
||||
use tempfile::tempdir;
|
||||
|
||||
@ -79,6 +80,7 @@ pub fn run_load_and_infer<'a>(
|
||||
module_src,
|
||||
dir.path().to_path_buf(),
|
||||
roc_target::TargetInfo::default_x86_64(),
|
||||
function_kind,
|
||||
roc_reporting::report::RenderTarget::Generic,
|
||||
RocCacheDir::Disallowed,
|
||||
roc_reporting::report::DEFAULT_PALETTE,
|
||||
@ -351,6 +353,7 @@ pub fn infer_queries<'a>(
|
||||
dependencies: impl IntoIterator<Item = (&'a str, &'a str)>,
|
||||
options: InferOptions,
|
||||
allow_can_errors: bool,
|
||||
function_kind: FunctionKind,
|
||||
) -> Result<InferredProgram, Box<dyn Error>> {
|
||||
let (
|
||||
LoadedModule {
|
||||
@ -364,7 +367,7 @@ pub fn infer_queries<'a>(
|
||||
..
|
||||
},
|
||||
src,
|
||||
) = run_load_and_infer(src, dependencies, options.no_promote)?;
|
||||
) = run_load_and_infer(src, dependencies, options.no_promote, function_kind)?;
|
||||
|
||||
let declarations = declarations_by_id.remove(&home).unwrap();
|
||||
let subs = solved.inner_mut();
|
||||
|
@ -402,7 +402,11 @@ fn find_names_needed(
|
||||
find_under_alias,
|
||||
);
|
||||
}
|
||||
Error | Structure(EmptyRecord) | Structure(EmptyTuple) | Structure(EmptyTagUnion) => {
|
||||
Error
|
||||
| Structure(EmptyRecord)
|
||||
| Structure(EmptyTuple)
|
||||
| Structure(EmptyTagUnion)
|
||||
| ErasedLambda => {
|
||||
// Errors and empty records don't need names.
|
||||
}
|
||||
}
|
||||
@ -859,6 +863,12 @@ fn write_content<'a>(
|
||||
|
||||
buf.push(']');
|
||||
}
|
||||
ErasedLambda => {
|
||||
debug_assert!(env.debug.print_lambda_sets);
|
||||
|
||||
// Easy mode 🤠
|
||||
buf.push('?');
|
||||
}
|
||||
RangedNumber(range) => {
|
||||
buf.push_str("Range(");
|
||||
for (i, &var) in range.variable_slice().iter().enumerate() {
|
||||
|
@ -587,15 +587,19 @@ impl<T> Clone for SubsSlice<T> {
|
||||
|
||||
impl<T> Default for SubsSlice<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
start: Default::default(),
|
||||
length: Default::default(),
|
||||
_marker: Default::default(),
|
||||
}
|
||||
Self::empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> SubsSlice<T> {
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
start: 0,
|
||||
length: 0,
|
||||
_marker: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_slice<'a>(&self, slice: &'a [T]) -> &'a [T] {
|
||||
&slice[self.indices()]
|
||||
}
|
||||
@ -883,6 +887,7 @@ fn subs_fmt_content(this: &Content, subs: &Subs, f: &mut fmt::Formatter) -> fmt:
|
||||
}
|
||||
write!(f, ", ^<{ambient_function_var:?}>)")
|
||||
}
|
||||
Content::ErasedLambda => write!(f, "ErasedLambda"),
|
||||
Content::RangedNumber(range) => {
|
||||
write!(f, "RangedNumber( {range:?})")
|
||||
}
|
||||
@ -1230,7 +1235,7 @@ impl IllegalCycleMark {
|
||||
pub struct Variable(u32);
|
||||
|
||||
macro_rules! define_const_var {
|
||||
($($(:pub)? $name:ident),* $(,)?) => {
|
||||
($($(#[$meta:meta])* $(:pub)? $name:ident),* $(,)?) => {
|
||||
#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
|
||||
enum ConstVariables {
|
||||
$( $name, )*
|
||||
@ -1238,7 +1243,7 @@ macro_rules! define_const_var {
|
||||
}
|
||||
|
||||
impl Variable {
|
||||
$( pub const $name: Variable = Variable(ConstVariables::$name as u32); )*
|
||||
$( $(#[$meta])* pub const $name: Variable = Variable(ConstVariables::$name as u32); )*
|
||||
|
||||
pub const NUM_RESERVED_VARS: usize = ConstVariables::FINAL_CONST_VAR as usize;
|
||||
}
|
||||
@ -1349,6 +1354,9 @@ define_const_var! {
|
||||
// The following are abound in derived abilities, so we cache them.
|
||||
:pub STR,
|
||||
:pub LIST_U8,
|
||||
|
||||
/// The erased lambda type.
|
||||
:pub ERASED_LAMBDA,
|
||||
}
|
||||
|
||||
impl Variable {
|
||||
@ -1694,25 +1702,31 @@ pub struct SubsSnapshot {
|
||||
}
|
||||
|
||||
impl Subs {
|
||||
// IFTTT INIT-TagNames
|
||||
pub const RESULT_TAG_NAMES: SubsSlice<TagName> = SubsSlice::new(0, 2);
|
||||
pub const TAG_NAME_ERR: SubsIndex<TagName> = SubsIndex::new(0);
|
||||
pub const TAG_NAME_OK: SubsIndex<TagName> = SubsIndex::new(1);
|
||||
pub const TAG_NAME_INVALID_NUM_STR: SubsIndex<TagName> = SubsIndex::new(2);
|
||||
pub const TAG_NAME_BAD_UTF_8: SubsIndex<TagName> = SubsIndex::new(3);
|
||||
pub const TAG_NAME_OUT_OF_BOUNDS: SubsIndex<TagName> = SubsIndex::new(4);
|
||||
// END INIT-TagNames
|
||||
|
||||
// IFTTT INIT-VariableSubsSlice
|
||||
pub const STR_SLICE: VariableSubsSlice = SubsSlice::new(0, 1);
|
||||
// END INIT-VariableSubsSlice
|
||||
|
||||
// IFTTT INIT-SymbolSubsSlice
|
||||
#[rustfmt::skip]
|
||||
pub const AB_ENCODING: SubsSlice<Symbol> = SubsSlice::new(0, 1);
|
||||
pub const AB_ENCODING: SubsSlice<Symbol> = SubsSlice::new(0, 1);
|
||||
#[rustfmt::skip]
|
||||
pub const AB_DECODING: SubsSlice<Symbol> = SubsSlice::new(1, 1);
|
||||
pub const AB_DECODING: SubsSlice<Symbol> = SubsSlice::new(1, 1);
|
||||
#[rustfmt::skip]
|
||||
pub const AB_HASHER: SubsSlice<Symbol> = SubsSlice::new(2, 1);
|
||||
pub const AB_HASHER: SubsSlice<Symbol> = SubsSlice::new(2, 1);
|
||||
#[rustfmt::skip]
|
||||
pub const AB_HASH: SubsSlice<Symbol> = SubsSlice::new(3, 1);
|
||||
pub const AB_HASH: SubsSlice<Symbol> = SubsSlice::new(3, 1);
|
||||
#[rustfmt::skip]
|
||||
pub const AB_EQ: SubsSlice<Symbol> = SubsSlice::new(4, 1);
|
||||
pub const AB_EQ: SubsSlice<Symbol> = SubsSlice::new(4, 1);
|
||||
// END INIT-SymbolSubsSlice
|
||||
|
||||
pub fn new() -> Self {
|
||||
Self::with_capacity(0)
|
||||
@ -1723,13 +1737,16 @@ impl Subs {
|
||||
|
||||
let mut tag_names = Vec::with_capacity(32);
|
||||
|
||||
// IFTTT INIT-TagNames
|
||||
tag_names.push(TagName("Err".into()));
|
||||
tag_names.push(TagName("Ok".into()));
|
||||
|
||||
tag_names.push(TagName("InvalidNumStr".into()));
|
||||
tag_names.push(TagName("BadUtf8".into()));
|
||||
tag_names.push(TagName("OutOfBounds".into()));
|
||||
// END INIT-TagNames
|
||||
|
||||
// IFTTT INIT-SymbolNames
|
||||
let mut symbol_names = Vec::with_capacity(32);
|
||||
|
||||
symbol_names.push(Symbol::ENCODE_ENCODING);
|
||||
@ -1737,13 +1754,15 @@ impl Subs {
|
||||
symbol_names.push(Symbol::HASH_HASHER);
|
||||
symbol_names.push(Symbol::HASH_HASH_ABILITY);
|
||||
symbol_names.push(Symbol::BOOL_EQ);
|
||||
// END INIT-SymbolNames
|
||||
|
||||
// IFTTT INIT-VariableSubsSlice
|
||||
let variables = vec![Variable::STR];
|
||||
// END INIT-VariableSubsSlice
|
||||
|
||||
let mut subs = Subs {
|
||||
utable: UnificationTable::default(),
|
||||
variables: vec![
|
||||
// Used for STR_SLICE
|
||||
Variable::STR,
|
||||
],
|
||||
variables,
|
||||
tag_names,
|
||||
symbol_names,
|
||||
field_names: Vec::new(),
|
||||
@ -1807,6 +1826,8 @@ impl Subs {
|
||||
Content::Structure(FlatType::Apply(Symbol::LIST_LIST, u8_slice)),
|
||||
);
|
||||
|
||||
subs.set_content(Variable::ERASED_LAMBDA, Content::ErasedLambda);
|
||||
|
||||
subs
|
||||
}
|
||||
|
||||
@ -2203,7 +2224,7 @@ impl Subs {
|
||||
| Content::RecursionVar { .. }
|
||||
| Content::RangedNumber(_)
|
||||
| Content::Error => return false,
|
||||
Content::LambdaSet(_) => return true,
|
||||
Content::LambdaSet(_) | Content::ErasedLambda => return false,
|
||||
Content::Structure(FlatType::Func(..)) => return true,
|
||||
Content::Structure(_) => return false,
|
||||
Content::Alias(_, _, real_var, _) => {
|
||||
@ -2367,7 +2388,10 @@ pub enum Content {
|
||||
structure: Variable,
|
||||
opt_name: Option<SubsIndex<Lowercase>>,
|
||||
},
|
||||
/// A resolved set of lambdas. Compatible when functions are kinded as lambda set.
|
||||
LambdaSet(LambdaSet),
|
||||
/// A type-erased lambda. Compatible when functions are not kinded.
|
||||
ErasedLambda,
|
||||
Structure(FlatType),
|
||||
Alias(Symbol, AliasVariables, Variable, AliasKind),
|
||||
RangedNumber(crate::num::NumericRange),
|
||||
@ -3595,6 +3619,7 @@ fn occurs(
|
||||
|
||||
occurs_union(subs, root_var, ctx, safe!(UnionLabels<Symbol>, solved))
|
||||
}
|
||||
ErasedLambda => Ok(()),
|
||||
RangedNumber(_range_vars) => Ok(()),
|
||||
})();
|
||||
|
||||
@ -3680,7 +3705,8 @@ fn explicit_substitute(
|
||||
| FlexAbleVar(_, _)
|
||||
| RigidAbleVar(_, _)
|
||||
| RecursionVar { .. }
|
||||
| Error => in_var,
|
||||
| Error
|
||||
| ErasedLambda => in_var,
|
||||
|
||||
Structure(flat_type) => {
|
||||
match flat_type {
|
||||
@ -3861,7 +3887,7 @@ fn get_var_names(
|
||||
subs.set_mark(var, Mark::GET_VAR_NAMES);
|
||||
|
||||
match desc.content {
|
||||
Error | FlexVar(None) | FlexAbleVar(None, _) => taken_names,
|
||||
Error | FlexVar(None) | FlexAbleVar(None, _) | ErasedLambda => taken_names,
|
||||
|
||||
FlexVar(Some(name_index)) | FlexAbleVar(Some(name_index), _) => add_name(
|
||||
subs,
|
||||
@ -4178,7 +4204,7 @@ fn content_to_err_type(
|
||||
ErrorType::Alias(symbol, err_args, Box::new(err_type), kind)
|
||||
}
|
||||
|
||||
LambdaSet(self::LambdaSet { .. }) => {
|
||||
LambdaSet(..) | ErasedLambda => {
|
||||
// Don't print lambda sets since we don't expect them to be exposed to the user
|
||||
ErrorType::Error
|
||||
}
|
||||
@ -4788,6 +4814,7 @@ impl StorageSubs {
|
||||
unspecialized: Self::offset_uls_slice(offsets, *unspecialized),
|
||||
ambient_function: Self::offset_variable(offsets, *ambient_function_var),
|
||||
}),
|
||||
ErasedLambda => ErasedLambda,
|
||||
RangedNumber(range) => RangedNumber(*range),
|
||||
Error => Content::Error,
|
||||
}
|
||||
@ -5155,7 +5182,7 @@ fn storage_copy_var_to_help(env: &mut StorageCopyVarToEnv<'_>, var: Variable) ->
|
||||
copy
|
||||
}
|
||||
|
||||
FlexVar(None) | Error => copy,
|
||||
FlexVar(None) | ErasedLambda | Error => copy,
|
||||
|
||||
RecursionVar {
|
||||
opt_name,
|
||||
@ -5390,6 +5417,7 @@ fn is_registered(content: &Content) -> bool {
|
||||
| Content::FlexAbleVar(..)
|
||||
| Content::RigidAbleVar(..) => false,
|
||||
Content::Structure(FlatType::EmptyRecord | FlatType::EmptyTagUnion) => false,
|
||||
Content::ErasedLambda => false,
|
||||
|
||||
Content::Structure(_)
|
||||
| Content::RecursionVar { .. }
|
||||
@ -5787,6 +5815,12 @@ fn copy_import_to_help(env: &mut CopyImportEnv<'_>, max_rank: Rank, var: Variabl
|
||||
copy
|
||||
}
|
||||
|
||||
ErasedLambda => {
|
||||
env.target.set(copy, make_descriptor(ErasedLambda));
|
||||
|
||||
copy
|
||||
}
|
||||
|
||||
RangedNumber(range) => {
|
||||
let new_content = RangedNumber(range);
|
||||
|
||||
@ -5861,7 +5895,7 @@ fn instantiate_rigids_help(subs: &mut Subs, max_rank: Rank, initial: Variable) {
|
||||
}
|
||||
})
|
||||
}
|
||||
FlexVar(_) | FlexAbleVar(_, _) | Error => (),
|
||||
FlexVar(_) | FlexAbleVar(_, _) | ErasedLambda | Error => (),
|
||||
|
||||
RecursionVar { structure, .. } => {
|
||||
stack.push(*structure);
|
||||
@ -6048,7 +6082,8 @@ pub fn get_member_lambda_sets_at_region(subs: &Subs, var: Variable, target_regio
|
||||
| Content::RecursionVar {
|
||||
structure: _,
|
||||
opt_name: _,
|
||||
} => {}
|
||||
}
|
||||
| Content::ErasedLambda => {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6073,7 +6108,7 @@ fn is_inhabited(subs: &Subs, var: Variable) -> bool {
|
||||
// are determined as illegal and reported during canonicalization, because you
|
||||
// cannot have a tag union without a non-recursive variant.
|
||||
| Content::RecursionVar { .. } => {}
|
||||
Content::LambdaSet(_) => {}
|
||||
Content::LambdaSet(_) | Content::ErasedLambda => {}
|
||||
Content::Structure(structure) => match structure {
|
||||
FlatType::Apply(_, args) => stack.extend(subs.get_subs_slice(*args)),
|
||||
FlatType::Func(args, _, ret) => {
|
||||
|
@ -11,16 +11,19 @@ use roc_mono::{
|
||||
use tempfile::tempdir;
|
||||
use test_solve_helpers::format_problems;
|
||||
|
||||
use crate::CompilerSettings;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct MonoOptions {
|
||||
pub(crate) struct MonoOptions {
|
||||
pub no_check: bool,
|
||||
}
|
||||
|
||||
pub fn write_compiled_ir<'a>(
|
||||
pub(crate) fn write_compiled_ir<'a>(
|
||||
writer: &mut impl io::Write,
|
||||
test_module: &str,
|
||||
dependencies: impl IntoIterator<Item = (&'a str, &'a str)>,
|
||||
options: MonoOptions,
|
||||
compiler_settings: CompilerSettings,
|
||||
allow_can_errors: bool,
|
||||
) -> io::Result<()> {
|
||||
use roc_packaging::cache::RocCacheDir;
|
||||
@ -41,6 +44,7 @@ pub fn write_compiled_ir<'a>(
|
||||
|
||||
let load_config = LoadConfig {
|
||||
target_info: roc_target::TargetInfo::default_x86_64(),
|
||||
function_kind: compiler_settings.function_kind,
|
||||
threading: Threading::Single,
|
||||
render: roc_reporting::report::RenderTarget::Generic,
|
||||
palette: roc_reporting::report::DEFAULT_PALETTE,
|
||||
|
@ -11,6 +11,7 @@ use libtest_mimic::{run, Arguments, Failed, Trial};
|
||||
use mono::MonoOptions;
|
||||
use regex::Regex;
|
||||
use roc_collections::VecMap;
|
||||
use roc_solve::FunctionKind;
|
||||
use test_solve_helpers::{
|
||||
infer_queries, Elaboration, InferOptions, InferredProgram, InferredQuery, MUTLILINE_MARKER,
|
||||
};
|
||||
@ -36,6 +37,10 @@ lazy_static! {
|
||||
.join("uitest")
|
||||
.join("tests");
|
||||
|
||||
/// # +set <setting>
|
||||
static ref RE_SETTING: Regex =
|
||||
Regex::new(r#"# \+set (?P<setting>.*?)=(?P<value>.*)"#).unwrap();
|
||||
|
||||
/// # +opt can:<opt>
|
||||
static ref RE_OPT_CAN: Regex =
|
||||
Regex::new(r#"# \+opt can:(?P<opt>.*)"#).unwrap();
|
||||
@ -94,6 +99,7 @@ fn into_test(path: PathBuf) -> io::Result<Trial> {
|
||||
fn run_test(path: PathBuf) -> Result<(), Failed> {
|
||||
let data = std::fs::read_to_string(&path)?;
|
||||
let TestCase {
|
||||
compiler_settings,
|
||||
can_options,
|
||||
infer_options,
|
||||
emit_options,
|
||||
@ -109,6 +115,7 @@ fn run_test(path: PathBuf) -> Result<(), Failed> {
|
||||
.map(|(md, src)| (&**md, &**src)),
|
||||
infer_options,
|
||||
can_options.allow_errors,
|
||||
compiler_settings.function_kind,
|
||||
)?;
|
||||
|
||||
{
|
||||
@ -121,6 +128,7 @@ fn run_test(path: PathBuf) -> Result<(), Failed> {
|
||||
&mut fd,
|
||||
program,
|
||||
inferred_program,
|
||||
compiler_settings,
|
||||
can_options,
|
||||
mono_options,
|
||||
emit_options,
|
||||
@ -141,6 +149,7 @@ struct Modules<'a> {
|
||||
}
|
||||
|
||||
struct TestCase<'a> {
|
||||
compiler_settings: CompilerSettings,
|
||||
can_options: CanOptions,
|
||||
infer_options: InferOptions,
|
||||
mono_options: MonoOptions,
|
||||
@ -148,6 +157,18 @@ struct TestCase<'a> {
|
||||
program: Modules<'a>,
|
||||
}
|
||||
|
||||
struct CompilerSettings {
|
||||
function_kind: FunctionKind,
|
||||
}
|
||||
|
||||
impl Default for CompilerSettings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
function_kind: FunctionKind::LambdaSet,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct CanOptions {
|
||||
allow_errors: bool,
|
||||
@ -166,6 +187,7 @@ impl<'a> TestCase<'a> {
|
||||
data = data[..drop_at].trim_end();
|
||||
}
|
||||
|
||||
let compiler_settings = Self::parse_compiler_settings(data)?;
|
||||
let can_options = Self::parse_can_options(data)?;
|
||||
let infer_options = Self::parse_infer_options(data)?;
|
||||
let mono_options = Self::parse_mono_options(data)?;
|
||||
@ -174,6 +196,7 @@ impl<'a> TestCase<'a> {
|
||||
let program = Self::parse_modules(data);
|
||||
|
||||
Ok(TestCase {
|
||||
compiler_settings,
|
||||
can_options,
|
||||
infer_options,
|
||||
mono_options,
|
||||
@ -229,6 +252,26 @@ impl<'a> TestCase<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_compiler_settings(data: &str) -> Result<CompilerSettings, Failed> {
|
||||
let mut settings = CompilerSettings::default();
|
||||
|
||||
let found_settings = RE_SETTING.captures_iter(data);
|
||||
for set in found_settings {
|
||||
let setting = set.name("setting").unwrap().as_str();
|
||||
let value = set.name("value").unwrap().as_str();
|
||||
match setting.trim() {
|
||||
"function_kind" => match value.trim() {
|
||||
"lambda_set" => settings.function_kind = FunctionKind::LambdaSet,
|
||||
"erased" => settings.function_kind = FunctionKind::Erased,
|
||||
other => return Err(format!("unknown function kind: {other:?}").into()),
|
||||
},
|
||||
other => return Err(format!("unknown compiler setting: {other:?}").into()),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(settings)
|
||||
}
|
||||
|
||||
fn parse_can_options(data: &str) -> Result<CanOptions, Failed> {
|
||||
let mut can_opts = CanOptions::default();
|
||||
|
||||
@ -321,6 +364,7 @@ fn assemble_query_output(
|
||||
writer: &mut impl io::Write,
|
||||
program: Modules<'_>,
|
||||
inferred_program: InferredProgram,
|
||||
compiler_settings: CompilerSettings,
|
||||
can_options: CanOptions,
|
||||
mono_options: MonoOptions,
|
||||
emit_options: EmitOptions,
|
||||
@ -369,6 +413,7 @@ fn assemble_query_output(
|
||||
test_module,
|
||||
other_modules,
|
||||
mono_options,
|
||||
compiler_settings,
|
||||
can_options.allow_errors,
|
||||
)?;
|
||||
}
|
||||
|
@ -0,0 +1,87 @@
|
||||
# +set function_kind=erased
|
||||
# +emit:mono
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
f = \s ->
|
||||
if Bool.true
|
||||
then \{} -> ""
|
||||
# ^^^^^^^^^ {}* -?-> Str
|
||||
else \{} -> s
|
||||
# ^^^^^^^^ {}* -?-> Str
|
||||
|
||||
main = (f "") {}
|
||||
# ^^^^ {} -?-> Str
|
||||
|
||||
# -emit:mono
|
||||
procedure Bool.2 ():
|
||||
let Bool.23 : Int1 = true;
|
||||
ret Bool.23;
|
||||
|
||||
procedure Test.1 (Test.2):
|
||||
let Test.34 : Int1 = CallByName Bool.2;
|
||||
if Test.34 then
|
||||
dec Test.2;
|
||||
let Test.38 : FunPtr(({}) -> Str) = FunctionPointer Test.3;
|
||||
let Test.35 : ?Erased = ErasedMake { value: <null>, callee: Test.38 };
|
||||
ret Test.35;
|
||||
else
|
||||
let Test.33 : {Str} = Struct {Test.2};
|
||||
let Test.31 : [<rnu><null>, C {Str}] = TagId(0) Test.33;
|
||||
let Test.32 : FunPtr(({}, ?Erased) -> Str) = FunctionPointer Test.4;
|
||||
let Test.26 : ?Erased = ErasedMake { value: Test.31, callee: Test.32 };
|
||||
ret Test.26;
|
||||
|
||||
procedure Test.3 (Test.36):
|
||||
let Test.37 : Str = "";
|
||||
ret Test.37;
|
||||
|
||||
procedure Test.4 (Test.27, #Attr.12):
|
||||
let Test.29 : [<rnu><null>, C {Str}] = ErasedLoad #Attr.12 .Value;
|
||||
let Test.30 : {Str} = UnionAtIndex (Id 0) (Index 0) Test.29;
|
||||
joinpoint #Derived_gen.0:
|
||||
let Test.2 : Str = StructAtIndex 0 Test.30;
|
||||
ret Test.2;
|
||||
in
|
||||
let #Derived_gen.1 : Int1 = lowlevel RefCountIsUnique Test.29;
|
||||
if #Derived_gen.1 then
|
||||
free Test.29;
|
||||
jump #Derived_gen.0;
|
||||
else
|
||||
inc Test.30;
|
||||
decref Test.29;
|
||||
jump #Derived_gen.0;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.6 : {} = Struct {};
|
||||
let Test.16 : Str = "";
|
||||
let Test.39 : FunPtr((Str) -> ?Erased) = FunctionPointer Test.1;
|
||||
let Test.17 : ?Erased = ErasedMake { value: <null>, callee: Test.39 };
|
||||
joinpoint Test.18 Test.7:
|
||||
joinpoint Test.8 Test.5:
|
||||
ret Test.5;
|
||||
in
|
||||
let Test.9 : Ptr([]) = ErasedLoad Test.7 .ValuePtr;
|
||||
let Test.11 : Ptr([]) = NullPointer;
|
||||
let Test.10 : Int1 = lowlevel Eq Test.9 Test.11;
|
||||
if Test.10 then
|
||||
dec Test.7;
|
||||
let Test.12 : FunPtr(({}) -> Str) = ErasedLoad Test.7 .Callee;
|
||||
let Test.13 : Str = CallByPtr Test.12 Test.6;
|
||||
jump Test.8 Test.13;
|
||||
else
|
||||
let Test.14 : FunPtr(({}, ?Erased) -> Str) = ErasedLoad Test.7 .Callee;
|
||||
let Test.15 : Str = CallByPtr Test.14 Test.6 Test.7;
|
||||
jump Test.8 Test.15;
|
||||
in
|
||||
let Test.19 : Ptr([]) = ErasedLoad Test.17 .ValuePtr;
|
||||
let Test.21 : Ptr([]) = NullPointer;
|
||||
let Test.20 : Int1 = lowlevel Eq Test.19 Test.21;
|
||||
if Test.20 then
|
||||
dec Test.17;
|
||||
let Test.22 : FunPtr((Str) -> ?Erased) = ErasedLoad Test.17 .Callee;
|
||||
let Test.23 : ?Erased = CallByPtr Test.22 Test.16;
|
||||
jump Test.18 Test.23;
|
||||
else
|
||||
let Test.24 : FunPtr((Str, ?Erased) -> ?Erased) = ErasedLoad Test.17 .Callee;
|
||||
let Test.25 : ?Erased = CallByPtr Test.24 Test.16 Test.17;
|
||||
jump Test.18 Test.25;
|
@ -3,22 +3,22 @@
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
f = \{} ->
|
||||
#^{-1} <1599><116>{} -<119>[[f(1)]]-> <115>[Ok <1607>{}]<79>*
|
||||
#^{-1} <1600><117>{} -<120>[[f(1)]]-> <116>[Ok <1608>{}]<80>*
|
||||
when g {} is
|
||||
# ^ <1589><1607>{} -<1597>[[g(2)]]-> <71>[Ok <1607>{}]<101>*
|
||||
# ^ <1590><1608>{} -<1598>[[g(2)]]-> <72>[Ok <1608>{}]<102>*
|
||||
_ -> Ok {}
|
||||
|
||||
g = \{} ->
|
||||
#^{-1} <1589><1607>{} -<1597>[[g(2)]]-> <71>[Ok <1607>{}]<101>*
|
||||
#^{-1} <1590><1608>{} -<1598>[[g(2)]]-> <72>[Ok <1608>{}]<102>*
|
||||
when h {} is
|
||||
# ^ <1594><1607>{} -<1602>[[h(3)]]-> <93>[Ok <1607>{}]<123>*
|
||||
# ^ <1595><1608>{} -<1603>[[h(3)]]-> <94>[Ok <1608>{}]<124>*
|
||||
_ -> Ok {}
|
||||
|
||||
h = \{} ->
|
||||
#^{-1} <1594><1607>{} -<1602>[[h(3)]]-> <93>[Ok <1607>{}]<123>*
|
||||
#^{-1} <1595><1608>{} -<1603>[[h(3)]]-> <94>[Ok <1608>{}]<124>*
|
||||
when f {} is
|
||||
# ^ <1599><116>{} -<119>[[f(1)]]-> <115>[Ok <1607>{}]<79>*
|
||||
# ^ <1600><117>{} -<120>[[f(1)]]-> <116>[Ok <1608>{}]<80>*
|
||||
_ -> Ok {}
|
||||
|
||||
main = f {}
|
||||
# ^ <1609><132>{} -<135>[[f(1)]]-> <137>[Ok <1607>{}]<1608>w_a
|
||||
# ^ <1610><133>{} -<136>[[f(1)]]-> <138>[Ok <1608>{}]<1609>w_a
|
||||
|
@ -5,7 +5,7 @@ use roc_debug_flags::{dbg_do, dbg_set};
|
||||
use roc_debug_flags::{
|
||||
ROC_PRINT_MISMATCHES, ROC_PRINT_UNIFICATIONS, ROC_VERIFY_OCCURS_ONE_RECURSION,
|
||||
};
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_error_macros::{internal_error, todo_lambda_erasure};
|
||||
use roc_module::ident::{Lowercase, TagName};
|
||||
use roc_module::symbol::{ModuleId, Symbol};
|
||||
use roc_types::num::{FloatWidth, IntLitWidth, NumericRange};
|
||||
@ -611,6 +611,7 @@ fn unify_context<M: MetaCollector>(env: &mut Env, pool: &mut Pool, ctx: Context)
|
||||
unify_opaque(env, pool, &ctx, *symbol, *args, *real_var)
|
||||
}
|
||||
LambdaSet(lset) => unify_lambda_set(env, pool, &ctx, *lset, &ctx.second_desc.content),
|
||||
ErasedLambda => unify_erased_lambda(env, pool, &ctx, &ctx.second_desc.content),
|
||||
&RangedNumber(range_vars) => unify_ranged_number(env, pool, &ctx, range_vars),
|
||||
Error => {
|
||||
// Error propagates. Whatever we're comparing it to doesn't matter!
|
||||
@ -680,7 +681,7 @@ fn unify_ranged_number<M: MetaCollector>(
|
||||
Some(range) => merge(env, ctx, RangedNumber(range)),
|
||||
None => not_in_range_mismatch(),
|
||||
},
|
||||
LambdaSet(..) => mismatch!(),
|
||||
LambdaSet(..) | ErasedLambda => mismatch!(),
|
||||
Error => merge(env, ctx, Error),
|
||||
}
|
||||
}
|
||||
@ -1004,6 +1005,7 @@ fn unify_alias<M: MetaCollector>(
|
||||
check_and_merge_valid_range(env, pool, ctx, ctx.second, *other_range_vars, ctx.first)
|
||||
}
|
||||
LambdaSet(..) => mismatch!("cannot unify alias {:?} with lambda set {:?}: lambda sets should never be directly behind an alias!", ctx.first, other_content),
|
||||
ErasedLambda => mismatch!("cannot unify alias {:?} with an erased lambda!", ctx.first),
|
||||
Error => merge(env, ctx, Error),
|
||||
}
|
||||
}
|
||||
@ -1198,10 +1200,9 @@ fn unify_structure<M: MetaCollector>(
|
||||
"Cannot unify structure \n{:?} \nwith lambda set\n {:?}",
|
||||
roc_types::subs::SubsFmtContent(&Content::Structure(*flat_type), env.subs),
|
||||
roc_types::subs::SubsFmtContent(other, env.subs),
|
||||
// &flat_type,
|
||||
// other
|
||||
)
|
||||
}
|
||||
ErasedLambda => mismatch!(),
|
||||
RangedNumber(other_range_vars) => {
|
||||
check_and_merge_valid_range(env, pool, ctx, ctx.second, *other_range_vars, ctx.first)
|
||||
}
|
||||
@ -1209,6 +1210,38 @@ fn unify_structure<M: MetaCollector>(
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn unify_erased_lambda<M: MetaCollector>(
|
||||
env: &mut Env,
|
||||
pool: &mut Pool,
|
||||
ctx: &Context,
|
||||
other: &Content,
|
||||
) -> Outcome<M> {
|
||||
match other {
|
||||
FlexVar(_) => {
|
||||
if M::UNIFYING_SPECIALIZATION {
|
||||
todo_lambda_erasure!()
|
||||
}
|
||||
merge(env, ctx, Content::ErasedLambda)
|
||||
}
|
||||
Content::LambdaSet(..) => {
|
||||
if M::UNIFYING_SPECIALIZATION {
|
||||
todo_lambda_erasure!()
|
||||
}
|
||||
merge(env, ctx, Content::ErasedLambda)
|
||||
}
|
||||
ErasedLambda => merge(env, ctx, Content::ErasedLambda),
|
||||
RecursionVar { structure, .. } => unify_pool(env, pool, ctx.first, *structure, ctx.mode),
|
||||
RigidVar(..) | RigidAbleVar(..) => mismatch!("Lambda sets never unify with rigid"),
|
||||
FlexAbleVar(..) => mismatch!("Lambda sets should never have abilities attached to them"),
|
||||
Structure(..) => mismatch!("Lambda set cannot unify with non-lambda set structure"),
|
||||
RangedNumber(..) => mismatch!("Lambda sets are never numbers"),
|
||||
Alias(..) => mismatch!("Lambda set can never be directly under an alias!"),
|
||||
Error => merge(env, ctx, Error),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn unify_lambda_set<M: MetaCollector>(
|
||||
@ -1254,6 +1287,7 @@ fn unify_lambda_set<M: MetaCollector>(
|
||||
env.remove_recursion_pair(ctx.first, ctx.second);
|
||||
outcome
|
||||
}
|
||||
ErasedLambda => merge(env, ctx, ErasedLambda),
|
||||
RigidVar(..) | RigidAbleVar(..) => mismatch!("Lambda sets never unify with rigid"),
|
||||
FlexAbleVar(..) => mismatch!("Lambda sets should never have abilities attached to them"),
|
||||
Structure(..) => mismatch!("Lambda set cannot unify with non-lambda set structure"),
|
||||
@ -3521,7 +3555,8 @@ fn unify_rigid<M: MetaCollector>(
|
||||
| RecursionVar { .. }
|
||||
| Structure(_)
|
||||
| Alias(..)
|
||||
| LambdaSet(..) => {
|
||||
| LambdaSet(..)
|
||||
| ErasedLambda => {
|
||||
// Type mismatch! Rigid can only unify with flex, even if the
|
||||
// rigid names are the same.
|
||||
mismatch!("Rigid {:?} with {:?}", ctx.first, &other)
|
||||
@ -3581,7 +3616,8 @@ fn unify_rigid_able<M: MetaCollector>(
|
||||
| Structure(_)
|
||||
| Alias(..)
|
||||
| RangedNumber(..)
|
||||
| LambdaSet(..) => {
|
||||
| LambdaSet(..)
|
||||
| ErasedLambda => {
|
||||
// Type mismatch! Rigid can only unify with flex, even if the
|
||||
// rigid names are the same.
|
||||
mismatch!("Rigid {:?} with {:?}", ctx.first, &other)
|
||||
@ -3621,7 +3657,8 @@ fn unify_flex<M: MetaCollector>(
|
||||
| Structure(_)
|
||||
| Alias(_, _, _, _)
|
||||
| RangedNumber(..)
|
||||
| LambdaSet(..) => {
|
||||
| LambdaSet(..)
|
||||
| ErasedLambda => {
|
||||
// TODO special-case boolean here
|
||||
// In all other cases, if left is flex, defer to right.
|
||||
merge(env, ctx, *other)
|
||||
@ -3716,7 +3753,7 @@ fn unify_flex_able<M: MetaCollector>(
|
||||
}
|
||||
|
||||
RigidVar(_) => mismatch!("FlexAble can never unify with non-able Rigid"),
|
||||
LambdaSet(..) => mismatch!("FlexAble with LambdaSet"),
|
||||
LambdaSet(..) | ErasedLambda => mismatch!("FlexAble with LambdaSet"),
|
||||
|
||||
Alias(name, _args, _real_var, AliasKind::Opaque) => {
|
||||
// Opaque type wins
|
||||
@ -3885,6 +3922,8 @@ fn unify_recursion<M: MetaCollector>(
|
||||
unify_pool(env, pool, structure, ctx.second, ctx.mode)
|
||||
}
|
||||
|
||||
ErasedLambda => mismatch!(),
|
||||
|
||||
Error => merge(env, ctx, Error),
|
||||
};
|
||||
|
||||
|
@ -20,6 +20,7 @@ roc_packaging = { path = "../packaging" }
|
||||
roc_parse = { path = "../compiler/parse" }
|
||||
roc_region = { path = "../compiler/region" }
|
||||
roc_reporting = { path = "../reporting" }
|
||||
roc_solve = { path = "../compiler/solve" }
|
||||
roc_target = { path = "../compiler/roc_target" }
|
||||
roc_types = { path = "../compiler/types" }
|
||||
|
||||
|
@ -454,6 +454,7 @@ pub fn load_module_for_docs(filename: PathBuf) -> LoadedModule {
|
||||
let arena = Bump::new();
|
||||
let load_config = LoadConfig {
|
||||
target_info: roc_target::TargetInfo::default_x86_64(), // This is just type-checking for docs, so "target" doesn't matter
|
||||
function_kind: roc_solve::FunctionKind::LambdaSet,
|
||||
render: roc_reporting::report::RenderTarget::ColorTerminal,
|
||||
palette: roc_reporting::report::DEFAULT_PALETTE,
|
||||
threading: Threading::AllAvailable,
|
||||
|
@ -116,4 +116,14 @@ macro_rules! todo_abilities {
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! todo_lambda_erasure {
|
||||
() => {
|
||||
$crate::_incomplete_project!("Lambda Erasure", 5575)
|
||||
};
|
||||
($($arg:tt)+) => {
|
||||
$crate::_incomplete_project!("Lambda Erasure", 5575, $($arg)+)
|
||||
};
|
||||
}
|
||||
|
||||
// END LARGE SCALE PROJECTS
|
||||
|
@ -10,7 +10,8 @@ use roc_build::{
|
||||
},
|
||||
};
|
||||
use roc_collections::MutMap;
|
||||
use roc_load::{ExecutionMode, LoadConfig, LoadedModule, LoadingProblem, Threading};
|
||||
use roc_error_macros::todo_lambda_erasure;
|
||||
use roc_load::{ExecutionMode, FunctionKind, LoadConfig, LoadedModule, LoadingProblem, Threading};
|
||||
use roc_mono::ir::{generate_glue_procs, GlueProc, OptLevel};
|
||||
use roc_mono::layout::{GlobalLayoutInterner, LayoutCache, LayoutInterner};
|
||||
use roc_packaging::cache::{self, RocCacheDir};
|
||||
@ -321,6 +322,7 @@ fn number_lambda_sets(subs: &Subs, initial: Variable) -> Vec<Variable> {
|
||||
stack.push(*var);
|
||||
}
|
||||
}
|
||||
ErasedLambda => todo_lambda_erasure!(),
|
||||
&RangedNumber(_) => {}
|
||||
}
|
||||
}
|
||||
@ -334,6 +336,8 @@ pub fn load_types(
|
||||
ignore_errors: IgnoreErrors,
|
||||
) -> Result<Vec<Types>, io::Error> {
|
||||
let target_info = (&Triple::host()).into();
|
||||
// TODO the function kind may need to be parameterizable.
|
||||
let function_kind = FunctionKind::LambdaSet;
|
||||
let arena = &Bump::new();
|
||||
let LoadedModule {
|
||||
module_id: home,
|
||||
@ -350,6 +354,7 @@ pub fn load_types(
|
||||
RocCacheDir::Persistent(cache::roc_cache_dir().as_path()),
|
||||
LoadConfig {
|
||||
target_info,
|
||||
function_kind,
|
||||
render: RenderTarget::Generic,
|
||||
palette: DEFAULT_PALETTE,
|
||||
threading,
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user