Merge pull request #5576 from roc-lang/spike-erasure

Implement function erasure
This commit is contained in:
Ayaz 2023-07-17 03:27:29 -05:00 committed by GitHub
commit b36ad76cdd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
112 changed files with 3068 additions and 629 deletions

5
Cargo.lock generated
View file

@ -3351,6 +3351,7 @@ dependencies = [
"roc_parse", "roc_parse",
"roc_region", "roc_region",
"roc_reporting", "roc_reporting",
"roc_solve",
"roc_target", "roc_target",
"roc_types", "roc_types",
"snafu", "snafu",
@ -3591,6 +3592,7 @@ dependencies = [
"roc_mono", "roc_mono",
"roc_packaging", "roc_packaging",
"roc_reporting", "roc_reporting",
"roc_solve",
"roc_target", "roc_target",
"serde", "serde",
"serial_test", "serial_test",
@ -3611,6 +3613,7 @@ dependencies = [
"roc_module", "roc_module",
"roc_packaging", "roc_packaging",
"roc_reporting", "roc_reporting",
"roc_solve",
"roc_target", "roc_target",
"roc_types", "roc_types",
] ]
@ -3794,6 +3797,7 @@ dependencies = [
"roc_problem", "roc_problem",
"roc_region", "roc_region",
"roc_reporting", "roc_reporting",
"roc_solve",
"roc_std", "roc_std",
"roc_target", "roc_target",
"roc_types", "roc_types",
@ -3849,6 +3853,7 @@ dependencies = [
"roc_parse", "roc_parse",
"roc_repl_eval", "roc_repl_eval",
"roc_reporting", "roc_reporting",
"roc_solve",
"roc_target", "roc_target",
"roc_types", "roc_types",
"tempfile", "tempfile",

View file

@ -12,6 +12,7 @@ pub fn load_module(
) -> LoadedModule { ) -> LoadedModule {
let load_config = LoadConfig { let load_config = LoadConfig {
target_info: TargetInfo::default_x86_64(), // editor only needs type info, so this is unused 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, render: roc_reporting::report::RenderTarget::ColorTerminal,
palette: DEFAULT_PALETTE, palette: DEFAULT_PALETTE,
threading, threading,

View file

@ -1489,6 +1489,8 @@ fn adjust_rank_content(
rank rank
} }
ErasedLambda => group_rank,
RangedNumber(_vars) => group_rank, RangedNumber(_vars) => group_rank,
} }
} }
@ -1669,6 +1671,7 @@ fn instantiate_rigids_help(
} }
} }
ErasedLambda => {}
RangedNumber(_vars) => {} RangedNumber(_vars) => {}
} }
@ -1981,6 +1984,12 @@ fn deep_copy_var_help(
copy copy
} }
ErasedLambda => {
subs.set(copy, make_descriptor(ErasedLambda));
copy
}
RangedNumber(vars) => { RangedNumber(vars) => {
let new_content = RangedNumber(vars); let new_content = RangedNumber(vars);

View file

@ -385,7 +385,7 @@ pub fn test(_matches: &ArgMatches, _triple: Triple) -> io::Result<i32> {
#[cfg(not(windows))] #[cfg(not(windows))]
pub fn test(matches: &ArgMatches, triple: Triple) -> io::Result<i32> { pub fn test(matches: &ArgMatches, triple: Triple) -> io::Result<i32> {
use roc_build::program::report_problems_monomorphized; 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_packaging::cache;
use roc_target::TargetInfo; use roc_target::TargetInfo;
@ -427,10 +427,13 @@ pub fn test(matches: &ArgMatches, triple: Triple) -> io::Result<i32> {
let target = &triple; let target = &triple;
let opt_level = opt_level; let opt_level = opt_level;
let target_info = TargetInfo::from(target); 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 // Step 1: compile the app and generate the .o file
let load_config = LoadConfig { let load_config = LoadConfig {
target_info, target_info,
function_kind,
// TODO: expose this from CLI? // TODO: expose this from CLI?
render: roc_reporting::report::RenderTarget::ColorTerminal, render: roc_reporting::report::RenderTarget::ColorTerminal,
palette: roc_reporting::report::DEFAULT_PALETTE, palette: roc_reporting::report::DEFAULT_PALETTE,

View file

@ -11,7 +11,7 @@ use roc_docs::generate_docs_html;
use roc_error_macros::user_error; use roc_error_macros::user_error;
use roc_gen_dev::AssemblyBackendMode; use roc_gen_dev::AssemblyBackendMode;
use roc_gen_llvm::llvm::build::LlvmBackendMode; 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_packaging::cache::{self, RocCacheDir};
use roc_target::Target; use roc_target::Target;
use std::fs::{self, FileType}; use std::fs::{self, FileType};
@ -123,10 +123,12 @@ fn main() -> io::Result<()> {
.get_one::<String>(FLAG_TARGET) .get_one::<String>(FLAG_TARGET)
.and_then(|s| Target::from_str(s).ok()) .and_then(|s| Target::from_str(s).ok())
.unwrap_or_default(); .unwrap_or_default();
let function_kind = FunctionKind::LambdaSet;
roc_linker::generate_stub_lib( roc_linker::generate_stub_lib(
input_path, input_path,
RocCacheDir::Persistent(cache::roc_cache_dir().as_path()), RocCacheDir::Persistent(cache::roc_cache_dir().as_path()),
&target.to_triple(), &target.to_triple(),
function_kind,
) )
} }
Some((CMD_BUILD, matches)) => { Some((CMD_BUILD, matches)) => {

View file

@ -14,8 +14,8 @@ use roc_module::low_level::LowLevel;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_mono::ir::{ use roc_mono::ir::{
Call, CallType, EntryPoint, Expr, HigherOrderLowLevel, HostExposedLayouts, ListLiteralElement, Call, CallType, EntryPoint, ErasedField, Expr, HigherOrderLowLevel, HostExposedLayouts,
Literal, ModifyRc, OptLevel, Proc, ProcLayout, SingleEntryPoint, Stmt, ListLiteralElement, Literal, ModifyRc, OptLevel, Proc, ProcLayout, SingleEntryPoint, Stmt,
}; };
use roc_mono::layout::{ use roc_mono::layout::{
Builtin, InLayout, Layout, LayoutInterner, LayoutRepr, Niche, RawFunctionLayout, Builtin, InLayout, Layout, LayoutInterner, LayoutRepr, Niche, RawFunctionLayout,
@ -181,6 +181,7 @@ where
let mut type_definitions = MutSet::default(); let mut type_definitions = MutSet::default();
let mut host_exposed_functions = Vec::new(); let mut host_exposed_functions = Vec::new();
let mut erased_functions = Vec::new();
// all other functions // all other functions
for proc in procs { for proc in procs {
@ -201,6 +202,17 @@ where
host_exposed_functions.push((bytes, hels.proc_layout.arguments)); 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(_) => { RawFunctionLayout::ZeroArgumentThunk(_) => {
let bytes = func_name_bytes_help( let bytes = func_name_bytes_help(
hels.symbol, hels.symbol,
@ -226,6 +238,11 @@ where
let (spec, type_names) = proc_spec(arena, interner, proc)?; 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); type_definitions.extend(type_names);
m.add_func(func_name, spec)?; m.add_func(func_name, spec)?;
@ -253,6 +270,7 @@ where
entry_point_layout, entry_point_layout,
Some(roc_main), Some(roc_main),
&host_exposed_functions, &host_exposed_functions,
&erased_functions,
)?; )?;
type_definitions.extend(env.type_names); type_definitions.extend(env.type_names);
@ -279,8 +297,14 @@ where
.collect(); .collect();
let mut env = Env::new(); let mut env = Env::new();
let entry_point_function = let entry_point_function = build_entry_point(
build_entry_point(&mut env, interner, layout, None, &host_exposed)?; &mut env,
interner,
layout,
None,
&host_exposed,
&erased_functions,
)?;
type_definitions.extend(env.type_names); type_definitions.extend(env.type_names);
@ -364,6 +388,7 @@ fn build_entry_point<'a>(
layout: roc_mono::ir::ProcLayout<'a>, layout: roc_mono::ir::ProcLayout<'a>,
entry_point_function: Option<FuncName>, entry_point_function: Option<FuncName>,
host_exposed_functions: &[([u8; SIZE], &'a [InLayout<'a>])], host_exposed_functions: &[([u8; SIZE], &'a [InLayout<'a>])],
erased_functions: &[([u8; SIZE], &'a [InLayout<'a>])],
) -> Result<FuncDef> { ) -> Result<FuncDef> {
let mut builder = FuncDefBuilder::new(); let mut builder = FuncDefBuilder::new();
let outer_block = builder.add_block(); 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 // 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); let host_exposed_func_name = FuncName(name_bytes);
if Some(host_exposed_func_name) == entry_point_function { if Some(host_exposed_func_name) == entry_point_function {
@ -788,6 +813,16 @@ fn call_spec<'a>(
let module = MOD_APP; let module = MOD_APP;
builder.add_call(block, spec_var, module, name, arg_value_id) builder.add_call(block, spec_var, module, name, arg_value_id)
} }
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 {
foreign_symbol: _, foreign_symbol: _,
ret_layout, ret_layout,
@ -1517,6 +1552,28 @@ fn expr_spec<'a>(
let value = with_new_heap_cell(builder, block, union_data)?; let value = with_new_heap_cell(builder, block, union_data)?;
builder.add_make_named(block, MOD_APP, type_name, value) 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(_) => { RuntimeErrorFunction(_) => {
let type_id = layout_spec(env, builder, interner, interner.get_repr(layout))?; 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"), _ => 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 // we model all our numbers as unit values
builder.add_make_tuple(block, &[]) 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)
}
}
}

View file

@ -8,8 +8,8 @@ use roc_gen_dev::AssemblyBackendMode;
use roc_gen_llvm::llvm::build::{module_from_builtins, LlvmBackendMode}; use roc_gen_llvm::llvm::build::{module_from_builtins, LlvmBackendMode};
use roc_gen_llvm::llvm::externs::add_default_roc_externs; use roc_gen_llvm::llvm::externs::add_default_roc_externs;
use roc_load::{ use roc_load::{
EntryPoint, ExecutionMode, ExpectMetadata, LoadConfig, LoadMonomorphizedError, LoadedModule, EntryPoint, ExecutionMode, ExpectMetadata, FunctionKind, LoadConfig, LoadMonomorphizedError,
LoadingProblem, MonomorphizedModule, Threading, LoadedModule, LoadingProblem, MonomorphizedModule, Threading,
}; };
use roc_mono::ir::{OptLevel, SingleEntryPoint}; use roc_mono::ir::{OptLevel, SingleEntryPoint};
use roc_packaging::cache::RocCacheDir; use roc_packaging::cache::RocCacheDir;
@ -740,8 +740,20 @@ pub fn standard_load_config(
BuildOrdering::AlwaysBuild => ExecutionMode::Executable, 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 { LoadConfig {
target_info, target_info,
function_kind,
render: RenderTarget::ColorTerminal, render: RenderTarget::ColorTerminal,
palette: DEFAULT_PALETTE, palette: DEFAULT_PALETTE,
threading, threading,
@ -1205,6 +1217,8 @@ pub fn check_file<'a>(
let load_config = LoadConfig { let load_config = LoadConfig {
target_info, target_info,
// TODO: we may not want this for just checking.
function_kind: FunctionKind::LambdaSet,
// TODO: expose this from CLI? // TODO: expose this from CLI?
render: RenderTarget::ColorTerminal, render: RenderTarget::ColorTerminal,
palette: DEFAULT_PALETTE, palette: DEFAULT_PALETTE,

View file

@ -64,26 +64,6 @@ pub type PExpectedTypeIndex = Index<PExpected<TypeOrVar>>;
pub type TypeOrVar = EitherIndex<TypeTag, Variable>; pub type TypeOrVar = EitherIndex<TypeTag, Variable>;
impl Constraints { 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 { pub fn new() -> Self {
let constraints = Vec::new(); let constraints = Vec::new();
let type_slices = Vec::with_capacity(16); let type_slices = Vec::with_capacity(16);

View file

@ -1148,6 +1148,7 @@ fn deep_copy_type_vars<C: CopyEnv>(
}) })
}) })
} }
ErasedLambda => ErasedLambda,
RangedNumber(range) => { RangedNumber(range) => {
perform_clone!(RangedNumber(range)) perform_clone!(RangedNumber(range))

View file

@ -140,6 +140,7 @@ fn index_var(
| Content::FlexAbleVar(_, _) | Content::FlexAbleVar(_, _)
| Content::RigidAbleVar(_, _) | Content::RigidAbleVar(_, _)
| Content::LambdaSet(_) | Content::LambdaSet(_)
| Content::ErasedLambda
| Content::RangedNumber(..) => return Err(TypeError), | Content::RangedNumber(..) => return Err(TypeError),
Content::Error => return Err(TypeError), Content::Error => return Err(TypeError),
Content::RecursionVar { Content::RecursionVar {

View file

@ -103,7 +103,7 @@ impl FlatDecodable {
| Content::RigidVar(_) | Content::RigidVar(_)
| Content::FlexAbleVar(_, _) | Content::FlexAbleVar(_, _)
| Content::RigidAbleVar(_, _) => Err(UnboundVar), | Content::RigidAbleVar(_, _) => Err(UnboundVar),
Content::LambdaSet(_) => Err(Underivable), Content::LambdaSet(_) | Content::ErasedLambda => Err(Underivable),
} }
} }

View file

@ -137,7 +137,7 @@ impl FlatEncodable {
| Content::RigidVar(_) | Content::RigidVar(_)
| Content::FlexAbleVar(_, _) | Content::FlexAbleVar(_, _)
| Content::RigidAbleVar(_, _) => Err(UnboundVar), | Content::RigidAbleVar(_, _) => Err(UnboundVar),
Content::LambdaSet(_) => Err(Underivable), Content::LambdaSet(_) | Content::ErasedLambda => Err(Underivable),
} }
} }

View file

@ -148,7 +148,7 @@ impl FlatHash {
| Content::RigidVar(_) | Content::RigidVar(_)
| Content::FlexAbleVar(_, _) | Content::FlexAbleVar(_, _)
| Content::RigidAbleVar(_, _) => Err(UnboundVar), | Content::RigidAbleVar(_, _) => Err(UnboundVar),
Content::LambdaSet(_) => Err(Underivable), Content::LambdaSet(_) | Content::ErasedLambda => Err(Underivable),
} }
} }

View file

@ -5,7 +5,7 @@ use crate::{
use bumpalo::collections::{CollectIn, Vec}; use bumpalo::collections::{CollectIn, Vec};
use roc_builtins::bitcode::{self, FloatWidth, IntWidth}; use roc_builtins::bitcode::{self, FloatWidth, IntWidth};
use roc_collections::all::MutMap; 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_module::symbol::{Interns, ModuleId, Symbol};
use roc_mono::code_gen_help::{CallerProc, CodeGenHelp, HelperOp}; use roc_mono::code_gen_help::{CallerProc, CodeGenHelp, HelperOp};
use roc_mono::ir::{ use roc_mono::ir::{
@ -3630,7 +3630,8 @@ impl<
} }
LayoutRepr::Union(UnionLayout::NonRecursive(_)) LayoutRepr::Union(UnionLayout::NonRecursive(_))
| LayoutRepr::Builtin(_) | LayoutRepr::Builtin(_)
| LayoutRepr::Struct(_) => { | LayoutRepr::Struct(_)
| LayoutRepr::Erased(_) => {
internal_error!("All primitive values should fit in a single register"); internal_error!("All primitive values should fit in a single register");
} }
} }
@ -4306,6 +4307,8 @@ impl<
dst, dst,
); );
} }
LayoutRepr::Erased(_) => todo_lambda_erasure!(),
} }
} }
@ -4527,5 +4530,6 @@ macro_rules! pointer_layouts {
| UnionLayout::NullableWrapped { .. } | UnionLayout::NullableWrapped { .. }
| UnionLayout::NullableUnwrapped { .. }, | UnionLayout::NullableUnwrapped { .. },
) )
| LayoutRepr::FunctionPointer(_)
}; };
} }

View file

@ -6,7 +6,7 @@ use crate::{
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
use roc_builtins::bitcode::{FloatWidth, IntWidth}; use roc_builtins::bitcode::{FloatWidth, IntWidth};
use roc_collections::all::{MutMap, MutSet}; 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_module::symbol::Symbol;
use roc_mono::{ use roc_mono::{
ir::{JoinPointId, Param}, ir::{JoinPointId, Param},
@ -830,6 +830,7 @@ impl<
self.copy_to_stack_offset(buf, size, from_offset, to_offset) self.copy_to_stack_offset(buf, size, from_offset, to_offset)
} }
LayoutRepr::Erased(_) => todo_lambda_erasure!(),
pointer_layouts!() => { pointer_layouts!() => {
// like a 64-bit integer // like a 64-bit integer
debug_assert_eq!(to_offset % 8, 0); debug_assert_eq!(to_offset % 8, 0);

View file

@ -10,7 +10,7 @@ use std::collections::hash_map::Entry;
use bumpalo::{collections::Vec, Bump}; use bumpalo::{collections::Vec, Bump};
use roc_builtins::bitcode::{self, FloatWidth, IntWidth}; use roc_builtins::bitcode::{self, FloatWidth, IntWidth};
use roc_collections::all::{MutMap, MutSet}; 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::ident::ModuleName;
use roc_module::low_level::{LowLevel, LowLevelWrapperType}; use roc_module::low_level::{LowLevel, LowLevelWrapperType};
use roc_module::symbol::{Interns, ModuleId, Symbol}; use roc_module::symbol::{Interns, ModuleId, Symbol};
@ -149,6 +149,13 @@ impl<'a> LastSeenMap<'a> {
self.set_last_seen(*sym, stmt); 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) => { Expr::Struct(syms) => {
for sym in *syms { for sym in *syms {
self.set_last_seen(*sym, stmt); self.set_last_seen(*sym, stmt);
@ -176,6 +183,7 @@ impl<'a> LastSeenMap<'a> {
Expr::Reset { symbol, .. } | Expr::ResetRef { symbol, .. } => { Expr::Reset { symbol, .. } | Expr::ResetRef { symbol, .. } => {
self.set_last_seen(*symbol, stmt); self.set_last_seen(*symbol, stmt);
} }
Expr::FunctionPointer { .. } => todo_lambda_erasure!(),
Expr::EmptyArray => {} Expr::EmptyArray => {}
Expr::RuntimeErrorFunction(_) => {} Expr::RuntimeErrorFunction(_) => {}
} }
@ -265,6 +273,7 @@ impl<'a> LastSeenMap<'a> {
match call_type { match call_type {
CallType::ByName { .. } => {} CallType::ByName { .. } => {}
CallType::ByPointer { .. } => {}
CallType::LowLevel { .. } => {} CallType::LowLevel { .. } => {}
CallType::HigherOrder { .. } => {} CallType::HigherOrder { .. } => {}
CallType::Foreign { .. } => {} CallType::Foreign { .. } => {}
@ -729,6 +738,10 @@ trait Backend<'a> {
self.build_fn_call(sym, fn_name, arguments, arg_layouts, ret_layout) self.build_fn_call(sym, fn_name, arguments, arg_layouts, ret_layout)
} }
CallType::ByPointer { .. } => {
todo_lambda_erasure!()
}
CallType::LowLevel { op: lowlevel, .. } => { CallType::LowLevel { op: lowlevel, .. } => {
let mut arg_layouts: bumpalo::collections::Vec<InLayout<'a>> = let mut arg_layouts: bumpalo::collections::Vec<InLayout<'a>> =
bumpalo::vec![in self.env().arena]; bumpalo::vec![in self.env().arena];
@ -839,6 +852,9 @@ trait Backend<'a> {
Expr::NullPointer => { Expr::NullPointer => {
self.load_literal_i64(sym, 0); self.load_literal_i64(sym, 0);
} }
Expr::FunctionPointer { .. } => todo_lambda_erasure!(),
Expr::ErasedMake { .. } => todo_lambda_erasure!(),
Expr::ErasedLoad { .. } => todo_lambda_erasure!(),
Expr::Reset { symbol, .. } => { Expr::Reset { symbol, .. } => {
let layout = *self.layout_map().get(symbol).unwrap(); let layout = *self.layout_map().get(symbol).unwrap();

View file

@ -444,6 +444,7 @@ fn build_exposed_proc<'a, B: Backend<'a>>(backend: &mut B, proc: &Proc<'a>) -> P
ret_layout: proc.ret_layout, ret_layout: proc.ret_layout,
is_self_recursive: roc_mono::ir::SelfRecursive::NotSelfRecursive, is_self_recursive: roc_mono::ir::SelfRecursive::NotSelfRecursive,
host_exposed_layouts: roc_mono::ir::HostExposedLayouts::NotHostExposed, 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, ret_layout: roc_mono::layout::Layout::UNIT,
is_self_recursive: roc_mono::ir::SelfRecursive::NotSelfRecursive, is_self_recursive: roc_mono::ir::SelfRecursive::NotSelfRecursive,
host_exposed_layouts: roc_mono::ir::HostExposedLayouts::NotHostExposed, host_exposed_layouts: roc_mono::ir::HostExposedLayouts::NotHostExposed,
is_erased: proc.is_erased,
} }
} }

View file

@ -119,8 +119,9 @@ impl<'a> LlvmAlignment<'a> for LayoutRepr<'a> {
.runtime_representation() .runtime_representation()
.llvm_alignment_bytes(interner), .llvm_alignment_bytes(interner),
Builtin(builtin) => builtin.llvm_alignment_bytes(interner), Builtin(builtin) => builtin.llvm_alignment_bytes(interner),
RecursivePointer(_) => interner.target_info().ptr_width() as u32, RecursivePointer(_) | Ptr(_) | FunctionPointer(_) | Erased(_) => {
Ptr(_) => interner.target_info().ptr_width() as u32, interner.target_info().ptr_width() as u32
}
} }
} }
} }

View file

@ -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, env,
layout_interner, layout_interner,
roc_function, roc_function,

View file

@ -9,6 +9,7 @@ use crate::llvm::refcounting::{
build_reset, decrement_refcount_layout, increment_refcount_layout, PointerToRefcount, build_reset, decrement_refcount_layout, increment_refcount_layout, PointerToRefcount,
}; };
use crate::llvm::struct_::{struct_from_fields, RocStruct}; use crate::llvm::struct_::{struct_from_fields, RocStruct};
use crate::llvm::{erased, fn_ptr};
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
use bumpalo::Bump; use bumpalo::Bump;
use inkwell::attributes::{Attribute, AttributeLoc}; use inkwell::attributes::{Attribute, AttributeLoc};
@ -23,11 +24,11 @@ use inkwell::passes::{PassManager, PassManagerBuilder};
use inkwell::types::{ use inkwell::types::{
AnyType, BasicMetadataTypeEnum, BasicType, BasicTypeEnum, FunctionType, IntType, StructType, AnyType, BasicMetadataTypeEnum, BasicType, BasicTypeEnum, FunctionType, IntType, StructType,
}; };
use inkwell::values::BasicValueEnum;
use inkwell::values::{ use inkwell::values::{
BasicMetadataValueEnum, CallSiteValue, FunctionValue, InstructionValue, IntValue, PointerValue, BasicMetadataValueEnum, CallSiteValue, FunctionValue, InstructionValue, IntValue, PointerValue,
StructValue, StructValue,
}; };
use inkwell::values::{BasicValueEnum, CallableValue};
use inkwell::OptimizationLevel; use inkwell::OptimizationLevel;
use inkwell::{AddressSpace, IntPredicate}; use inkwell::{AddressSpace, IntPredicate};
use morphic_lib::{ use morphic_lib::{
@ -38,6 +39,7 @@ use roc_collections::all::{MutMap, MutSet};
use roc_debug_flags::dbg_do; use roc_debug_flags::dbg_do;
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
use roc_debug_flags::ROC_PRINT_LLVM_FN_VERIFICATION; 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_module::symbol::{Interns, Symbol};
use roc_mono::ir::{ use roc_mono::ir::{
BranchInfo, CallType, CrashTag, EntryPoint, GlueLayouts, HostExposedLambdaSet, 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 // NOTE fake layout; it is only used for debug prints
let roc_main_fn = let roc_main_fn = function_value_by_func_spec(env, FuncBorrowSpec::Some(*func_spec), symbol);
function_value_by_func_spec(env, *func_spec, symbol, &[], Niche::NONE, Layout::UNIT);
let main_fn_name = "$Test.main"; 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 // NOTE fake layout; it is only used for debug prints
let roc_main_fn = let roc_main_fn = function_value_by_func_spec(env, FuncBorrowSpec::Some(*func_spec), symbol);
function_value_by_func_spec(env, *func_spec, symbol, &[], Niche::NONE, Layout::UNIT);
let output_type = match roc_main_fn.get_type().get_return_type() { let output_type = match roc_main_fn.get_type().get_return_type() {
Some(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"); let entry = context.append_basic_block(c_function, "entry");
builder.position_at_end(entry); builder.position_at_end(entry);
let roc_main_fn_result = call_roc_function( let roc_main_fn_result = call_direct_roc_function(
env, env,
layout_interner, layout_interner,
roc_main_fn, roc_main_fn,
@ -912,7 +912,6 @@ pub(crate) fn build_exp_call<'a, 'ctx>(
CallType::ByName { CallType::ByName {
name, name,
specialization_id, specialization_id,
arg_layouts,
ret_layout, ret_layout,
.. ..
} => { } => {
@ -927,17 +926,39 @@ pub(crate) fn build_exp_call<'a, 'ctx>(
let callee_var = CalleeSpecVar(&bytes); let callee_var = CalleeSpecVar(&bytes);
let func_spec = func_spec_solutions.callee_spec(callee_var).unwrap(); let func_spec = func_spec_solutions.callee_spec(callee_var).unwrap();
roc_call_with_args( roc_call_direct_with_args(
env, env,
layout_interner, layout_interner,
arg_layouts,
*ret_layout, *ret_layout,
*name, *name,
func_spec, FuncBorrowSpec::Some(func_spec),
arg_tuples.into_bump_slice(), 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 } => { CallType::LowLevel { op, update_mode } => {
let bytes = update_mode.to_bytes(); let bytes = update_mode.to_bytes();
let update_var = UpdateModeVar(&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 { Reset {
symbol, symbol,
update_mode, update_mode,
@ -3849,7 +3888,7 @@ fn expose_function_to_host_help_c_abi_generic<'a, 'ctx>(
builder.position_at_end(entry); builder.position_at_end(entry);
let wrapped_layout = roc_call_result_layout(env.arena, return_layout); let wrapped_layout = roc_call_result_layout(env.arena, return_layout);
call_roc_function( call_direct_roc_function(
env, env,
layout_interner, layout_interner,
roc_function, roc_function,
@ -3857,7 +3896,7 @@ fn expose_function_to_host_help_c_abi_generic<'a, 'ctx>(
arguments_for_call, arguments_for_call,
) )
} else { } else {
call_roc_function( call_direct_roc_function(
env, env,
layout_interner, layout_interner,
roc_function, 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 wrapper_result = roc_call_result_layout(env.arena, return_layout);
let roc_value = call_roc_function( let roc_value = call_direct_roc_function(
env, env,
layout_interner, layout_interner,
roc_wrapper_function, 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 arguments = Vec::from_iter_in(it, env.arena);
let value = call_roc_function( let value = call_direct_roc_function(
env, env,
layout_interner, layout_interner,
roc_function, roc_function,
@ -4558,7 +4597,7 @@ fn set_jump_and_catch_long_jump<'a, 'ctx>(
{ {
builder.position_at_end(then_block); builder.position_at_end(then_block);
let call_result = call_roc_function( let call_result = call_direct_roc_function(
env, env,
layout_interner, layout_interner,
roc_function, roc_function,
@ -4826,15 +4865,19 @@ pub(crate) fn build_proc_headers<'a, 'r, 'ctx>(
let it = func_solutions.specs(); let it = func_solutions.specs();
let mut function_values = std::vec::Vec::with_capacity(it.size_hint().0); 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 { for specialization in it {
let fn_val = build_proc_header( let func_spec = if is_erased {
env, FuncBorrowSpec::Erased
layout_interner, } else {
*specialization, FuncBorrowSpec::Some(*specialization)
symbol, };
&proc,
layout_ids, let fn_val =
); build_proc_header(env, layout_interner, func_spec, symbol, &proc, layout_ids);
if proc.args.is_empty() { if proc.args.is_empty() {
// this is a 0-argument thunk, i.e. a top-level constant definition // 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 // NOTE fake layout; it is only used for debug prints
let getter_fn = let getter_fn = function_value_by_func_spec(env, FuncBorrowSpec::Some(*func_spec), symbol);
function_value_by_func_spec(env, *func_spec, symbol, &[], niche, Layout::UNIT);
let name = getter_fn.get_name().to_str().unwrap(); let name = getter_fn.get_name().to_str().unwrap();
let getter_name = symbol.as_str(&env.interns); 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 // NOTE fake layout; it is only used for debug prints
let roc_main_fn = 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(); let name = roc_main_fn.get_name().to_str().unwrap();
@ -5133,11 +5175,19 @@ fn build_procedures_help<'a>(
mod_solutions 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>( fn func_spec_name<'a>(
arena: &'a Bump, arena: &'a Bump,
interns: &Interns, interns: &Interns,
symbol: Symbol, symbol: Symbol,
func_spec: FuncSpec, func_spec: FuncBorrowSpec,
) -> bumpalo::collections::String<'a> { ) -> bumpalo::collections::String<'a> {
use std::fmt::Write; 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(); let module_string = interns.module_ids.get_name(symbol.module_id()).unwrap();
write!(buf, "{module_string}_{ident_string}_").unwrap(); write!(buf, "{module_string}_{ident_string}_").unwrap();
for byte in func_spec.0.iter() { match func_spec {
write!(buf, "{byte:x?}").unwrap(); FuncBorrowSpec::Some(func_spec) => {
for byte in func_spec.0.iter() {
write!(buf, "{byte:x?}").unwrap();
}
}
FuncBorrowSpec::Erased => write!(buf, "erased").unwrap(),
} }
buf buf
@ -5157,7 +5212,7 @@ fn func_spec_name<'a>(
fn build_proc_header<'a, 'ctx>( fn build_proc_header<'a, 'ctx>(
env: &Env<'a, 'ctx, '_>, env: &Env<'a, 'ctx, '_>,
layout_interner: &STLayoutInterner<'a>, layout_interner: &STLayoutInterner<'a>,
func_spec: FuncSpec, func_spec: FuncBorrowSpec,
symbol: Symbol, symbol: Symbol,
proc: &roc_mono::ir::Proc<'a>, proc: &roc_mono::ir::Proc<'a>,
layout_ids: &mut LayoutIds<'a>, layout_ids: &mut LayoutIds<'a>,
@ -5260,14 +5315,7 @@ fn expose_alias_to_host<'a>(
"we expect only one specialization of this symbol" "we expect only one specialization of this symbol"
); );
function_value_by_func_spec( function_value_by_func_spec(env, FuncBorrowSpec::Some(*func_spec), hels.symbol)
env,
*func_spec,
hels.symbol,
hels.proc_layout.arguments,
Niche::NONE,
hels.proc_layout.result,
)
} }
None => { None => {
// morphic did not generate a specialization for this function, // 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) => { RawFunctionLayout::ZeroArgumentThunk(result) => {
// Define only the return value size, since this is a thunk // 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); builder.build_store(output, call_result);
} else { } else {
let call_result = call_roc_function( let call_result = call_direct_roc_function(
env, env,
layout_interner, layout_interner,
evaluator, evaluator,
@ -5571,73 +5620,43 @@ pub fn verify_fn(fn_val: FunctionValue<'_>) {
} }
} }
pub(crate) fn function_value_by_func_spec<'a, 'ctx>( pub(crate) fn function_value_by_func_spec<'ctx>(
env: &Env<'a, 'ctx, '_>, env: &Env<'_, 'ctx, '_>,
func_spec: FuncSpec, func_spec: FuncBorrowSpec,
symbol: Symbol, symbol: Symbol,
arguments: &[InLayout<'a>],
niche: Niche<'a>,
result: InLayout<'a>,
) -> FunctionValue<'ctx> { ) -> FunctionValue<'ctx> {
let fn_name = func_spec_name(env.arena, &env.interns, symbol, func_spec); let fn_name = func_spec_name(env.arena, &env.interns, symbol, func_spec);
let fn_name = fn_name.as_str(); 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>( fn function_value_by_name_help<'ctx>(
env: &Env<'a, 'ctx, '_>, env: &Env<'_, 'ctx, '_>,
arguments: &[InLayout<'a>],
_niche: Niche<'a>,
result: InLayout<'a>,
symbol: Symbol, symbol: Symbol,
fn_name: &str, fn_name: &str,
) -> FunctionValue<'ctx> { ) -> FunctionValue<'ctx> {
env.module.get_function(fn_name).unwrap_or_else(|| { env.module.get_function(fn_name).unwrap_or_else(|| {
if symbol.is_builtin() { if symbol.is_builtin() {
eprintln!( panic!("Unrecognized builtin function: {fn_name:?} (symbol: {symbol:?})")
"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:?})",)
} else { } else {
// Unrecognized non-builtin function: panic!("Unrecognized non-builtin function: {fn_name:?} (symbol: {symbol:?})")
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:?})",)
} }
}) })
} }
#[inline(always)] #[inline(always)]
fn roc_call_with_args<'a, 'ctx>( fn roc_call_direct_with_args<'a, 'ctx>(
env: &Env<'a, 'ctx, '_>, env: &Env<'a, 'ctx, '_>,
layout_interner: &STLayoutInterner<'a>, layout_interner: &STLayoutInterner<'a>,
argument_layouts: &[InLayout<'a>],
result_layout: InLayout<'a>, result_layout: InLayout<'a>,
name: LambdaName<'a>, name: LambdaName<'a>,
func_spec: FuncSpec, func_spec: FuncBorrowSpec,
arguments: &[BasicValueEnum<'ctx>], arguments: &[BasicValueEnum<'ctx>],
) -> BasicValueEnum<'ctx> { ) -> BasicValueEnum<'ctx> {
let fn_val = function_value_by_func_spec( let fn_val = function_value_by_func_spec(env, func_spec, name.name());
env,
func_spec,
name.name(),
argument_layouts,
name.niche(),
result_layout,
);
call_roc_function( call_direct_roc_function(
env, env,
layout_interner, layout_interner,
fn_val, 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, '_>, env: &Env<'a, 'ctx, '_>,
layout_interner: &STLayoutInterner<'a>, layout_interner: &STLayoutInterner<'a>,
roc_function: FunctionValue<'ctx>, roc_function: FunctionValue<'ctx>,
result_layout: LayoutRepr<'a>, result_layout: LayoutRepr<'a>,
arguments: &[BasicValueEnum<'ctx>], arguments: &[BasicValueEnum<'ctx>],
) -> 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) { match RocReturn::from_layout(layout_interner, result_layout) {
RocReturn::ByPointer if !pass_by_pointer => { RocReturn::ByPointer if !pass_by_pointer => {
@ -5667,14 +5742,10 @@ pub fn call_roc_function<'a, 'ctx>(
arguments.push(result_alloca.into()); arguments.push(result_alloca.into());
debug_assert_eq!( debug_assert_eq!(roc_function_type.get_param_types().len(), arguments.len());
roc_function.get_type().get_param_types().len(), let call = build_call(&arguments);
arguments.len()
);
let call = env.builder.build_call(roc_function, &arguments, "call");
// roc functions should have the fast calling convention // 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.set_call_convention(FAST_CALL_CONV);
env.builder env.builder
@ -5689,14 +5760,10 @@ pub fn call_roc_function<'a, 'ctx>(
arguments.push(result_alloca.into()); arguments.push(result_alloca.into());
debug_assert_eq!( debug_assert_eq!(roc_function_type.get_param_types().len(), arguments.len());
roc_function.get_type().get_param_types().len(), let call = build_call(&arguments);
arguments.len()
);
let call = env.builder.build_call(roc_function, &arguments, "call");
// roc functions should have the fast calling convention // 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.set_call_convention(FAST_CALL_CONV);
if result_layout.is_passed_by_reference(layout_interner) { if result_layout.is_passed_by_reference(layout_interner) {
@ -5710,25 +5777,18 @@ pub fn call_roc_function<'a, 'ctx>(
} }
} }
RocReturn::Return => { RocReturn::Return => {
debug_assert_eq!( debug_assert_eq!(roc_function_type.get_param_types().len(), arguments.len());
roc_function.get_type().get_param_types().len(),
arguments.len()
);
let it = arguments.iter().map(|x| (*x).into()); let it = arguments.iter().map(|x| (*x).into());
let arguments = Vec::from_iter_in(it, env.arena); 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 // 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.set_call_convention(FAST_CALL_CONV);
call.try_as_basic_value().left().unwrap_or_else(|| { call.try_as_basic_value()
panic!( .left()
"LLVM error: Invalid call by name for name {:?}", .unwrap_or_else(|| internal_error!("LLVM error: Invalid call by name",))
roc_function.get_name()
)
})
} }
} }
} }
@ -5905,7 +5965,7 @@ pub enum CCReturn {
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct FunctionSpec<'ctx> { pub(crate) struct FunctionSpec<'ctx> {
/// The function type /// The function type
pub typ: FunctionType<'ctx>, pub typ: FunctionType<'ctx>,
call_conv: u32, call_conv: u32,
@ -5975,7 +6035,7 @@ impl<'ctx> FunctionSpec<'ctx> {
} }
/// Fastcc calling convention /// Fastcc calling convention
fn fastcc<'a, 'env>( pub fn fastcc<'a, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
roc_return: RocReturn, roc_return: RocReturn,
return_type: BasicTypeEnum<'ctx>, return_type: BasicTypeEnum<'ctx>,
@ -6223,7 +6283,7 @@ fn build_foreign_symbol<'a, 'ctx>(
} }
}; };
call_roc_function( call_direct_roc_function(
env, env,
layout_interner, layout_interner,
fastcc_function, fastcc_function,
@ -6366,7 +6426,7 @@ fn get_foreign_symbol<'ctx>(
/// Add a function to a module, after asserting that the function is unique. /// 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! /// We never want to define the same function twice in the same module!
/// The result can be bugs that are difficult to track down. /// The result can be bugs that are difficult to track down.
pub fn add_func<'ctx>( pub(crate) fn add_func<'ctx>(
ctx: &Context, ctx: &Context,
module: &Module<'ctx>, module: &Module<'ctx>,
name: &str, name: &str,

View file

@ -168,6 +168,8 @@ fn build_eq<'a, 'ctx>(
), ),
LayoutRepr::LambdaSet(_) => unreachable!("cannot compare closures"), 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( LayoutRepr::Union(union_layout) => build_tag_eq(
env, env,
@ -399,6 +401,8 @@ fn build_neq<'a, 'ctx>(
unreachable!("recursion pointers should never be compared directly") unreachable!("recursion pointers should never be compared directly")
} }
LayoutRepr::LambdaSet(_) => unreachable!("cannot compare closure"), 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); env.builder.set_current_debug_location(di_location);
let call = env let call = env
.builder .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); call.set_call_convention(FAST_CALL_CONV);
@ -1429,12 +1433,47 @@ fn build_box_eq_help<'a, 'ctx>(
"compare_pointers", "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 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 // clear the tag_id so we get a pointer to the actual data
let box1 = box1.into_pointer_value(); let box1 = box1.into_pointer_value();

View file

@ -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 crate::llvm::memcpy::build_memcpy;
use bumpalo::collections::Vec as AVec; use bumpalo::collections::{CollectIn, Vec as AVec};
use inkwell::context::Context; use inkwell::context::Context;
use inkwell::types::{BasicType, BasicTypeEnum, FloatType, IntType, StructType}; use inkwell::types::{BasicType, BasicTypeEnum, FloatType, IntType, StructType};
use inkwell::values::PointerValue; use inkwell::values::PointerValue;
use inkwell::AddressSpace; use inkwell::AddressSpace;
use roc_builtins::bitcode::{FloatWidth, IntWidth}; use roc_builtins::bitcode::{FloatWidth, IntWidth};
use roc_mono::layout::{ use roc_mono::layout::{
round_up_to_alignment, Builtin, InLayout, Layout, LayoutInterner, LayoutRepr, STLayoutInterner, round_up_to_alignment, Builtin, FunctionPointer, InLayout, Layout, LayoutInterner, LayoutRepr,
UnionLayout, STLayoutInterner, UnionLayout,
}; };
use roc_target::TargetInfo; use roc_target::TargetInfo;
@ -48,6 +49,22 @@ pub fn basic_type_from_layout<'a, 'ctx>(
.ptr_type(AddressSpace::default()) .ptr_type(AddressSpace::default())
.as_basic_type_enum(), .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), Builtin(builtin) => basic_type_from_builtin(env, &builtin),
} }
} }

View 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()
}

View file

@ -9,7 +9,7 @@ use inkwell::types::{BasicMetadataTypeEnum, BasicType, BasicTypeEnum};
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue}; use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue};
use inkwell::AddressSpace; use inkwell::AddressSpace;
use roc_builtins::bitcode; 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_module::symbol::Symbol;
use roc_mono::ir::LookupType; use roc_mono::ir::LookupType;
use roc_mono::layout::{ use roc_mono::layout::{
@ -387,6 +387,8 @@ fn build_clone<'a, 'ctx>(
union_layout, union_layout,
) )
} }
LayoutRepr::FunctionPointer(_) => todo_lambda_erasure!(),
LayoutRepr::Erased(_) => todo_lambda_erasure!(),
} }
} }

View 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()
}

View file

@ -31,7 +31,7 @@ use crate::llvm::{
build::{ build::{
cast_basic_basic, complex_bitcast_check_size, create_entry_block_alloca, cast_basic_basic, complex_bitcast_check_size, create_entry_block_alloca,
entry_block_alloca_zerofill, function_value_by_func_spec, load_roc_value, 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::{ build_list::{
list_append_unsafe, list_concat, list_drop_at, list_get_unsafe, list_len, list_map, 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; } = higher_order;
let PassedFunction { let PassedFunction {
argument_layouts, argument_layouts: _,
return_layout: result_layout, return_layout: result_layout,
owns_captured_environment: function_owns_closure_data, owns_captured_environment: function_owns_closure_data,
name: function_name, name: function_name,
@ -2624,11 +2624,8 @@ pub(crate) fn run_higher_order_low_level<'a, 'ctx>(
() => {{ () => {{
let function = function_value_by_func_spec( let function = function_value_by_func_spec(
env, env,
func_spec, FuncBorrowSpec::Some(func_spec),
function_name.name(), function_name.name(),
argument_layouts,
function_name.niche(),
return_layout,
); );
let (closure, closure_layout) = let (closure, closure_layout) =

View file

@ -11,6 +11,8 @@ mod lowlevel;
pub mod refcounting; pub mod refcounting;
mod align; mod align;
mod erased;
mod fn_ptr;
mod memcpy; mod memcpy;
mod scope; mod scope;
mod struct_; mod struct_;

View file

@ -14,16 +14,20 @@ use bumpalo::collections::Vec;
use inkwell::basic_block::BasicBlock; use inkwell::basic_block::BasicBlock;
use inkwell::module::Linkage; use inkwell::module::Linkage;
use inkwell::types::{AnyTypeEnum, BasicMetadataTypeEnum, BasicType, BasicTypeEnum}; 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 inkwell::{AddressSpace, IntPredicate};
use roc_module::symbol::Interns; use roc_module::symbol::Interns;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_mono::ir::ErasedField;
use roc_mono::layout::{ 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::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::convert::{argument_type_from_layout, argument_type_from_union_layout};
use super::erased;
pub struct PointerToRefcount<'ctx> { pub struct PointerToRefcount<'ctx> {
value: PointerValue<'ctx>, value: PointerValue<'ctx>,
@ -388,6 +392,94 @@ fn modify_refcount_struct_help<'a, 'ctx>(
builder.build_return(None); 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>( pub fn increment_refcount_layout<'a, 'ctx>(
env: &Env<'a, 'ctx, '_>, env: &Env<'a, 'ctx, '_>,
layout_interner: &STLayoutInterner<'a>, layout_interner: &STLayoutInterner<'a>,
@ -624,6 +716,12 @@ fn modify_refcount_layout_build_function<'a, 'ctx>(
mode, mode,
lambda_set.runtime_representation(), lambda_set.runtime_representation(),
), ),
FunctionPointer(_) => None,
Erased(_) => {
let function = modify_refcount_erased(env, layout_interner, layout_ids, mode);
Some(function)
}
} }
} }

View file

@ -3,7 +3,7 @@ use bumpalo::collections::{String, Vec};
use roc_builtins::bitcode::{self, FloatWidth, IntWidth}; use roc_builtins::bitcode::{self, FloatWidth, IntWidth};
use roc_collections::all::MutMap; 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::low_level::{LowLevel, LowLevelWrapperType};
use roc_module::symbol::{Interns, Symbol}; use roc_module::symbol::{Interns, Symbol};
use roc_mono::code_gen_help::{CodeGenHelp, HelperOp, REFCOUNT_MAX}; use roc_mono::code_gen_help::{CodeGenHelp, HelperOp, REFCOUNT_MAX};
@ -1136,6 +1136,10 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
storage, 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::Reset { symbol: arg, .. } => self.expr_reset(*arg, sym, storage),
Expr::ResetRef { symbol: arg, .. } => self.expr_resetref(*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, .. } => { CallType::LowLevel { op: lowlevel, .. } => {
self.expr_call_low_level(*lowlevel, arguments, ret_sym, ret_layout, ret_storage) self.expr_call_low_level(*lowlevel, arguments, ret_sym, ret_layout, ret_storage)
} }

View file

@ -1,4 +1,5 @@
use roc_builtins::bitcode::{FloatWidth, IntWidth}; use roc_builtins::bitcode::{FloatWidth, IntWidth};
use roc_error_macros::todo_lambda_erasure;
use roc_mono::layout::{InLayout, LayoutInterner, LayoutRepr, STLayoutInterner, UnionLayout}; use roc_mono::layout::{InLayout, LayoutInterner, LayoutRepr, STLayoutInterner, UnionLayout};
use crate::{PTR_SIZE, PTR_TYPE}; use crate::{PTR_SIZE, PTR_TYPE};
@ -99,6 +100,8 @@ impl WasmLayout {
) )
| LayoutRepr::Ptr(_) | LayoutRepr::Ptr(_)
| LayoutRepr::RecursivePointer(_) => Self::Primitive(PTR_TYPE, PTR_SIZE), | LayoutRepr::RecursivePointer(_) => Self::Primitive(PTR_TYPE, PTR_SIZE),
LayoutRepr::FunctionPointer(_) => todo_lambda_erasure!(),
LayoutRepr::Erased(_) => todo_lambda_erasure!(),
} }
} }

View file

@ -1,7 +1,7 @@
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
use bumpalo::Bump; use bumpalo::Bump;
use roc_builtins::bitcode::{self, FloatWidth, IntWidth}; 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::low_level::LowLevel;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_mono::code_gen_help::HelperOp; use roc_mono::code_gen_help::HelperOp;
@ -2120,6 +2120,9 @@ impl<'a> LowLevelCall<'a> {
self.arguments, self.arguments,
) )
} }
LayoutRepr::FunctionPointer(_) => todo_lambda_erasure!(),
LayoutRepr::Erased(_) => todo_lambda_erasure!(),
} }
} }

View file

@ -5,7 +5,6 @@ use std::sync::{Arc, RwLock};
use bumpalo::Bump; use bumpalo::Bump;
use roc_can::abilities::AbilitiesStore; use roc_can::abilities::AbilitiesStore;
use roc_can::constraint::Constraints;
use roc_can::module::ExposedByModule; use roc_can::module::ExposedByModule;
use roc_collections::MutMap; use roc_collections::MutMap;
use roc_derive::SharedDerivedModule; use roc_derive::SharedDerivedModule;
@ -15,7 +14,7 @@ use roc_module::symbol::Symbol;
use roc_solve::ability::AbilityResolver; use roc_solve::ability::AbilityResolver;
use roc_solve::specialize::{compact_lambda_sets_of_vars, Phase}; use roc_solve::specialize::{compact_lambda_sets_of_vars, Phase};
use roc_solve::Pools; 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::{get_member_lambda_sets_at_region, Content, FlatType, LambdaSet};
use roc_types::subs::{ExposedTypesStorageSubs, Subs, Variable}; use roc_types::subs::{ExposedTypesStorageSubs, Subs, Variable};
use roc_types::types::Polarity; 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. /// Unifies two variables and performs lambda set compaction.
/// Ranks and other ability demands are disregarded. /// Ranks and other ability demands are disregarded.
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
@ -396,9 +382,8 @@ pub fn unify(
exposed_types: exposed_by_module, exposed_types: exposed_by_module,
}; };
let must_implement_constraints = with_empty_solve_constraints(|c| { let must_implement_constraints = {
let mut env = Env { let mut env = SolveEnv {
constraints: c,
subs, subs,
derived_env: &derived_env, derived_env: &derived_env,
arena, arena,
@ -406,7 +391,8 @@ pub fn unify(
}; };
compact_lambda_sets_of_vars(&mut env, lambda_sets_to_specialize, &late_phase) 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 // 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. // 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 // After we land https://github.com/roc-lang/roc/issues/3207 this concern should totally

View file

@ -14,6 +14,7 @@ roc_load_internal = { path = "../load_internal" }
roc_module = { path = "../module" } roc_module = { path = "../module" }
roc_packaging = { path = "../../packaging" } roc_packaging = { path = "../../packaging" }
roc_reporting = { path = "../../reporting" } roc_reporting = { path = "../../reporting" }
roc_solve = { path = "../solve" }
roc_target = { path = "../roc_target" } roc_target = { path = "../roc_target" }
roc_types = { path = "../types" } roc_types = { path = "../types" }
@ -25,6 +26,7 @@ roc_can = { path = "../can" }
roc_module = { path = "../module" } roc_module = { path = "../module" }
roc_packaging = { path = "../../packaging" } roc_packaging = { path = "../../packaging" }
roc_reporting = { path = "../../reporting" } roc_reporting = { path = "../../reporting" }
roc_solve = { path = "../solve" }
roc_target = { path = "../roc_target" } roc_target = { path = "../roc_target" }
roc_error_macros = { path = "../../error_macros" } roc_error_macros = { path = "../../error_macros" }

View file

@ -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 cwd = std::env::current_dir().unwrap();
let source = roc_builtins::roc::module_source(module_id); let source = roc_builtins::roc::module_source(module_id);
let target_info = roc_target::TargetInfo::default_x86_64(); 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( let res_module = roc_load_internal::file::load_and_typecheck_str(
&arena, &arena,
@ -85,6 +86,7 @@ fn write_types_for_module_real(module_id: ModuleId, filename: &str, output_path:
cwd, cwd,
Default::default(), Default::default(),
target_info, target_info,
function_kind,
roc_reporting::report::RenderTarget::ColorTerminal, roc_reporting::report::RenderTarget::ColorTerminal,
roc_reporting::report::DEFAULT_PALETTE, roc_reporting::report::DEFAULT_PALETTE,
RocCacheDir::Disallowed, RocCacheDir::Disallowed,

View file

@ -24,6 +24,7 @@ pub use roc_load_internal::file::{
pub use roc_load_internal::module::{ pub use roc_load_internal::module::{
EntryPoint, Expectations, ExposedToHost, LoadedModule, MonomorphizedModule, EntryPoint, Expectations, ExposedToHost, LoadedModule, MonomorphizedModule,
}; };
pub use roc_solve::FunctionKind;
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn load<'a>( fn load<'a>(
@ -51,6 +52,7 @@ pub fn load_single_threaded<'a>(
arena: &'a Bump, arena: &'a Bump,
load_start: LoadStart<'a>, load_start: LoadStart<'a>,
target_info: TargetInfo, target_info: TargetInfo,
function_kind: FunctionKind,
render: RenderTarget, render: RenderTarget,
palette: Palette, palette: Palette,
roc_cache_dir: RocCacheDir<'_>, roc_cache_dir: RocCacheDir<'_>,
@ -64,6 +66,7 @@ pub fn load_single_threaded<'a>(
load_start, load_start,
exposed_types, exposed_types,
target_info, target_info,
function_kind,
cached_subs, cached_subs,
render, render,
palette, palette,
@ -170,6 +173,7 @@ pub fn load_and_typecheck_str<'a>(
source: &'a str, source: &'a str,
src_dir: PathBuf, src_dir: PathBuf,
target_info: TargetInfo, target_info: TargetInfo,
function_kind: FunctionKind,
render: RenderTarget, render: RenderTarget,
roc_cache_dir: RocCacheDir<'_>, roc_cache_dir: RocCacheDir<'_>,
palette: Palette, palette: Palette,
@ -185,6 +189,7 @@ pub fn load_and_typecheck_str<'a>(
arena, arena,
load_start, load_start,
target_info, target_info,
function_kind,
render, render,
palette, palette,
roc_cache_dir, roc_cache_dir,

View file

@ -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_https_problem_report_string;
use roc_reporting::report::{to_file_problem_report_string, Palette, RenderTarget}; 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::module::{extract_module_owned_implementations, SolveConfig, Solved, SolvedModule};
use roc_solve::FunctionKind;
use roc_solve_problem::TypeError; use roc_solve_problem::TypeError;
use roc_target::TargetInfo; use roc_target::TargetInfo;
use roc_types::subs::{CopiedImport, ExposedTypesStorageSubs, Subs, VarStore, Variable}; use roc_types::subs::{CopiedImport, ExposedTypesStorageSubs, Subs, VarStore, Variable};
@ -113,6 +114,7 @@ pub struct LoadConfig {
pub palette: Palette, pub palette: Palette,
pub threading: Threading, pub threading: Threading,
pub exec_mode: ExecutionMode, pub exec_mode: ExecutionMode,
pub function_kind: FunctionKind,
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -336,6 +338,7 @@ fn start_phase<'a>(
types, types,
constraints, constraints,
constraint, constraint,
state.function_kind,
pending_derives, pending_derives,
var_store, var_store,
imported_modules, imported_modules,
@ -679,6 +682,7 @@ struct State<'a> {
pub output_path: Option<&'a str>, pub output_path: Option<&'a str>,
pub platform_path: PlatformPath<'a>, pub platform_path: PlatformPath<'a>,
pub target_info: TargetInfo, pub target_info: TargetInfo,
pub(self) function_kind: FunctionKind,
/// Note: only packages and platforms actually expose any modules; /// Note: only packages and platforms actually expose any modules;
/// for all others, this will be empty. /// for all others, this will be empty.
@ -740,6 +744,7 @@ impl<'a> State<'a> {
root_id: ModuleId, root_id: ModuleId,
opt_platform_shorthand: Option<&'a str>, opt_platform_shorthand: Option<&'a str>,
target_info: TargetInfo, target_info: TargetInfo,
function_kind: FunctionKind,
exposed_types: ExposedByModule, exposed_types: ExposedByModule,
arc_modules: Arc<Mutex<PackageModuleIds<'a>>>, arc_modules: Arc<Mutex<PackageModuleIds<'a>>>,
ident_ids_by_module: SharedIdentIdsByModule, ident_ids_by_module: SharedIdentIdsByModule,
@ -759,6 +764,7 @@ impl<'a> State<'a> {
opt_platform_shorthand, opt_platform_shorthand,
cache_dir, cache_dir,
target_info, target_info,
function_kind,
platform_data: None, platform_data: None,
output_path: None, output_path: None,
platform_path: PlatformPath::NotSpecified, platform_path: PlatformPath::NotSpecified,
@ -865,6 +871,7 @@ enum BuildTask<'a> {
types: Types, types: Types,
constraints: Constraints, constraints: Constraints,
constraint: ConstraintSoa, constraint: ConstraintSoa,
function_kind: FunctionKind,
pending_derives: PendingDerives, pending_derives: PendingDerives,
var_store: VarStore, var_store: VarStore,
declarations: Declarations, declarations: Declarations,
@ -969,6 +976,7 @@ pub fn load_and_typecheck_str<'a>(
src_dir: PathBuf, src_dir: PathBuf,
exposed_types: ExposedByModule, exposed_types: ExposedByModule,
target_info: TargetInfo, target_info: TargetInfo,
function_kind: FunctionKind,
render: RenderTarget, render: RenderTarget,
palette: Palette, palette: Palette,
roc_cache_dir: RocCacheDir<'_>, roc_cache_dir: RocCacheDir<'_>,
@ -988,6 +996,7 @@ pub fn load_and_typecheck_str<'a>(
palette, palette,
threading, threading,
exec_mode: ExecutionMode::Check, exec_mode: ExecutionMode::Check,
function_kind,
}; };
match load( match load(
@ -1243,6 +1252,7 @@ pub fn load<'a>(
load_start, load_start,
exposed_types, exposed_types,
load_config.target_info, load_config.target_info,
load_config.function_kind,
cached_types, cached_types,
load_config.render, load_config.render,
load_config.palette, load_config.palette,
@ -1254,6 +1264,7 @@ pub fn load<'a>(
load_start, load_start,
exposed_types, exposed_types,
load_config.target_info, load_config.target_info,
load_config.function_kind,
cached_types, cached_types,
load_config.render, load_config.render,
load_config.palette, load_config.palette,
@ -1270,6 +1281,7 @@ pub fn load_single_threaded<'a>(
load_start: LoadStart<'a>, load_start: LoadStart<'a>,
exposed_types: ExposedByModule, exposed_types: ExposedByModule,
target_info: TargetInfo, target_info: TargetInfo,
function_kind: FunctionKind,
cached_types: MutMap<ModuleId, TypeState>, cached_types: MutMap<ModuleId, TypeState>,
render: RenderTarget, render: RenderTarget,
palette: Palette, palette: Palette,
@ -1297,6 +1309,7 @@ pub fn load_single_threaded<'a>(
root_id, root_id,
opt_platform_shorthand, opt_platform_shorthand,
target_info, target_info,
function_kind,
exposed_types, exposed_types,
arc_modules, arc_modules,
ident_ids_by_module, ident_ids_by_module,
@ -1584,6 +1597,7 @@ fn load_multi_threaded<'a>(
load_start: LoadStart<'a>, load_start: LoadStart<'a>,
exposed_types: ExposedByModule, exposed_types: ExposedByModule,
target_info: TargetInfo, target_info: TargetInfo,
function_kind: FunctionKind,
cached_types: MutMap<ModuleId, TypeState>, cached_types: MutMap<ModuleId, TypeState>,
render: RenderTarget, render: RenderTarget,
palette: Palette, palette: Palette,
@ -1627,6 +1641,7 @@ fn load_multi_threaded<'a>(
root_id, root_id,
opt_platform_shorthand, opt_platform_shorthand,
target_info, target_info,
function_kind,
exposed_types, exposed_types,
arc_modules, arc_modules,
ident_ids_by_module, ident_ids_by_module,
@ -4417,6 +4432,7 @@ impl<'a> BuildTask<'a> {
types: Types, types: Types,
constraints: Constraints, constraints: Constraints,
constraint: ConstraintSoa, constraint: ConstraintSoa,
function_kind: FunctionKind,
pending_derives: PendingDerives, pending_derives: PendingDerives,
var_store: VarStore, var_store: VarStore,
imported_modules: MutMap<ModuleId, Region>, imported_modules: MutMap<ModuleId, Region>,
@ -4439,6 +4455,7 @@ impl<'a> BuildTask<'a> {
types, types,
constraints, constraints,
constraint, constraint,
function_kind,
pending_derives, pending_derives,
var_store, var_store,
declarations, declarations,
@ -4709,6 +4726,7 @@ fn run_solve_solve(
mut types: Types, mut types: Types,
mut constraints: Constraints, mut constraints: Constraints,
constraint: ConstraintSoa, constraint: ConstraintSoa,
function_kind: FunctionKind,
pending_derives: PendingDerives, pending_derives: PendingDerives,
var_store: VarStore, var_store: VarStore,
module: Module, module: Module,
@ -4760,6 +4778,7 @@ fn run_solve_solve(
types, types,
constraints: &constraints, constraints: &constraints,
root_constraint: actual_constraint, root_constraint: actual_constraint,
function_kind,
pending_derives, pending_derives,
exposed_by_module: &exposed_for_module.exposed_by_module, exposed_by_module: &exposed_for_module.exposed_by_module,
derived_module, derived_module,
@ -4824,6 +4843,7 @@ fn run_solve<'a>(
types: Types, types: Types,
constraints: Constraints, constraints: Constraints,
constraint: ConstraintSoa, constraint: ConstraintSoa,
function_kind: FunctionKind,
pending_derives: PendingDerives, pending_derives: PendingDerives,
var_store: VarStore, var_store: VarStore,
decls: Declarations, decls: Declarations,
@ -4851,6 +4871,7 @@ fn run_solve<'a>(
types, types,
constraints, constraints,
constraint, constraint,
function_kind,
pending_derives, pending_derives,
var_store, var_store,
module, module,
@ -4875,6 +4896,7 @@ fn run_solve<'a>(
types, types,
constraints, constraints,
constraint, constraint,
function_kind,
pending_derives, pending_derives,
var_store, var_store,
module, module,
@ -6130,6 +6152,7 @@ fn run_task<'a>(
types, types,
constraints, constraints,
constraint, constraint,
function_kind,
pending_derives, pending_derives,
var_store, var_store,
ident_ids, ident_ids,
@ -6145,6 +6168,7 @@ fn run_task<'a>(
types, types,
constraints, constraints,
constraint, constraint,
function_kind,
pending_derives, pending_derives,
var_store, var_store,
declarations, declarations,

View file

@ -29,6 +29,7 @@ use roc_region::all::LineInfo;
use roc_reporting::report::RenderTarget; use roc_reporting::report::RenderTarget;
use roc_reporting::report::RocDocAllocator; use roc_reporting::report::RocDocAllocator;
use roc_reporting::report::{can_problem, DEFAULT_PALETTE}; use roc_reporting::report::{can_problem, DEFAULT_PALETTE};
use roc_solve::FunctionKind;
use roc_target::TargetInfo; use roc_target::TargetInfo;
use roc_types::pretty_print::name_and_print_var; use roc_types::pretty_print::name_and_print_var;
use roc_types::pretty_print::DebugPrint; use roc_types::pretty_print::DebugPrint;
@ -40,6 +41,7 @@ fn load_and_typecheck(
filename: PathBuf, filename: PathBuf,
exposed_types: ExposedByModule, exposed_types: ExposedByModule,
target_info: TargetInfo, target_info: TargetInfo,
function_kind: FunctionKind,
) -> Result<LoadedModule, LoadingProblem> { ) -> Result<LoadedModule, LoadingProblem> {
use LoadResult::*; use LoadResult::*;
@ -52,6 +54,7 @@ fn load_and_typecheck(
)?; )?;
let load_config = LoadConfig { let load_config = LoadConfig {
target_info, target_info,
function_kind,
render: RenderTarget::Generic, render: RenderTarget::Generic,
palette: DEFAULT_PALETTE, palette: DEFAULT_PALETTE,
threading: Threading::Single, threading: Threading::Single,
@ -176,7 +179,13 @@ fn multiple_modules_help<'a>(
writeln!(file, "{source}")?; writeln!(file, "{source}")?;
file_handles.push(file); 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) Ok(result)
@ -190,7 +199,13 @@ fn load_fixture(
let src_dir = fixtures_dir().join(dir_name); let src_dir = fixtures_dir().join(dir_name);
let filename = src_dir.join(format!("{module_name}.roc")); let filename = src_dir.join(format!("{module_name}.roc"));
let arena = Bump::new(); 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 { let mut loaded_module = match loaded {
Ok(x) => x, Ok(x) => x,
Err(roc_load_internal::file::LoadingProblem::FormattedReport(report)) => { 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 src_dir = fixtures_dir().join("interface_with_deps");
let filename = src_dir.join("Primary.roc"); let filename = src_dir.join("Primary.roc");
let arena = Bump::new(); 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 mut loaded_module = loaded.expect("Test module failed to load");
let home = loaded_module.module_id; let home = loaded_module.module_id;

View file

@ -800,6 +800,7 @@ macro_rules! define_builtins {
$( $(
$module_id:literal $module_const:ident: $module_name:literal => { $module_id:literal $module_const:ident: $module_name:literal => {
$( $(
$(#[$ident_meta:meta])*
$ident_id:literal $ident_const:ident: $ident_name:literal $ident_id:literal $ident_const:ident: $ident_name:literal
$(exposed_apply_type=$exposed_apply_type:literal)? $(exposed_apply_type=$exposed_apply_type:literal)?
$(exposed_type=$exposed_type:literal)? $(exposed_type=$exposed_type:literal)?
@ -951,6 +952,7 @@ macro_rules! define_builtins {
impl Symbol { impl Symbol {
$( $(
$( $(
$(#[$ident_meta])*
pub const $ident_const: Symbol = Symbol::new(ModuleId::$module_const, IdentId($ident_id)); pub const $ident_const: Symbol = Symbol::new(ModuleId::$module_const, IdentId($ident_id));
)* )*
$( $(

View file

@ -9,6 +9,7 @@ use bumpalo::collections::Vec;
use bumpalo::Bump; use bumpalo::Bump;
use roc_collections::all::{MutMap, MutSet}; use roc_collections::all::{MutMap, MutSet};
use roc_collections::ReferenceMatrix; use roc_collections::ReferenceMatrix;
use roc_error_macros::todo_lambda_erasure;
use roc_module::low_level::LowLevel; use roc_module::low_level::LowLevel;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
@ -560,6 +561,10 @@ impl<'a> BorrowInfState<'a> {
self.own_args_using_params(arguments, ps); self.own_args_using_params(arguments, ps);
} }
ByPointer { .. } => {
todo_lambda_erasure!()
}
LowLevel { op, .. } => { LowLevel { op, .. } => {
debug_assert!(!op.is_higher_order()); debug_assert!(!op.is_higher_order());
@ -703,6 +708,27 @@ impl<'a> BorrowInfState<'a> {
self.own_args_if_param(xs); 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, .. } => { Reset { symbol: x, .. } | ResetRef { symbol: x, .. } => {
self.own_var(z); self.own_var(z);
self.own_var(*x); self.own_var(*x);
@ -714,7 +740,7 @@ impl<'a> BorrowInfState<'a> {
Call(call) => self.collect_call(interner, param_map, z, call), Call(call) => self.collect_call(interner, param_map, z, call),
Literal(_) | NullPointer | RuntimeErrorFunction(_) => {} Literal(_) | NullPointer | RuntimeErrorFunction(_) | FunctionPointer { .. } => {}
StructAtIndex { structure: x, .. } => { StructAtIndex { structure: x, .. } => {
// if the structure (record/tag/array) is owned, the extracted value is // 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]), PtrStore => arena.alloc_slice_copy(&[owned, owned]),
PtrLoad => arena.alloc_slice_copy(&[owned]), PtrLoad => arena.alloc_slice_copy(&[owned]),
PtrCast => arena.alloc_slice_copy(&[owned]),
Alloca => arena.alloc_slice_copy(&[owned]), Alloca => arena.alloc_slice_copy(&[owned]),
PtrClearTagId | PtrCast | RefCountIncRcPtr | RefCountDecRcPtr | RefCountIncDataPtr PtrClearTagId | RefCountIncRcPtr | RefCountDecRcPtr | RefCountIncDataPtr
| RefCountDecDataPtr | RefCountIsUnique => { | RefCountDecDataPtr | RefCountIsUnique => {
unreachable!("Only inserted *after* borrow checking: {:?}", op); 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, .. } => { ByName { name, .. } => {
info.keys.push(name.name()); info.keys.push(name.name());
} }
ByPointer { .. } => {
todo_lambda_erasure!()
}
Foreign { .. } => {} Foreign { .. } => {}
LowLevel { .. } => {} LowLevel { .. } => {}
HigherOrder(HigherOrderLowLevel { HigherOrder(HigherOrderLowLevel {

View file

@ -25,7 +25,7 @@ pub fn eq_generic<'a>(
use crate::layout::Builtin::*; use crate::layout::Builtin::*;
use LayoutRepr::*; use LayoutRepr::*;
let main_body = match layout_interner.get_repr(layout) { let main_body = match layout_interner.get_repr(layout) {
Builtin(Int(_) | Float(_) | Bool | Decimal) => { Builtin(Int(_) | Float(_) | Bool | Decimal) | FunctionPointer(_) => {
unreachable!( unreachable!(
"No generated proc for `==`. Use direct code gen for {:?}", "No generated proc for `==`. Use direct code gen for {:?}",
layout layout
@ -39,6 +39,7 @@ pub fn eq_generic<'a>(
Union(union_layout) => eq_tag_union(root, ident_ids, ctx, layout_interner, union_layout), 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), Ptr(inner_layout) => eq_boxed(root, ident_ids, ctx, layout_interner, inner_layout),
LambdaSet(_) => unreachable!("`==` is not defined on functions"), LambdaSet(_) => unreachable!("`==` is not defined on functions"),
Erased(_) => unreachable!("`==` is not defined on erased types"),
RecursivePointer(_) => { RecursivePointer(_) => {
unreachable!( unreachable!(
"Can't perform `==` on RecursivePointer. Should have been replaced by a tag union." "Can't perform `==` on RecursivePointer. Should have been replaced by a tag union."

View file

@ -453,6 +453,7 @@ impl<'a> CodeGenHelp<'a> {
ret_layout, ret_layout,
is_self_recursive: SelfRecursive::NotSelfRecursive, is_self_recursive: SelfRecursive::NotSelfRecursive,
host_exposed_layouts: HostExposedLayouts::NotHostExposed, host_exposed_layouts: HostExposedLayouts::NotHostExposed,
is_erased: false,
}); });
proc_symbol proc_symbol
@ -582,6 +583,10 @@ impl<'a> CodeGenHelp<'a> {
// This line is the whole point of the function // This line is the whole point of the function
LayoutRepr::RecursivePointer(_) => LayoutRepr::Union(ctx.recursive_union.unwrap()), 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)) layout_interner.insert(Layout::new(LayoutWrapper::Direct(repr), semantic))
@ -768,6 +773,7 @@ impl<'a> CallerProc<'a> {
ret_layout: Layout::UNIT, ret_layout: Layout::UNIT,
is_self_recursive: SelfRecursive::NotSelfRecursive, is_self_recursive: SelfRecursive::NotSelfRecursive,
host_exposed_layouts: HostExposedLayouts::NotHostExposed, host_exposed_layouts: HostExposedLayouts::NotHostExposed,
is_erased: false,
}; };
if false { if false {
@ -838,5 +844,7 @@ fn layout_needs_helper_proc<'a>(
LayoutRepr::LambdaSet(_) => true, LayoutRepr::LambdaSet(_) => true,
LayoutRepr::RecursivePointer(_) => false, LayoutRepr::RecursivePointer(_) => false,
LayoutRepr::Ptr(_) => false, LayoutRepr::Ptr(_) => false,
LayoutRepr::FunctionPointer(_) => false,
LayoutRepr::Erased(_) => true,
} }
} }

View file

@ -2,6 +2,7 @@
use bumpalo::collections::vec::Vec; use bumpalo::collections::vec::Vec;
use bumpalo::collections::CollectIn; use bumpalo::collections::CollectIn;
use roc_error_macros::todo_lambda_erasure;
use roc_module::low_level::{LowLevel, LowLevel::*}; use roc_module::low_level::{LowLevel, LowLevel::*};
use roc_module::symbol::{IdentIds, Symbol}; use roc_module::symbol::{IdentIds, Symbol};
use roc_target::PtrWidth; use roc_target::PtrWidth;
@ -188,7 +189,8 @@ pub fn refcount_generic<'a>(
match layout_interner.get_repr(layout) { match layout_interner.get_repr(layout) {
LayoutRepr::Builtin( LayoutRepr::Builtin(
Builtin::Int(_) | Builtin::Float(_) | Builtin::Bool | Builtin::Decimal, Builtin::Int(_) | Builtin::Float(_) | Builtin::Bool | Builtin::Decimal,
) => { )
| LayoutRepr::FunctionPointer(_) => {
// Generate a dummy function that immediately returns Unit // Generate a dummy function that immediately returns Unit
// Some higher-order Zig builtins *always* call an RC function on List elements. // Some higher-order Zig builtins *always* call an RC function on List elements.
rc_return_stmt(root, ident_ids, ctx) rc_return_stmt(root, ident_ids, ctx)
@ -230,6 +232,9 @@ pub fn refcount_generic<'a>(
structure, structure,
) )
} }
LayoutRepr::Erased(_) => {
todo_lambda_erasure!()
}
LayoutRepr::RecursivePointer(_) => unreachable!( LayoutRepr::RecursivePointer(_) => unreachable!(
"We should never call a refcounting helper on a RecursivePointer layout directly" "We should never call a refcounting helper on a RecursivePointer layout directly"
), ),

View file

@ -6,12 +6,12 @@ use roc_module::symbol::Symbol;
use crate::{ use crate::{
ir::{ ir::{
Call, CallSpecId, CallType, Expr, HigherOrderLowLevel, JoinPointId, ListLiteralElement, Call, CallSpecId, CallType, ErasedField, Expr, HigherOrderLowLevel, JoinPointId,
ModifyRc, Param, Proc, ProcLayout, Stmt, ListLiteralElement, ModifyRc, Param, Proc, ProcLayout, Stmt,
}, },
layout::{ layout::{
Builtin, InLayout, Layout, LayoutInterner, LayoutRepr, STLayoutInterner, TagIdIntType, Builtin, FunctionPointer, InLayout, Layout, LayoutInterner, LayoutRepr, STLayoutInterner,
UnionLayout, TagIdIntType, UnionLayout,
}, },
}; };
@ -27,6 +27,9 @@ pub enum UseKind {
SwitchCond, SwitchCond,
ExpectCond, ExpectCond,
ExpectLookup, ExpectLookup,
ErasedMake(ErasedField),
Erased,
FunctionPointer,
} }
pub enum ProblemKind<'a> { pub enum ProblemKind<'a> {
@ -70,6 +73,9 @@ pub enum ProblemKind<'a> {
proc_layout: ProcLayout<'a>, proc_layout: ProcLayout<'a>,
similar: Vec<ProcLayout<'a>>, similar: Vec<ProcLayout<'a>>,
}, },
PtrToUndefinedProc {
symbol: Symbol,
},
DuplicateCallSpecId { DuplicateCallSpecId {
old_call_line: usize, old_call_line: usize,
}, },
@ -114,6 +120,24 @@ pub enum ProblemKind<'a> {
num_needed: usize, num_needed: usize,
num_given: 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> { pub struct Problem<'a> {
@ -271,7 +295,7 @@ impl<'a, 'r> Ctx<'a, 'r> {
match body { match body {
Stmt::Let(x, e, x_layout, rest) => { 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) { if self.not_equiv(e_layout, *x_layout) {
self.problem(ProblemKind::SymbolDefMismatch { self.problem(ProblemKind::SymbolDefMismatch {
symbol: *x, 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 { match e {
Expr::Literal(_) => None, Expr::Literal(_) => None,
Expr::NullPointer => None, Expr::NullPointer => None,
@ -464,6 +488,40 @@ impl<'a, 'r> Ctx<'a, 'r> {
// TODO don't know what the element layout is // TODO don't know what the element layout is
None 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 { &Expr::Reset {
symbol, symbol,
update_mode: _, update_mode: _,
@ -642,6 +700,23 @@ impl<'a, 'r> Ctx<'a, 'r> {
} }
Some(*ret_layout) 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 { CallType::HigherOrder(HigherOrderLowLevel {
op: _, op: _,
closure_env_layout: _, 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> { enum TagPayloads<'a> {

View file

@ -4,7 +4,7 @@ use roc_module::symbol::{Interns, Symbol};
use ven_pretty::{text, Arena, DocAllocator, DocBuilder}; use ven_pretty::{text, Arena, DocAllocator, DocBuilder};
use crate::{ use crate::{
ir::{Parens, ProcLayout}, ir::{ErasedField, Parens, ProcLayout},
layout::LayoutInterner, layout::LayoutInterner,
}; };
@ -265,6 +265,15 @@ where
}; };
stack(f, [no_spec_doc, similar_doc]) 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 } => { ProblemKind::DuplicateCallSpecId { old_call_line } => {
title = "DUPLICATE CALL SPEC ID"; title = "DUPLICATE CALL SPEC ID";
docs_before = vec![(old_call_line, f.reflow("This call has a specialization 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), 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) (title, docs_before, doc)
} }
@ -429,6 +506,13 @@ fn format_use_kind(use_kind: UseKind) -> &'static str {
UseKind::SwitchCond => "switch condition", UseKind::SwitchCond => "switch condition",
UseKind::ExpectCond => "expect condition", UseKind::ExpectCond => "expect condition",
UseKind::ExpectLookup => "lookup for an expect", 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",
} }
} }

View file

@ -17,8 +17,8 @@ use roc_module::low_level::LowLevel;
use roc_module::symbol::{IdentIds, ModuleId, Symbol}; use roc_module::symbol::{IdentIds, ModuleId, Symbol};
use crate::ir::{ use crate::ir::{
BranchInfo, Call, CallType, Expr, JoinPointId, ListLiteralElement, Literal, ModifyRc, Proc, BranchInfo, Call, CallType, ErasedField, Expr, JoinPointId, ListLiteralElement, Literal,
ProcLayout, Stmt, UpdateModeId, ModifyRc, Proc, ProcLayout, Stmt, UpdateModeId,
}; };
use crate::layout::{ use crate::layout::{
Builtin, InLayout, Layout, LayoutInterner, LayoutRepr, STLayoutInterner, UnionLayout, 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); environment.add_list_child(*binding, *child, index as u64);
} }
} }
Reset { .. } | Expr::ResetRef { .. } => { /* do nothing */ } ErasedMake { value, callee: _ } => {
RuntimeErrorFunction(_) | GetTagId { .. } | EmptyArray | NullPointer => { /* do nothing */ 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 // 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 // only inserted for internal purposes. RC should not touch it
PtrStore => RC::NoRc, PtrStore => RC::NoRc,
PtrLoad => RC::NoRc, PtrLoad => RC::NoRc,
PtrCast => RC::NoRc,
Alloca => RC::NoRc, Alloca => RC::NoRc,
PtrClearTagId | PtrCast | RefCountIncRcPtr | RefCountDecRcPtr | RefCountIncDataPtr PtrClearTagId | RefCountIncRcPtr | RefCountDecRcPtr | RefCountIncDataPtr
| RefCountDecDataPtr | RefCountIsUnique => { | RefCountDecDataPtr | RefCountIsUnique => {
unreachable!("Only inserted *after* borrow checking: {:?}", lowlevel); unreachable!("Only inserted *after* borrow checking: {:?}", lowlevel);
} }

View file

@ -14,6 +14,7 @@ use roc_error_macros::internal_error;
use roc_module::low_level::LowLevel; use roc_module::low_level::LowLevel;
use roc_module::{low_level::LowLevelWrapperType, symbol::Symbol}; use roc_module::{low_level::LowLevelWrapperType, symbol::Symbol};
use crate::ir::ErasedField;
use crate::{ use crate::{
borrow::{lowlevel_borrow_signature, Ownership}, borrow::{lowlevel_borrow_signature, Ownership},
ir::{ ir::{
@ -872,7 +873,11 @@ fn insert_refcount_operations_binding<'a>(
} }
match expr { 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. // Literals, empty arrays, and runtime errors are not (and have nothing) reference counted.
new_let!(stmt) new_let!(stmt)
} }
@ -883,6 +888,25 @@ fn insert_refcount_operations_binding<'a>(
inc_owned!(arguments.iter().copied(), new_let) 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::GetTagId { structure, .. }
| Expr::StructAtIndex { structure, .. } | Expr::StructAtIndex { structure, .. }
| Expr::UnionAtIndex { structure, .. } | Expr::UnionAtIndex { structure, .. }
@ -942,6 +966,14 @@ fn insert_refcount_operations_binding<'a>(
inc_owned!(arguments.iter().copied(), new_let) 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 { .. } => { CallType::Foreign { .. } => {
// Foreign functions should be responsible for their own memory management. // 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. // But previously they were assumed to be called with borrowed parameters, so we do the same now.

View file

@ -1,11 +1,13 @@
#![allow(clippy::manual_map)] #![allow(clippy::manual_map)]
use crate::borrow::Ownership; use crate::borrow::Ownership;
use crate::ir::erased::{build_erased_function, ResolvedErasedLambda};
use crate::ir::literal::{make_num_literal, IntOrFloatValue}; use crate::ir::literal::{make_num_literal, IntOrFloatValue};
use crate::layout::{ use crate::layout::{
self, Builtin, ClosureCallOptions, ClosureRepresentation, EnumDispatch, InLayout, LambdaName, self, Builtin, ClosureCallOptions, ClosureDataKind, ClosureRepresentation, EnumDispatch,
LambdaSet, Layout, LayoutCache, LayoutInterner, LayoutProblem, LayoutRepr, Niche, InLayout, LambdaName, LambdaSet, Layout, LayoutCache, LayoutInterner, LayoutProblem,
RawFunctionLayout, TLLayoutInterner, TagIdIntType, UnionLayout, WrappedVariant, LayoutRepr, Niche, RawFunctionLayout, TLLayoutInterner, TagIdIntType, UnionLayout,
WrappedVariant,
}; };
use bumpalo::collections::{CollectIn, Vec}; use bumpalo::collections::{CollectIn, Vec};
use bumpalo::Bump; 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, ROC_PRINT_IR_AFTER_RESET_REUSE, ROC_PRINT_IR_AFTER_SPECIALIZATION, ROC_PRINT_RUNTIME_ERROR_GEN,
}; };
use roc_derive::SharedDerivedModule; 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::storage::{ExternalModuleStorage, ExternalModuleStorageSnapshot};
use roc_late_solve::{resolve_ability_specialization, AbilitiesView, Resolved, UnificationFailed}; use roc_late_solve::{resolve_ability_specialization, AbilitiesView, Resolved, UnificationFailed};
use roc_module::ident::{ForeignSymbol, Lowercase, TagName}; use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
@ -42,7 +44,9 @@ use pattern::{from_can_pattern, store_pattern, Pattern};
pub use literal::{ListLiteralElement, Literal}; pub use literal::{ListLiteralElement, Literal};
mod boxed;
mod decision_tree; mod decision_tree;
mod erased;
mod literal; mod literal;
mod pattern; mod pattern;
@ -305,6 +309,7 @@ pub struct Proc<'a> {
pub ret_layout: InLayout<'a>, pub ret_layout: InLayout<'a>,
pub is_self_recursive: SelfRecursive, pub is_self_recursive: SelfRecursive,
pub host_exposed_layouts: HostExposedLayouts<'a>, pub host_exposed_layouts: HostExposedLayouts<'a>,
pub is_erased: bool,
} }
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
@ -1682,6 +1687,13 @@ impl<'a> Call<'a> {
alloc.text("CallByName ").append(alloc.intersperse(it, " ")) 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, .. } => { LowLevel { op: lowlevel, .. } => {
let it = arguments.iter().map(|s| symbol_to_doc(alloc, *s, pretty)); 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>], arg_layouts: &'a [InLayout<'a>],
specialization_id: CallSpecId, specialization_id: CallSpecId,
}, },
ByPointer {
pointer: Symbol,
ret_layout: InLayout<'a>,
arg_layouts: &'a [InLayout<'a>],
},
Foreign { Foreign {
foreign_symbol: ForeignSymbol, foreign_symbol: ForeignSymbol,
ret_layout: InLayout<'a>, ret_layout: InLayout<'a>,
@ -1826,6 +1843,15 @@ pub struct ReuseToken {
pub update_mode: UpdateModeId, 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)] #[derive(Clone, Debug, PartialEq)]
pub enum Expr<'a> { pub enum Expr<'a> {
Literal(Literal<'a>), Literal(Literal<'a>),
@ -1872,6 +1898,28 @@ pub enum Expr<'a> {
}, },
EmptyArray, 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 { Reset {
symbol: Symbol, symbol: Symbol,
update_mode: UpdateModeId, update_mode: UpdateModeId,
@ -2053,6 +2101,38 @@ impl<'a> Expr<'a> {
.text("GetTagId ") .text("GetTagId ")
.append(symbol_to_doc(alloc, *structure, pretty)), .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 { UnionAtIndex {
tag_id, tag_id,
structure, structure,
@ -2096,24 +2176,6 @@ impl<'a> Expr<'a> {
arguments: std::slice::from_ref(symbol), 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> { 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 runtime_error = runtime_error(env, msg.into_bump_str());
let is_erased = layout.is_erased_function();
let (args, ret_layout) = match layout { let (args, ret_layout) = match layout {
RawFunctionLayout::Function(arg_layouts, lambda_set, ret_layout) => { RawFunctionLayout::Function(arg_layouts, lambda_set, ret_layout) => {
let real_arg_layouts = let real_arg_layouts =
@ -3195,6 +3258,9 @@ fn generate_runtime_error_function<'a>(
(args.into_bump_slice(), ret_layout) (args.into_bump_slice(), ret_layout)
} }
RawFunctionLayout::ErasedFunction(..) => {
todo_lambda_erasure!()
}
RawFunctionLayout::ZeroArgumentThunk(ret_layout) => (&[] as &[_], ret_layout), RawFunctionLayout::ZeroArgumentThunk(ret_layout) => (&[] as &[_], ret_layout),
}; };
@ -3206,6 +3272,7 @@ fn generate_runtime_error_function<'a>(
ret_layout, ret_layout,
is_self_recursive: SelfRecursive::NotSelfRecursive, is_self_recursive: SelfRecursive::NotSelfRecursive,
host_exposed_layouts: HostExposedLayouts::NotHostExposed, host_exposed_layouts: HostExposedLayouts::NotHostExposed,
is_erased,
} }
} }
@ -3284,6 +3351,9 @@ fn generate_host_exposed_function<'a>(
(function_name, (top_level, proc)) (function_name, (top_level, proc))
} }
RawFunctionLayout::ErasedFunction(..) => {
todo_lambda_erasure!()
}
RawFunctionLayout::ZeroArgumentThunk(result) => { RawFunctionLayout::ZeroArgumentThunk(result) => {
let assigned = env.unique_symbol(); let assigned = env.unique_symbol();
let hole = env.arena.alloc(Stmt::Ret(assigned)); let hole = env.arena.alloc(Stmt::Ret(assigned));
@ -3298,6 +3368,7 @@ fn generate_host_exposed_function<'a>(
ret_layout: result, ret_layout: result,
is_self_recursive: SelfRecursive::NotSelfRecursive, is_self_recursive: SelfRecursive::NotSelfRecursive,
host_exposed_layouts: HostExposedLayouts::NotHostExposed, host_exposed_layouts: HostExposedLayouts::NotHostExposed,
is_erased: false,
}; };
let top_level = ProcLayout::from_raw_named(env.arena, lambda_name, layout); 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, ret_layout: return_layout,
is_self_recursive: SelfRecursive::NotSelfRecursive, is_self_recursive: SelfRecursive::NotSelfRecursive,
host_exposed_layouts: HostExposedLayouts::NotHostExposed, host_exposed_layouts: HostExposedLayouts::NotHostExposed,
is_erased: false,
}; };
let top_level = ProcLayout::new( let top_level = ProcLayout::new(
@ -3433,6 +3505,7 @@ fn specialize_proc_help<'a>(
SpecializedLayout::FunctionPointerBody { SpecializedLayout::FunctionPointerBody {
ret_layout, ret_layout,
closure: opt_closure_layout, closure: opt_closure_layout,
is_erased,
} => { } => {
// this is a function body like // this is a function body like
// //
@ -3458,18 +3531,23 @@ fn specialize_proc_help<'a>(
ret_layout, ret_layout,
is_self_recursive: recursivity, is_self_recursive: recursivity,
host_exposed_layouts, host_exposed_layouts,
is_erased,
} }
} }
SpecializedLayout::FunctionBody { SpecializedLayout::FunctionBody {
arguments: proc_args, arguments: proc_args,
closure: opt_closure_layout, closure: opt_closure_layout,
ret_layout, ret_layout,
is_erased,
} => { } => {
let mut proc_args = Vec::from_iter_in(proc_args.iter().copied(), env.arena); let mut proc_args = Vec::from_iter_in(proc_args.iter().copied(), env.arena);
// unpack the closure symbols, if any // unpack the closure symbols, if any
match (opt_closure_layout, captured_symbols) { 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()); // debug_assert!(!captured.is_empty());
// An argument from the closure list may have taken on a specialized symbol // 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([])) => {} (None, CapturedSymbols::None) | (None, CapturedSymbols::Captured([])) => {}
_ => unreachable!("to closure or not to closure?"), _ => unreachable!("to closure or not to closure?"),
} }
@ -3637,13 +3724,7 @@ fn specialize_proc_help<'a>(
.maybe_get_specialized(*symbol, *layout) .maybe_get_specialized(*symbol, *layout)
}); });
let closure_data_layout = match opt_closure_layout { let closure_data_layout = opt_closure_layout.map(|clos| clos.data_layout());
Some(lambda_set) => {
let lambda_set = lambda_set.full_layout;
Some(lambda_set)
}
None => None,
};
Proc { Proc {
name: lambda_name, name: lambda_name,
@ -3653,6 +3734,7 @@ fn specialize_proc_help<'a>(
ret_layout, ret_layout,
is_self_recursive: recursivity, is_self_recursive: recursivity,
host_exposed_layouts, host_exposed_layouts,
is_erased,
} }
} }
}; };
@ -3665,13 +3747,15 @@ enum SpecializedLayout<'a> {
/// A body like `foo = \a,b,c -> ...` /// A body like `foo = \a,b,c -> ...`
FunctionBody { FunctionBody {
arguments: &'a [(InLayout<'a>, Symbol)], arguments: &'a [(InLayout<'a>, Symbol)],
closure: Option<LambdaSet<'a>>, closure: Option<ClosureDataKind<'a>>,
ret_layout: InLayout<'a>, ret_layout: InLayout<'a>,
is_erased: bool,
}, },
/// A body like `foo = Num.add` /// A body like `foo = Num.add`
FunctionPointerBody { FunctionPointerBody {
closure: Option<LambdaSet<'a>>, closure: Option<LambdaSet<'a>>,
ret_layout: InLayout<'a>, ret_layout: InLayout<'a>,
is_erased: bool,
}, },
} }
@ -3693,7 +3777,20 @@ fn build_specialized_proc_from_var<'a>(
lambda_name, lambda_name,
pattern_symbols, pattern_symbols,
pattern_layouts_vec, 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, ret_layout,
) )
} }
@ -3717,7 +3814,7 @@ fn build_specialized_proc<'a>(
lambda_name: LambdaName<'a>, lambda_name: LambdaName<'a>,
pattern_symbols: &[Symbol], pattern_symbols: &[Symbol],
pattern_layouts: Vec<'a, InLayout<'a>>, pattern_layouts: Vec<'a, InLayout<'a>>,
lambda_set: Option<LambdaSet<'a>>, closure_data: Option<ClosureDataKind<'a>>,
ret_layout: InLayout<'a>, ret_layout: InLayout<'a>,
) -> Result<SpecializedLayout<'a>, LayoutProblem> { ) -> Result<SpecializedLayout<'a>, LayoutProblem> {
use SpecializedLayout::*; use SpecializedLayout::*;
@ -3730,6 +3827,8 @@ fn build_specialized_proc<'a>(
proc_args.push((arg_layout, *arg_name)); proc_args.push((arg_layout, *arg_name));
} }
let is_erased = matches!(closure_data, Some(ClosureDataKind::Erased));
// Given // Given
// //
// foo = // foo =
@ -3749,12 +3848,12 @@ fn build_specialized_proc<'a>(
// then // then
let proc_name = lambda_name.name(); let proc_name = lambda_name.name();
match lambda_set { match closure_data {
Some(lambda_set) if pattern_symbols.last() == Some(&Symbol::ARG_CLOSURE) => { 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`, // 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) // it stores the closure structure (just an integer in this case)
let lambda_set_layout = lambda_set.full_layout; let closure_data_layout = closure_data.data_layout();
proc_args.push((lambda_set_layout, Symbol::ARG_CLOSURE)); proc_args.push((closure_data_layout, Symbol::ARG_CLOSURE));
debug_assert_eq!( debug_assert_eq!(
pattern_layouts_len + 1, pattern_layouts_len + 1,
@ -3766,11 +3865,12 @@ fn build_specialized_proc<'a>(
Ok(FunctionBody { Ok(FunctionBody {
arguments: proc_args, arguments: proc_args,
closure: Some(lambda_set), closure: Some(closure_data),
ret_layout, ret_layout,
is_erased,
}) })
} }
Some(lambda_set) => { Some(closure_data) => {
// a function that returns a function, but is not itself a closure // a function that returns a function, but is not itself a closure
// e.g. f = Num.add // e.g. f = Num.add
@ -3786,14 +3886,16 @@ fn build_specialized_proc<'a>(
arguments: proc_args, arguments: proc_args,
closure: None, closure: None,
ret_layout, ret_layout,
is_erased,
}) })
} }
Ordering::Greater => { Ordering::Greater => {
if pattern_symbols.is_empty() { if pattern_symbols.is_empty() {
let ret_layout = lambda_set.full_layout; let ret_layout = closure_data.data_layout();
Ok(FunctionPointerBody { Ok(FunctionPointerBody {
closure: None, closure: None,
ret_layout, ret_layout,
is_erased,
}) })
} else { } else {
// so far, the problem when hitting this branch was always somewhere 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, arguments: proc_args,
closure: None, closure: None,
ret_layout, ret_layout,
is_erased,
}) })
} }
Ordering::Greater => { Ordering::Greater => {
@ -3831,6 +3934,7 @@ fn build_specialized_proc<'a>(
Ok(FunctionPointerBody { Ok(FunctionPointerBody {
closure: None, closure: None,
ret_layout, ret_layout,
is_erased,
}) })
} else { } else {
// so far, the problem when hitting this branch was always somewhere 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); lambda_set.extend_argument_list_for_named(arena, lambda_name, arguments);
ProcLayout::new(arena, arguments, lambda_name.niche(), result) 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) => { RawFunctionLayout::ZeroArgumentThunk(result) => {
ProcLayout::new(arena, &[], Niche::NONE, result) ProcLayout::new(arena, &[], Niche::NONE, result)
} }
@ -4824,6 +4939,7 @@ pub fn with_hole<'a>(
hole, hole,
) )
} }
RawFunctionLayout::ErasedFunction(_, _) => todo_lambda_erasure!(),
RawFunctionLayout::ZeroArgumentThunk(_) => unreachable!(), RawFunctionLayout::ZeroArgumentThunk(_) => unreachable!(),
} }
} }
@ -4925,6 +5041,7 @@ pub fn with_hole<'a>(
hole, hole,
) )
} }
RawFunctionLayout::ErasedFunction(..) => todo_lambda_erasure!(),
RawFunctionLayout::ZeroArgumentThunk(_) => { RawFunctionLayout::ZeroArgumentThunk(_) => {
internal_error!("should not be a thunk!") internal_error!("should not be a thunk!")
} }
@ -5140,6 +5257,44 @@ pub fn with_hole<'a>(
RawFunctionLayout::ZeroArgumentThunk(_) => { RawFunctionLayout::ZeroArgumentThunk(_) => {
unreachable!("a closure syntactically always must have at least one argument") 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) => { RawFunctionLayout::Function(_argument_layouts, lambda_set, _ret_layout) => {
let mut captured_symbols = Vec::from_iter_in(captured_symbols, env.arena); let mut captured_symbols = Vec::from_iter_in(captured_symbols, env.arena);
captured_symbols.sort(); captured_symbols.sort();
@ -5172,9 +5327,8 @@ pub fn with_hole<'a>(
env, env,
env.arena.alloc(format!("RuntimeError: {e:?}",)), env.arena.alloc(format!("RuntimeError: {e:?}",)),
); );
} else {
drop(inserted);
} }
drop(inserted);
// define the closure data // define the closure data
@ -5323,6 +5477,7 @@ pub fn with_hole<'a>(
env.arena.alloc(result), env.arena.alloc(result),
); );
} }
RawFunctionLayout::ErasedFunction(..) => todo_lambda_erasure!(),
RawFunctionLayout::ZeroArgumentThunk(_) => { RawFunctionLayout::ZeroArgumentThunk(_) => {
unreachable!("calling a non-closure layout") unreachable!("calling a non-closure layout")
} }
@ -5357,6 +5512,7 @@ pub fn with_hole<'a>(
hole, hole,
); );
} }
RawFunctionLayout::ErasedFunction(..) => todo_lambda_erasure!(),
RawFunctionLayout::ZeroArgumentThunk(_) => { RawFunctionLayout::ZeroArgumentThunk(_) => {
unreachable!("calling a non-closure layout") unreachable!("calling a non-closure layout")
} }
@ -5424,6 +5580,22 @@ pub fn with_hole<'a>(
env.arena.alloc(result), 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(_) => { RawFunctionLayout::ZeroArgumentThunk(_) => {
unreachable!( unreachable!(
"{:?} cannot be called in the source language", "{:?} cannot be called in the source language",
@ -5557,6 +5729,7 @@ pub fn with_hole<'a>(
hole, hole,
) )
} }
RawFunctionLayout::ErasedFunction(..) => todo_lambda_erasure!(),
RawFunctionLayout::ZeroArgumentThunk(_) => unreachable!("match_on_closure_argument received a zero-argument thunk"), 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"), _ => 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) Stmt::Let(assigned, expr, layout, hole)
} }
@ -5618,7 +5791,7 @@ pub fn with_hole<'a>(
debug_assert_eq!(arg_symbols.len(), 1); debug_assert_eq!(arg_symbols.len(), 1);
let x = arg_symbols[0]; 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) Stmt::Let(assigned, expr, layout, hole)
} }
@ -6478,6 +6651,7 @@ fn tag_union_to_function<'a>(
hole, hole,
) )
} }
RawFunctionLayout::ErasedFunction(..) => todo_lambda_erasure!(),
RawFunctionLayout::ZeroArgumentThunk(_) => unreachable!(), RawFunctionLayout::ZeroArgumentThunk(_) => unreachable!(),
} }
} }
@ -7502,6 +7676,15 @@ fn substitute_in_call<'a>(
ret_layout: *ret_layout, ret_layout: *ret_layout,
specialization_id: *specialization_id, 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::Foreign { .. } => None,
CallType::LowLevel { .. } => None, CallType::LowLevel { .. } => None,
CallType::HigherOrder { .. } => 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 { StructAtIndex {
index, index,
structure, structure,
@ -7968,6 +8179,7 @@ fn specialize_symbol<'a>(
// data for a lambda set. // data for a lambda set.
let layout = match raw { let layout = match raw {
RawFunctionLayout::ZeroArgumentThunk(layout) => layout, RawFunctionLayout::ZeroArgumentThunk(layout) => layout,
RawFunctionLayout::ErasedFunction(..) => todo_lambda_erasure!(),
RawFunctionLayout::Function(_, lambda_set, _) => layout_cache RawFunctionLayout::Function(_, lambda_set, _) => layout_cache
.put_in_direct_no_semantic(LayoutRepr::LambdaSet(lambda_set)), .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) => { RawFunctionLayout::ZeroArgumentThunk(ret_layout) => {
// this is a 0-argument thunk // this is a 0-argument thunk
let top_level = ProcLayout::new(env.arena, &[], Niche::NONE, ret_layout); 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)) => { Ok(RawFunctionLayout::ZeroArgumentThunk(ret_layout)) => {
if procs.is_module_thunk(proc_name) { if procs.is_module_thunk(proc_name) {
// here we turn a call to a module thunk into forcing of that thunk // 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 // 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) build_call(env, call, assigned, lambda_set.full_layout, hole)
} }
RawFunctionLayout::ErasedFunction(..) => todo_lambda_erasure!(),
RawFunctionLayout::ZeroArgumentThunk(_) => { RawFunctionLayout::ZeroArgumentThunk(_) => {
unreachable!() unreachable!()
} }
@ -9805,6 +10082,7 @@ pub fn find_lambda_sets_help(
stack.extend(subs.variables[subs_slice.indices()].iter()); stack.extend(subs.variables[subs_slice.indices()].iter());
} }
} }
Content::ErasedLambda => {}
} }
} }
@ -9943,6 +10221,8 @@ where
LayoutRepr::RecursivePointer(_) => { LayoutRepr::RecursivePointer(_) => {
/* do nothing, we've already generated for this type through the Union(_) */ /* 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 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( let unbox_stmt = Stmt::Let(
unboxed, unboxed,
@ -10021,6 +10301,7 @@ where
ret_layout: *field, ret_layout: *field,
is_self_recursive: SelfRecursive::NotSelfRecursive, is_self_recursive: SelfRecursive::NotSelfRecursive,
host_exposed_layouts: HostExposedLayouts::NotHostExposed, host_exposed_layouts: HostExposedLayouts::NotHostExposed,
is_erased: false,
}; };
answer.push(GlueProc { answer.push(GlueProc {
@ -10105,7 +10386,7 @@ where
let field_get_stmt = Stmt::Let(result, field_get_expr, *field, ret_stmt); 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 unbox_stmt = Stmt::Let(unboxed, unbox_expr, interned, arena.alloc(field_get_stmt));
let proc = Proc { let proc = Proc {
@ -10116,6 +10397,7 @@ where
ret_layout: *field, ret_layout: *field,
is_self_recursive: SelfRecursive::NotSelfRecursive, is_self_recursive: SelfRecursive::NotSelfRecursive,
host_exposed_layouts: HostExposedLayouts::NotHostExposed, host_exposed_layouts: HostExposedLayouts::NotHostExposed,
is_erased: false,
}; };
answer.push(GlueProc { answer.push(GlueProc {

View 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,
}
}

View 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)
}

View file

@ -26,8 +26,11 @@ use std::collections::HashMap;
use std::hash::Hash; use std::hash::Hash;
use ven_pretty::{DocAllocator, DocBuilder}; use ven_pretty::{DocAllocator, DocBuilder};
mod erased;
mod intern; mod intern;
mod semantic; mod semantic;
pub use erased::Erased;
pub use intern::{ pub use intern::{
GlobalLayoutInterner, InLayout, LayoutInterner, STLayoutInterner, TLLayoutInterner, GlobalLayoutInterner, InLayout, LayoutInterner, STLayoutInterner, TLLayoutInterner,
}; };
@ -484,6 +487,7 @@ impl From<LayoutProblem> for RuntimeError {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum RawFunctionLayout<'a> { pub enum RawFunctionLayout<'a> {
Function(&'a [InLayout<'a>], LambdaSet<'a>, InLayout<'a>), Function(&'a [InLayout<'a>], LambdaSet<'a>, InLayout<'a>),
ErasedFunction(&'a [InLayout<'a>], InLayout<'a>),
ZeroArgumentThunk(InLayout<'a>), ZeroArgumentThunk(InLayout<'a>),
} }
@ -492,6 +496,10 @@ impl<'a> RawFunctionLayout<'a> {
matches!(self, RawFunctionLayout::ZeroArgumentThunk(_)) matches!(self, RawFunctionLayout::ZeroArgumentThunk(_))
} }
pub fn is_erased_function(&self) -> bool {
matches!(self, RawFunctionLayout::ErasedFunction(_, _))
}
fn new_help<'b>( fn new_help<'b>(
env: &mut Env<'a, 'b>, env: &mut Env<'a, 'b>,
var: Variable, var: Variable,
@ -508,6 +516,7 @@ impl<'a> RawFunctionLayout<'a> {
LambdaSet(_) => { LambdaSet(_) => {
internal_error!("lambda set should only appear under a function, where it's handled independently."); 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), Structure(flat_type) => Self::layout_from_flat_type(env, flat_type),
RangedNumber(..) => Layout::new_help(env, var, content).then(Self::ZeroArgumentThunk), 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 fn_args = fn_args.into_bump_slice();
let lambda_set = cached!( let closure_data = build_function_closure_data(env, args, closure_var, ret_var);
LambdaSet::from_var(env, args, closure_var, ret_var), let closure_data = cached!(closure_data, cache_criteria, env.subs);
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) => { TagUnion(tags, ext) if tags.is_newtype_wrapper(env.subs) => {
debug_assert!(ext_var_is_empty_tag_union(env.subs, ext)); debug_assert!(ext_var_is_empty_tag_union(env.subs, ext));
@ -678,6 +691,47 @@ pub enum LayoutRepr<'a> {
Union(UnionLayout<'a>), Union(UnionLayout<'a>),
LambdaSet(LambdaSet<'a>), LambdaSet(LambdaSet<'a>),
RecursivePointer(InLayout<'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)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
@ -1312,6 +1366,13 @@ pub struct LambdaName<'a> {
} }
impl<'a> 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)] #[inline(always)]
pub fn name(&self) -> Symbol { pub fn name(&self) -> Symbol {
self.name 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)] #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct LambdaSet<'a> { pub struct LambdaSet<'a> {
pub(crate) args: &'a &'a [InLayout<'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::FlexAbleVar(_, _)
| Content::RigidAbleVar(_, _) | Content::RigidAbleVar(_, _)
| Content::RangedNumber(_) | Content::RangedNumber(_)
| Content::Error => {} | Content::Error
| Content::ErasedLambda => {}
} }
} }
(max_depth_any_ctor, max_depth_only_lset, total) (max_depth_any_ctor, max_depth_only_lset, total)
@ -2393,6 +2486,9 @@ impl<'a> Layout<'a> {
LambdaSet(_) => { LambdaSet(_) => {
internal_error!("lambda set should only appear under a function, where it's handled independently."); 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), Structure(flat_type) => layout_from_flat_type(env, flat_type),
Alias(symbol, _args, actual_var, _) => { Alias(symbol, _args, actual_var, _) => {
@ -2529,6 +2625,7 @@ impl<'a> LayoutRepr<'a> {
pub const DEC: Self = LayoutRepr::Builtin(Builtin::Decimal); pub const DEC: Self = LayoutRepr::Builtin(Builtin::Decimal);
pub const STR: Self = LayoutRepr::Builtin(Builtin::Str); pub const STR: Self = LayoutRepr::Builtin(Builtin::Str);
pub const OPAQUE_PTR: Self = LayoutRepr::Ptr(Layout::VOID); 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 { pub const fn struct_(field_layouts: &'a [InLayout<'a>]) -> Self {
Self::Struct(field_layouts) 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! // We cannot memcpy pointers, because then we would have the same pointer in multiple places!
false false
} }
Erased(e) => e.safe_to_memcpy(),
FunctionPointer(..) => true,
} }
} }
@ -2659,8 +2758,10 @@ impl<'a> LayoutRepr<'a> {
LambdaSet(lambda_set) => interner LambdaSet(lambda_set) => interner
.get_repr(lambda_set.runtime_representation()) .get_repr(lambda_set.runtime_representation())
.stack_size_without_alignment(interner), .stack_size_without_alignment(interner),
RecursivePointer(_) => interner.target_info().ptr_width() as u32, RecursivePointer(_) | Ptr(_) | FunctionPointer(_) => {
Ptr(_) => interner.target_info().ptr_width() as u32, 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()) .get_repr(lambda_set.runtime_representation())
.alignment_bytes(interner), .alignment_bytes(interner),
Builtin(builtin) => builtin.alignment_bytes(interner.target_info()), Builtin(builtin) => builtin.alignment_bytes(interner.target_info()),
RecursivePointer(_) => interner.target_info().ptr_width() as u32, RecursivePointer(_) | Ptr(_) | FunctionPointer(_) => {
Ptr(_) => interner.target_info().ptr_width() as u32, 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") unreachable!("should be looked up to get an actual layout")
} }
Ptr(inner) => interner.get_repr(*inner).alignment_bytes(interner), 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, Builtin(List(_)) | Builtin(Str) => true,
Erased(_) => true,
_ => false, _ => false,
} }
} }
@ -2808,6 +2915,8 @@ impl<'a> LayoutRepr<'a> {
// author must make sure that invariants are upheld // author must make sure that invariants are upheld
false false
} }
FunctionPointer(_) => false,
Erased(e) => e.is_refcounted(),
} }
} }
@ -2869,6 +2978,12 @@ impl<'a> LayoutRepr<'a> {
RecursivePointer(_) => { RecursivePointer(_) => {
/* do nothing, we've already generated for this type through the Union(_) */ /* 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 { } else {
let mut criteria = CACHEABLE; let mut criteria = CACHEABLE;
let lambda_set = cached!( let closure_data = build_function_closure_data(env, args, closure_var, ret_var);
LambdaSet::from_var(env, args, closure_var, ret_var), let closure_data = cached!(closure_data, criteria, env.subs);
criteria,
env.subs
);
let lambda_set = lambda_set.full_layout;
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) => { Record(fields, ext_var) => {
@ -4461,7 +4577,7 @@ fn layout_from_num_content<'a>(
Alias(_, _, _, _) => { Alias(_, _, _, _) => {
todo!("TODO recursively resolve type aliases in num_from_content"); todo!("TODO recursively resolve type aliases in num_from_content");
} }
Structure(_) | RangedNumber(..) | LambdaSet(_) => { Structure(_) | RangedNumber(..) | LambdaSet(_) | ErasedLambda => {
panic!("Invalid Num.Num type application: {content:?}"); panic!("Invalid Num.Num type application: {content:?}");
} }
Error => Err(LayoutProblem::Erroneous), Error => Err(LayoutProblem::Erroneous),

View 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))
}
}

View file

@ -75,11 +75,12 @@ cache_interned_layouts! {
15, DEC, pub, nosema!(LayoutRepr::DEC) 15, DEC, pub, nosema!(LayoutRepr::DEC)
16, STR, pub, nosema!(LayoutRepr::STR) 16, STR, pub, nosema!(LayoutRepr::STR)
17, OPAQUE_PTR, pub, nosema!(LayoutRepr::OPAQUE_PTR) 17, OPAQUE_PTR, pub, nosema!(LayoutRepr::OPAQUE_PTR)
18, NAKED_RECURSIVE_PTR, pub(super), nosema!(LayoutRepr::RecursivePointer(Layout::VOID)) 18, ERASED, pub, nosema!(LayoutRepr::ERASED)
19, STR_PTR, pub, nosema!(LayoutRepr::Ptr(Layout::STR)) 19, NAKED_RECURSIVE_PTR, pub(super), nosema!(LayoutRepr::RecursivePointer(Layout::VOID))
20, LIST_U8, pub, nosema!(LayoutRepr::Builtin(crate::layout::Builtin::List(Layout::U8))) 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 { macro_rules! impl_to_from_int_width {
@ -354,6 +355,8 @@ pub trait LayoutInterner<'a>: Sized {
.text("Ptr(") .text("Ptr(")
.append(self.to_doc(inner, alloc, seen_rec, parens)) .append(self.to_doc(inner, alloc, seen_rec, parens))
.append(")"), .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 bumpalo::{collections::Vec, Bump};
use roc_module::symbol::Symbol; 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}; use super::{InLayout, LayoutInterner, NeedsRecursionPointerFixup};
@ -1121,6 +1126,13 @@ mod reify {
// another recursive union's layout, do not change it. // another recursive union's layout, do not change it.
LayoutRepr::RecursivePointer(if l == Layout::VOID { slot } else { l }) 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 { pub mod dbg_deep {
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use crate::layout::{Builtin, LambdaSet, LayoutRepr, UnionLayout}; use crate::layout::{Builtin, Erased, LambdaSet, LayoutRepr, UnionLayout};
use super::{InLayout, LayoutInterner}; use super::{InLayout, LayoutInterner};
@ -1447,6 +1459,12 @@ pub mod dbg_deep {
LayoutRepr::RecursivePointer(rp) => { LayoutRepr::RecursivePointer(rp) => {
f.debug_tuple("RecursivePointer").field(&rp.0).finish() 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 { pub mod dbg_stable {
use roc_module::symbol::Symbol; 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}; use super::{InLayout, LayoutInterner};
@ -1620,6 +1638,12 @@ pub mod dbg_stable {
LayoutRepr::RecursivePointer(rp) => { LayoutRepr::RecursivePointer(rp) => {
f.debug_tuple("RecursivePointer").field(&rp.0).finish() 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(),
} }
} }
} }

View file

@ -670,6 +670,7 @@ impl<'a> TrmcEnv<'a> {
// because we do not allow polymorphic recursion, this is the only constraint // because we do not allow polymorphic recursion, this is the only constraint
name == lambda_name name == lambda_name
} }
CallType::ByPointer { .. } => false,
CallType::Foreign { .. } | CallType::LowLevel { .. } | CallType::HigherOrder(_) => { CallType::Foreign { .. } | CallType::LowLevel { .. } | CallType::HigherOrder(_) => {
false false
} }
@ -812,6 +813,7 @@ impl<'a> TrmcEnv<'a> {
ret_layout: proc.ret_layout, ret_layout: proc.ret_layout,
is_self_recursive: SelfRecursive::NotSelfRecursive, is_self_recursive: SelfRecursive::NotSelfRecursive,
host_exposed_layouts: proc.host_exposed_layouts.clone(), 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), Some(ru) => ru.symbol == needle || arguments.contains(&needle),
}, },
Expr::Struct(fields) => fields.contains(&needle), Expr::Struct(fields) => fields.contains(&needle),
Expr::NullPointer => false, Expr::NullPointer | Expr::FunctionPointer { .. } => false,
Expr::StructAtIndex { structure, .. } Expr::StructAtIndex { structure, .. }
| Expr::GetTagId { structure, .. } | Expr::GetTagId { structure, .. }
| Expr::UnionAtIndex { structure, .. } | Expr::UnionAtIndex { structure, .. }
@ -1108,6 +1110,10 @@ fn expr_contains_symbol(expr: &Expr, needle: Symbol) -> bool {
Expr::EmptyArray => false, Expr::EmptyArray => false,
Expr::Reset { symbol, .. } | Expr::ResetRef { symbol, .. } => needle == *symbol, Expr::Reset { symbol, .. } | Expr::ResetRef { symbol, .. } => needle == *symbol,
Expr::RuntimeErrorFunction(_) => false, Expr::RuntimeErrorFunction(_) => false,
Expr::ErasedMake { value, callee } => {
value.map(|v| v == needle).unwrap_or(false) || needle == *callee
}
Expr::ErasedLoad { symbol, field: _ } => needle == *symbol,
} }
} }

View file

@ -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::{Env as UEnv, MustImplementConstraints};
use roc_unify::unify::{MustImplementAbility, Obligated}; use roc_unify::unify::{MustImplementAbility, Obligated};
use crate::env::Env; use crate::env::InferenceEnv;
use crate::{aliases::Aliases, to_var::type_to_var}; use crate::{aliases::Aliases, to_var::type_to_var};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -56,7 +56,7 @@ pub struct PendingDerivesTable(
impl PendingDerivesTable { impl PendingDerivesTable {
pub fn new( pub fn new(
env: &mut Env, env: &mut InferenceEnv,
types: &mut Types, types: &mut Types,
aliases: &mut Aliases, aliases: &mut Aliases,
pending_derives: PendingDerives, pending_derives: PendingDerives,
@ -827,6 +827,12 @@ trait DerivableVisitor {
context: NotDerivableContext::NoContext, context: NotDerivableContext::NoContext,
}) })
} }
ErasedLambda => {
return Err(NotDerivable {
var,
context: NotDerivableContext::NoContext,
})
}
Error => { Error => {
return Err(NotDerivable { return Err(NotDerivable {
var, var,

View file

@ -9,7 +9,7 @@ use roc_types::{
}; };
use crate::to_var::type_to_var_help; use crate::to_var::type_to_var_help;
use crate::{ability::ObligationCache, env::Env}; use crate::{ability::ObligationCache, env::InferenceEnv};
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
struct DelayedAliasVariables { struct DelayedAliasVariables {
@ -121,7 +121,7 @@ impl Aliases {
} }
fn instantiate_result_result( fn instantiate_result_result(
env: &mut Env, env: &mut InferenceEnv,
rank: Rank, rank: Rank,
alias_variables: AliasVariables, alias_variables: AliasVariables,
) -> Variable { ) -> Variable {
@ -143,7 +143,7 @@ impl Aliases {
/// Build an alias of the form `Num range := range` /// Build an alias of the form `Num range := range`
fn build_num_opaque( fn build_num_opaque(
env: &mut Env, env: &mut InferenceEnv,
rank: Rank, rank: Rank,
symbol: Symbol, symbol: Symbol,
range_var: Variable, range_var: Variable,
@ -160,7 +160,7 @@ impl Aliases {
fn instantiate_builtin_aliases_real_var( fn instantiate_builtin_aliases_real_var(
&mut self, &mut self,
env: &mut Env, env: &mut InferenceEnv,
rank: Rank, rank: Rank,
symbol: Symbol, symbol: Symbol,
alias_variables: AliasVariables, alias_variables: AliasVariables,
@ -228,7 +228,7 @@ impl Aliases {
pub fn instantiate_real_var( pub fn instantiate_real_var(
&mut self, &mut self,
env: &mut Env, env: &mut InferenceEnv,
rank: Rank, rank: Rank,
problems: &mut Vec<TypeError>, problems: &mut Vec<TypeError>,
abilities_store: &AbilitiesStore, abilities_store: &AbilitiesStore,

View file

@ -10,10 +10,15 @@ use roc_types::{
types::{RecordField, Uls}, types::{RecordField, Uls},
}; };
use crate::env::Env; use crate::env::SolveEnv;
// TODO: eventually, we could possibly use the arena in Env instead. // 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 mut visited = bumpalo::collections::Vec::with_capacity_in(256, arena);
let pool = env.pools.get_mut(rank); 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)); subs.set_content_unchecked(copy, Structure(new_flat_type));
} }
FlexVar(_) | FlexAbleVar(_, _) | Error => { FlexVar(_) | FlexAbleVar(_, _) | Error | ErasedLambda => {
subs.set_content_unchecked(copy, content); subs.set_content_unchecked(copy, content);
} }

View file

@ -4,7 +4,7 @@ use roc_derive::SharedDerivedModule;
use roc_types::subs::{Content, Descriptor, Mark, OptVariable, Rank, Subs, Variable}; use roc_types::subs::{Content, Descriptor, Mark, OptVariable, Rank, Subs, Variable};
use roc_unify::unify::Env as UEnv; use roc_unify::unify::Env as UEnv;
use crate::Pools; use crate::{FunctionKind, Pools};
pub struct DerivedEnv<'a> { pub struct DerivedEnv<'a> {
pub derived_module: &'a SharedDerivedModule, pub derived_module: &'a SharedDerivedModule,
@ -12,15 +12,38 @@ pub struct DerivedEnv<'a> {
pub exposed_types: &'a ExposedByModule, pub exposed_types: &'a ExposedByModule,
} }
pub struct Env<'a> { /// Environment necessary for solving and specialization.
pub struct SolveEnv<'a> {
pub arena: &'a Bump, pub arena: &'a Bump,
pub constraints: &'a Constraints,
pub derived_env: &'a DerivedEnv<'a>, pub derived_env: &'a DerivedEnv<'a>,
pub subs: &'a mut Subs, pub subs: &'a mut Subs,
pub pools: &'a mut Pools, 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)] #[inline(always)]
pub fn register(&mut self, rank: Rank, content: Content) -> Variable { pub fn register(&mut self, rank: Rank, content: Content) -> Variable {
let descriptor = Descriptor { let descriptor = Descriptor {
@ -40,13 +63,7 @@ impl<'a> Env<'a> {
/// Introduce some variables to Pools at the given rank. /// Introduce some variables to Pools at the given rank.
/// Also, set each of their ranks in Subs to be 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]) { pub fn introduce(&mut self, rank: Rank, vars: &[Variable]) {
let pool: &mut Vec<Variable> = self.pools.get_mut(rank); introduce(self.subs, self.pools, rank, vars);
for &var in vars.iter() {
self.subs.set_rank(var, rank);
}
pool.extend(vars);
} }
#[inline(always)] #[inline(always)]
@ -78,4 +95,25 @@ impl<'a> Env<'a> {
pub fn uenv(&mut self) -> UEnv { pub fn uenv(&mut self) -> UEnv {
UEnv::new(self.subs) 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);
} }

View 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,
}

View file

@ -14,9 +14,11 @@ pub mod specialize;
mod aliases; mod aliases;
mod deep_copy; mod deep_copy;
mod env; mod env;
mod kinds;
mod pools; mod pools;
mod to_var; mod to_var;
pub use aliases::Aliases; pub use aliases::Aliases;
pub use env::{DerivedEnv, Env}; pub use env::{DerivedEnv, SolveEnv};
pub use kinds::FunctionKind;
pub use pools::Pools; pub use pools::Pools;

View file

@ -1,3 +1,4 @@
use crate::FunctionKind;
use crate::{aliases::Aliases, solve}; use crate::{aliases::Aliases, solve};
use roc_can::abilities::{AbilitiesStore, ResolvedImpl}; use roc_can::abilities::{AbilitiesStore, ResolvedImpl};
use roc_can::constraint::{Constraint, Constraints}; 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 /// All types introduced in the module. Canonicalized, but not necessarily yet associated with
/// a variable substitution. /// a variable substitution.
pub types: Types, 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. /// 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. /// Due for checking and instantiation after the solver runs over the module.
pub pending_derives: PendingDerives, pub pending_derives: PendingDerives,

View file

@ -3,7 +3,7 @@ use crate::ability::{
CheckedDerives, ObligationCache, PendingDerivesTable, Resolved, CheckedDerives, ObligationCache, PendingDerivesTable, Resolved,
}; };
use crate::deep_copy::deep_copy_var_in; 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::module::{SolveConfig, Solved};
use crate::pools::Pools; use crate::pools::Pools;
use crate::specialize::{ use crate::specialize::{
@ -122,6 +122,7 @@ fn run_in_place(
pending_derives, pending_derives,
exposed_by_module, exposed_by_module,
derived_module, derived_module,
function_kind,
} = config; } = config;
let mut pools = Pools::default(); let mut pools = Pools::default();
@ -141,9 +142,10 @@ fn run_in_place(
exposed_types: exposed_by_module, exposed_types: exposed_by_module,
}; };
let mut env = Env { let mut env = InferenceEnv {
arena: &arena, arena: &arena,
constraints, constraints,
function_kind,
derived_env: &derived_env, derived_env: &derived_env,
subs, subs,
pools: &mut pools, pools: &mut pools,
@ -218,7 +220,7 @@ enum Work<'a> {
} }
fn solve( fn solve(
env: &mut Env, env: &mut InferenceEnv,
mut can_types: Types, mut can_types: Types,
mut state: State, mut state: State,
rank: Rank, rank: Rank,
@ -576,7 +578,11 @@ fn solve(
// then we copy from that module's Subs into our own. If the value // 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 // is being looked up in this module, then we use our Subs as both
// the source and destination. // 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 expectation = &env.constraints.expectations[expectation_index.index()];
let expected = either_type_index_to_var( 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( fn compact_lambdas_and_check_obligations(
env: &mut Env, env: &mut InferenceEnv,
problems: &mut Vec<TypeError>, problems: &mut Vec<TypeError>,
abilities_store: &mut AbilitiesStore, abilities_store: &mut AbilitiesStore,
obligation_cache: &mut ObligationCache, obligation_cache: &mut ObligationCache,
@ -1401,7 +1407,7 @@ fn compact_lambdas_and_check_obligations(
obligations, obligations,
awaiting_specialization: new_awaiting, awaiting_specialization: new_awaiting,
} = compact_lambda_sets_of_vars( } = compact_lambda_sets_of_vars(
env, &mut env.as_solve_env(),
lambda_sets_to_specialize, lambda_sets_to_specialize,
&SolvePhase { abilities_store }, &SolvePhase { abilities_store },
); );
@ -1414,7 +1420,7 @@ fn compact_lambdas_and_check_obligations(
awaiting_specialization.union(new_awaiting); 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]; let mut stack = vec![var];
while let Some(var) = stack.pop() { while let Some(var) = stack.pop() {
use {Content::*, FlatType::*}; 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. // Aggressive but necessary - there aren't many usages.
#[inline(always)] #[inline(always)]
fn check_ability_specialization( fn check_ability_specialization(
env: &mut Env, env: &mut InferenceEnv,
rank: Rank, rank: Rank,
abilities_store: &mut AbilitiesStore, abilities_store: &mut AbilitiesStore,
obligation_cache: &mut ObligationCache, 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 // We need to freshly instantiate the root signature so that all unifications are reflected
// in the specialization type, but not the original signature type. // in the specialization type, but not the original signature type.
let root_signature_var = let root_signature_var = {
deep_copy_var_in(env, Rank::toplevel(), root_signature_var, env.arena); 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 snapshot = env.subs.snapshot();
let unified = unify_introduced_ability_specialization( let unified = unify_introduced_ability_specialization(
&mut env.uenv(), &mut env.uenv(),
@ -1777,7 +1791,7 @@ impl<T> LocalDefVarsVec<T> {
impl LocalDefVarsVec<(Symbol, Loc<Variable>)> { impl LocalDefVarsVec<(Symbol, Loc<Variable>)> {
fn from_def_types( fn from_def_types(
env: &mut Env, env: &mut InferenceEnv,
rank: Rank, rank: Rank,
problems: &mut Vec<TypeError>, problems: &mut Vec<TypeError>,
abilities_store: &mut AbilitiesStore, abilities_store: &mut AbilitiesStore,
@ -1811,7 +1825,7 @@ impl LocalDefVarsVec<(Symbol, Loc<Variable>)> {
} }
fn check_for_infinite_type( fn check_for_infinite_type(
env: &mut Env, env: &mut InferenceEnv,
problems: &mut Vec<TypeError>, problems: &mut Vec<TypeError>,
symbol: Symbol, symbol: Symbol,
loc_var: Loc<Variable>, loc_var: Loc<Variable>,
@ -1874,7 +1888,7 @@ fn circular_error(
/// Ensures that variables introduced at the `young_rank`, but that should be /// 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 /// stuck at a lower level, are marked at that level and not generalized at the
/// present `young_rank`. See [adjust_rank]. /// 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 subs = &mut env.subs;
let pools = &mut env.pools; let pools = &mut env.pools;
@ -2302,6 +2316,8 @@ fn adjust_rank_content(
rank rank
} }
ErasedLambda => group_rank,
RangedNumber(_) => group_rank, RangedNumber(_) => group_rank,
} }
} }

View file

@ -22,7 +22,7 @@ use roc_unify::unify::{unify, Mode, MustImplementConstraints};
use crate::{ use crate::{
ability::builtin_module_with_unlisted_ability_impl, ability::builtin_module_with_unlisted_ability_impl,
deep_copy::deep_copy_var_in, deep_copy::deep_copy_var_in,
env::{DerivedEnv, Env}, env::{DerivedEnv, SolveEnv},
}; };
/// What phase in the compiler is reaching out to specialize lambda sets? /// 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] #[must_use]
pub fn compact_lambda_sets_of_vars<P: Phase>( pub fn compact_lambda_sets_of_vars<P: Phase>(
env: &mut Env, env: &mut SolveEnv,
uls_of_var: UlsOfVar, uls_of_var: UlsOfVar,
phase: &P, phase: &P,
) -> CompactionResult { ) -> CompactionResult {
@ -464,7 +464,7 @@ enum OneCompactionResult {
#[must_use] #[must_use]
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn compact_lambda_set<P: Phase>( fn compact_lambda_set<P: Phase>(
env: &mut Env, env: &mut SolveEnv,
resolved_concrete: Variable, resolved_concrete: Variable,
this_lambda_set: Variable, this_lambda_set: Variable,
phase: &P, phase: &P,
@ -690,6 +690,7 @@ fn make_specialization_decision<P: Phase>(
| FlexVar(..) | FlexVar(..)
| RigidVar(..) | RigidVar(..)
| LambdaSet(..) | LambdaSet(..)
| ErasedLambda
| RangedNumber(..) => { | RangedNumber(..) => {
internal_error!("unexpected") internal_error!("unexpected")
} }

View file

@ -22,8 +22,8 @@ use roc_unify::unify::{unify, Mode, Unified};
use crate::{ use crate::{
ability::{AbilityImplError, ObligationCache}, ability::{AbilityImplError, ObligationCache},
deep_copy::deep_copy_var_in, deep_copy::deep_copy_var_in,
env::Env, env::InferenceEnv,
Aliases, Aliases, FunctionKind,
}; };
std::thread_local! { std::thread_local! {
@ -42,7 +42,7 @@ fn put_scratchpad(scratchpad: bumpalo::Bump) {
} }
pub(crate) fn either_type_index_to_var( pub(crate) fn either_type_index_to_var(
env: &mut Env, env: &mut InferenceEnv,
rank: Rank, rank: Rank,
problems: &mut Vec<TypeError>, problems: &mut Vec<TypeError>,
abilities_store: &mut AbilitiesStore, abilities_store: &mut AbilitiesStore,
@ -70,7 +70,8 @@ pub(crate) fn either_type_index_to_var(
|| matches!( || matches!(
types[type_index], types[type_index],
TypeTag::EmptyRecord | TypeTag::EmptyTagUnion TypeTag::EmptyRecord | TypeTag::EmptyTagUnion
) ),
"different variable was returned for type index variable cell!"
); );
var var
} }
@ -82,7 +83,7 @@ pub(crate) fn either_type_index_to_var(
} }
pub(crate) fn type_to_var( pub(crate) fn type_to_var(
env: &mut Env, env: &mut InferenceEnv,
rank: Rank, rank: Rank,
problems: &mut Vec<TypeError>, problems: &mut Vec<TypeError>,
abilities_store: &mut AbilitiesStore, abilities_store: &mut AbilitiesStore,
@ -126,7 +127,7 @@ enum RegisterVariable {
impl RegisterVariable { impl RegisterVariable {
fn from_type( fn from_type(
env: &mut Env, env: &mut InferenceEnv,
rank: Rank, rank: Rank,
arena: &'_ bumpalo::Bump, arena: &'_ bumpalo::Bump,
types: &mut Types, types: &mut Types,
@ -149,7 +150,7 @@ impl RegisterVariable {
reserved reserved
} else { } else {
// for any other rank, we need to copy; it takes care of adjusting the rank // 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 // 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!) // was not already transformed before (if it was, we'd be in the Variable branch!)
@ -165,7 +166,7 @@ impl RegisterVariable {
#[inline(always)] #[inline(always)]
fn with_stack( fn with_stack(
env: &mut Env, env: &mut InferenceEnv,
rank: Rank, rank: Rank,
arena: &'_ bumpalo::Bump, arena: &'_ bumpalo::Bump,
types: &mut Types, types: &mut Types,
@ -253,7 +254,7 @@ enum TypeToVar {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub(crate) fn type_to_var_help( pub(crate) fn type_to_var_help(
env: &mut Env, env: &mut InferenceEnv,
rank: Rank, rank: Rank,
problems: &mut Vec<TypeError>, problems: &mut Vec<TypeError>,
abilities_store: &AbilitiesStore, abilities_store: &AbilitiesStore,
@ -346,20 +347,29 @@ pub(crate) fn type_to_var_help(
name, name,
ambient_function, ambient_function,
} => { } => {
let captures = types.get_type_arguments(typ_index); match env.function_kind {
let union_lambdas = FunctionKind::LambdaSet => {
create_union_lambda(env, rank, arena, types, name, captures, &mut stack); 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 { let content = Content::LambdaSet(subs::LambdaSet {
solved: union_lambdas, solved: union_lambdas,
// We may figure out the lambda set is recursive during solving, but it never // We may figure out the lambda set is recursive during solving, but it never
// is to begin with. // is to begin with.
recursion_var: OptVariable::NONE, recursion_var: OptVariable::NONE,
unspecialized: SubsSlice::default(), unspecialized: SubsSlice::default(),
ambient_function, 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 } => { UnspecializedLambdaSet { unspecialized } => {
let unspecialized_slice = SubsSlice::extend_new( let unspecialized_slice = SubsSlice::extend_new(
@ -911,7 +921,7 @@ pub(crate) fn type_to_var_help(
#[inline(always)] #[inline(always)]
fn roc_result_to_var( fn roc_result_to_var(
env: &mut Env, env: &mut InferenceEnv,
rank: Rank, rank: Rank,
arena: &'_ bumpalo::Bump, arena: &'_ bumpalo::Bump,
types: &mut Types, types: &mut Types,
@ -1090,7 +1100,7 @@ fn find_tag_name_run(slice: &[TagName], subs: &mut Subs) -> Option<SubsSlice<Tag
#[inline(always)] #[inline(always)]
fn register_tag_arguments( fn register_tag_arguments(
env: &mut Env, env: &mut InferenceEnv,
rank: Rank, rank: Rank,
arena: &'_ bumpalo::Bump, arena: &'_ bumpalo::Bump,
types: &mut Types, types: &mut Types,
@ -1114,7 +1124,7 @@ fn register_tag_arguments(
/// Assumes that the tags are sorted and there are no duplicates! /// Assumes that the tags are sorted and there are no duplicates!
fn insert_tags_fast_path( fn insert_tags_fast_path(
env: &mut Env, env: &mut InferenceEnv,
rank: Rank, rank: Rank,
arena: &'_ bumpalo::Bump, arena: &'_ bumpalo::Bump,
types: &mut Types, types: &mut Types,
@ -1185,7 +1195,7 @@ fn insert_tags_fast_path(
} }
fn insert_tags_slow_path( fn insert_tags_slow_path(
env: &mut Env, env: &mut InferenceEnv,
rank: Rank, rank: Rank,
arena: &'_ bumpalo::Bump, arena: &'_ bumpalo::Bump,
types: &mut Types, types: &mut Types,
@ -1215,7 +1225,7 @@ fn insert_tags_slow_path(
} }
fn type_to_union_tags( fn type_to_union_tags(
env: &mut Env, env: &mut InferenceEnv,
rank: Rank, rank: Rank,
arena: &'_ bumpalo::Bump, arena: &'_ bumpalo::Bump,
types: &mut Types, types: &mut Types,
@ -1274,7 +1284,7 @@ fn type_to_union_tags(
} }
fn create_union_lambda( fn create_union_lambda(
env: &mut Env, env: &mut InferenceEnv,
rank: Rank, rank: Rank,
arena: &'_ bumpalo::Bump, arena: &'_ bumpalo::Bump,
types: &mut Types, types: &mut Types,

View file

@ -8,6 +8,7 @@ extern crate bumpalo;
#[cfg(test)] #[cfg(test)]
mod solve_expr { mod solve_expr {
use roc_load::LoadedModule; use roc_load::LoadedModule;
use roc_solve::FunctionKind;
use test_solve_helpers::{format_problems, run_load_and_infer}; use test_solve_helpers::{format_problems, run_load_and_infer};
use roc_types::pretty_print::{name_and_print_var, DebugPrint}; use roc_types::pretty_print::{name_and_print_var, DebugPrint};
@ -27,7 +28,7 @@ mod solve_expr {
.. ..
}, },
src, 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 mut can_problems = can_problems.remove(&home).unwrap_or_default();
let type_problems = type_problems.remove(&home).unwrap_or_default(); let type_problems = type_problems.remove(&home).unwrap_or_default();

View file

@ -3,7 +3,10 @@ use std::path::PathBuf;
use bumpalo::Bump; use bumpalo::Bump;
use roc_packaging::cache::RocCacheDir; use roc_packaging::cache::RocCacheDir;
use roc_solve::module::{SolveConfig, SolveOutput}; use roc_solve::{
module::{SolveConfig, SolveOutput},
FunctionKind,
};
use ven_pretty::DocAllocator; use ven_pretty::DocAllocator;
use roc_can::{ use roc_can::{
@ -426,6 +429,7 @@ fn check_derived_typechecks_and_golden(
constraints: &constraints, constraints: &constraints,
root_constraint: constr, root_constraint: constr,
types, types,
function_kind: FunctionKind::LambdaSet,
pending_derives: Default::default(), pending_derives: Default::default(),
exposed_by_module: &exposed_for_module.exposed_by_module, exposed_by_module: &exposed_for_module.exposed_by_module,
derived_module: Default::default(), derived_module: Default::default(),
@ -520,6 +524,7 @@ where
path.parent().unwrap().to_path_buf(), path.parent().unwrap().to_path_buf(),
Default::default(), Default::default(),
target_info, target_info,
FunctionKind::LambdaSet,
roc_reporting::report::RenderTarget::ColorTerminal, roc_reporting::report::RenderTarget::ColorTerminal,
roc_reporting::report::DEFAULT_PALETTE, roc_reporting::report::DEFAULT_PALETTE,
RocCacheDir::Disallowed, RocCacheDir::Disallowed,

View 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)
);
}

View file

@ -5,6 +5,7 @@ use roc_load::{EntryPoint, ExecutionMode, LoadConfig, Threading};
use roc_mono::ir::SingleEntryPoint; use roc_mono::ir::SingleEntryPoint;
use roc_packaging::cache::RocCacheDir; use roc_packaging::cache::RocCacheDir;
use roc_region::all::LineInfo; use roc_region::all::LineInfo;
use roc_solve::FunctionKind;
use tempfile::tempdir; use tempfile::tempdir;
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
@ -58,6 +59,7 @@ pub fn helper(
palette: roc_reporting::report::DEFAULT_PALETTE, palette: roc_reporting::report::DEFAULT_PALETTE,
threading: Threading::Single, threading: Threading::Single,
exec_mode: ExecutionMode::Executable, exec_mode: ExecutionMode::Executable,
function_kind: FunctionKind::LambdaSet,
}; };
let loaded = roc_load::load_and_monomorphize_from_str( let loaded = roc_load::load_and_monomorphize_from_str(
arena, arena,

View file

@ -8,7 +8,9 @@ use roc_collections::all::MutSet;
use roc_command_utils::zig; use roc_command_utils::zig;
use roc_gen_llvm::llvm::externs::add_default_roc_externs; use roc_gen_llvm::llvm::externs::add_default_roc_externs;
use roc_gen_llvm::{llvm::build::LlvmBackendMode, run_roc::RocCallResult}; 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_mono::ir::{CrashTag, OptLevel, SingleEntryPoint};
use roc_packaging::cache::RocCacheDir; use roc_packaging::cache::RocCacheDir;
use roc_region::all::LineInfo; use roc_region::all::LineInfo;
@ -44,13 +46,13 @@ fn promote_expr_to_module(src: &str) -> String {
buffer buffer
} }
#[allow(clippy::too_many_arguments)]
fn create_llvm_module<'a>( fn create_llvm_module<'a>(
arena: &'a bumpalo::Bump, arena: &'a bumpalo::Bump,
src: &str, src: &str,
config: HelperConfig, config: HelperConfig,
context: &'a inkwell::context::Context, context: &'a inkwell::context::Context,
target: &Triple, target: &Triple,
function_kind: FunctionKind,
) -> (&'static str, String, &'a Module<'a>) { ) -> (&'static str, String, &'a Module<'a>) {
let target_info = roc_target::TargetInfo::from(target); let target_info = roc_target::TargetInfo::from(target);
@ -70,6 +72,7 @@ fn create_llvm_module<'a>(
let load_config = LoadConfig { let load_config = LoadConfig {
target_info, target_info,
function_kind,
render: RenderTarget::ColorTerminal, render: RenderTarget::ColorTerminal,
palette: DEFAULT_PALETTE, palette: DEFAULT_PALETTE,
threading: Threading::Single, threading: Threading::Single,
@ -330,11 +333,12 @@ pub fn helper<'a>(
config: HelperConfig, config: HelperConfig,
src: &str, src: &str,
context: &'a inkwell::context::Context, context: &'a inkwell::context::Context,
function_kind: FunctionKind,
) -> (&'static str, String, Library) { ) -> (&'static str, String, Library) {
let target = target_lexicon::Triple::host(); let target = target_lexicon::Triple::host();
let (main_fn_name, delayed_errors, module) = 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 res_lib = if config.add_debug_info {
let module = annotate_with_debug_info(module, context); let module = annotate_with_debug_info(module, context);
@ -420,11 +424,12 @@ fn compile_to_wasm_bytes<'a>(
config: HelperConfig, config: HelperConfig,
src: &str, src: &str,
context: &'a inkwell::context::Context, context: &'a inkwell::context::Context,
function_kind: FunctionKind,
) -> Vec<u8> { ) -> Vec<u8> {
let target = wasm32_target_tripple(); let target = wasm32_target_tripple();
let (_main_fn_name, _delayed_errors, llvm_module) = 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 content_hash = crate::helpers::src_hash(src);
let wasm_file = llvm_module_to_wasm_file(&TEMP_DIR, content_hash, llvm_module); 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")] #[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 where
T: FromWasm32Memory + Wasm32Result, T: FromWasm32Memory + Wasm32Result,
{ {
@ -526,15 +535,19 @@ where
opt_level: OPT_LEVEL, 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) 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 { macro_rules! assert_wasm_evals_to {
($src:expr, $expected:expr, $ty:ty, $transform:expr, $ignore_problems:expr) => { ($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), Err(msg) => panic!("Wasm test failed: {}", msg),
Ok(actual) => { Ok(actual) => {
assert_eq!($transform(actual), $expected, "Wasm test failed") assert_eq!($transform(actual), $expected, "Wasm test failed")
@ -577,9 +590,13 @@ pub fn try_run_lib_function<T>(
} }
// only used in tests // only used in tests
#[allow(unused)] pub(crate) fn llvm_evals_to<T, U, F>(
pub(crate) fn llvm_evals_to<T, U, F>(src: &str, expected: U, transform: F, ignore_problems: bool) src: &str,
where expected: U,
transform: F,
ignore_problems: bool,
function_kind: FunctionKind,
) where
U: PartialEq + std::fmt::Debug, U: PartialEq + std::fmt::Debug,
F: FnOnce(T) -> U, F: FnOnce(T) -> U,
{ {
@ -596,7 +613,8 @@ where
opt_level: crate::helpers::llvm::OPT_LEVEL, 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); 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 { macro_rules! assert_llvm_evals_to {
($src:expr, $expected:expr, $ty:ty, $transform:expr, $ignore_problems:expr) => { ($src:expr, $expected:expr, $ty:ty, $transform:expr, $ignore_problems:expr) => {
crate::helpers::llvm::llvm_evals_to::<$ty, _, _>( crate::helpers::llvm::llvm_evals_to::<$ty, _, _>(
@ -628,6 +645,7 @@ macro_rules! assert_llvm_evals_to {
$expected, $expected,
$transform, $transform,
$ignore_problems, $ignore_problems,
roc_load::FunctionKind::LambdaSet,
); );
}; };
@ -653,8 +671,6 @@ macro_rules! assert_llvm_evals_to {
// //
// let (_main_fn_name, _delayed_errors, _module) = // let (_main_fn_name, _delayed_errors, _module) =
// $crate::helpers::llvm::create_llvm_module(&arena, $src, config, &context, &target); // $crate::helpers::llvm::create_llvm_module(&arena, $src, config, &context, &target);
#[allow(unused_macros)]
macro_rules! assert_evals_to { macro_rules! assert_evals_to {
($src:expr, $expected:expr, $ty:ty) => {{ ($src:expr, $expected:expr, $ty:ty) => {{
assert_evals_to!($src, $expected, $ty, $crate::helpers::llvm::identity, false); 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 { pub fn identity<T>(value: T) -> T {
value value
} }
#[allow(unused_imports)]
pub(crate) use assert_evals_to; pub(crate) use assert_evals_to;
#[allow(unused_imports)] pub(crate) use assert_evals_to_erased;
pub(crate) use assert_llvm_evals_to; pub(crate) use assert_llvm_evals_to;
#[allow(unused_imports)] #[cfg(feature = "gen-llvm-wasm")]
pub(crate) use assert_wasm_evals_to; pub(crate) use assert_wasm_evals_to;

View file

@ -7,6 +7,7 @@ use roc_gen_wasm::DEBUG_SETTINGS;
use roc_load::{ExecutionMode, LoadConfig, Threading}; use roc_load::{ExecutionMode, LoadConfig, Threading};
use roc_packaging::cache::RocCacheDir; use roc_packaging::cache::RocCacheDir;
use roc_reporting::report::DEFAULT_PALETTE_HTML; use roc_reporting::report::DEFAULT_PALETTE_HTML;
use roc_solve::FunctionKind;
use roc_std::RocStr; use roc_std::RocStr;
use roc_wasm_interp::{wasi, ImportDispatcher, Instance, WasiDispatcher}; use roc_wasm_interp::{wasi, ImportDispatcher, Instance, WasiDispatcher};
use roc_wasm_module::{Export, ExportType, Value, WasmModule}; 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, palette: DEFAULT_PALETTE_HTML,
threading: Threading::Single, threading: Threading::Single,
exec_mode: ExecutionMode::Executable, exec_mode: ExecutionMode::Executable,
function_kind: FunctionKind::LambdaSet,
}; };
let loaded = roc_load::load_and_monomorphize_from_str( let loaded = roc_load::load_and_monomorphize_from_str(
arena, arena,

View file

@ -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;

View file

@ -8,6 +8,7 @@ pub mod gen_abilities;
pub mod gen_compare; pub mod gen_compare;
pub mod gen_definitions; pub mod gen_definitions;
pub mod gen_dict; pub mod gen_dict;
pub mod gen_erased;
pub mod gen_list; pub mod gen_list;
pub mod gen_num; pub mod gen_num;
pub mod gen_panic; pub mod gen_panic;

View file

@ -117,6 +117,7 @@ fn build_app_mono<'a>(
ret_layout: int_layout, ret_layout: int_layout,
is_self_recursive: SelfRecursive::NotSelfRecursive, is_self_recursive: SelfRecursive::NotSelfRecursive,
host_exposed_layouts: HostExposedLayouts::NotHostExposed, host_exposed_layouts: HostExposedLayouts::NotHostExposed,
is_erased: false,
}; };
let proc_layout = ProcLayout { let proc_layout = ProcLayout {

View file

@ -14,7 +14,7 @@ procedure List.66 (#Attr.2, #Attr.3):
let List.537 : [] = lowlevel ListGetUnsafe #Attr.2 #Attr.3; let List.537 : [] = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.537; 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: joinpoint List.527 List.439 List.440 List.441 List.442 List.443:
let List.529 : Int1 = CallByName Num.22 List.442 List.443; let List.529 : Int1 = CallByName Num.22 List.442 List.443;
if List.529 then 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; dec List.439;
ret List.440; ret List.440;
in 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): procedure List.93 (List.436, List.437, List.438):
let List.525 : U64 = 0i64; let List.525 : U64 = 0i64;
@ -45,8 +45,8 @@ procedure Num.22 (#Attr.2, #Attr.3):
procedure Test.10 (Test.66, #Attr.12): procedure Test.10 (Test.66, #Attr.12):
let Test.9 : {} = UnionAtIndex (Id 0) (Index 0) #Attr.12; let Test.9 : {} = UnionAtIndex (Id 0) (Index 0) #Attr.12;
let #Derived_gen.20 : Int1 = lowlevel RefCountIsUnique #Attr.12; let #Derived_gen.18 : Int1 = lowlevel RefCountIsUnique #Attr.12;
if #Derived_gen.20 then if #Derived_gen.18 then
free #Attr.12; free #Attr.12;
ret Test.9; ret Test.9;
else else
@ -60,7 +60,7 @@ procedure Test.10 (Test.66, #Attr.12):
procedure Test.14 (Test.45, #Attr.12): procedure Test.14 (Test.45, #Attr.12):
let Test.13 : {{}, []} = UnionAtIndex (Id 1) (Index 1) #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; 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.50 : {} = Struct {};
let Test.51 : U8 = GetTagId Test.12; let Test.51 : U8 = GetTagId Test.12;
joinpoint Test.52 Test.15: joinpoint Test.52 Test.15:
@ -87,14 +87,14 @@ procedure Test.14 (Test.45, #Attr.12):
jump Test.52 Test.53; jump Test.52 Test.53;
in in
let #Derived_gen.19 : Int1 = lowlevel RefCountIsUnique #Attr.12; let #Derived_gen.20 : Int1 = lowlevel RefCountIsUnique #Attr.12;
if #Derived_gen.19 then if #Derived_gen.20 then
free #Attr.12; free #Attr.12;
jump #Derived_gen.18; jump #Derived_gen.19;
else else
inc Test.12; inc Test.12;
decref #Attr.12; decref #Attr.12;
jump #Derived_gen.18; jump #Derived_gen.19;
procedure Test.20 (Test.21, Test.18): procedure Test.20 (Test.21, Test.18):
let Test.23 : [C {}, C []] = CallByName Test.32 Test.21 Test.18; let Test.23 : [C {}, C []] = CallByName Test.32 Test.21 Test.18;

View file

@ -18,7 +18,7 @@ procedure List.66 (#Attr.2, #Attr.3):
let List.537 : Int1 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; let List.537 : Int1 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.537; 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: joinpoint List.527 List.439 List.440 List.441 List.442 List.443:
let List.529 : Int1 = CallByName Num.22 List.442 List.443; let List.529 : Int1 = CallByName Num.22 List.442 List.443;
if List.529 then 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; dec List.439;
ret List.440; ret List.440;
in 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): procedure List.93 (List.436, List.437, List.438):
let List.525 : U64 = 0i64; let List.525 : U64 = 0i64;
@ -54,7 +54,7 @@ procedure Str.3 (#Attr.2, #Attr.3):
procedure Test.1 (Test.5): procedure Test.1 (Test.5):
ret 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: joinpoint Test.27 Test.12 #Attr.12:
let Test.8 : Int1 = UnionAtIndex (Id 2) (Index 1) #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; 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; decref #Attr.12;
jump #Derived_gen.14; jump #Derived_gen.14;
in in
jump Test.27 #Derived_gen.7 #Derived_gen.8; jump Test.27 #Derived_gen.10 #Derived_gen.11;
procedure Test.2 (Test.13): procedure Test.2 (Test.13):
ret Test.13; ret Test.13;

View file

@ -51,20 +51,6 @@ procedure List.71 (#Attr.2, #Attr.3):
ret List.541; ret List.541;
procedure List.83 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2): 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: joinpoint List.523 List.123 List.124 List.125:
let List.531 : U64 = 0i64; let List.531 : U64 = 0i64;
let List.525 : Int1 = CallByName Num.24 List.124 List.531; 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 else
ret List.125; ret List.125;
in 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): procedure Num.20 (#Attr.2, #Attr.3):
let Num.293 : U64 = lowlevel NumSub #Attr.2 #Attr.3; let Num.293 : U64 = lowlevel NumSub #Attr.2 #Attr.3;

View file

@ -235,7 +235,7 @@ procedure List.8 (#Attr.2, #Attr.3):
let List.668 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; let List.668 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
ret List.668; 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: joinpoint List.715 List.439 List.440 List.441 List.442 List.443:
let List.717 : Int1 = CallByName Num.22 List.442 List.443; let List.717 : Int1 = CallByName Num.22 List.442 List.443;
if List.717 then 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; let List.716 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) List.440;
ret List.716; ret List.716;
in 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: joinpoint List.644 List.439 List.440 List.441 List.442 List.443:
let List.646 : Int1 = CallByName Num.22 List.442 List.443; let List.646 : Int1 = CallByName Num.22 List.442 List.443;
if List.646 then 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; dec List.439;
ret List.440; ret List.440;
in 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: joinpoint List.624 List.439 List.440 List.441 List.442 List.443:
let List.626 : Int1 = CallByName Num.22 List.442 List.443; let List.626 : Int1 = CallByName Num.22 List.442 List.443;
if List.626 then 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; dec List.439;
ret List.440; ret List.440;
in 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: joinpoint List.556 List.439 List.440 List.441 List.442 List.443:
let List.558 : Int1 = CallByName Num.22 List.442 List.443; let List.558 : Int1 = CallByName Num.22 List.442 List.443;
if List.558 then 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; dec List.439;
ret List.440; ret List.440;
in 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): procedure List.93 (List.436, List.437, List.438):
let List.554 : U64 = 0i64; let List.554 : U64 = 0i64;
@ -388,8 +388,8 @@ procedure Str.9 (Str.79):
else else
let Str.300 : U8 = StructAtIndex 3 Str.80; let Str.300 : U8 = StructAtIndex 3 Str.80;
let Str.301 : U64 = StructAtIndex 0 Str.80; let Str.301 : U64 = StructAtIndex 0 Str.80;
let #Derived_gen.58 : Str = StructAtIndex 1 Str.80; let #Derived_gen.57 : Str = StructAtIndex 1 Str.80;
dec #Derived_gen.58; dec #Derived_gen.57;
let Str.299 : {U64, U8} = Struct {Str.301, Str.300}; let Str.299 : {U64, U8} = Struct {Str.301, Str.300};
let Str.298 : [C {U64, U8}, C Str] = TagId(0) Str.299; let Str.298 : [C {U64, U8}, C Str] = TagId(0) Str.299;
ret Str.298; ret Str.298;
@ -1331,14 +1331,14 @@ procedure TotallyNotJson.82 (TotallyNotJson.802, TotallyNotJson.803):
procedure TotallyNotJson.832 (TotallyNotJson.1493): procedure TotallyNotJson.832 (TotallyNotJson.1493):
let TotallyNotJson.1845 : List Str = StructAtIndex 1 TotallyNotJson.1493; let TotallyNotJson.1845 : List Str = StructAtIndex 1 TotallyNotJson.1493;
let #Derived_gen.59 : List Str = StructAtIndex 0 TotallyNotJson.1493; let #Derived_gen.58 : List Str = StructAtIndex 0 TotallyNotJson.1493;
dec #Derived_gen.59; dec #Derived_gen.58;
ret TotallyNotJson.1845; ret TotallyNotJson.1845;
procedure TotallyNotJson.840 (TotallyNotJson.1214): procedure TotallyNotJson.840 (TotallyNotJson.1214):
let TotallyNotJson.1566 : List Str = StructAtIndex 1 TotallyNotJson.1214; let TotallyNotJson.1566 : List Str = StructAtIndex 1 TotallyNotJson.1214;
let #Derived_gen.57 : List Str = StructAtIndex 0 TotallyNotJson.1214; let #Derived_gen.59 : List Str = StructAtIndex 0 TotallyNotJson.1214;
dec #Derived_gen.57; dec #Derived_gen.59;
ret TotallyNotJson.1566; ret TotallyNotJson.1566;
procedure TotallyNotJson.87 (TotallyNotJson.809): procedure TotallyNotJson.87 (TotallyNotJson.809):
@ -1393,7 +1393,7 @@ procedure TotallyNotJson.95 (TotallyNotJson.829):
dec TotallyNotJson.1841; dec TotallyNotJson.1841;
ret TotallyNotJson.1839; ret TotallyNotJson.1839;
procedure TotallyNotJson.96 (#Derived_gen.41): procedure TotallyNotJson.96 (#Derived_gen.29):
joinpoint TotallyNotJson.1847 TotallyNotJson.1168: joinpoint TotallyNotJson.1847 TotallyNotJson.1168:
let TotallyNotJson.834 : List Str = StructAtIndex 0 TotallyNotJson.1168; let TotallyNotJson.834 : List Str = StructAtIndex 0 TotallyNotJson.1168;
let TotallyNotJson.833 : List Str = StructAtIndex 1 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}; let TotallyNotJson.1848 : {List Str, List Str} = Struct {TotallyNotJson.834, TotallyNotJson.833};
ret TotallyNotJson.1848; ret TotallyNotJson.1848;
in in
jump TotallyNotJson.1847 #Derived_gen.41; jump TotallyNotJson.1847 #Derived_gen.29;
procedure TotallyNotJson.97 (TotallyNotJson.837): procedure TotallyNotJson.97 (TotallyNotJson.837):
let TotallyNotJson.838 : List Str = CallByName Str.55 TotallyNotJson.837; let TotallyNotJson.838 : List Str = CallByName Str.55 TotallyNotJson.837;
@ -1446,7 +1446,7 @@ procedure TotallyNotJson.97 (TotallyNotJson.837):
dec TotallyNotJson.1562; dec TotallyNotJson.1562;
ret TotallyNotJson.1560; ret TotallyNotJson.1560;
procedure TotallyNotJson.98 (#Derived_gen.47): procedure TotallyNotJson.98 (#Derived_gen.35):
joinpoint TotallyNotJson.1568 TotallyNotJson.1169: joinpoint TotallyNotJson.1568 TotallyNotJson.1169:
let TotallyNotJson.842 : List Str = StructAtIndex 0 TotallyNotJson.1169; let TotallyNotJson.842 : List Str = StructAtIndex 0 TotallyNotJson.1169;
let TotallyNotJson.841 : List Str = StructAtIndex 1 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}; let TotallyNotJson.1569 : {List Str, List Str} = Struct {TotallyNotJson.842, TotallyNotJson.841};
ret TotallyNotJson.1569; ret TotallyNotJson.1569;
in in
jump TotallyNotJson.1568 #Derived_gen.47; jump TotallyNotJson.1568 #Derived_gen.35;
procedure Test.0 (): procedure Test.0 ():
let Test.12 : Str = "bar"; let Test.12 : Str = "bar";

View file

@ -192,23 +192,7 @@ procedure List.8 (#Attr.2, #Attr.3):
let List.600 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; let List.600 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
ret List.600; ret List.600;
procedure List.80 (#Derived_gen.11, #Derived_gen.12, #Derived_gen.13, #Derived_gen.14, #Derived_gen.15): procedure List.80 (#Derived_gen.10, #Derived_gen.11, #Derived_gen.12, #Derived_gen.13, #Derived_gen.14):
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):
joinpoint List.647 List.439 List.440 List.441 List.442 List.443: joinpoint List.647 List.439 List.440 List.441 List.442 List.443:
let List.649 : Int1 = CallByName Num.22 List.442 List.443; let List.649 : Int1 = CallByName Num.22 List.442 List.443;
if List.649 then 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; let List.648 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) List.440;
ret List.648; ret List.648;
in 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: joinpoint List.576 List.439 List.440 List.441 List.442 List.443:
let List.578 : Int1 = CallByName Num.22 List.442 List.443; let List.578 : Int1 = CallByName Num.22 List.442 List.443;
if List.578 then 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; dec List.439;
ret List.440; ret List.440;
in 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): procedure List.93 (List.436, List.437, List.438):
let List.554 : U64 = 0i64; let List.554 : U64 = 0i64;
@ -323,8 +323,8 @@ procedure Str.9 (Str.79):
else else
let Str.300 : U8 = StructAtIndex 3 Str.80; let Str.300 : U8 = StructAtIndex 3 Str.80;
let Str.301 : U64 = StructAtIndex 0 Str.80; let Str.301 : U64 = StructAtIndex 0 Str.80;
let #Derived_gen.38 : Str = StructAtIndex 1 Str.80; let #Derived_gen.36 : Str = StructAtIndex 1 Str.80;
dec #Derived_gen.38; dec #Derived_gen.36;
let Str.299 : {U64, U8} = Struct {Str.301, Str.300}; let Str.299 : {U64, U8} = Struct {Str.301, Str.300};
let Str.298 : [C {U64, U8}, C Str] = TagId(0) Str.299; let Str.298 : [C {U64, U8}, C Str] = TagId(0) Str.299;
ret Str.298; ret Str.298;
@ -1215,8 +1215,8 @@ procedure TotallyNotJson.82 (TotallyNotJson.802, TotallyNotJson.803):
procedure TotallyNotJson.832 (TotallyNotJson.1493): procedure TotallyNotJson.832 (TotallyNotJson.1493):
let TotallyNotJson.1494 : List Str = StructAtIndex 1 TotallyNotJson.1493; let TotallyNotJson.1494 : List Str = StructAtIndex 1 TotallyNotJson.1493;
let #Derived_gen.36 : List Str = StructAtIndex 0 TotallyNotJson.1493; let #Derived_gen.38 : List Str = StructAtIndex 0 TotallyNotJson.1493;
dec #Derived_gen.36; dec #Derived_gen.38;
ret TotallyNotJson.1494; ret TotallyNotJson.1494;
procedure TotallyNotJson.840 (TotallyNotJson.1214): procedure TotallyNotJson.840 (TotallyNotJson.1214):
@ -1277,7 +1277,7 @@ procedure TotallyNotJson.95 (TotallyNotJson.829):
dec TotallyNotJson.1489; dec TotallyNotJson.1489;
ret TotallyNotJson.1488; ret TotallyNotJson.1488;
procedure TotallyNotJson.96 (#Derived_gen.32): procedure TotallyNotJson.96 (#Derived_gen.26):
joinpoint TotallyNotJson.1496 TotallyNotJson.1168: joinpoint TotallyNotJson.1496 TotallyNotJson.1168:
let TotallyNotJson.834 : List Str = StructAtIndex 0 TotallyNotJson.1168; let TotallyNotJson.834 : List Str = StructAtIndex 0 TotallyNotJson.1168;
let TotallyNotJson.833 : List Str = StructAtIndex 1 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}; let TotallyNotJson.1497 : {List Str, List Str} = Struct {TotallyNotJson.834, TotallyNotJson.833};
ret TotallyNotJson.1497; ret TotallyNotJson.1497;
in in
jump TotallyNotJson.1496 #Derived_gen.32; jump TotallyNotJson.1496 #Derived_gen.26;
procedure TotallyNotJson.97 (TotallyNotJson.837): procedure TotallyNotJson.97 (TotallyNotJson.837):
let TotallyNotJson.838 : List Str = CallByName Str.55 TotallyNotJson.837; let TotallyNotJson.838 : List Str = CallByName Str.55 TotallyNotJson.837;
@ -1330,7 +1330,7 @@ procedure TotallyNotJson.97 (TotallyNotJson.837):
dec TotallyNotJson.1210; dec TotallyNotJson.1210;
ret TotallyNotJson.1209; ret TotallyNotJson.1209;
procedure TotallyNotJson.98 (#Derived_gen.10): procedure TotallyNotJson.98 (#Derived_gen.30):
joinpoint TotallyNotJson.1217 TotallyNotJson.1169: joinpoint TotallyNotJson.1217 TotallyNotJson.1169:
let TotallyNotJson.842 : List Str = StructAtIndex 0 TotallyNotJson.1169; let TotallyNotJson.842 : List Str = StructAtIndex 0 TotallyNotJson.1169;
let TotallyNotJson.841 : List Str = StructAtIndex 1 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}; let TotallyNotJson.1218 : {List Str, List Str} = Struct {TotallyNotJson.842, TotallyNotJson.841};
ret TotallyNotJson.1218; ret TotallyNotJson.1218;
in in
jump TotallyNotJson.1217 #Derived_gen.10; jump TotallyNotJson.1217 #Derived_gen.30;
procedure Test.0 (): procedure Test.0 ():
let Test.11 : Str = "foo"; let Test.11 : Str = "foo";

View file

@ -199,23 +199,7 @@ procedure List.8 (#Attr.2, #Attr.3):
let List.600 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; let List.600 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
ret List.600; ret List.600;
procedure List.80 (#Derived_gen.15, #Derived_gen.16, #Derived_gen.17, #Derived_gen.18, #Derived_gen.19): procedure List.80 (#Derived_gen.14, #Derived_gen.15, #Derived_gen.16, #Derived_gen.17, #Derived_gen.18):
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):
joinpoint List.647 List.439 List.440 List.441 List.442 List.443: joinpoint List.647 List.439 List.440 List.441 List.442 List.443:
let List.649 : Int1 = CallByName Num.22 List.442 List.443; let List.649 : Int1 = CallByName Num.22 List.442 List.443;
if List.649 then 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; let List.648 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) List.440;
ret List.648; ret List.648;
in 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: joinpoint List.576 List.439 List.440 List.441 List.442 List.443:
let List.578 : Int1 = CallByName Num.22 List.442 List.443; let List.578 : Int1 = CallByName Num.22 List.442 List.443;
if List.578 then 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; dec List.439;
ret List.440; ret List.440;
in 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): procedure List.93 (List.436, List.437, List.438):
let List.554 : U64 = 0i64; let List.554 : U64 = 0i64;
@ -330,8 +330,8 @@ procedure Str.9 (Str.79):
else else
let Str.300 : U8 = StructAtIndex 3 Str.80; let Str.300 : U8 = StructAtIndex 3 Str.80;
let Str.301 : U64 = StructAtIndex 0 Str.80; let Str.301 : U64 = StructAtIndex 0 Str.80;
let #Derived_gen.42 : Str = StructAtIndex 1 Str.80; let #Derived_gen.40 : Str = StructAtIndex 1 Str.80;
dec #Derived_gen.42; dec #Derived_gen.40;
let Str.299 : {U64, U8} = Struct {Str.301, Str.300}; let Str.299 : {U64, U8} = Struct {Str.301, Str.300};
let Str.298 : [C {U64, U8}, C Str] = TagId(0) Str.299; let Str.298 : [C {U64, U8}, C Str] = TagId(0) Str.299;
ret Str.298; ret Str.298;
@ -1222,8 +1222,8 @@ procedure TotallyNotJson.82 (TotallyNotJson.802, TotallyNotJson.803):
procedure TotallyNotJson.832 (TotallyNotJson.1493): procedure TotallyNotJson.832 (TotallyNotJson.1493):
let TotallyNotJson.1494 : List Str = StructAtIndex 1 TotallyNotJson.1493; let TotallyNotJson.1494 : List Str = StructAtIndex 1 TotallyNotJson.1493;
let #Derived_gen.40 : List Str = StructAtIndex 0 TotallyNotJson.1493; let #Derived_gen.42 : List Str = StructAtIndex 0 TotallyNotJson.1493;
dec #Derived_gen.40; dec #Derived_gen.42;
ret TotallyNotJson.1494; ret TotallyNotJson.1494;
procedure TotallyNotJson.840 (TotallyNotJson.1214): procedure TotallyNotJson.840 (TotallyNotJson.1214):
@ -1284,7 +1284,7 @@ procedure TotallyNotJson.95 (TotallyNotJson.829):
dec TotallyNotJson.1489; dec TotallyNotJson.1489;
ret TotallyNotJson.1488; ret TotallyNotJson.1488;
procedure TotallyNotJson.96 (#Derived_gen.36): procedure TotallyNotJson.96 (#Derived_gen.30):
joinpoint TotallyNotJson.1496 TotallyNotJson.1168: joinpoint TotallyNotJson.1496 TotallyNotJson.1168:
let TotallyNotJson.834 : List Str = StructAtIndex 0 TotallyNotJson.1168; let TotallyNotJson.834 : List Str = StructAtIndex 0 TotallyNotJson.1168;
let TotallyNotJson.833 : List Str = StructAtIndex 1 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}; let TotallyNotJson.1497 : {List Str, List Str} = Struct {TotallyNotJson.834, TotallyNotJson.833};
ret TotallyNotJson.1497; ret TotallyNotJson.1497;
in in
jump TotallyNotJson.1496 #Derived_gen.36; jump TotallyNotJson.1496 #Derived_gen.30;
procedure TotallyNotJson.97 (TotallyNotJson.837): procedure TotallyNotJson.97 (TotallyNotJson.837):
let TotallyNotJson.838 : List Str = CallByName Str.55 TotallyNotJson.837; let TotallyNotJson.838 : List Str = CallByName Str.55 TotallyNotJson.837;
@ -1337,7 +1337,7 @@ procedure TotallyNotJson.97 (TotallyNotJson.837):
dec TotallyNotJson.1210; dec TotallyNotJson.1210;
ret TotallyNotJson.1209; ret TotallyNotJson.1209;
procedure TotallyNotJson.98 (#Derived_gen.14): procedure TotallyNotJson.98 (#Derived_gen.34):
joinpoint TotallyNotJson.1217 TotallyNotJson.1169: joinpoint TotallyNotJson.1217 TotallyNotJson.1169:
let TotallyNotJson.842 : List Str = StructAtIndex 0 TotallyNotJson.1169; let TotallyNotJson.842 : List Str = StructAtIndex 0 TotallyNotJson.1169;
let TotallyNotJson.841 : List Str = StructAtIndex 1 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}; let TotallyNotJson.1218 : {List Str, List Str} = Struct {TotallyNotJson.842, TotallyNotJson.841};
ret TotallyNotJson.1218; ret TotallyNotJson.1218;
in in
jump TotallyNotJson.1217 #Derived_gen.14; jump TotallyNotJson.1217 #Derived_gen.34;
procedure Test.0 (): procedure Test.0 ():
let Test.11 : Str = "foo"; let Test.11 : Str = "foo";

View file

@ -88,7 +88,22 @@ procedure List.8 (#Attr.2, #Attr.3):
let List.529 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; let List.529 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
ret List.529; 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: joinpoint List.578 List.439 List.440 List.441 List.442 List.443:
let List.580 : Int1 = CallByName Num.22 List.442 List.443; let List.580 : Int1 = CallByName Num.22 List.442 List.443;
if List.580 then 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; let List.579 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) List.440;
ret List.579; ret List.579;
in in
jump List.578 #Derived_gen.3 #Derived_gen.4 #Derived_gen.5 #Derived_gen.6 #Derived_gen.7; jump List.578 #Derived_gen.8 #Derived_gen.9 #Derived_gen.10 #Derived_gen.11 #Derived_gen.12;
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;
procedure List.93 (List.436, List.437, List.438): procedure List.93 (List.436, List.437, List.438):
let List.538 : U64 = 0i64; let List.538 : U64 = 0i64;

View file

@ -147,38 +147,7 @@ procedure List.8 (#Attr.2, #Attr.3):
let List.599 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; let List.599 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
ret List.599; ret List.599;
procedure List.80 (#Derived_gen.10, #Derived_gen.11, #Derived_gen.12, #Derived_gen.13, #Derived_gen.14): procedure List.80 (#Derived_gen.16, #Derived_gen.17, #Derived_gen.18, #Derived_gen.19, #Derived_gen.20):
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):
joinpoint List.627 List.439 List.440 List.441 List.442 List.443: joinpoint List.627 List.439 List.440 List.441 List.442 List.443:
let List.629 : Int1 = CallByName Num.22 List.442 List.443; let List.629 : Int1 = CallByName Num.22 List.442 List.443;
if List.629 then 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; let List.628 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) List.440;
ret List.628; ret List.628;
in 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): procedure List.93 (List.436, List.437, List.438):
let List.552 : U64 = 0i64; let List.552 : U64 = 0i64;

View file

@ -150,22 +150,7 @@ procedure List.8 (#Attr.2, #Attr.3):
let List.599 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; let List.599 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
ret List.599; ret List.599;
procedure List.80 (#Derived_gen.20, #Derived_gen.21, #Derived_gen.22, #Derived_gen.23, #Derived_gen.24): procedure List.80 (#Derived_gen.11, #Derived_gen.12, #Derived_gen.13, #Derived_gen.14, #Derived_gen.15):
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):
joinpoint List.627 List.439 List.440 List.441 List.442 List.443: joinpoint List.627 List.439 List.440 List.441 List.442 List.443:
let List.629 : Int1 = CallByName Num.22 List.442 List.443; let List.629 : Int1 = CallByName Num.22 List.442 List.443;
if List.629 then 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; let List.628 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) List.440;
ret List.628; ret List.628;
in 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: joinpoint List.554 List.439 List.440 List.441 List.442 List.443:
let List.556 : Int1 = CallByName Num.22 List.442 List.443; let List.556 : Int1 = CallByName Num.22 List.442 List.443;
if List.556 then 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; dec List.439;
ret List.440; ret List.440;
in 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): procedure List.93 (List.436, List.437, List.438):
let List.552 : U64 = 0i64; let List.552 : U64 = 0i64;

View file

@ -247,8 +247,8 @@ procedure Str.9 (Str.79):
else else
let Str.300 : U8 = StructAtIndex 3 Str.80; let Str.300 : U8 = StructAtIndex 3 Str.80;
let Str.301 : U64 = StructAtIndex 0 Str.80; let Str.301 : U64 = StructAtIndex 0 Str.80;
let #Derived_gen.7 : Str = StructAtIndex 1 Str.80; let #Derived_gen.6 : Str = StructAtIndex 1 Str.80;
dec #Derived_gen.7; dec #Derived_gen.6;
let Str.299 : {U64, U8} = Struct {Str.301, Str.300}; let Str.299 : {U64, U8} = Struct {Str.301, Str.300};
let Str.298 : [C {U64, U8}, C Str] = TagId(0) Str.299; let Str.298 : [C {U64, U8}, C Str] = TagId(0) Str.299;
ret Str.298; ret Str.298;
@ -365,8 +365,8 @@ procedure TotallyNotJson.534 (TotallyNotJson.535):
procedure TotallyNotJson.536 (TotallyNotJson.1192): procedure TotallyNotJson.536 (TotallyNotJson.1192):
let TotallyNotJson.1193 : List U8 = StructAtIndex 1 TotallyNotJson.1192; let TotallyNotJson.1193 : List U8 = StructAtIndex 1 TotallyNotJson.1192;
let #Derived_gen.6 : List U8 = StructAtIndex 0 TotallyNotJson.1192; let #Derived_gen.7 : List U8 = StructAtIndex 0 TotallyNotJson.1192;
dec #Derived_gen.6; dec #Derived_gen.7;
ret TotallyNotJson.1193; ret TotallyNotJson.1193;
procedure TotallyNotJson.60 (): procedure TotallyNotJson.60 ():

View file

@ -248,8 +248,8 @@ procedure Str.9 (Str.79):
else else
let Str.310 : U8 = StructAtIndex 3 Str.80; let Str.310 : U8 = StructAtIndex 3 Str.80;
let Str.311 : U64 = StructAtIndex 0 Str.80; let Str.311 : U64 = StructAtIndex 0 Str.80;
let #Derived_gen.6 : Str = StructAtIndex 1 Str.80; let #Derived_gen.7 : Str = StructAtIndex 1 Str.80;
dec #Derived_gen.6; dec #Derived_gen.7;
let Str.309 : {U64, U8} = Struct {Str.311, Str.310}; let Str.309 : {U64, U8} = Struct {Str.311, Str.310};
let Str.308 : [C {U64, U8}, C Str] = TagId(0) Str.309; let Str.308 : [C {U64, U8}, C Str] = TagId(0) Str.309;
ret Str.308; ret Str.308;
@ -397,8 +397,8 @@ procedure TotallyNotJson.534 (TotallyNotJson.535):
procedure TotallyNotJson.536 (TotallyNotJson.1192): procedure TotallyNotJson.536 (TotallyNotJson.1192):
let TotallyNotJson.1193 : List U8 = StructAtIndex 1 TotallyNotJson.1192; let TotallyNotJson.1193 : List U8 = StructAtIndex 1 TotallyNotJson.1192;
let #Derived_gen.7 : List U8 = StructAtIndex 0 TotallyNotJson.1192; let #Derived_gen.6 : List U8 = StructAtIndex 0 TotallyNotJson.1192;
dec #Derived_gen.7; dec #Derived_gen.6;
ret TotallyNotJson.1193; ret TotallyNotJson.1193;
procedure TotallyNotJson.60 (): procedure TotallyNotJson.60 ():

View file

@ -6,10 +6,10 @@ procedure List.5 (#Attr.2, #Attr.3):
procedure Test.2 (Test.5): procedure Test.2 (Test.5):
let Test.6 : List [<rnnu>C List *self] = UnionAtIndex (Id 0) (Index 0) Test.5; let Test.6 : List [<rnnu>C List *self] = UnionAtIndex (Id 0) (Index 0) Test.5;
inc Test.6; 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.15 : {} = Struct {};
let Test.7 : List [<rnnu>C List *self] = CallByName List.5 Test.6 Test.15; 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; ret Test.14;
procedure Test.0 (): procedure Test.0 ():

View file

@ -19,7 +19,7 @@ procedure Test.11 (Test.29, #Attr.12):
procedure Test.11 (Test.29, Test.10): procedure Test.11 (Test.29, Test.10):
ret 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: joinpoint Test.37 Test.36 #Attr.12:
let Test.12 : {} = UnionAtIndex (Id 1) (Index 1) #Attr.12; let Test.12 : {} = UnionAtIndex (Id 1) (Index 1) #Attr.12;
let Test.13 : I64 = UnionAtIndex (Id 1) (Index 0) #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; decref #Attr.12;
jump #Derived_gen.10; jump #Derived_gen.10;
in in
jump Test.37 #Derived_gen.2 #Derived_gen.3; jump Test.37 #Derived_gen.7 #Derived_gen.8;
procedure Test.2 (): procedure Test.2 ():
let Test.6 : Str = "Hello"; let Test.6 : Str = "Hello";

View file

@ -136,7 +136,23 @@ procedure List.8 (#Attr.2, #Attr.3):
let List.598 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; let List.598 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
ret List.598; 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: joinpoint List.573 List.439 List.440 List.441 List.442 List.443:
let List.575 : Int1 = CallByName Num.22 List.442 List.443; let List.575 : Int1 = CallByName Num.22 List.442 List.443;
if List.575 then 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; dec List.439;
ret List.440; ret List.440;
in 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: joinpoint List.626 List.439 List.440 List.441 List.442 List.443:
let List.628 : Int1 = CallByName Num.22 List.442 List.443; let List.628 : Int1 = CallByName Num.22 List.442 List.443;
if List.628 then 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; let List.627 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) List.440;
ret List.627; ret List.627;
in in
jump List.626 #Derived_gen.16 #Derived_gen.17 #Derived_gen.18 #Derived_gen.19 #Derived_gen.20; jump List.626 #Derived_gen.8 #Derived_gen.9 #Derived_gen.10 #Derived_gen.11 #Derived_gen.12;
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;
procedure List.93 (List.436, List.437, List.438): procedure List.93 (List.436, List.437, List.438):
let List.551 : U64 = 0i64; let List.551 : U64 = 0i64;

View file

@ -131,7 +131,7 @@ procedure List.8 (#Attr.2, #Attr.3):
let List.616 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; let List.616 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
ret List.616; 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: joinpoint List.601 List.439 List.440 List.441 List.442 List.443:
let List.603 : Int1 = CallByName Num.22 List.442 List.443; let List.603 : Int1 = CallByName Num.22 List.442 List.443;
if List.603 then 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; dec List.439;
ret List.440; ret List.440;
in 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: joinpoint List.553 List.439 List.440 List.441 List.442 List.443:
let List.555 : Int1 = CallByName Num.22 List.442 List.443; let List.555 : Int1 = CallByName Num.22 List.442 List.443;
if List.555 then 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; dec List.439;
ret List.440; ret List.440;
in 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): procedure List.93 (List.436, List.437, List.438):
let List.551 : U64 = 0i64; let List.551 : U64 = 0i64;

View file

@ -16,6 +16,7 @@ const EXPANDED_STACK_SIZE: usize = 8 * 1024 * 1024;
use bumpalo::Bump; use bumpalo::Bump;
use roc_collections::all::MutMap; use roc_collections::all::MutMap;
use roc_load::ExecutionMode; use roc_load::ExecutionMode;
use roc_load::FunctionKind;
use roc_load::LoadConfig; use roc_load::LoadConfig;
use roc_load::LoadMonomorphizedError; use roc_load::LoadMonomorphizedError;
use roc_load::Threading; 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 { let load_config = LoadConfig {
target_info: TARGET_INFO, target_info: TARGET_INFO,
// TODO parameterize
function_kind: FunctionKind::LambdaSet,
threading: Threading::Single, threading: Threading::Single,
render: roc_reporting::report::RenderTarget::Generic, render: roc_reporting::report::RenderTarget::Generic,
palette: roc_reporting::report::DEFAULT_PALETTE, palette: roc_reporting::report::DEFAULT_PALETTE,

View file

@ -11,7 +11,7 @@ use roc_can::{
}; };
use roc_derive::SharedDerivedModule; use roc_derive::SharedDerivedModule;
use roc_late_solve::AbilitiesView; use roc_late_solve::AbilitiesView;
use roc_load::LoadedModule; use roc_load::{FunctionKind, LoadedModule};
use roc_module::symbol::{Interns, ModuleId}; use roc_module::symbol::{Interns, ModuleId};
use roc_packaging::cache::RocCacheDir; use roc_packaging::cache::RocCacheDir;
use roc_problem::can::Problem; use roc_problem::can::Problem;
@ -48,6 +48,7 @@ pub fn run_load_and_infer<'a>(
src: &str, src: &str,
dependencies: impl IntoIterator<Item = (&'a str, &'a str)>, dependencies: impl IntoIterator<Item = (&'a str, &'a str)>,
no_promote: bool, no_promote: bool,
function_kind: FunctionKind,
) -> Result<(LoadedModule, String), std::io::Error> { ) -> Result<(LoadedModule, String), std::io::Error> {
use tempfile::tempdir; use tempfile::tempdir;
@ -79,6 +80,7 @@ pub fn run_load_and_infer<'a>(
module_src, module_src,
dir.path().to_path_buf(), dir.path().to_path_buf(),
roc_target::TargetInfo::default_x86_64(), roc_target::TargetInfo::default_x86_64(),
function_kind,
roc_reporting::report::RenderTarget::Generic, roc_reporting::report::RenderTarget::Generic,
RocCacheDir::Disallowed, RocCacheDir::Disallowed,
roc_reporting::report::DEFAULT_PALETTE, roc_reporting::report::DEFAULT_PALETTE,
@ -351,6 +353,7 @@ pub fn infer_queries<'a>(
dependencies: impl IntoIterator<Item = (&'a str, &'a str)>, dependencies: impl IntoIterator<Item = (&'a str, &'a str)>,
options: InferOptions, options: InferOptions,
allow_can_errors: bool, allow_can_errors: bool,
function_kind: FunctionKind,
) -> Result<InferredProgram, Box<dyn Error>> { ) -> Result<InferredProgram, Box<dyn Error>> {
let ( let (
LoadedModule { LoadedModule {
@ -364,7 +367,7 @@ pub fn infer_queries<'a>(
.. ..
}, },
src, 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 declarations = declarations_by_id.remove(&home).unwrap();
let subs = solved.inner_mut(); let subs = solved.inner_mut();

View file

@ -402,7 +402,11 @@ fn find_names_needed(
find_under_alias, 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. // Errors and empty records don't need names.
} }
} }
@ -859,6 +863,12 @@ fn write_content<'a>(
buf.push(']'); buf.push(']');
} }
ErasedLambda => {
debug_assert!(env.debug.print_lambda_sets);
// Easy mode 🤠
buf.push('?');
}
RangedNumber(range) => { RangedNumber(range) => {
buf.push_str("Range("); buf.push_str("Range(");
for (i, &var) in range.variable_slice().iter().enumerate() { for (i, &var) in range.variable_slice().iter().enumerate() {

View file

@ -587,15 +587,19 @@ impl<T> Clone for SubsSlice<T> {
impl<T> Default for SubsSlice<T> { impl<T> Default for SubsSlice<T> {
fn default() -> Self { fn default() -> Self {
Self { Self::empty()
start: Default::default(),
length: Default::default(),
_marker: Default::default(),
}
} }
} }
impl<T> SubsSlice<T> { 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] { pub fn get_slice<'a>(&self, slice: &'a [T]) -> &'a [T] {
&slice[self.indices()] &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:?}>)") write!(f, ", ^<{ambient_function_var:?}>)")
} }
Content::ErasedLambda => write!(f, "ErasedLambda"),
Content::RangedNumber(range) => { Content::RangedNumber(range) => {
write!(f, "RangedNumber( {range:?})") write!(f, "RangedNumber( {range:?})")
} }
@ -1230,7 +1235,7 @@ impl IllegalCycleMark {
pub struct Variable(u32); pub struct Variable(u32);
macro_rules! define_const_var { macro_rules! define_const_var {
($($(:pub)? $name:ident),* $(,)?) => { ($($(#[$meta:meta])* $(:pub)? $name:ident),* $(,)?) => {
#[allow(non_camel_case_types, clippy::upper_case_acronyms)] #[allow(non_camel_case_types, clippy::upper_case_acronyms)]
enum ConstVariables { enum ConstVariables {
$( $name, )* $( $name, )*
@ -1238,7 +1243,7 @@ macro_rules! define_const_var {
} }
impl Variable { 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; 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. // The following are abound in derived abilities, so we cache them.
:pub STR, :pub STR,
:pub LIST_U8, :pub LIST_U8,
/// The erased lambda type.
:pub ERASED_LAMBDA,
} }
impl Variable { impl Variable {
@ -1694,25 +1702,31 @@ pub struct SubsSnapshot {
} }
impl Subs { impl Subs {
// IFTTT INIT-TagNames
pub const RESULT_TAG_NAMES: SubsSlice<TagName> = SubsSlice::new(0, 2); 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_ERR: SubsIndex<TagName> = SubsIndex::new(0);
pub const TAG_NAME_OK: SubsIndex<TagName> = SubsIndex::new(1); 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_INVALID_NUM_STR: SubsIndex<TagName> = SubsIndex::new(2);
pub const TAG_NAME_BAD_UTF_8: SubsIndex<TagName> = SubsIndex::new(3); pub const TAG_NAME_BAD_UTF_8: SubsIndex<TagName> = SubsIndex::new(3);
pub const TAG_NAME_OUT_OF_BOUNDS: SubsIndex<TagName> = SubsIndex::new(4); 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); pub const STR_SLICE: VariableSubsSlice = SubsSlice::new(0, 1);
// END INIT-VariableSubsSlice
// IFTTT INIT-SymbolSubsSlice
#[rustfmt::skip] #[rustfmt::skip]
pub const AB_ENCODING: SubsSlice<Symbol> = SubsSlice::new(0, 1); pub const AB_ENCODING: SubsSlice<Symbol> = SubsSlice::new(0, 1);
#[rustfmt::skip] #[rustfmt::skip]
pub const AB_DECODING: SubsSlice<Symbol> = SubsSlice::new(1, 1); pub const AB_DECODING: SubsSlice<Symbol> = SubsSlice::new(1, 1);
#[rustfmt::skip] #[rustfmt::skip]
pub const AB_HASHER: SubsSlice<Symbol> = SubsSlice::new(2, 1); pub const AB_HASHER: SubsSlice<Symbol> = SubsSlice::new(2, 1);
#[rustfmt::skip] #[rustfmt::skip]
pub const AB_HASH: SubsSlice<Symbol> = SubsSlice::new(3, 1); pub const AB_HASH: SubsSlice<Symbol> = SubsSlice::new(3, 1);
#[rustfmt::skip] #[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 { pub fn new() -> Self {
Self::with_capacity(0) Self::with_capacity(0)
@ -1723,13 +1737,16 @@ impl Subs {
let mut tag_names = Vec::with_capacity(32); let mut tag_names = Vec::with_capacity(32);
// IFTTT INIT-TagNames
tag_names.push(TagName("Err".into())); tag_names.push(TagName("Err".into()));
tag_names.push(TagName("Ok".into())); tag_names.push(TagName("Ok".into()));
tag_names.push(TagName("InvalidNumStr".into())); tag_names.push(TagName("InvalidNumStr".into()));
tag_names.push(TagName("BadUtf8".into())); tag_names.push(TagName("BadUtf8".into()));
tag_names.push(TagName("OutOfBounds".into())); tag_names.push(TagName("OutOfBounds".into()));
// END INIT-TagNames
// IFTTT INIT-SymbolNames
let mut symbol_names = Vec::with_capacity(32); let mut symbol_names = Vec::with_capacity(32);
symbol_names.push(Symbol::ENCODE_ENCODING); symbol_names.push(Symbol::ENCODE_ENCODING);
@ -1737,13 +1754,15 @@ impl Subs {
symbol_names.push(Symbol::HASH_HASHER); symbol_names.push(Symbol::HASH_HASHER);
symbol_names.push(Symbol::HASH_HASH_ABILITY); symbol_names.push(Symbol::HASH_HASH_ABILITY);
symbol_names.push(Symbol::BOOL_EQ); symbol_names.push(Symbol::BOOL_EQ);
// END INIT-SymbolNames
// IFTTT INIT-VariableSubsSlice
let variables = vec![Variable::STR];
// END INIT-VariableSubsSlice
let mut subs = Subs { let mut subs = Subs {
utable: UnificationTable::default(), utable: UnificationTable::default(),
variables: vec![ variables,
// Used for STR_SLICE
Variable::STR,
],
tag_names, tag_names,
symbol_names, symbol_names,
field_names: Vec::new(), field_names: Vec::new(),
@ -1807,6 +1826,8 @@ impl Subs {
Content::Structure(FlatType::Apply(Symbol::LIST_LIST, u8_slice)), Content::Structure(FlatType::Apply(Symbol::LIST_LIST, u8_slice)),
); );
subs.set_content(Variable::ERASED_LAMBDA, Content::ErasedLambda);
subs subs
} }
@ -2203,7 +2224,7 @@ impl Subs {
| Content::RecursionVar { .. } | Content::RecursionVar { .. }
| Content::RangedNumber(_) | Content::RangedNumber(_)
| Content::Error => return false, | Content::Error => return false,
Content::LambdaSet(_) => return true, Content::LambdaSet(_) | Content::ErasedLambda => return false,
Content::Structure(FlatType::Func(..)) => return true, Content::Structure(FlatType::Func(..)) => return true,
Content::Structure(_) => return false, Content::Structure(_) => return false,
Content::Alias(_, _, real_var, _) => { Content::Alias(_, _, real_var, _) => {
@ -2367,7 +2388,10 @@ pub enum Content {
structure: Variable, structure: Variable,
opt_name: Option<SubsIndex<Lowercase>>, opt_name: Option<SubsIndex<Lowercase>>,
}, },
/// A resolved set of lambdas. Compatible when functions are kinded as lambda set.
LambdaSet(LambdaSet), LambdaSet(LambdaSet),
/// A type-erased lambda. Compatible when functions are not kinded.
ErasedLambda,
Structure(FlatType), Structure(FlatType),
Alias(Symbol, AliasVariables, Variable, AliasKind), Alias(Symbol, AliasVariables, Variable, AliasKind),
RangedNumber(crate::num::NumericRange), RangedNumber(crate::num::NumericRange),
@ -3595,6 +3619,7 @@ fn occurs(
occurs_union(subs, root_var, ctx, safe!(UnionLabels<Symbol>, solved)) occurs_union(subs, root_var, ctx, safe!(UnionLabels<Symbol>, solved))
} }
ErasedLambda => Ok(()),
RangedNumber(_range_vars) => Ok(()), RangedNumber(_range_vars) => Ok(()),
})(); })();
@ -3680,7 +3705,8 @@ fn explicit_substitute(
| FlexAbleVar(_, _) | FlexAbleVar(_, _)
| RigidAbleVar(_, _) | RigidAbleVar(_, _)
| RecursionVar { .. } | RecursionVar { .. }
| Error => in_var, | Error
| ErasedLambda => in_var,
Structure(flat_type) => { Structure(flat_type) => {
match flat_type { match flat_type {
@ -3861,7 +3887,7 @@ fn get_var_names(
subs.set_mark(var, Mark::GET_VAR_NAMES); subs.set_mark(var, Mark::GET_VAR_NAMES);
match desc.content { 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( FlexVar(Some(name_index)) | FlexAbleVar(Some(name_index), _) => add_name(
subs, subs,
@ -4178,7 +4204,7 @@ fn content_to_err_type(
ErrorType::Alias(symbol, err_args, Box::new(err_type), kind) 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 // Don't print lambda sets since we don't expect them to be exposed to the user
ErrorType::Error ErrorType::Error
} }
@ -4788,6 +4814,7 @@ impl StorageSubs {
unspecialized: Self::offset_uls_slice(offsets, *unspecialized), unspecialized: Self::offset_uls_slice(offsets, *unspecialized),
ambient_function: Self::offset_variable(offsets, *ambient_function_var), ambient_function: Self::offset_variable(offsets, *ambient_function_var),
}), }),
ErasedLambda => ErasedLambda,
RangedNumber(range) => RangedNumber(*range), RangedNumber(range) => RangedNumber(*range),
Error => Content::Error, Error => Content::Error,
} }
@ -5155,7 +5182,7 @@ fn storage_copy_var_to_help(env: &mut StorageCopyVarToEnv<'_>, var: Variable) ->
copy copy
} }
FlexVar(None) | Error => copy, FlexVar(None) | ErasedLambda | Error => copy,
RecursionVar { RecursionVar {
opt_name, opt_name,
@ -5390,6 +5417,7 @@ fn is_registered(content: &Content) -> bool {
| Content::FlexAbleVar(..) | Content::FlexAbleVar(..)
| Content::RigidAbleVar(..) => false, | Content::RigidAbleVar(..) => false,
Content::Structure(FlatType::EmptyRecord | FlatType::EmptyTagUnion) => false, Content::Structure(FlatType::EmptyRecord | FlatType::EmptyTagUnion) => false,
Content::ErasedLambda => false,
Content::Structure(_) Content::Structure(_)
| Content::RecursionVar { .. } | Content::RecursionVar { .. }
@ -5787,6 +5815,12 @@ fn copy_import_to_help(env: &mut CopyImportEnv<'_>, max_rank: Rank, var: Variabl
copy copy
} }
ErasedLambda => {
env.target.set(copy, make_descriptor(ErasedLambda));
copy
}
RangedNumber(range) => { RangedNumber(range) => {
let new_content = 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, .. } => { RecursionVar { structure, .. } => {
stack.push(*structure); stack.push(*structure);
@ -6048,7 +6082,8 @@ pub fn get_member_lambda_sets_at_region(subs: &Subs, var: Variable, target_regio
| Content::RecursionVar { | Content::RecursionVar {
structure: _, structure: _,
opt_name: _, 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 // are determined as illegal and reported during canonicalization, because you
// cannot have a tag union without a non-recursive variant. // cannot have a tag union without a non-recursive variant.
| Content::RecursionVar { .. } => {} | Content::RecursionVar { .. } => {}
Content::LambdaSet(_) => {} Content::LambdaSet(_) | Content::ErasedLambda => {}
Content::Structure(structure) => match structure { Content::Structure(structure) => match structure {
FlatType::Apply(_, args) => stack.extend(subs.get_subs_slice(*args)), FlatType::Apply(_, args) => stack.extend(subs.get_subs_slice(*args)),
FlatType::Func(args, _, ret) => { FlatType::Func(args, _, ret) => {

View file

@ -11,16 +11,19 @@ use roc_mono::{
use tempfile::tempdir; use tempfile::tempdir;
use test_solve_helpers::format_problems; use test_solve_helpers::format_problems;
use crate::CompilerSettings;
#[derive(Default)] #[derive(Default)]
pub struct MonoOptions { pub(crate) struct MonoOptions {
pub no_check: bool, pub no_check: bool,
} }
pub fn write_compiled_ir<'a>( pub(crate) fn write_compiled_ir<'a>(
writer: &mut impl io::Write, writer: &mut impl io::Write,
test_module: &str, test_module: &str,
dependencies: impl IntoIterator<Item = (&'a str, &'a str)>, dependencies: impl IntoIterator<Item = (&'a str, &'a str)>,
options: MonoOptions, options: MonoOptions,
compiler_settings: CompilerSettings,
allow_can_errors: bool, allow_can_errors: bool,
) -> io::Result<()> { ) -> io::Result<()> {
use roc_packaging::cache::RocCacheDir; use roc_packaging::cache::RocCacheDir;
@ -41,6 +44,7 @@ pub fn write_compiled_ir<'a>(
let load_config = LoadConfig { let load_config = LoadConfig {
target_info: roc_target::TargetInfo::default_x86_64(), target_info: roc_target::TargetInfo::default_x86_64(),
function_kind: compiler_settings.function_kind,
threading: Threading::Single, threading: Threading::Single,
render: roc_reporting::report::RenderTarget::Generic, render: roc_reporting::report::RenderTarget::Generic,
palette: roc_reporting::report::DEFAULT_PALETTE, palette: roc_reporting::report::DEFAULT_PALETTE,

View file

@ -11,6 +11,7 @@ use libtest_mimic::{run, Arguments, Failed, Trial};
use mono::MonoOptions; use mono::MonoOptions;
use regex::Regex; use regex::Regex;
use roc_collections::VecMap; use roc_collections::VecMap;
use roc_solve::FunctionKind;
use test_solve_helpers::{ use test_solve_helpers::{
infer_queries, Elaboration, InferOptions, InferredProgram, InferredQuery, MUTLILINE_MARKER, infer_queries, Elaboration, InferOptions, InferredProgram, InferredQuery, MUTLILINE_MARKER,
}; };
@ -36,6 +37,10 @@ lazy_static! {
.join("uitest") .join("uitest")
.join("tests"); .join("tests");
/// # +set <setting>
static ref RE_SETTING: Regex =
Regex::new(r#"# \+set (?P<setting>.*?)=(?P<value>.*)"#).unwrap();
/// # +opt can:<opt> /// # +opt can:<opt>
static ref RE_OPT_CAN: Regex = static ref RE_OPT_CAN: Regex =
Regex::new(r#"# \+opt can:(?P<opt>.*)"#).unwrap(); 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> { fn run_test(path: PathBuf) -> Result<(), Failed> {
let data = std::fs::read_to_string(&path)?; let data = std::fs::read_to_string(&path)?;
let TestCase { let TestCase {
compiler_settings,
can_options, can_options,
infer_options, infer_options,
emit_options, emit_options,
@ -109,6 +115,7 @@ fn run_test(path: PathBuf) -> Result<(), Failed> {
.map(|(md, src)| (&**md, &**src)), .map(|(md, src)| (&**md, &**src)),
infer_options, infer_options,
can_options.allow_errors, can_options.allow_errors,
compiler_settings.function_kind,
)?; )?;
{ {
@ -121,6 +128,7 @@ fn run_test(path: PathBuf) -> Result<(), Failed> {
&mut fd, &mut fd,
program, program,
inferred_program, inferred_program,
compiler_settings,
can_options, can_options,
mono_options, mono_options,
emit_options, emit_options,
@ -141,6 +149,7 @@ struct Modules<'a> {
} }
struct TestCase<'a> { struct TestCase<'a> {
compiler_settings: CompilerSettings,
can_options: CanOptions, can_options: CanOptions,
infer_options: InferOptions, infer_options: InferOptions,
mono_options: MonoOptions, mono_options: MonoOptions,
@ -148,6 +157,18 @@ struct TestCase<'a> {
program: Modules<'a>, program: Modules<'a>,
} }
struct CompilerSettings {
function_kind: FunctionKind,
}
impl Default for CompilerSettings {
fn default() -> Self {
Self {
function_kind: FunctionKind::LambdaSet,
}
}
}
#[derive(Default)] #[derive(Default)]
struct CanOptions { struct CanOptions {
allow_errors: bool, allow_errors: bool,
@ -166,6 +187,7 @@ impl<'a> TestCase<'a> {
data = data[..drop_at].trim_end(); data = data[..drop_at].trim_end();
} }
let compiler_settings = Self::parse_compiler_settings(data)?;
let can_options = Self::parse_can_options(data)?; let can_options = Self::parse_can_options(data)?;
let infer_options = Self::parse_infer_options(data)?; let infer_options = Self::parse_infer_options(data)?;
let mono_options = Self::parse_mono_options(data)?; let mono_options = Self::parse_mono_options(data)?;
@ -174,6 +196,7 @@ impl<'a> TestCase<'a> {
let program = Self::parse_modules(data); let program = Self::parse_modules(data);
Ok(TestCase { Ok(TestCase {
compiler_settings,
can_options, can_options,
infer_options, infer_options,
mono_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> { fn parse_can_options(data: &str) -> Result<CanOptions, Failed> {
let mut can_opts = CanOptions::default(); let mut can_opts = CanOptions::default();
@ -321,6 +364,7 @@ fn assemble_query_output(
writer: &mut impl io::Write, writer: &mut impl io::Write,
program: Modules<'_>, program: Modules<'_>,
inferred_program: InferredProgram, inferred_program: InferredProgram,
compiler_settings: CompilerSettings,
can_options: CanOptions, can_options: CanOptions,
mono_options: MonoOptions, mono_options: MonoOptions,
emit_options: EmitOptions, emit_options: EmitOptions,
@ -369,6 +413,7 @@ fn assemble_query_output(
test_module, test_module,
other_modules, other_modules,
mono_options, mono_options,
compiler_settings,
can_options.allow_errors, can_options.allow_errors,
)?; )?;
} }

View file

@ -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;

View file

@ -3,22 +3,22 @@
app "test" provides [main] to "./platform" app "test" provides [main] to "./platform"
f = \{} -> f = \{} ->
#^{-1} <1599><116>{} -<119>[[f(1)]]-> <115>[Ok <1607>{}]<79>* #^{-1} <1600><117>{} -<120>[[f(1)]]-> <116>[Ok <1608>{}]<80>*
when g {} is when g {} is
# ^ <1589><1607>{} -<1597>[[g(2)]]-> <71>[Ok <1607>{}]<101>* # ^ <1590><1608>{} -<1598>[[g(2)]]-> <72>[Ok <1608>{}]<102>*
_ -> Ok {} _ -> Ok {}
g = \{} -> g = \{} ->
#^{-1} <1589><1607>{} -<1597>[[g(2)]]-> <71>[Ok <1607>{}]<101>* #^{-1} <1590><1608>{} -<1598>[[g(2)]]-> <72>[Ok <1608>{}]<102>*
when h {} is when h {} is
# ^ <1594><1607>{} -<1602>[[h(3)]]-> <93>[Ok <1607>{}]<123>* # ^ <1595><1608>{} -<1603>[[h(3)]]-> <94>[Ok <1608>{}]<124>*
_ -> Ok {} _ -> Ok {}
h = \{} -> h = \{} ->
#^{-1} <1594><1607>{} -<1602>[[h(3)]]-> <93>[Ok <1607>{}]<123>* #^{-1} <1595><1608>{} -<1603>[[h(3)]]-> <94>[Ok <1608>{}]<124>*
when f {} is when f {} is
# ^ <1599><116>{} -<119>[[f(1)]]-> <115>[Ok <1607>{}]<79>* # ^ <1600><117>{} -<120>[[f(1)]]-> <116>[Ok <1608>{}]<80>*
_ -> Ok {} _ -> Ok {}
main = f {} main = f {}
# ^ <1609><132>{} -<135>[[f(1)]]-> <137>[Ok <1607>{}]<1608>w_a # ^ <1610><133>{} -<136>[[f(1)]]-> <138>[Ok <1608>{}]<1609>w_a

View file

@ -5,7 +5,7 @@ use roc_debug_flags::{dbg_do, dbg_set};
use roc_debug_flags::{ use roc_debug_flags::{
ROC_PRINT_MISMATCHES, ROC_PRINT_UNIFICATIONS, ROC_VERIFY_OCCURS_ONE_RECURSION, 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::ident::{Lowercase, TagName};
use roc_module::symbol::{ModuleId, Symbol}; use roc_module::symbol::{ModuleId, Symbol};
use roc_types::num::{FloatWidth, IntLitWidth, NumericRange}; 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) unify_opaque(env, pool, &ctx, *symbol, *args, *real_var)
} }
LambdaSet(lset) => unify_lambda_set(env, pool, &ctx, *lset, &ctx.second_desc.content), 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), &RangedNumber(range_vars) => unify_ranged_number(env, pool, &ctx, range_vars),
Error => { Error => {
// Error propagates. Whatever we're comparing it to doesn't matter! // 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)), Some(range) => merge(env, ctx, RangedNumber(range)),
None => not_in_range_mismatch(), None => not_in_range_mismatch(),
}, },
LambdaSet(..) => mismatch!(), LambdaSet(..) | ErasedLambda => mismatch!(),
Error => merge(env, ctx, Error), 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) 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), 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), Error => merge(env, ctx, Error),
} }
} }
@ -1198,10 +1200,9 @@ fn unify_structure<M: MetaCollector>(
"Cannot unify structure \n{:?} \nwith lambda set\n {:?}", "Cannot unify structure \n{:?} \nwith lambda set\n {:?}",
roc_types::subs::SubsFmtContent(&Content::Structure(*flat_type), env.subs), roc_types::subs::SubsFmtContent(&Content::Structure(*flat_type), env.subs),
roc_types::subs::SubsFmtContent(other, env.subs), roc_types::subs::SubsFmtContent(other, env.subs),
// &flat_type,
// other
) )
} }
ErasedLambda => mismatch!(),
RangedNumber(other_range_vars) => { RangedNumber(other_range_vars) => {
check_and_merge_valid_range(env, pool, ctx, ctx.second, *other_range_vars, ctx.first) 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)] #[inline(always)]
#[must_use] #[must_use]
fn unify_lambda_set<M: MetaCollector>( fn unify_lambda_set<M: MetaCollector>(
@ -1254,6 +1287,7 @@ fn unify_lambda_set<M: MetaCollector>(
env.remove_recursion_pair(ctx.first, ctx.second); env.remove_recursion_pair(ctx.first, ctx.second);
outcome outcome
} }
ErasedLambda => merge(env, ctx, ErasedLambda),
RigidVar(..) | RigidAbleVar(..) => mismatch!("Lambda sets never unify with rigid"), RigidVar(..) | RigidAbleVar(..) => mismatch!("Lambda sets never unify with rigid"),
FlexAbleVar(..) => mismatch!("Lambda sets should never have abilities attached to them"), FlexAbleVar(..) => mismatch!("Lambda sets should never have abilities attached to them"),
Structure(..) => mismatch!("Lambda set cannot unify with non-lambda set structure"), Structure(..) => mismatch!("Lambda set cannot unify with non-lambda set structure"),
@ -3521,7 +3555,8 @@ fn unify_rigid<M: MetaCollector>(
| RecursionVar { .. } | RecursionVar { .. }
| Structure(_) | Structure(_)
| Alias(..) | Alias(..)
| LambdaSet(..) => { | LambdaSet(..)
| ErasedLambda => {
// Type mismatch! Rigid can only unify with flex, even if the // Type mismatch! Rigid can only unify with flex, even if the
// rigid names are the same. // rigid names are the same.
mismatch!("Rigid {:?} with {:?}", ctx.first, &other) mismatch!("Rigid {:?} with {:?}", ctx.first, &other)
@ -3581,7 +3616,8 @@ fn unify_rigid_able<M: MetaCollector>(
| Structure(_) | Structure(_)
| Alias(..) | Alias(..)
| RangedNumber(..) | RangedNumber(..)
| LambdaSet(..) => { | LambdaSet(..)
| ErasedLambda => {
// Type mismatch! Rigid can only unify with flex, even if the // Type mismatch! Rigid can only unify with flex, even if the
// rigid names are the same. // rigid names are the same.
mismatch!("Rigid {:?} with {:?}", ctx.first, &other) mismatch!("Rigid {:?} with {:?}", ctx.first, &other)
@ -3621,7 +3657,8 @@ fn unify_flex<M: MetaCollector>(
| Structure(_) | Structure(_)
| Alias(_, _, _, _) | Alias(_, _, _, _)
| RangedNumber(..) | RangedNumber(..)
| LambdaSet(..) => { | LambdaSet(..)
| ErasedLambda => {
// TODO special-case boolean here // TODO special-case boolean here
// In all other cases, if left is flex, defer to right. // In all other cases, if left is flex, defer to right.
merge(env, ctx, *other) merge(env, ctx, *other)
@ -3716,7 +3753,7 @@ fn unify_flex_able<M: MetaCollector>(
} }
RigidVar(_) => mismatch!("FlexAble can never unify with non-able Rigid"), 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) => { Alias(name, _args, _real_var, AliasKind::Opaque) => {
// Opaque type wins // Opaque type wins
@ -3885,6 +3922,8 @@ fn unify_recursion<M: MetaCollector>(
unify_pool(env, pool, structure, ctx.second, ctx.mode) unify_pool(env, pool, structure, ctx.second, ctx.mode)
} }
ErasedLambda => mismatch!(),
Error => merge(env, ctx, Error), Error => merge(env, ctx, Error),
}; };

View file

@ -20,6 +20,7 @@ roc_packaging = { path = "../packaging" }
roc_parse = { path = "../compiler/parse" } roc_parse = { path = "../compiler/parse" }
roc_region = { path = "../compiler/region" } roc_region = { path = "../compiler/region" }
roc_reporting = { path = "../reporting" } roc_reporting = { path = "../reporting" }
roc_solve = { path = "../compiler/solve" }
roc_target = { path = "../compiler/roc_target" } roc_target = { path = "../compiler/roc_target" }
roc_types = { path = "../compiler/types" } roc_types = { path = "../compiler/types" }

View file

@ -454,6 +454,7 @@ pub fn load_module_for_docs(filename: PathBuf) -> LoadedModule {
let arena = Bump::new(); let arena = Bump::new();
let load_config = LoadConfig { let load_config = LoadConfig {
target_info: roc_target::TargetInfo::default_x86_64(), // This is just type-checking for docs, so "target" doesn't matter 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, render: roc_reporting::report::RenderTarget::ColorTerminal,
palette: roc_reporting::report::DEFAULT_PALETTE, palette: roc_reporting::report::DEFAULT_PALETTE,
threading: Threading::AllAvailable, threading: Threading::AllAvailable,

View file

@ -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 // END LARGE SCALE PROJECTS

View file

@ -10,7 +10,8 @@ use roc_build::{
}, },
}; };
use roc_collections::MutMap; 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::ir::{generate_glue_procs, GlueProc, OptLevel};
use roc_mono::layout::{GlobalLayoutInterner, LayoutCache, LayoutInterner}; use roc_mono::layout::{GlobalLayoutInterner, LayoutCache, LayoutInterner};
use roc_packaging::cache::{self, RocCacheDir}; use roc_packaging::cache::{self, RocCacheDir};
@ -321,6 +322,7 @@ fn number_lambda_sets(subs: &Subs, initial: Variable) -> Vec<Variable> {
stack.push(*var); stack.push(*var);
} }
} }
ErasedLambda => todo_lambda_erasure!(),
&RangedNumber(_) => {} &RangedNumber(_) => {}
} }
} }
@ -334,6 +336,8 @@ pub fn load_types(
ignore_errors: IgnoreErrors, ignore_errors: IgnoreErrors,
) -> Result<Vec<Types>, io::Error> { ) -> Result<Vec<Types>, io::Error> {
let target_info = (&Triple::host()).into(); 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 arena = &Bump::new();
let LoadedModule { let LoadedModule {
module_id: home, module_id: home,
@ -350,6 +354,7 @@ pub fn load_types(
RocCacheDir::Persistent(cache::roc_cache_dir().as_path()), RocCacheDir::Persistent(cache::roc_cache_dir().as_path()),
LoadConfig { LoadConfig {
target_info, target_info,
function_kind,
render: RenderTarget::Generic, render: RenderTarget::Generic,
palette: DEFAULT_PALETTE, palette: DEFAULT_PALETTE,
threading, threading,

Some files were not shown because too many files have changed in this diff Show more