diff --git a/crates/compiler/alias_analysis/src/lib.rs b/crates/compiler/alias_analysis/src/lib.rs index cae53c3a08..b4165b4ffb 100644 --- a/crates/compiler/alias_analysis/src/lib.rs +++ b/crates/compiler/alias_analysis/src/lib.rs @@ -9,13 +9,13 @@ use morphic_lib::{ TypeDefBuilder, TypeId, TypeName, UpdateModeVar, ValueId, }; use roc_collections::all::{MutMap, MutSet}; -use roc_error_macros::{internal_error, todo_lambda_erasure}; +use roc_error_macros::internal_error; 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, @@ -201,7 +201,17 @@ where host_exposed_functions.push((bytes, hels.proc_layout.arguments)); } - RawFunctionLayout::ErasedFunction(..) => todo_lambda_erasure!(), + 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, @@ -789,8 +799,15 @@ fn call_spec<'a>( let module = MOD_APP; builder.add_call(block, spec_var, module, name, arg_value_id) } - ByPointer { .. } => { - todo_lambda_erasure!() + 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: _, @@ -1521,9 +1538,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 { .. } => todo_lambda_erasure!(), - ErasedMake { .. } => todo_lambda_erasure!(), - ErasedLoad { .. } => todo_lambda_erasure!(), + 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))?; @@ -1639,8 +1675,8 @@ fn layout_spec_help<'a>( _ => internal_error!("somehow, a non-recursive layout is under a recursive pointer"), }, - FunctionPointer(_) => todo_lambda_erasure!(), - Erased(_) => todo_lambda_erasure!(), + FunctionPointer(_) => function_pointer_type(builder), + Erased(_) => erasure_type(builder), } } @@ -1717,3 +1753,78 @@ fn new_num(builder: &mut FuncDefBuilder, block: BlockId) -> Result { // we model all our numbers as unit values builder.add_make_tuple(block, &[]) } + +fn function_pointer_type(builder: &mut TC) -> Result { + builder.add_tuple_type(&[]) +} + +const ERASURE_CALEE_INDEX: u32 = 0; +const ERASURE_VALUE_INDEX: u32 = 1; + +/// Erasure type modeled as +/// +/// ``` +/// Tuple(callee: FnPtr, value: HeapCell) +/// ``` +fn erasure_type(builder: &mut TC) -> Result { + 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(builder: &mut TC) -> TypeId { + builder.add_heap_cell_type() +} + +fn box_erasure_value_unknown( + builder: &mut FuncDefBuilder, + block: BlockId, + value: ValueId, +) -> Result { + 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 { + let heap_cell = erasure_box_value_type(builder); + builder.add_unknown_with(block, &[], heap_cell) +} + +/// Erasure value modeled as +/// +/// ``` +/// 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 { + builder.add_make_tuple(block, &[value, callee]) +} + +fn erasure_load( + builder: &mut FuncDefBuilder, + block: BlockId, + value: ValueId, + field: ErasedField, + loaded_type: TypeId, +) -> Result { + match field { + ErasedField::Callee => builder.add_get_tuple_field(block, value, ERASURE_CALEE_INDEX), + ErasedField::Value => { + 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) + } + } +}