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

View File

@ -12,6 +12,7 @@ pub fn load_module(
) -> LoadedModule {
let load_config = LoadConfig {
target_info: TargetInfo::default_x86_64(), // editor only needs type info, so this is unused
function_kind: roc_solve::FunctionKind::LambdaSet, // TODO the editor may need to dynamically change this
render: roc_reporting::report::RenderTarget::ColorTerminal,
palette: DEFAULT_PALETTE,
threading,

View File

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

View File

@ -385,7 +385,7 @@ pub fn test(_matches: &ArgMatches, _triple: Triple) -> io::Result<i32> {
#[cfg(not(windows))]
pub fn test(matches: &ArgMatches, triple: Triple) -> io::Result<i32> {
use roc_build::program::report_problems_monomorphized;
use roc_load::{ExecutionMode, LoadConfig, LoadMonomorphizedError};
use roc_load::{ExecutionMode, FunctionKind, LoadConfig, LoadMonomorphizedError};
use roc_packaging::cache;
use roc_target::TargetInfo;
@ -427,10 +427,13 @@ pub fn test(matches: &ArgMatches, triple: Triple) -> io::Result<i32> {
let target = &triple;
let opt_level = opt_level;
let target_info = TargetInfo::from(target);
// TODO may need to determine this dynamically based on dev builds.
let function_kind = FunctionKind::LambdaSet;
// Step 1: compile the app and generate the .o file
let load_config = LoadConfig {
target_info,
function_kind,
// TODO: expose this from CLI?
render: roc_reporting::report::RenderTarget::ColorTerminal,
palette: roc_reporting::report::DEFAULT_PALETTE,

View File

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

View File

@ -14,8 +14,8 @@ use roc_module::low_level::LowLevel;
use roc_module::symbol::Symbol;
use roc_mono::ir::{
Call, CallType, EntryPoint, Expr, HigherOrderLowLevel, HostExposedLayouts, ListLiteralElement,
Literal, ModifyRc, OptLevel, Proc, ProcLayout, SingleEntryPoint, Stmt,
Call, CallType, EntryPoint, ErasedField, Expr, HigherOrderLowLevel, HostExposedLayouts,
ListLiteralElement, Literal, ModifyRc, OptLevel, Proc, ProcLayout, SingleEntryPoint, Stmt,
};
use roc_mono::layout::{
Builtin, InLayout, Layout, LayoutInterner, LayoutRepr, Niche, RawFunctionLayout,
@ -181,6 +181,7 @@ where
let mut type_definitions = MutSet::default();
let mut host_exposed_functions = Vec::new();
let mut erased_functions = Vec::new();
// all other functions
for proc in procs {
@ -201,6 +202,17 @@ where
host_exposed_functions.push((bytes, hels.proc_layout.arguments));
}
RawFunctionLayout::ErasedFunction(..) => {
let it = hels.proc_layout.arguments.iter().copied();
let bytes = func_name_bytes_help(
hels.symbol,
it,
Niche::NONE,
hels.proc_layout.result,
);
host_exposed_functions.push((bytes, hels.proc_layout.arguments));
}
RawFunctionLayout::ZeroArgumentThunk(_) => {
let bytes = func_name_bytes_help(
hels.symbol,
@ -226,6 +238,11 @@ where
let (spec, type_names) = proc_spec(arena, interner, proc)?;
if proc.is_erased {
let args = &*arena.alloc_slice_fill_iter(proc.args.iter().map(|(lay, _)| *lay));
erased_functions.push((bytes, args));
}
type_definitions.extend(type_names);
m.add_func(func_name, spec)?;
@ -253,6 +270,7 @@ where
entry_point_layout,
Some(roc_main),
&host_exposed_functions,
&erased_functions,
)?;
type_definitions.extend(env.type_names);
@ -279,8 +297,14 @@ where
.collect();
let mut env = Env::new();
let entry_point_function =
build_entry_point(&mut env, interner, layout, None, &host_exposed)?;
let entry_point_function = build_entry_point(
&mut env,
interner,
layout,
None,
&host_exposed,
&erased_functions,
)?;
type_definitions.extend(env.type_names);
@ -364,6 +388,7 @@ fn build_entry_point<'a>(
layout: roc_mono::ir::ProcLayout<'a>,
entry_point_function: Option<FuncName>,
host_exposed_functions: &[([u8; SIZE], &'a [InLayout<'a>])],
erased_functions: &[([u8; SIZE], &'a [InLayout<'a>])],
) -> Result<FuncDef> {
let mut builder = FuncDefBuilder::new();
let outer_block = builder.add_block();
@ -394,7 +419,7 @@ fn build_entry_point<'a>(
}
// add fake calls to host-exposed functions so they are specialized
for (name_bytes, layouts) in host_exposed_functions {
for (name_bytes, layouts) in host_exposed_functions.iter().chain(erased_functions) {
let host_exposed_func_name = FuncName(name_bytes);
if Some(host_exposed_func_name) == entry_point_function {
@ -788,6 +813,16 @@ fn call_spec<'a>(
let module = MOD_APP;
builder.add_call(block, spec_var, module, name, arg_value_id)
}
ByPointer {
pointer,
ret_layout,
arg_layouts: _,
} => {
let result_type = layout_spec(env, builder, interner, interner.get_repr(*ret_layout))?;
let fnptr = env.symbols[pointer];
let arg_value_id = build_tuple_value(builder, env, block, call.arguments)?;
builder.add_unknown_with(block, &[fnptr, arg_value_id], result_type)
}
Foreign {
foreign_symbol: _,
ret_layout,
@ -1517,6 +1552,28 @@ fn expr_spec<'a>(
let value = with_new_heap_cell(builder, block, union_data)?;
builder.add_make_named(block, MOD_APP, type_name, value)
}
FunctionPointer { .. } => {
let pointer_type = layout_spec(env, builder, interner, interner.get_repr(layout))?;
builder.add_unknown_with(block, &[], pointer_type)
}
ErasedMake { callee, value } => {
let value = match value {
Some(v) => box_erasure_value_unknown(builder, block, env.symbols[v]),
// model nullptr
None => box_erasure_value_unknown_nullptr(builder, block),
}?;
let callee = env.symbols[callee];
erasure_make(builder, block, value, callee)
}
ErasedLoad { symbol, field } => {
let value = env.symbols[symbol];
let loaded_type = layout_spec(env, builder, interner, interner.get_repr(layout))?;
erasure_load(builder, block, value, *field, loaded_type)
}
RuntimeErrorFunction(_) => {
let type_id = layout_spec(env, builder, interner, interner.get_repr(layout))?;
@ -1631,6 +1688,9 @@ fn layout_spec_help<'a>(
}
_ => internal_error!("somehow, a non-recursive layout is under a recursive pointer"),
},
FunctionPointer(_) => function_pointer_type(builder),
Erased(_) => erasure_type(builder),
}
}
@ -1707,3 +1767,78 @@ fn new_num(builder: &mut FuncDefBuilder, block: BlockId) -> Result<ValueId> {
// we model all our numbers as unit values
builder.add_make_tuple(block, &[])
}
fn function_pointer_type<TC: TypeContext>(builder: &mut TC) -> Result<TypeId> {
builder.add_tuple_type(&[])
}
const ERASURE_CALEE_INDEX: u32 = 0;
const ERASURE_VALUE_INDEX: u32 = 1;
/// Erasure type modeled as
///
/// ```text
/// Tuple(callee: FnPtr, value: HeapCell)
/// ```
fn erasure_type<TC: TypeContext>(builder: &mut TC) -> Result<TypeId> {
let value_cell_id = builder.add_heap_cell_type();
let callee_id = function_pointer_type(builder)?;
builder.add_tuple_type(&[value_cell_id, callee_id])
}
fn erasure_box_value_type<TC: TypeContext>(builder: &mut TC) -> TypeId {
builder.add_heap_cell_type()
}
fn box_erasure_value_unknown(
builder: &mut FuncDefBuilder,
block: BlockId,
value: ValueId,
) -> Result<ValueId> {
let heap_cell = erasure_box_value_type(builder);
builder.add_unknown_with(block, &[value], heap_cell)
}
fn box_erasure_value_unknown_nullptr(
builder: &mut FuncDefBuilder,
block: BlockId,
) -> Result<ValueId> {
let heap_cell = erasure_box_value_type(builder);
builder.add_unknown_with(block, &[], heap_cell)
}
/// Erasure value modeled as
///
/// ```text
/// callee = make_tuple(&[])
/// value = unknown(make_tuple(...captures))
///
/// x : Tuple(callee: FnPtr, value: HeapCell)
/// x = make_tuple(callee, value)
/// ```
fn erasure_make(
builder: &mut FuncDefBuilder,
block: BlockId,
value: ValueId,
callee: ValueId,
) -> Result<ValueId> {
builder.add_make_tuple(block, &[value, callee])
}
fn erasure_load(
builder: &mut FuncDefBuilder,
block: BlockId,
value: ValueId,
field: ErasedField,
loaded_type: TypeId,
) -> Result<ValueId> {
match field {
ErasedField::Callee => builder.add_get_tuple_field(block, value, ERASURE_CALEE_INDEX),
ErasedField::Value | ErasedField::ValuePtr => {
let unknown_heap_cell_value =
builder.add_get_tuple_field(block, value, ERASURE_VALUE_INDEX)?;
// Cast the unknown cell to the wanted type
builder.add_unknown_with(block, &[unknown_heap_cell_value], loaded_type)
}
}
}

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::externs::add_default_roc_externs;
use roc_load::{
EntryPoint, ExecutionMode, ExpectMetadata, LoadConfig, LoadMonomorphizedError, LoadedModule,
LoadingProblem, MonomorphizedModule, Threading,
EntryPoint, ExecutionMode, ExpectMetadata, FunctionKind, LoadConfig, LoadMonomorphizedError,
LoadedModule, LoadingProblem, MonomorphizedModule, Threading,
};
use roc_mono::ir::{OptLevel, SingleEntryPoint};
use roc_packaging::cache::RocCacheDir;
@ -740,8 +740,20 @@ pub fn standard_load_config(
BuildOrdering::AlwaysBuild => ExecutionMode::Executable,
};
// UNSTABLE(lambda-erasure)
let function_kind = if cfg!(debug_assertions) {
if std::env::var("EXPERIMENTAL_ROC_ERASE").is_ok() {
FunctionKind::Erased
} else {
FunctionKind::LambdaSet
}
} else {
FunctionKind::LambdaSet
};
LoadConfig {
target_info,
function_kind,
render: RenderTarget::ColorTerminal,
palette: DEFAULT_PALETTE,
threading,
@ -1205,6 +1217,8 @@ pub fn check_file<'a>(
let load_config = LoadConfig {
target_info,
// TODO: we may not want this for just checking.
function_kind: FunctionKind::LambdaSet,
// TODO: expose this from CLI?
render: RenderTarget::ColorTerminal,
palette: DEFAULT_PALETTE,

View File

@ -64,26 +64,6 @@ pub type PExpectedTypeIndex = Index<PExpected<TypeOrVar>>;
pub type TypeOrVar = EitherIndex<TypeTag, Variable>;
impl Constraints {
pub fn empty() -> Self {
Self {
constraints: Default::default(),
type_slices: Default::default(),
variables: Default::default(),
loc_symbols: Default::default(),
let_constraints: Default::default(),
categories: Default::default(),
pattern_categories: Default::default(),
expectations: Default::default(),
pattern_expectations: Default::default(),
includes_tags: Default::default(),
strings: Default::default(),
sketched_rows: Default::default(),
eq: Default::default(),
pattern_eq: Default::default(),
cycles: Default::default(),
}
}
pub fn new() -> Self {
let constraints = Vec::new();
let type_slices = Vec::with_capacity(16);

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

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

View File

@ -9,6 +9,7 @@ use crate::llvm::refcounting::{
build_reset, decrement_refcount_layout, increment_refcount_layout, PointerToRefcount,
};
use crate::llvm::struct_::{struct_from_fields, RocStruct};
use crate::llvm::{erased, fn_ptr};
use bumpalo::collections::Vec;
use bumpalo::Bump;
use inkwell::attributes::{Attribute, AttributeLoc};
@ -23,11 +24,11 @@ use inkwell::passes::{PassManager, PassManagerBuilder};
use inkwell::types::{
AnyType, BasicMetadataTypeEnum, BasicType, BasicTypeEnum, FunctionType, IntType, StructType,
};
use inkwell::values::BasicValueEnum;
use inkwell::values::{
BasicMetadataValueEnum, CallSiteValue, FunctionValue, InstructionValue, IntValue, PointerValue,
StructValue,
};
use inkwell::values::{BasicValueEnum, CallableValue};
use inkwell::OptimizationLevel;
use inkwell::{AddressSpace, IntPredicate};
use morphic_lib::{
@ -38,6 +39,7 @@ use roc_collections::all::{MutMap, MutSet};
use roc_debug_flags::dbg_do;
#[cfg(debug_assertions)]
use roc_debug_flags::ROC_PRINT_LLVM_FN_VERIFICATION;
use roc_error_macros::{internal_error, todo_lambda_erasure};
use roc_module::symbol::{Interns, Symbol};
use roc_mono::ir::{
BranchInfo, CallType, CrashTag, EntryPoint, GlueLayouts, HostExposedLambdaSet,
@ -604,8 +606,7 @@ fn promote_to_main_function<'a, 'ctx>(
);
// NOTE fake layout; it is only used for debug prints
let roc_main_fn =
function_value_by_func_spec(env, *func_spec, symbol, &[], Niche::NONE, Layout::UNIT);
let roc_main_fn = function_value_by_func_spec(env, FuncBorrowSpec::Some(*func_spec), symbol);
let main_fn_name = "$Test.main";
@ -654,8 +655,7 @@ fn promote_to_wasm_test_wrapper<'a, 'ctx>(
);
// NOTE fake layout; it is only used for debug prints
let roc_main_fn =
function_value_by_func_spec(env, *func_spec, symbol, &[], Niche::NONE, Layout::UNIT);
let roc_main_fn = function_value_by_func_spec(env, FuncBorrowSpec::Some(*func_spec), symbol);
let output_type = match roc_main_fn.get_type().get_return_type() {
Some(return_type) => {
@ -690,7 +690,7 @@ fn promote_to_wasm_test_wrapper<'a, 'ctx>(
let entry = context.append_basic_block(c_function, "entry");
builder.position_at_end(entry);
let roc_main_fn_result = call_roc_function(
let roc_main_fn_result = call_direct_roc_function(
env,
layout_interner,
roc_main_fn,
@ -912,7 +912,6 @@ pub(crate) fn build_exp_call<'a, 'ctx>(
CallType::ByName {
name,
specialization_id,
arg_layouts,
ret_layout,
..
} => {
@ -927,17 +926,39 @@ pub(crate) fn build_exp_call<'a, 'ctx>(
let callee_var = CalleeSpecVar(&bytes);
let func_spec = func_spec_solutions.callee_spec(callee_var).unwrap();
roc_call_with_args(
roc_call_direct_with_args(
env,
layout_interner,
arg_layouts,
*ret_layout,
*name,
func_spec,
FuncBorrowSpec::Some(func_spec),
arg_tuples.into_bump_slice(),
)
}
CallType::ByPointer {
pointer,
arg_layouts,
ret_layout,
} => {
let mut args: Vec<BasicValueEnum> = Vec::with_capacity_in(arguments.len(), env.arena);
for symbol in arguments.iter() {
args.push(scope.load_symbol(symbol));
}
let pointer = scope.load_symbol(pointer).into_pointer_value();
roc_call_erased_with_args(
env,
layout_interner,
pointer,
arg_layouts,
*ret_layout,
args.into_bump_slice(),
)
}
CallType::LowLevel { op, update_mode } => {
let bytes = update_mode.to_bytes();
let update_var = UpdateModeVar(&bytes);
@ -1085,6 +1106,24 @@ pub(crate) fn build_exp_expr<'a, 'ctx>(
)
}
FunctionPointer { lambda_name } => {
let alloca = fn_ptr::build(env, *lambda_name);
alloca.into()
}
ErasedMake { value, callee } => {
let value = value.map(|sym| scope.load_symbol(&sym).into_pointer_value());
let callee = scope.load_symbol(callee).into_pointer_value();
erased::build(env, value, callee).into()
}
ErasedLoad { symbol, field } => {
let value = scope.load_symbol(symbol).into_struct_value();
let wanted_llvm_type =
basic_type_from_layout(env, layout_interner, layout_interner.get_repr(layout))
.into_pointer_type();
erased::load(env, value, *field, wanted_llvm_type).into()
}
Reset {
symbol,
update_mode,
@ -3849,7 +3888,7 @@ fn expose_function_to_host_help_c_abi_generic<'a, 'ctx>(
builder.position_at_end(entry);
let wrapped_layout = roc_call_result_layout(env.arena, return_layout);
call_roc_function(
call_direct_roc_function(
env,
layout_interner,
roc_function,
@ -3857,7 +3896,7 @@ fn expose_function_to_host_help_c_abi_generic<'a, 'ctx>(
arguments_for_call,
)
} else {
call_roc_function(
call_direct_roc_function(
env,
layout_interner,
roc_function,
@ -3995,7 +4034,7 @@ fn expose_function_to_host_help_c_abi_gen_test<'a, 'ctx>(
let wrapper_result = roc_call_result_layout(env.arena, return_layout);
let roc_value = call_roc_function(
let roc_value = call_direct_roc_function(
env,
layout_interner,
roc_wrapper_function,
@ -4248,7 +4287,7 @@ fn expose_function_to_host_help_c_abi_v2<'a, 'ctx>(
let arguments = Vec::from_iter_in(it, env.arena);
let value = call_roc_function(
let value = call_direct_roc_function(
env,
layout_interner,
roc_function,
@ -4558,7 +4597,7 @@ fn set_jump_and_catch_long_jump<'a, 'ctx>(
{
builder.position_at_end(then_block);
let call_result = call_roc_function(
let call_result = call_direct_roc_function(
env,
layout_interner,
roc_function,
@ -4826,15 +4865,19 @@ pub(crate) fn build_proc_headers<'a, 'r, 'ctx>(
let it = func_solutions.specs();
let mut function_values = std::vec::Vec::with_capacity(it.size_hint().0);
let is_erased = proc.is_erased;
debug_assert!(!is_erased || func_solutions.specs().count() == 1);
for specialization in it {
let fn_val = build_proc_header(
env,
layout_interner,
*specialization,
symbol,
&proc,
layout_ids,
);
let func_spec = if is_erased {
FuncBorrowSpec::Erased
} else {
FuncBorrowSpec::Some(*specialization)
};
let fn_val =
build_proc_header(env, layout_interner, func_spec, symbol, &proc, layout_ids);
if proc.args.is_empty() {
// this is a 0-argument thunk, i.e. a top-level constant definition
@ -4889,8 +4932,7 @@ pub fn build_procedures<'a>(
);
// NOTE fake layout; it is only used for debug prints
let getter_fn =
function_value_by_func_spec(env, *func_spec, symbol, &[], niche, Layout::UNIT);
let getter_fn = function_value_by_func_spec(env, FuncBorrowSpec::Some(*func_spec), symbol);
let name = getter_fn.get_name().to_str().unwrap();
let getter_name = symbol.as_str(&env.interns);
@ -5006,7 +5048,7 @@ pub fn build_procedures_expose_expects<'a>(
// NOTE fake layout; it is only used for debug prints
let roc_main_fn =
function_value_by_func_spec(env, *func_spec, symbol, &[], captures_niche, Layout::UNIT);
function_value_by_func_spec(env, FuncBorrowSpec::Some(*func_spec), symbol);
let name = roc_main_fn.get_name().to_str().unwrap();
@ -5133,11 +5175,19 @@ fn build_procedures_help<'a>(
mod_solutions
}
pub enum FuncBorrowSpec {
/// This function has an specialization due to alias analysis.
Some(FuncSpec),
/// This function does not have a specialization due to alias analysis,
/// because it is type-erased, and thus has no statically determined AA specialization.
Erased,
}
fn func_spec_name<'a>(
arena: &'a Bump,
interns: &Interns,
symbol: Symbol,
func_spec: FuncSpec,
func_spec: FuncBorrowSpec,
) -> bumpalo::collections::String<'a> {
use std::fmt::Write;
@ -5147,8 +5197,13 @@ fn func_spec_name<'a>(
let module_string = interns.module_ids.get_name(symbol.module_id()).unwrap();
write!(buf, "{module_string}_{ident_string}_").unwrap();
for byte in func_spec.0.iter() {
write!(buf, "{byte:x?}").unwrap();
match func_spec {
FuncBorrowSpec::Some(func_spec) => {
for byte in func_spec.0.iter() {
write!(buf, "{byte:x?}").unwrap();
}
}
FuncBorrowSpec::Erased => write!(buf, "erased").unwrap(),
}
buf
@ -5157,7 +5212,7 @@ fn func_spec_name<'a>(
fn build_proc_header<'a, 'ctx>(
env: &Env<'a, 'ctx, '_>,
layout_interner: &STLayoutInterner<'a>,
func_spec: FuncSpec,
func_spec: FuncBorrowSpec,
symbol: Symbol,
proc: &roc_mono::ir::Proc<'a>,
layout_ids: &mut LayoutIds<'a>,
@ -5260,14 +5315,7 @@ fn expose_alias_to_host<'a>(
"we expect only one specialization of this symbol"
);
function_value_by_func_spec(
env,
*func_spec,
hels.symbol,
hels.proc_layout.arguments,
Niche::NONE,
hels.proc_layout.result,
)
function_value_by_func_spec(env, FuncBorrowSpec::Some(*func_spec), hels.symbol)
}
None => {
// morphic did not generate a specialization for this function,
@ -5290,6 +5338,7 @@ fn expose_alias_to_host<'a>(
)
}
RawFunctionLayout::ErasedFunction(..) => todo_lambda_erasure!(),
RawFunctionLayout::ZeroArgumentThunk(result) => {
// Define only the return value size, since this is a thunk
//
@ -5400,7 +5449,7 @@ fn build_closure_caller<'a, 'ctx>(
builder.build_store(output, call_result);
} else {
let call_result = call_roc_function(
let call_result = call_direct_roc_function(
env,
layout_interner,
evaluator,
@ -5571,73 +5620,43 @@ pub fn verify_fn(fn_val: FunctionValue<'_>) {
}
}
pub(crate) fn function_value_by_func_spec<'a, 'ctx>(
env: &Env<'a, 'ctx, '_>,
func_spec: FuncSpec,
pub(crate) fn function_value_by_func_spec<'ctx>(
env: &Env<'_, 'ctx, '_>,
func_spec: FuncBorrowSpec,
symbol: Symbol,
arguments: &[InLayout<'a>],
niche: Niche<'a>,
result: InLayout<'a>,
) -> FunctionValue<'ctx> {
let fn_name = func_spec_name(env.arena, &env.interns, symbol, func_spec);
let fn_name = fn_name.as_str();
function_value_by_name_help(env, arguments, niche, result, symbol, fn_name)
function_value_by_name_help(env, symbol, fn_name)
}
fn function_value_by_name_help<'a, 'ctx>(
env: &Env<'a, 'ctx, '_>,
arguments: &[InLayout<'a>],
_niche: Niche<'a>,
result: InLayout<'a>,
fn function_value_by_name_help<'ctx>(
env: &Env<'_, 'ctx, '_>,
symbol: Symbol,
fn_name: &str,
) -> FunctionValue<'ctx> {
env.module.get_function(fn_name).unwrap_or_else(|| {
if symbol.is_builtin() {
eprintln!(
"Unrecognized builtin function: {:?}\nLayout: {:?}\n",
fn_name,
(arguments, result)
);
eprintln!("Is the function defined? If so, maybe there is a problem with the layout");
panic!("Unrecognized builtin function: {fn_name:?} (symbol: {symbol:?})",)
panic!("Unrecognized builtin function: {fn_name:?} (symbol: {symbol:?})")
} else {
// Unrecognized non-builtin function:
eprintln!(
"Unrecognized non-builtin function: {:?}\n\nSymbol: {:?}\nLayout: {:?}\n",
fn_name,
symbol,
(arguments, result)
);
eprintln!("Is the function defined? If so, maybe there is a problem with the layout");
panic!("Unrecognized non-builtin function: {fn_name:?} (symbol: {symbol:?})",)
panic!("Unrecognized non-builtin function: {fn_name:?} (symbol: {symbol:?})")
}
})
}
#[inline(always)]
fn roc_call_with_args<'a, 'ctx>(
fn roc_call_direct_with_args<'a, 'ctx>(
env: &Env<'a, 'ctx, '_>,
layout_interner: &STLayoutInterner<'a>,
argument_layouts: &[InLayout<'a>],
result_layout: InLayout<'a>,
name: LambdaName<'a>,
func_spec: FuncSpec,
func_spec: FuncBorrowSpec,
arguments: &[BasicValueEnum<'ctx>],
) -> BasicValueEnum<'ctx> {
let fn_val = function_value_by_func_spec(
env,
func_spec,
name.name(),
argument_layouts,
name.niche(),
result_layout,
);
let fn_val = function_value_by_func_spec(env, func_spec, name.name());
call_roc_function(
call_direct_roc_function(
env,
layout_interner,
fn_val,
@ -5646,14 +5665,70 @@ fn roc_call_with_args<'a, 'ctx>(
)
}
pub fn call_roc_function<'a, 'ctx>(
#[inline(always)]
fn roc_call_erased_with_args<'a, 'ctx>(
env: &Env<'a, 'ctx, '_>,
layout_interner: &STLayoutInterner<'a>,
pointer: PointerValue<'ctx>,
argument_layouts: &[InLayout<'a>],
result_layout: InLayout<'a>,
arguments: &[BasicValueEnum<'ctx>],
) -> BasicValueEnum<'ctx> {
let function_type =
fn_ptr::function_type(env, layout_interner, argument_layouts, result_layout);
let function_ptr_type = function_type.ptr_type(AddressSpace::default());
let function_pointer = fn_ptr::cast_to_function_ptr_type(env, pointer, function_ptr_type);
let callable_function_pointer = CallableValue::try_from(function_pointer).unwrap();
let build_call = |arguments: &[BasicMetadataValueEnum<'ctx>]| {
env.builder
.build_call(callable_function_pointer, arguments, "call")
};
call_roc_function_help(
env,
layout_interner,
build_call,
function_type,
layout_interner.get_repr(result_layout),
arguments,
)
}
pub(crate) fn call_direct_roc_function<'a, 'ctx>(
env: &Env<'a, 'ctx, '_>,
layout_interner: &STLayoutInterner<'a>,
roc_function: FunctionValue<'ctx>,
result_layout: LayoutRepr<'a>,
arguments: &[BasicValueEnum<'ctx>],
) -> BasicValueEnum<'ctx> {
let pass_by_pointer = roc_function.get_type().get_param_types().len() == arguments.len() + 1;
let function_type = roc_function.get_type();
let build_call = |arguments: &[BasicMetadataValueEnum<'ctx>]| {
env.builder.build_call(roc_function, arguments, "call")
};
debug_assert_eq!(roc_function.get_call_conventions(), FAST_CALL_CONV);
call_roc_function_help(
env,
layout_interner,
build_call,
function_type,
result_layout,
arguments,
)
}
fn call_roc_function_help<'a, 'ctx>(
env: &Env<'a, 'ctx, '_>,
layout_interner: &STLayoutInterner<'a>,
build_call: impl FnOnce(&[BasicMetadataValueEnum<'ctx>]) -> CallSiteValue<'ctx>,
roc_function_type: FunctionType<'ctx>,
result_layout: LayoutRepr<'a>,
arguments: &[BasicValueEnum<'ctx>],
) -> BasicValueEnum<'ctx> {
let pass_by_pointer = roc_function_type.get_param_types().len() == arguments.len() + 1;
match RocReturn::from_layout(layout_interner, result_layout) {
RocReturn::ByPointer if !pass_by_pointer => {
@ -5667,14 +5742,10 @@ pub fn call_roc_function<'a, 'ctx>(
arguments.push(result_alloca.into());
debug_assert_eq!(
roc_function.get_type().get_param_types().len(),
arguments.len()
);
let call = env.builder.build_call(roc_function, &arguments, "call");
debug_assert_eq!(roc_function_type.get_param_types().len(), arguments.len());
let call = build_call(&arguments);
// roc functions should have the fast calling convention
debug_assert_eq!(roc_function.get_call_conventions(), FAST_CALL_CONV);
call.set_call_convention(FAST_CALL_CONV);
env.builder
@ -5689,14 +5760,10 @@ pub fn call_roc_function<'a, 'ctx>(
arguments.push(result_alloca.into());
debug_assert_eq!(
roc_function.get_type().get_param_types().len(),
arguments.len()
);
let call = env.builder.build_call(roc_function, &arguments, "call");
debug_assert_eq!(roc_function_type.get_param_types().len(), arguments.len());
let call = build_call(&arguments);
// roc functions should have the fast calling convention
debug_assert_eq!(roc_function.get_call_conventions(), FAST_CALL_CONV);
call.set_call_convention(FAST_CALL_CONV);
if result_layout.is_passed_by_reference(layout_interner) {
@ -5710,25 +5777,18 @@ pub fn call_roc_function<'a, 'ctx>(
}
}
RocReturn::Return => {
debug_assert_eq!(
roc_function.get_type().get_param_types().len(),
arguments.len()
);
debug_assert_eq!(roc_function_type.get_param_types().len(), arguments.len());
let it = arguments.iter().map(|x| (*x).into());
let arguments = Vec::from_iter_in(it, env.arena);
let call = env.builder.build_call(roc_function, &arguments, "call");
let call = build_call(&arguments);
// roc functions should have the fast calling convention
debug_assert_eq!(roc_function.get_call_conventions(), FAST_CALL_CONV);
call.set_call_convention(FAST_CALL_CONV);
call.try_as_basic_value().left().unwrap_or_else(|| {
panic!(
"LLVM error: Invalid call by name for name {:?}",
roc_function.get_name()
)
})
call.try_as_basic_value()
.left()
.unwrap_or_else(|| internal_error!("LLVM error: Invalid call by name",))
}
}
}
@ -5905,7 +5965,7 @@ pub enum CCReturn {
}
#[derive(Debug, Clone, Copy)]
pub struct FunctionSpec<'ctx> {
pub(crate) struct FunctionSpec<'ctx> {
/// The function type
pub typ: FunctionType<'ctx>,
call_conv: u32,
@ -5975,7 +6035,7 @@ impl<'ctx> FunctionSpec<'ctx> {
}
/// Fastcc calling convention
fn fastcc<'a, 'env>(
pub fn fastcc<'a, 'env>(
env: &Env<'a, 'ctx, 'env>,
roc_return: RocReturn,
return_type: BasicTypeEnum<'ctx>,
@ -6223,7 +6283,7 @@ fn build_foreign_symbol<'a, 'ctx>(
}
};
call_roc_function(
call_direct_roc_function(
env,
layout_interner,
fastcc_function,
@ -6366,7 +6426,7 @@ fn get_foreign_symbol<'ctx>(
/// Add a function to a module, after asserting that the function is unique.
/// We never want to define the same function twice in the same module!
/// The result can be bugs that are difficult to track down.
pub fn add_func<'ctx>(
pub(crate) fn add_func<'ctx>(
ctx: &Context,
module: &Module<'ctx>,
name: &str,

View File

@ -168,6 +168,8 @@ fn build_eq<'a, 'ctx>(
),
LayoutRepr::LambdaSet(_) => unreachable!("cannot compare closures"),
LayoutRepr::FunctionPointer(_) => unreachable!("cannot compare function pointers"),
LayoutRepr::Erased(_) => unreachable!("cannot compare erased types"),
LayoutRepr::Union(union_layout) => build_tag_eq(
env,
@ -399,6 +401,8 @@ fn build_neq<'a, 'ctx>(
unreachable!("recursion pointers should never be compared directly")
}
LayoutRepr::LambdaSet(_) => unreachable!("cannot compare closure"),
LayoutRepr::FunctionPointer(_) => unreachable!("cannot compare function pointers"),
LayoutRepr::Erased(_) => unreachable!("cannot compare erased types"),
}
}
@ -1364,7 +1368,7 @@ fn build_box_eq<'a, 'ctx>(
env.builder.set_current_debug_location(di_location);
let call = env
.builder
.build_call(function, &[tag1.into(), tag2.into()], "tag_eq");
.build_call(function, &[tag1.into(), tag2.into()], "box_eq");
call.set_call_convention(FAST_CALL_CONV);
@ -1429,12 +1433,47 @@ fn build_box_eq_help<'a, 'ctx>(
"compare_pointers",
);
let compare_inner_value = ctx.append_basic_block(parent, "compare_inner_value");
let check_null_then_compare_inner_values =
ctx.append_basic_block(parent, "check_null_then_compare_inner_values");
env.builder.build_conditional_branch(
ptr_equal,
return_true,
check_null_then_compare_inner_values,
);
env.builder
.build_conditional_branch(ptr_equal, return_true, compare_inner_value);
.position_at_end(check_null_then_compare_inner_values);
env.builder.position_at_end(compare_inner_value);
// Check for nullability, then compare inner values
let box1_is_null = env
.builder
.build_is_null(box1.into_pointer_value(), "box1_is_null");
let check_box2_is_null = ctx.append_basic_block(parent, "check_if_box2_is_null");
let return_false = ctx.append_basic_block(parent, "return_false");
let compare_inner_values = ctx.append_basic_block(parent, "compare_inner_values");
env.builder
.build_conditional_branch(box1_is_null, return_false, check_box2_is_null);
{
env.builder.position_at_end(check_box2_is_null);
let box2_is_null = env
.builder
.build_is_null(box2.into_pointer_value(), "box2_is_null");
env.builder
.build_conditional_branch(box2_is_null, return_false, compare_inner_values);
}
{
env.builder.position_at_end(return_false);
env.builder
.build_return(Some(&env.context.bool_type().const_zero()));
}
// Compare the inner values.
env.builder.position_at_end(compare_inner_values);
// clear the tag_id so we get a pointer to the actual data
let box1 = box1.into_pointer_value();

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 bumpalo::collections::Vec as AVec;
use bumpalo::collections::{CollectIn, Vec as AVec};
use inkwell::context::Context;
use inkwell::types::{BasicType, BasicTypeEnum, FloatType, IntType, StructType};
use inkwell::values::PointerValue;
use inkwell::AddressSpace;
use roc_builtins::bitcode::{FloatWidth, IntWidth};
use roc_mono::layout::{
round_up_to_alignment, Builtin, InLayout, Layout, LayoutInterner, LayoutRepr, STLayoutInterner,
UnionLayout,
round_up_to_alignment, Builtin, FunctionPointer, InLayout, Layout, LayoutInterner, LayoutRepr,
STLayoutInterner, UnionLayout,
};
use roc_target::TargetInfo;
@ -48,6 +49,22 @@ pub fn basic_type_from_layout<'a, 'ctx>(
.ptr_type(AddressSpace::default())
.as_basic_type_enum(),
FunctionPointer(self::FunctionPointer { args, ret }) => {
let args = args.iter().map(|arg| {
basic_type_from_layout(env, layout_interner, layout_interner.get_repr(*arg))
});
let ret_repr = layout_interner.get_repr(ret);
let ret = basic_type_from_layout(env, layout_interner, ret_repr);
let roc_return = RocReturn::from_layout(layout_interner, ret_repr);
let fn_spec = FunctionSpec::fastcc(env, roc_return, ret, args.collect_in(env.arena));
fn_spec.typ.ptr_type(AddressSpace::default()).into()
}
Erased(_) => erased::basic_type(env).into(),
Builtin(builtin) => basic_type_from_builtin(env, &builtin),
}
}

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::AddressSpace;
use roc_builtins::bitcode;
use roc_error_macros::internal_error;
use roc_error_macros::{internal_error, todo_lambda_erasure};
use roc_module::symbol::Symbol;
use roc_mono::ir::LookupType;
use roc_mono::layout::{
@ -387,6 +387,8 @@ fn build_clone<'a, 'ctx>(
union_layout,
)
}
LayoutRepr::FunctionPointer(_) => todo_lambda_erasure!(),
LayoutRepr::Erased(_) => todo_lambda_erasure!(),
}
}

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

View File

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

View File

@ -14,16 +14,20 @@ use bumpalo::collections::Vec;
use inkwell::basic_block::BasicBlock;
use inkwell::module::Linkage;
use inkwell::types::{AnyTypeEnum, BasicMetadataTypeEnum, BasicType, BasicTypeEnum};
use inkwell::values::{BasicValueEnum, FunctionValue, InstructionValue, IntValue, PointerValue};
use inkwell::values::{
BasicValueEnum, CallableValue, FunctionValue, InstructionValue, IntValue, PointerValue,
};
use inkwell::{AddressSpace, IntPredicate};
use roc_module::symbol::Interns;
use roc_module::symbol::Symbol;
use roc_mono::ir::ErasedField;
use roc_mono::layout::{
Builtin, InLayout, LayoutIds, LayoutInterner, LayoutRepr, STLayoutInterner, UnionLayout,
Builtin, InLayout, Layout, LayoutIds, LayoutInterner, LayoutRepr, STLayoutInterner, UnionLayout,
};
use super::build::{cast_if_necessary_for_opaque_recursive_pointers, load_roc_value, FunctionSpec};
use super::convert::{argument_type_from_layout, argument_type_from_union_layout};
use super::erased;
pub struct PointerToRefcount<'ctx> {
value: PointerValue<'ctx>,
@ -388,6 +392,94 @@ fn modify_refcount_struct_help<'a, 'ctx>(
builder.build_return(None);
}
fn modify_refcount_erased<'a, 'ctx>(
env: &Env<'a, 'ctx, '_>,
layout_interner: &STLayoutInterner<'a>,
layout_ids: &mut LayoutIds<'a>,
mode: Mode,
) -> FunctionValue<'ctx> {
let block = env.builder.get_insert_block().expect("to be in a function");
let di_location = env.builder.get_current_debug_location().unwrap();
let (_, fn_name) = function_name_from_mode(
layout_ids,
&env.interns,
"increment_erased",
"decrement_erased",
layout_interner.get_repr(Layout::ERASED),
mode,
);
let function = match env.module.get_function(fn_name.as_str()) {
Some(function_value) => function_value,
None => {
let arg_type = erased::basic_type(env);
let function_value = build_header(env, arg_type.into(), mode, &fn_name);
modify_refcount_erased_help(env, mode, function_value);
function_value
}
};
env.builder.position_at_end(block);
env.builder.set_current_debug_location(di_location);
function
}
fn modify_refcount_erased_help<'ctx>(
env: &Env<'_, 'ctx, '_>,
mode: Mode,
fn_val: FunctionValue<'ctx>,
) {
let builder = env.builder;
let ctx = env.context;
// Add a basic block for the entry point
let entry = ctx.append_basic_block(fn_val, "entry");
builder.position_at_end(entry);
debug_info_init!(env, fn_val);
// Add args to scope
let arg_symbol = Symbol::ARG_1;
let arg_val = fn_val.get_param_iter().next().unwrap().into_struct_value();
arg_val.set_name(arg_symbol.as_str(&env.interns));
let refcounter = erased::load_refcounter(env, arg_val, mode);
let refcounter_is_null = env.builder.build_is_null(refcounter, "refcounter_unset");
let call_refcounter_block = ctx.append_basic_block(fn_val, "call_refcounter");
let noop_block = ctx.append_basic_block(fn_val, "noop");
builder.build_conditional_branch(refcounter_is_null, noop_block, call_refcounter_block);
{
builder.position_at_end(call_refcounter_block);
let value = erased::load(
env,
arg_val,
ErasedField::Value,
erased::opaque_ptr_type(env),
);
builder.build_call(
CallableValue::try_from(refcounter).unwrap(),
&[value.into()],
"call_refcounter",
);
builder.build_return(None);
}
{
builder.position_at_end(noop_block);
builder.build_return(None);
}
}
pub fn increment_refcount_layout<'a, 'ctx>(
env: &Env<'a, 'ctx, '_>,
layout_interner: &STLayoutInterner<'a>,
@ -624,6 +716,12 @@ fn modify_refcount_layout_build_function<'a, 'ctx>(
mode,
lambda_set.runtime_representation(),
),
FunctionPointer(_) => None,
Erased(_) => {
let function = modify_refcount_erased(env, layout_interner, layout_ids, mode);
Some(function)
}
}
}

View File

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

View File

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

View File

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

View File

@ -5,7 +5,6 @@ use std::sync::{Arc, RwLock};
use bumpalo::Bump;
use roc_can::abilities::AbilitiesStore;
use roc_can::constraint::Constraints;
use roc_can::module::ExposedByModule;
use roc_collections::MutMap;
use roc_derive::SharedDerivedModule;
@ -15,7 +14,7 @@ use roc_module::symbol::Symbol;
use roc_solve::ability::AbilityResolver;
use roc_solve::specialize::{compact_lambda_sets_of_vars, Phase};
use roc_solve::Pools;
use roc_solve::{DerivedEnv, Env};
use roc_solve::{DerivedEnv, SolveEnv};
use roc_types::subs::{get_member_lambda_sets_at_region, Content, FlatType, LambdaSet};
use roc_types::subs::{ExposedTypesStorageSubs, Subs, Variable};
use roc_types::types::Polarity;
@ -342,19 +341,6 @@ impl MetaCollector for ChangedVariableCollector {
}
}
std::thread_local! {
static SCRATCHPAD_FOR_OCCURS: std::cell::RefCell<Option<Constraints>> = std::cell::RefCell::new(Some(Constraints::empty()));
}
fn with_empty_solve_constraints<T>(f: impl FnOnce(&Constraints) -> T) -> T {
SCRATCHPAD_FOR_OCCURS.with(|cell| {
let constr = cell.take().unwrap();
let result = f(&constr);
cell.replace(Some(constr));
result
})
}
/// Unifies two variables and performs lambda set compaction.
/// Ranks and other ability demands are disregarded.
#[allow(clippy::too_many_arguments)]
@ -396,9 +382,8 @@ pub fn unify(
exposed_types: exposed_by_module,
};
let must_implement_constraints = with_empty_solve_constraints(|c| {
let mut env = Env {
constraints: c,
let must_implement_constraints = {
let mut env = SolveEnv {
subs,
derived_env: &derived_env,
arena,
@ -406,7 +391,8 @@ pub fn unify(
};
compact_lambda_sets_of_vars(&mut env, lambda_sets_to_specialize, &late_phase)
});
};
// At this point we can't do anything with must-implement constraints, since we're no
// longer solving. We must assume that they were totally caught during solving.
// After we land https://github.com/roc-lang/roc/issues/3207 this concern should totally

View File

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

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

View File

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

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

View File

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

View File

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

View File

@ -9,6 +9,7 @@ use bumpalo::collections::Vec;
use bumpalo::Bump;
use roc_collections::all::{MutMap, MutSet};
use roc_collections::ReferenceMatrix;
use roc_error_macros::todo_lambda_erasure;
use roc_module::low_level::LowLevel;
use roc_module::symbol::Symbol;
@ -560,6 +561,10 @@ impl<'a> BorrowInfState<'a> {
self.own_args_using_params(arguments, ps);
}
ByPointer { .. } => {
todo_lambda_erasure!()
}
LowLevel { op, .. } => {
debug_assert!(!op.is_higher_order());
@ -703,6 +708,27 @@ impl<'a> BorrowInfState<'a> {
self.own_args_if_param(xs);
}
ErasedMake { value, callee } => {
value.map(|v| {
// if the value is owned, the erasure is
self.if_is_owned_then_own(v, z);
// if the erasure is owned, the value is
self.if_is_owned_then_own(z, v);
});
// the erasure owns the callee (which should always be a stack value)
self.own_var(*callee);
}
ErasedLoad { symbol, field: _ } => {
// if the extracted value is owned, the erasure is too
self.if_is_owned_then_own(*symbol, z);
// if the erasure is owned, so is the extracted value
self.if_is_owned_then_own(z, *symbol);
}
Reset { symbol: x, .. } | ResetRef { symbol: x, .. } => {
self.own_var(z);
self.own_var(*x);
@ -714,7 +740,7 @@ impl<'a> BorrowInfState<'a> {
Call(call) => self.collect_call(interner, param_map, z, call),
Literal(_) | NullPointer | RuntimeErrorFunction(_) => {}
Literal(_) | NullPointer | RuntimeErrorFunction(_) | FunctionPointer { .. } => {}
StructAtIndex { structure: x, .. } => {
// if the structure (record/tag/array) is owned, the extracted value is
@ -1036,9 +1062,10 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[Ownership] {
PtrStore => arena.alloc_slice_copy(&[owned, owned]),
PtrLoad => arena.alloc_slice_copy(&[owned]),
PtrCast => arena.alloc_slice_copy(&[owned]),
Alloca => arena.alloc_slice_copy(&[owned]),
PtrClearTagId | PtrCast | RefCountIncRcPtr | RefCountDecRcPtr | RefCountIncDataPtr
PtrClearTagId | RefCountIncRcPtr | RefCountDecRcPtr | RefCountIncDataPtr
| RefCountDecDataPtr | RefCountIsUnique => {
unreachable!("Only inserted *after* borrow checking: {:?}", op);
}
@ -1056,6 +1083,9 @@ fn call_info_call<'a>(call: &crate::ir::Call<'a>, info: &mut CallInfo<'a>) {
ByName { name, .. } => {
info.keys.push(name.name());
}
ByPointer { .. } => {
todo_lambda_erasure!()
}
Foreign { .. } => {}
LowLevel { .. } => {}
HigherOrder(HigherOrderLowLevel {

View File

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

View File

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

View File

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

View File

@ -6,12 +6,12 @@ use roc_module::symbol::Symbol;
use crate::{
ir::{
Call, CallSpecId, CallType, Expr, HigherOrderLowLevel, JoinPointId, ListLiteralElement,
ModifyRc, Param, Proc, ProcLayout, Stmt,
Call, CallSpecId, CallType, ErasedField, Expr, HigherOrderLowLevel, JoinPointId,
ListLiteralElement, ModifyRc, Param, Proc, ProcLayout, Stmt,
},
layout::{
Builtin, InLayout, Layout, LayoutInterner, LayoutRepr, STLayoutInterner, TagIdIntType,
UnionLayout,
Builtin, FunctionPointer, InLayout, Layout, LayoutInterner, LayoutRepr, STLayoutInterner,
TagIdIntType, UnionLayout,
},
};
@ -27,6 +27,9 @@ pub enum UseKind {
SwitchCond,
ExpectCond,
ExpectLookup,
ErasedMake(ErasedField),
Erased,
FunctionPointer,
}
pub enum ProblemKind<'a> {
@ -70,6 +73,9 @@ pub enum ProblemKind<'a> {
proc_layout: ProcLayout<'a>,
similar: Vec<ProcLayout<'a>>,
},
PtrToUndefinedProc {
symbol: Symbol,
},
DuplicateCallSpecId {
old_call_line: usize,
},
@ -114,6 +120,24 @@ pub enum ProblemKind<'a> {
num_needed: usize,
num_given: usize,
},
ErasedMakeValueNotBoxed {
symbol: Symbol,
def_layout: InLayout<'a>,
def_line: usize,
},
ErasedMakeCalleeNotFunctionPointer {
symbol: Symbol,
def_layout: InLayout<'a>,
def_line: usize,
},
ErasedLoadValueNotBoxed {
symbol: Symbol,
target_layout: InLayout<'a>,
},
ErasedLoadCalleeNotFunctionPointer {
symbol: Symbol,
target_layout: InLayout<'a>,
},
}
pub struct Problem<'a> {
@ -271,7 +295,7 @@ impl<'a, 'r> Ctx<'a, 'r> {
match body {
Stmt::Let(x, e, x_layout, rest) => {
if let Some(e_layout) = self.check_expr(e) {
if let Some(e_layout) = self.check_expr(e, *x_layout) {
if self.not_equiv(e_layout, *x_layout) {
self.problem(ProblemKind::SymbolDefMismatch {
symbol: *x,
@ -388,7 +412,7 @@ impl<'a, 'r> Ctx<'a, 'r> {
}
}
fn check_expr(&mut self, e: &Expr<'a>) -> Option<InLayout<'a>> {
fn check_expr(&mut self, e: &Expr<'a>, target_layout: InLayout<'a>) -> Option<InLayout<'a>> {
match e {
Expr::Literal(_) => None,
Expr::NullPointer => None,
@ -464,6 +488,40 @@ impl<'a, 'r> Ctx<'a, 'r> {
// TODO don't know what the element layout is
None
}
&Expr::ErasedMake { value, callee } => Some(self.check_erased_make(value, callee)),
&Expr::ErasedLoad { symbol, field } => {
Some(self.check_erased_load(symbol, field, target_layout))
}
&Expr::FunctionPointer { lambda_name } => {
let lambda_symbol = lambda_name.name();
let proc = self.procs.iter().find(|((name, proc), _)| {
*name == lambda_symbol && proc.niche == lambda_name.niche()
});
match proc {
None => {
self.problem(ProblemKind::PtrToUndefinedProc {
symbol: lambda_symbol,
});
Some(target_layout)
}
Some(((_, proc_layout), _)) => {
let ProcLayout {
arguments, result, ..
} = proc_layout;
let fn_ptr =
self.interner
.insert_direct_no_semantic(LayoutRepr::FunctionPointer(
FunctionPointer {
args: arguments,
ret: *result,
},
));
Some(fn_ptr)
}
}
}
&Expr::Reset {
symbol,
update_mode: _,
@ -642,6 +700,23 @@ impl<'a, 'r> Ctx<'a, 'r> {
}
Some(*ret_layout)
}
CallType::ByPointer {
pointer,
ret_layout,
arg_layouts,
} => {
let expected_layout =
self.interner
.insert_direct_no_semantic(LayoutRepr::FunctionPointer(FunctionPointer {
args: arg_layouts,
ret: *ret_layout,
}));
self.check_sym_layout(*pointer, expected_layout, UseKind::FunctionPointer);
for (arg, wanted_layout) in arguments.iter().zip(arg_layouts.iter()) {
self.check_sym_layout(*arg, *wanted_layout, UseKind::CallArg);
}
Some(*ret_layout)
}
CallType::HigherOrder(HigherOrderLowLevel {
op: _,
closure_env_layout: _,
@ -700,6 +775,84 @@ impl<'a, 'r> Ctx<'a, 'r> {
}
}
}
fn check_erased_make(&mut self, value: Option<Symbol>, callee: Symbol) -> InLayout<'a> {
if let Some(value) = value {
self.with_sym_layout(value, |this, def_line, layout| {
let repr = this.interner.get_repr(layout);
if !matches!(
repr,
LayoutRepr::Union(UnionLayout::NullableUnwrapped { .. })
) {
this.problem(ProblemKind::ErasedMakeValueNotBoxed {
symbol: value,
def_layout: layout,
def_line,
});
}
Option::<()>::None
});
}
self.with_sym_layout(callee, |this, def_line, layout| {
let repr = this.interner.get_repr(layout);
if !matches!(repr, LayoutRepr::FunctionPointer(_)) {
this.problem(ProblemKind::ErasedMakeCalleeNotFunctionPointer {
symbol: callee,
def_layout: layout,
def_line,
});
}
Option::<()>::None
});
Layout::ERASED
}
fn check_erased_load(
&mut self,
symbol: Symbol,
field: ErasedField,
target_layout: InLayout<'a>,
) -> InLayout<'a> {
self.check_sym_layout(symbol, Layout::ERASED, UseKind::Erased);
match field {
ErasedField::Value => {
let repr = self.interner.get_repr(target_layout);
if !matches!(
repr,
LayoutRepr::Union(UnionLayout::NullableUnwrapped { .. })
) {
self.problem(ProblemKind::ErasedLoadValueNotBoxed {
symbol,
target_layout,
});
}
}
ErasedField::ValuePtr => {
let repr = self.interner.get_repr(target_layout);
if !matches!(repr, LayoutRepr::Ptr(_)) {
self.problem(ProblemKind::ErasedLoadValueNotBoxed {
symbol,
target_layout,
});
}
}
ErasedField::Callee => {
let repr = self.interner.get_repr(target_layout);
if !matches!(repr, LayoutRepr::FunctionPointer(_)) {
self.problem(ProblemKind::ErasedLoadCalleeNotFunctionPointer {
symbol,
target_layout,
});
}
}
}
target_layout
}
}
enum TagPayloads<'a> {

View File

@ -4,7 +4,7 @@ use roc_module::symbol::{Interns, Symbol};
use ven_pretty::{text, Arena, DocAllocator, DocBuilder};
use crate::{
ir::{Parens, ProcLayout},
ir::{ErasedField, Parens, ProcLayout},
layout::LayoutInterner,
};
@ -265,6 +265,15 @@ where
};
stack(f, [no_spec_doc, similar_doc])
}
ProblemKind::PtrToUndefinedProc { symbol } => {
title = "PROC SPECIALIZATION NOT DEFINED";
docs_before = vec![];
f.concat([
f.reflow("The proc "),
format_symbol(f, interns, symbol),
f.reflow(" is not defined"),
])
}
ProblemKind::DuplicateCallSpecId { old_call_line } => {
title = "DUPLICATE CALL SPEC ID";
docs_before = vec![(old_call_line, f.reflow("This call has a specialization ID"))];
@ -406,6 +415,74 @@ where
f.as_string(num_given),
])
}
ProblemKind::ErasedMakeValueNotBoxed {
symbol,
def_layout,
def_line,
} => {
title = "ERASED VALUE IS NOT BOXED";
docs_before = vec![(
def_line,
f.concat([
f.reflow("The value "),
format_symbol(f, interns, symbol),
f.reflow(" defined here"),
]),
)];
f.concat([
f.reflow("must be boxed in order to be erased, but has layout "),
interner.to_doc_top(def_layout, f),
])
}
ProblemKind::ErasedMakeCalleeNotFunctionPointer {
symbol,
def_layout,
def_line,
} => {
title = "ERASED CALLEE IS NOT A FUNCTION POINTER";
docs_before = vec![(
def_line,
f.concat([
f.reflow("The value "),
format_symbol(f, interns, symbol),
f.reflow(" defined here"),
]),
)];
f.concat([
f.reflow(
"must be a function pointer in order to be an erasure callee, but has layout ",
),
interner.to_doc_top(def_layout, f),
])
}
ProblemKind::ErasedLoadValueNotBoxed {
symbol,
target_layout,
} => {
title = "ERASED VALUE IS NOT BOXED";
docs_before = vec![];
f.concat([
f.reflow("The erased value load "),
format_symbol(f, interns, symbol),
f.reflow(" has layout "),
interner.to_doc_top(target_layout, f),
f.reflow(", but should be boxed!"),
])
}
ProblemKind::ErasedLoadCalleeNotFunctionPointer {
symbol,
target_layout,
} => {
title = "ERASED CALLEE IS NOT A FUNCTION POINTER";
docs_before = vec![];
f.concat([
f.reflow("The erased callee load "),
format_symbol(f, interns, symbol),
f.reflow(" has layout "),
interner.to_doc_top(target_layout, f),
f.reflow(", but should be a function pointer!"),
])
}
};
(title, docs_before, doc)
}
@ -429,6 +506,13 @@ fn format_use_kind(use_kind: UseKind) -> &'static str {
UseKind::SwitchCond => "switch condition",
UseKind::ExpectCond => "expect condition",
UseKind::ExpectLookup => "lookup for an expect",
UseKind::ErasedMake(kind) => match kind {
ErasedField::Value => "erased value field",
ErasedField::ValuePtr => "erased value pointer",
ErasedField::Callee => "erased callee field",
},
UseKind::Erased => "erasure",
UseKind::FunctionPointer => "function pointer",
}
}

View File

@ -17,8 +17,8 @@ use roc_module::low_level::LowLevel;
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
use crate::ir::{
BranchInfo, Call, CallType, Expr, JoinPointId, ListLiteralElement, Literal, ModifyRc, Proc,
ProcLayout, Stmt, UpdateModeId,
BranchInfo, Call, CallType, ErasedField, Expr, JoinPointId, ListLiteralElement, Literal,
ModifyRc, Proc, ProcLayout, Stmt, UpdateModeId,
};
use crate::layout::{
Builtin, InLayout, Layout, LayoutInterner, LayoutRepr, STLayoutInterner, UnionLayout,
@ -228,9 +228,27 @@ fn specialize_drops_stmt<'a, 'i>(
environment.add_list_child(*binding, *child, index as u64);
}
}
Reset { .. } | Expr::ResetRef { .. } => { /* do nothing */ }
RuntimeErrorFunction(_) | GetTagId { .. } | EmptyArray | NullPointer => { /* do nothing */
ErasedMake { value, callee: _ } => {
if let Some(value) = value {
environment.add_struct_child(*binding, *value, 0);
}
}
ErasedLoad { symbol, field } => {
match field {
ErasedField::Value => {
environment.add_struct_child(*symbol, *binding, 0);
}
ErasedField::Callee | ErasedField::ValuePtr => {
// nothing to own
}
}
}
Reset { .. } | Expr::ResetRef { .. } => { /* do nothing */ }
RuntimeErrorFunction(_)
| FunctionPointer { .. }
| GetTagId { .. }
| EmptyArray
| NullPointer => { /* do nothing */ }
}
// now store the let binding for later
@ -1595,9 +1613,10 @@ fn low_level_no_rc(lowlevel: &LowLevel) -> RC {
// only inserted for internal purposes. RC should not touch it
PtrStore => RC::NoRc,
PtrLoad => RC::NoRc,
PtrCast => RC::NoRc,
Alloca => RC::NoRc,
PtrClearTagId | PtrCast | RefCountIncRcPtr | RefCountDecRcPtr | RefCountIncDataPtr
PtrClearTagId | RefCountIncRcPtr | RefCountDecRcPtr | RefCountIncDataPtr
| RefCountDecDataPtr | RefCountIsUnique => {
unreachable!("Only inserted *after* borrow checking: {:?}", lowlevel);
}

View File

@ -14,6 +14,7 @@ use roc_error_macros::internal_error;
use roc_module::low_level::LowLevel;
use roc_module::{low_level::LowLevelWrapperType, symbol::Symbol};
use crate::ir::ErasedField;
use crate::{
borrow::{lowlevel_borrow_signature, Ownership},
ir::{
@ -872,7 +873,11 @@ fn insert_refcount_operations_binding<'a>(
}
match expr {
Expr::Literal(_) | Expr::NullPointer | Expr::EmptyArray | Expr::RuntimeErrorFunction(_) => {
Expr::Literal(_)
| Expr::NullPointer
| Expr::FunctionPointer { .. }
| Expr::EmptyArray
| Expr::RuntimeErrorFunction(_) => {
// Literals, empty arrays, and runtime errors are not (and have nothing) reference counted.
new_let!(stmt)
}
@ -883,6 +888,25 @@ fn insert_refcount_operations_binding<'a>(
inc_owned!(arguments.iter().copied(), new_let)
}
Expr::ErasedMake { value, callee: _ } => {
let new_let = new_let!(stmt);
if let Some(value) = value {
inc_owned!([*value], new_let)
} else {
new_let
}
}
Expr::ErasedLoad { symbol, field } => {
let new_let = new_let!(stmt);
match field {
ErasedField::Value => inc_owned!([*symbol], new_let),
ErasedField::Callee | ErasedField::ValuePtr => new_let,
}
}
Expr::GetTagId { structure, .. }
| Expr::StructAtIndex { structure, .. }
| Expr::UnionAtIndex { structure, .. }
@ -942,6 +966,14 @@ fn insert_refcount_operations_binding<'a>(
inc_owned!(arguments.iter().copied(), new_let)
}
// A normal Roc function call, but we don't actually know where its target is.
// As such, we assume that it takes all parameters as owned, as will the function
// itself.
CallType::ByPointer { .. } => {
let new_let = new_let!(stmt);
inc_owned!(arguments.iter().copied(), new_let)
}
CallType::Foreign { .. } => {
// Foreign functions should be responsible for their own memory management.
// But previously they were assumed to be called with borrowed parameters, so we do the same now.

View File

@ -1,11 +1,13 @@
#![allow(clippy::manual_map)]
use crate::borrow::Ownership;
use crate::ir::erased::{build_erased_function, ResolvedErasedLambda};
use crate::ir::literal::{make_num_literal, IntOrFloatValue};
use crate::layout::{
self, Builtin, ClosureCallOptions, ClosureRepresentation, EnumDispatch, InLayout, LambdaName,
LambdaSet, Layout, LayoutCache, LayoutInterner, LayoutProblem, LayoutRepr, Niche,
RawFunctionLayout, TLLayoutInterner, TagIdIntType, UnionLayout, WrappedVariant,
self, Builtin, ClosureCallOptions, ClosureDataKind, ClosureRepresentation, EnumDispatch,
InLayout, LambdaName, LambdaSet, Layout, LayoutCache, LayoutInterner, LayoutProblem,
LayoutRepr, Niche, RawFunctionLayout, TLLayoutInterner, TagIdIntType, UnionLayout,
WrappedVariant,
};
use bumpalo::collections::{CollectIn, Vec};
use bumpalo::Bump;
@ -21,7 +23,7 @@ use roc_debug_flags::{
ROC_PRINT_IR_AFTER_RESET_REUSE, ROC_PRINT_IR_AFTER_SPECIALIZATION, ROC_PRINT_RUNTIME_ERROR_GEN,
};
use roc_derive::SharedDerivedModule;
use roc_error_macros::{internal_error, todo_abilities};
use roc_error_macros::{internal_error, todo_abilities, todo_lambda_erasure};
use roc_late_solve::storage::{ExternalModuleStorage, ExternalModuleStorageSnapshot};
use roc_late_solve::{resolve_ability_specialization, AbilitiesView, Resolved, UnificationFailed};
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
@ -42,7 +44,9 @@ use pattern::{from_can_pattern, store_pattern, Pattern};
pub use literal::{ListLiteralElement, Literal};
mod boxed;
mod decision_tree;
mod erased;
mod literal;
mod pattern;
@ -305,6 +309,7 @@ pub struct Proc<'a> {
pub ret_layout: InLayout<'a>,
pub is_self_recursive: SelfRecursive,
pub host_exposed_layouts: HostExposedLayouts<'a>,
pub is_erased: bool,
}
#[derive(Clone, Debug, PartialEq, Eq)]
@ -1682,6 +1687,13 @@ impl<'a> Call<'a> {
alloc.text("CallByName ").append(alloc.intersperse(it, " "))
}
CallType::ByPointer { pointer, .. } => {
let it = std::iter::once(pointer)
.chain(arguments.iter().copied())
.map(|s| symbol_to_doc(alloc, s, pretty));
alloc.text("CallByPtr ").append(alloc.intersperse(it, " "))
}
LowLevel { op: lowlevel, .. } => {
let it = arguments.iter().map(|s| symbol_to_doc(alloc, *s, pretty));
@ -1759,6 +1771,11 @@ pub enum CallType<'a> {
arg_layouts: &'a [InLayout<'a>],
specialization_id: CallSpecId,
},
ByPointer {
pointer: Symbol,
ret_layout: InLayout<'a>,
arg_layouts: &'a [InLayout<'a>],
},
Foreign {
foreign_symbol: ForeignSymbol,
ret_layout: InLayout<'a>,
@ -1826,6 +1843,15 @@ pub struct ReuseToken {
pub update_mode: UpdateModeId,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ErasedField {
/// Load a dereferenceable pointer to the value.
Value,
/// Load a non-dereferenceable pointer to the value.
ValuePtr,
Callee,
}
#[derive(Clone, Debug, PartialEq)]
pub enum Expr<'a> {
Literal(Literal<'a>),
@ -1872,6 +1898,28 @@ pub enum Expr<'a> {
},
EmptyArray,
/// Creates a type-erased value.
ErasedMake {
/// The erased value. If this is an erased function, the value are the function captures,
/// or `None` if the function is not a closure.
value: Option<Symbol>,
/// The function pointer of the erased value, if it's an erased function.
callee: Symbol,
},
/// Loads a field from a type-erased value.
ErasedLoad {
/// The erased symbol.
symbol: Symbol,
/// The field to load.
field: ErasedField,
},
/// Returns a pointer to the given function.
FunctionPointer {
lambda_name: LambdaName<'a>,
},
Reset {
symbol: Symbol,
update_mode: UpdateModeId,
@ -2053,6 +2101,38 @@ impl<'a> Expr<'a> {
.text("GetTagId ")
.append(symbol_to_doc(alloc, *structure, pretty)),
ErasedMake { value, callee } => {
let value = match value {
Some(v) => symbol_to_doc(alloc, *v, pretty),
None => alloc.text("<null>"),
};
let callee = symbol_to_doc(alloc, *callee, pretty);
alloc
.text("ErasedMake { value: ")
.append(value)
.append(", callee: ")
.append(callee)
.append(" }")
}
ErasedLoad { symbol, field } => {
let field = match field {
ErasedField::Value => ".Value",
ErasedField::ValuePtr => ".ValuePtr",
ErasedField::Callee => ".Callee",
};
alloc
.text("ErasedLoad ")
.append(symbol_to_doc(alloc, *symbol, pretty))
.append(alloc.text(" "))
.append(field)
}
FunctionPointer { lambda_name } => alloc
.text("FunctionPointer ")
.append(symbol_to_doc(alloc, lambda_name.name(), pretty)),
UnionAtIndex {
tag_id,
structure,
@ -2096,24 +2176,6 @@ impl<'a> Expr<'a> {
arguments: std::slice::from_ref(symbol),
})
}
pub(crate) fn expr_box(symbol: &'a Symbol, element_layout: &'a InLayout<'a>) -> Expr<'a> {
Expr::Tag {
tag_layout: UnionLayout::NonNullableUnwrapped(std::slice::from_ref(element_layout)),
tag_id: 0,
arguments: std::slice::from_ref(symbol),
reuse: None,
}
}
pub(crate) fn expr_unbox(symbol: Symbol, element_layout: &'a InLayout<'a>) -> Expr<'a> {
Expr::UnionAtIndex {
structure: symbol,
tag_id: 0,
union_layout: UnionLayout::NonNullableUnwrapped(std::slice::from_ref(element_layout)),
index: 0,
}
}
}
impl<'a> Stmt<'a> {
@ -3179,6 +3241,7 @@ fn generate_runtime_error_function<'a>(
let runtime_error = runtime_error(env, msg.into_bump_str());
let is_erased = layout.is_erased_function();
let (args, ret_layout) = match layout {
RawFunctionLayout::Function(arg_layouts, lambda_set, ret_layout) => {
let real_arg_layouts =
@ -3195,6 +3258,9 @@ fn generate_runtime_error_function<'a>(
(args.into_bump_slice(), ret_layout)
}
RawFunctionLayout::ErasedFunction(..) => {
todo_lambda_erasure!()
}
RawFunctionLayout::ZeroArgumentThunk(ret_layout) => (&[] as &[_], ret_layout),
};
@ -3206,6 +3272,7 @@ fn generate_runtime_error_function<'a>(
ret_layout,
is_self_recursive: SelfRecursive::NotSelfRecursive,
host_exposed_layouts: HostExposedLayouts::NotHostExposed,
is_erased,
}
}
@ -3284,6 +3351,9 @@ fn generate_host_exposed_function<'a>(
(function_name, (top_level, proc))
}
RawFunctionLayout::ErasedFunction(..) => {
todo_lambda_erasure!()
}
RawFunctionLayout::ZeroArgumentThunk(result) => {
let assigned = env.unique_symbol();
let hole = env.arena.alloc(Stmt::Ret(assigned));
@ -3298,6 +3368,7 @@ fn generate_host_exposed_function<'a>(
ret_layout: result,
is_self_recursive: SelfRecursive::NotSelfRecursive,
host_exposed_layouts: HostExposedLayouts::NotHostExposed,
is_erased: false,
};
let top_level = ProcLayout::from_raw_named(env.arena, lambda_name, layout);
@ -3362,6 +3433,7 @@ fn generate_host_exposed_lambda_set<'a>(
ret_layout: return_layout,
is_self_recursive: SelfRecursive::NotSelfRecursive,
host_exposed_layouts: HostExposedLayouts::NotHostExposed,
is_erased: false,
};
let top_level = ProcLayout::new(
@ -3433,6 +3505,7 @@ fn specialize_proc_help<'a>(
SpecializedLayout::FunctionPointerBody {
ret_layout,
closure: opt_closure_layout,
is_erased,
} => {
// this is a function body like
//
@ -3458,18 +3531,23 @@ fn specialize_proc_help<'a>(
ret_layout,
is_self_recursive: recursivity,
host_exposed_layouts,
is_erased,
}
}
SpecializedLayout::FunctionBody {
arguments: proc_args,
closure: opt_closure_layout,
ret_layout,
is_erased,
} => {
let mut proc_args = Vec::from_iter_in(proc_args.iter().copied(), env.arena);
// unpack the closure symbols, if any
match (opt_closure_layout, captured_symbols) {
(Some(closure_layout), CapturedSymbols::Captured(captured)) => {
(
Some(ClosureDataKind::LambdaSet(closure_layout)),
CapturedSymbols::Captured(captured),
) => {
// debug_assert!(!captured.is_empty());
// An argument from the closure list may have taken on a specialized symbol
@ -3626,6 +3704,15 @@ fn specialize_proc_help<'a>(
}
}
}
(Some(ClosureDataKind::Erased), CapturedSymbols::Captured(captured)) => {
specialized_body = erased::unpack_closure_data(
env,
layout_cache,
Symbol::ARG_CLOSURE,
captured,
specialized_body,
);
}
(None, CapturedSymbols::None) | (None, CapturedSymbols::Captured([])) => {}
_ => unreachable!("to closure or not to closure?"),
}
@ -3637,13 +3724,7 @@ fn specialize_proc_help<'a>(
.maybe_get_specialized(*symbol, *layout)
});
let closure_data_layout = match opt_closure_layout {
Some(lambda_set) => {
let lambda_set = lambda_set.full_layout;
Some(lambda_set)
}
None => None,
};
let closure_data_layout = opt_closure_layout.map(|clos| clos.data_layout());
Proc {
name: lambda_name,
@ -3653,6 +3734,7 @@ fn specialize_proc_help<'a>(
ret_layout,
is_self_recursive: recursivity,
host_exposed_layouts,
is_erased,
}
}
};
@ -3665,13 +3747,15 @@ enum SpecializedLayout<'a> {
/// A body like `foo = \a,b,c -> ...`
FunctionBody {
arguments: &'a [(InLayout<'a>, Symbol)],
closure: Option<LambdaSet<'a>>,
closure: Option<ClosureDataKind<'a>>,
ret_layout: InLayout<'a>,
is_erased: bool,
},
/// A body like `foo = Num.add`
FunctionPointerBody {
closure: Option<LambdaSet<'a>>,
ret_layout: InLayout<'a>,
is_erased: bool,
},
}
@ -3693,7 +3777,20 @@ fn build_specialized_proc_from_var<'a>(
lambda_name,
pattern_symbols,
pattern_layouts_vec,
Some(closure_layout),
Some(ClosureDataKind::LambdaSet(closure_layout)),
ret_layout,
)
}
RawFunctionLayout::ErasedFunction(pattern_layouts, ret_layout) => {
let mut pattern_layouts_vec = Vec::with_capacity_in(pattern_layouts.len(), env.arena);
pattern_layouts_vec.extend_from_slice(pattern_layouts);
build_specialized_proc(
env.arena,
lambda_name,
pattern_symbols,
pattern_layouts_vec,
Some(ClosureDataKind::Erased),
ret_layout,
)
}
@ -3717,7 +3814,7 @@ fn build_specialized_proc<'a>(
lambda_name: LambdaName<'a>,
pattern_symbols: &[Symbol],
pattern_layouts: Vec<'a, InLayout<'a>>,
lambda_set: Option<LambdaSet<'a>>,
closure_data: Option<ClosureDataKind<'a>>,
ret_layout: InLayout<'a>,
) -> Result<SpecializedLayout<'a>, LayoutProblem> {
use SpecializedLayout::*;
@ -3730,6 +3827,8 @@ fn build_specialized_proc<'a>(
proc_args.push((arg_layout, *arg_name));
}
let is_erased = matches!(closure_data, Some(ClosureDataKind::Erased));
// Given
//
// foo =
@ -3749,12 +3848,12 @@ fn build_specialized_proc<'a>(
// then
let proc_name = lambda_name.name();
match lambda_set {
Some(lambda_set) if pattern_symbols.last() == Some(&Symbol::ARG_CLOSURE) => {
match closure_data {
Some(closure_data) if pattern_symbols.last() == Some(&Symbol::ARG_CLOSURE) => {
// here we define the lifted (now top-level) f function. Its final argument is `Symbol::ARG_CLOSURE`,
// it stores the closure structure (just an integer in this case)
let lambda_set_layout = lambda_set.full_layout;
proc_args.push((lambda_set_layout, Symbol::ARG_CLOSURE));
let closure_data_layout = closure_data.data_layout();
proc_args.push((closure_data_layout, Symbol::ARG_CLOSURE));
debug_assert_eq!(
pattern_layouts_len + 1,
@ -3766,11 +3865,12 @@ fn build_specialized_proc<'a>(
Ok(FunctionBody {
arguments: proc_args,
closure: Some(lambda_set),
closure: Some(closure_data),
ret_layout,
is_erased,
})
}
Some(lambda_set) => {
Some(closure_data) => {
// a function that returns a function, but is not itself a closure
// e.g. f = Num.add
@ -3786,14 +3886,16 @@ fn build_specialized_proc<'a>(
arguments: proc_args,
closure: None,
ret_layout,
is_erased,
})
}
Ordering::Greater => {
if pattern_symbols.is_empty() {
let ret_layout = lambda_set.full_layout;
let ret_layout = closure_data.data_layout();
Ok(FunctionPointerBody {
closure: None,
ret_layout,
is_erased,
})
} else {
// so far, the problem when hitting this branch was always somewhere else
@ -3824,6 +3926,7 @@ fn build_specialized_proc<'a>(
arguments: proc_args,
closure: None,
ret_layout,
is_erased,
})
}
Ordering::Greater => {
@ -3831,6 +3934,7 @@ fn build_specialized_proc<'a>(
Ok(FunctionPointerBody {
closure: None,
ret_layout,
is_erased,
})
} else {
// so far, the problem when hitting this branch was always somewhere else
@ -3971,6 +4075,17 @@ impl<'a> ProcLayout<'a> {
lambda_set.extend_argument_list_for_named(arena, lambda_name, arguments);
ProcLayout::new(arena, arguments, lambda_name.niche(), result)
}
RawFunctionLayout::ErasedFunction(arguments, result) => {
let arguments = if lambda_name.no_captures() {
arguments
} else {
let mut extended_args = Vec::with_capacity_in(arguments.len(), arena);
extended_args.extend(arguments.iter().chain(&[Layout::ERASED]).copied());
extended_args.into_bump_slice()
};
ProcLayout::new(arena, arguments, lambda_name.niche(), result)
}
RawFunctionLayout::ZeroArgumentThunk(result) => {
ProcLayout::new(arena, &[], Niche::NONE, result)
}
@ -4824,6 +4939,7 @@ pub fn with_hole<'a>(
hole,
)
}
RawFunctionLayout::ErasedFunction(_, _) => todo_lambda_erasure!(),
RawFunctionLayout::ZeroArgumentThunk(_) => unreachable!(),
}
}
@ -4925,6 +5041,7 @@ pub fn with_hole<'a>(
hole,
)
}
RawFunctionLayout::ErasedFunction(..) => todo_lambda_erasure!(),
RawFunctionLayout::ZeroArgumentThunk(_) => {
internal_error!("should not be a thunk!")
}
@ -5140,6 +5257,44 @@ pub fn with_hole<'a>(
RawFunctionLayout::ZeroArgumentThunk(_) => {
unreachable!("a closure syntactically always must have at least one argument")
}
RawFunctionLayout::ErasedFunction(argument_layouts, ret_layout) => {
let captured_symbols = if captured_symbols.is_empty() {
CapturedSymbols::None
} else {
let captured_symbols = Vec::from_iter_in(captured_symbols, env.arena);
let captured_symbols = captured_symbols.into_bump_slice();
CapturedSymbols::Captured(captured_symbols)
};
let resolved_erased_lambda = ResolvedErasedLambda::new(
env,
layout_cache,
name,
captured_symbols,
argument_layouts,
ret_layout,
);
let inserted = procs.insert_anonymous(
env,
resolved_erased_lambda.lambda_name(),
function_type,
arguments,
loc_body,
captured_symbols,
return_type,
layout_cache,
);
if let Err(e) = inserted {
return runtime_error(
env,
env.arena.alloc(format!("RuntimeError: {:?}", e,)),
);
}
drop(inserted);
build_erased_function(env, layout_cache, resolved_erased_lambda, assigned, hole)
}
RawFunctionLayout::Function(_argument_layouts, lambda_set, _ret_layout) => {
let mut captured_symbols = Vec::from_iter_in(captured_symbols, env.arena);
captured_symbols.sort();
@ -5172,9 +5327,8 @@ pub fn with_hole<'a>(
env,
env.arena.alloc(format!("RuntimeError: {e:?}",)),
);
} else {
drop(inserted);
}
drop(inserted);
// define the closure data
@ -5323,6 +5477,7 @@ pub fn with_hole<'a>(
env.arena.alloc(result),
);
}
RawFunctionLayout::ErasedFunction(..) => todo_lambda_erasure!(),
RawFunctionLayout::ZeroArgumentThunk(_) => {
unreachable!("calling a non-closure layout")
}
@ -5357,6 +5512,7 @@ pub fn with_hole<'a>(
hole,
);
}
RawFunctionLayout::ErasedFunction(..) => todo_lambda_erasure!(),
RawFunctionLayout::ZeroArgumentThunk(_) => {
unreachable!("calling a non-closure layout")
}
@ -5424,6 +5580,22 @@ pub fn with_hole<'a>(
env.arena.alloc(result),
);
}
RawFunctionLayout::ErasedFunction(arg_layouts, ret_layout) => {
let hole_layout =
layout_cache.from_var(env.arena, fn_var, env.subs).unwrap();
result = erased::call_erased_function(
env,
layout_cache,
procs,
loc_expr.value,
fn_var,
(arg_layouts, ret_layout),
arg_symbols,
assigned,
hole,
hole_layout,
);
}
RawFunctionLayout::ZeroArgumentThunk(_) => {
unreachable!(
"{:?} cannot be called in the source language",
@ -5557,6 +5729,7 @@ pub fn with_hole<'a>(
hole,
)
}
RawFunctionLayout::ErasedFunction(..) => todo_lambda_erasure!(),
RawFunctionLayout::ZeroArgumentThunk(_) => unreachable!("match_on_closure_argument received a zero-argument thunk"),
}
}};
@ -5610,7 +5783,7 @@ pub fn with_hole<'a>(
_ => unreachable!("invalid layout for a box expression"),
};
let expr = Expr::expr_box(arena.alloc(x), element_layout);
let expr = boxed::box_(arena.alloc(x), element_layout);
Stmt::Let(assigned, expr, layout, hole)
}
@ -5618,7 +5791,7 @@ pub fn with_hole<'a>(
debug_assert_eq!(arg_symbols.len(), 1);
let x = arg_symbols[0];
let expr = Expr::expr_unbox(x, arena.alloc(layout));
let expr = boxed::unbox(x, arena.alloc(layout));
Stmt::Let(assigned, expr, layout, hole)
}
@ -6478,6 +6651,7 @@ fn tag_union_to_function<'a>(
hole,
)
}
RawFunctionLayout::ErasedFunction(..) => todo_lambda_erasure!(),
RawFunctionLayout::ZeroArgumentThunk(_) => unreachable!(),
}
}
@ -7502,6 +7676,15 @@ fn substitute_in_call<'a>(
ret_layout: *ret_layout,
specialization_id: *specialization_id,
}),
CallType::ByPointer {
pointer,
arg_layouts,
ret_layout,
} => substitute(subs, *pointer).map(|new| CallType::ByPointer {
pointer: new,
arg_layouts,
ret_layout: *ret_layout,
}),
CallType::Foreign { .. } => None,
CallType::LowLevel { .. } => None,
CallType::HigherOrder { .. } => None,
@ -7651,6 +7834,34 @@ fn substitute_in_expr<'a>(
}
}
ErasedMake { value, callee } => {
match (
value.and_then(|v| substitute(subs, v)),
substitute(subs, *callee),
) {
(None, None) => None,
(Some(value), None) => Some(ErasedMake {
value: Some(value),
callee: *callee,
}),
(None, Some(callee)) => Some(ErasedMake {
value: *value,
callee,
}),
(Some(value), Some(callee)) => Some(ErasedMake {
value: Some(value),
callee,
}),
}
}
ErasedLoad { symbol, field } => substitute(subs, *symbol).map(|new_symbol| ErasedLoad {
symbol: new_symbol,
field: *field,
}),
FunctionPointer { .. } => None,
StructAtIndex {
index,
structure,
@ -7968,6 +8179,7 @@ fn specialize_symbol<'a>(
// data for a lambda set.
let layout = match raw {
RawFunctionLayout::ZeroArgumentThunk(layout) => layout,
RawFunctionLayout::ErasedFunction(..) => todo_lambda_erasure!(),
RawFunctionLayout::Function(_, lambda_set, _) => layout_cache
.put_in_direct_no_semantic(LayoutRepr::LambdaSet(lambda_set)),
};
@ -8119,6 +8331,36 @@ fn specialize_symbol<'a>(
)
}
}
RawFunctionLayout::ErasedFunction(argument_layouts, ret_layout) => {
let erased_lambda = erased::ResolvedErasedLambda::new(
env,
layout_cache,
original,
captured,
argument_layouts,
ret_layout,
);
let lambda_name = erased_lambda.lambda_name();
let proc_layout =
ProcLayout::from_raw_named(env.arena, lambda_name, res_layout);
procs.insert_passed_by_name(
env,
arg_var,
lambda_name,
proc_layout,
layout_cache,
);
erased::build_erased_function(
env,
layout_cache,
erased_lambda,
assign_to,
result,
)
}
RawFunctionLayout::ZeroArgumentThunk(ret_layout) => {
// this is a 0-argument thunk
let top_level = ProcLayout::new(env.arena, &[], Niche::NONE, ret_layout);
@ -8367,6 +8609,40 @@ fn call_by_name<'a>(
)
}
}
Ok(RawFunctionLayout::ErasedFunction(arg_layouts, ret_layout)) => {
// TODO(erased-lambdas) call-by-name should never apply here
let arena = env.arena;
let arg_symbols = Vec::from_iter_in(
loc_args.iter().map(|(arg_var, arg_expr)| {
possible_reuse_symbol_or_specialize(
env,
procs,
layout_cache,
&arg_expr.value,
*arg_var,
)
}),
arena,
)
.into_bump_slice();
let result = erased::call_erased_function(
env,
layout_cache,
procs,
roc_can::expr::Expr::Var(proc_name, fn_var),
fn_var,
(arg_layouts, ret_layout),
arg_symbols,
assigned,
hole,
// TODO is this right??
ret_layout,
);
let iter = loc_args.into_iter().rev().zip(arg_symbols.iter().rev());
assign_to_symbols(env, procs, layout_cache, iter, result)
}
Ok(RawFunctionLayout::ZeroArgumentThunk(ret_layout)) => {
if procs.is_module_thunk(proc_name) {
// here we turn a call to a module thunk into forcing of that thunk
@ -8852,6 +9128,7 @@ fn call_specialized_proc<'a>(
// but now we need to remove it because the `match_on_lambda_set` will add it again
build_call(env, call, assigned, lambda_set.full_layout, hole)
}
RawFunctionLayout::ErasedFunction(..) => todo_lambda_erasure!(),
RawFunctionLayout::ZeroArgumentThunk(_) => {
unreachable!()
}
@ -9805,6 +10082,7 @@ pub fn find_lambda_sets_help(
stack.extend(subs.variables[subs_slice.indices()].iter());
}
}
Content::ErasedLambda => {}
}
}
@ -9943,6 +10221,8 @@ where
LayoutRepr::RecursivePointer(_) => {
/* do nothing, we've already generated for this type through the Union(_) */
}
LayoutRepr::FunctionPointer(_) => todo_lambda_erasure!(),
LayoutRepr::Erased(_) => todo_lambda_erasure!(),
}
}
@ -10004,7 +10284,7 @@ where
let field_get_stmt = Stmt::Let(result, field_get_expr, *field, ret_stmt);
let unbox_expr = Expr::expr_unbox(argument, arena.alloc(interned_unboxed_struct_layout));
let unbox_expr = boxed::unbox(argument, arena.alloc(interned_unboxed_struct_layout));
let unbox_stmt = Stmt::Let(
unboxed,
@ -10021,6 +10301,7 @@ where
ret_layout: *field,
is_self_recursive: SelfRecursive::NotSelfRecursive,
host_exposed_layouts: HostExposedLayouts::NotHostExposed,
is_erased: false,
};
answer.push(GlueProc {
@ -10105,7 +10386,7 @@ where
let field_get_stmt = Stmt::Let(result, field_get_expr, *field, ret_stmt);
let unbox_expr = Expr::expr_unbox(argument, arena.alloc(interned));
let unbox_expr = boxed::unbox(argument, arena.alloc(interned));
let unbox_stmt = Stmt::Let(unboxed, unbox_expr, interned, arena.alloc(field_get_stmt));
let proc = Proc {
@ -10116,6 +10397,7 @@ where
ret_layout: *field,
is_self_recursive: SelfRecursive::NotSelfRecursive,
host_exposed_layouts: HostExposedLayouts::NotHostExposed,
is_erased: false,
};
answer.push(GlueProc {

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 ven_pretty::{DocAllocator, DocBuilder};
mod erased;
mod intern;
mod semantic;
pub use erased::Erased;
pub use intern::{
GlobalLayoutInterner, InLayout, LayoutInterner, STLayoutInterner, TLLayoutInterner,
};
@ -484,6 +487,7 @@ impl From<LayoutProblem> for RuntimeError {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum RawFunctionLayout<'a> {
Function(&'a [InLayout<'a>], LambdaSet<'a>, InLayout<'a>),
ErasedFunction(&'a [InLayout<'a>], InLayout<'a>),
ZeroArgumentThunk(InLayout<'a>),
}
@ -492,6 +496,10 @@ impl<'a> RawFunctionLayout<'a> {
matches!(self, RawFunctionLayout::ZeroArgumentThunk(_))
}
pub fn is_erased_function(&self) -> bool {
matches!(self, RawFunctionLayout::ErasedFunction(_, _))
}
fn new_help<'b>(
env: &mut Env<'a, 'b>,
var: Variable,
@ -508,6 +516,7 @@ impl<'a> RawFunctionLayout<'a> {
LambdaSet(_) => {
internal_error!("lambda set should only appear under a function, where it's handled independently.");
}
ErasedLambda => internal_error!("erased lambda type should only appear under a function, where it's handled independently"),
Structure(flat_type) => Self::layout_from_flat_type(env, flat_type),
RangedNumber(..) => Layout::new_help(env, var, content).then(Self::ZeroArgumentThunk),
@ -604,13 +613,17 @@ impl<'a> RawFunctionLayout<'a> {
let fn_args = fn_args.into_bump_slice();
let lambda_set = cached!(
LambdaSet::from_var(env, args, closure_var, ret_var),
cache_criteria,
env.subs
);
let closure_data = build_function_closure_data(env, args, closure_var, ret_var);
let closure_data = cached!(closure_data, cache_criteria, env.subs);
Cacheable(Ok(Self::Function(fn_args, lambda_set, ret)), cache_criteria)
let function_layout = match closure_data {
ClosureDataKind::LambdaSet(lambda_set) => {
Self::Function(fn_args, lambda_set, ret)
}
ClosureDataKind::Erased => Self::ErasedFunction(fn_args, ret),
};
Cacheable(Ok(function_layout), cache_criteria)
}
TagUnion(tags, ext) if tags.is_newtype_wrapper(env.subs) => {
debug_assert!(ext_var_is_empty_tag_union(env.subs, ext));
@ -678,6 +691,47 @@ pub enum LayoutRepr<'a> {
Union(UnionLayout<'a>),
LambdaSet(LambdaSet<'a>),
RecursivePointer(InLayout<'a>),
/// Only used in erased functions.
FunctionPointer(FunctionPointer<'a>),
/// The layout of an erasure.
Erased(Erased),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct FunctionPointer<'a> {
pub args: &'a [InLayout<'a>],
pub ret: InLayout<'a>,
}
impl<'a> FunctionPointer<'a> {
pub fn to_doc<'b, D, A, I>(
self,
alloc: &'b D,
interner: &I,
seen_rec: &mut SeenRecPtrs<'a>,
parens: Parens,
) -> DocBuilder<'b, D, A>
where
D: DocAllocator<'b, A>,
D::Doc: Clone,
A: Clone,
I: LayoutInterner<'a>,
{
let Self { args, ret } = self;
let args = args
.iter()
.map(|arg| interner.to_doc(*arg, alloc, seen_rec, parens));
let args = alloc.intersperse(args, alloc.text(", "));
let ret = interner.to_doc(ret, alloc, seen_rec, parens);
alloc
.text("FunPtr((")
.append(args)
.append(alloc.text(") -> "))
.append(ret)
.append(")")
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
@ -1312,6 +1366,13 @@ pub struct LambdaName<'a> {
}
impl<'a> LambdaName<'a> {
pub(crate) fn from_captures(symbol: Symbol, captures: &'a [InLayout<'a>]) -> Self {
Self {
name: symbol,
niche: Niche(NichePriv::Captures(captures)),
}
}
#[inline(always)]
pub fn name(&self) -> Symbol {
self.name
@ -1343,6 +1404,37 @@ impl<'a> LambdaName<'a> {
}
}
/// Closure data for a function
#[derive(Debug, Clone, Copy)]
pub(crate) enum ClosureDataKind<'a> {
/// The function is compiled with lambda sets.
LambdaSet(LambdaSet<'a>),
/// The function is compiled as type-erased.
Erased,
}
impl<'a> ClosureDataKind<'a> {
pub fn data_layout(&self) -> InLayout<'a> {
match self {
Self::LambdaSet(lambda_set) => lambda_set.full_layout,
Self::Erased => Layout::ERASED,
}
}
}
fn build_function_closure_data<'a>(
env: &mut Env<'a, '_>,
args: VariableSubsSlice,
closure_var: Variable,
ret_var: Variable,
) -> Cacheable<Result<ClosureDataKind<'a>, LayoutProblem>> {
match env.subs.get_content_without_compacting(closure_var) {
Content::ErasedLambda => cacheable(Ok(ClosureDataKind::Erased)),
_ => LambdaSet::from_var(env, args, closure_var, ret_var)
.map(|result| result.map(ClosureDataKind::LambdaSet)),
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct LambdaSet<'a> {
pub(crate) args: &'a &'a [InLayout<'a>],
@ -2113,7 +2205,8 @@ fn lambda_set_size(subs: &Subs, var: Variable) -> (usize, usize, usize) {
| Content::FlexAbleVar(_, _)
| Content::RigidAbleVar(_, _)
| Content::RangedNumber(_)
| Content::Error => {}
| Content::Error
| Content::ErasedLambda => {}
}
}
(max_depth_any_ctor, max_depth_only_lset, total)
@ -2393,6 +2486,9 @@ impl<'a> Layout<'a> {
LambdaSet(_) => {
internal_error!("lambda set should only appear under a function, where it's handled independently.");
}
ErasedLambda => {
internal_error!("erased lambda type should only appear under a function, where it's handled independently.");
}
Structure(flat_type) => layout_from_flat_type(env, flat_type),
Alias(symbol, _args, actual_var, _) => {
@ -2529,6 +2625,7 @@ impl<'a> LayoutRepr<'a> {
pub const DEC: Self = LayoutRepr::Builtin(Builtin::Decimal);
pub const STR: Self = LayoutRepr::Builtin(Builtin::Str);
pub const OPAQUE_PTR: Self = LayoutRepr::Ptr(Layout::VOID);
pub const ERASED: Self = LayoutRepr::Erased(Erased);
pub const fn struct_(field_layouts: &'a [InLayout<'a>]) -> Self {
Self::Struct(field_layouts)
@ -2574,6 +2671,8 @@ impl<'a> LayoutRepr<'a> {
// We cannot memcpy pointers, because then we would have the same pointer in multiple places!
false
}
Erased(e) => e.safe_to_memcpy(),
FunctionPointer(..) => true,
}
}
@ -2659,8 +2758,10 @@ impl<'a> LayoutRepr<'a> {
LambdaSet(lambda_set) => interner
.get_repr(lambda_set.runtime_representation())
.stack_size_without_alignment(interner),
RecursivePointer(_) => interner.target_info().ptr_width() as u32,
Ptr(_) => interner.target_info().ptr_width() as u32,
RecursivePointer(_) | Ptr(_) | FunctionPointer(_) => {
interner.target_info().ptr_width() as u32
}
Erased(e) => e.stack_size_without_alignment(interner.target_info()),
}
}
@ -2712,8 +2813,10 @@ impl<'a> LayoutRepr<'a> {
.get_repr(lambda_set.runtime_representation())
.alignment_bytes(interner),
Builtin(builtin) => builtin.alignment_bytes(interner.target_info()),
RecursivePointer(_) => interner.target_info().ptr_width() as u32,
Ptr(_) => interner.target_info().ptr_width() as u32,
RecursivePointer(_) | Ptr(_) | FunctionPointer(_) => {
interner.target_info().ptr_width() as u32
}
Erased(e) => e.alignment_bytes(interner.target_info()),
}
}
@ -2735,6 +2838,8 @@ impl<'a> LayoutRepr<'a> {
unreachable!("should be looked up to get an actual layout")
}
Ptr(inner) => interner.get_repr(*inner).alignment_bytes(interner),
FunctionPointer(_) => ptr_width,
Erased(e) => e.allocation_alignment_bytes(interner.target_info()),
}
}
@ -2751,6 +2856,8 @@ impl<'a> LayoutRepr<'a> {
Builtin(List(_)) | Builtin(Str) => true,
Erased(_) => true,
_ => false,
}
}
@ -2808,6 +2915,8 @@ impl<'a> LayoutRepr<'a> {
// author must make sure that invariants are upheld
false
}
FunctionPointer(_) => false,
Erased(e) => e.is_refcounted(),
}
}
@ -2869,6 +2978,12 @@ impl<'a> LayoutRepr<'a> {
RecursivePointer(_) => {
/* do nothing, we've already generated for this type through the Union(_) */
}
FunctionPointer(_) => {
// drop through
}
Erased(_) => {
// erasures are just pointers, so they do not vary
}
}
}
@ -3219,14 +3334,15 @@ fn layout_from_flat_type<'a>(
} else {
let mut criteria = CACHEABLE;
let lambda_set = cached!(
LambdaSet::from_var(env, args, closure_var, ret_var),
criteria,
env.subs
);
let lambda_set = lambda_set.full_layout;
let closure_data = build_function_closure_data(env, args, closure_var, ret_var);
let closure_data = cached!(closure_data, criteria, env.subs);
Cacheable(Ok(lambda_set), criteria)
match closure_data {
ClosureDataKind::LambdaSet(lambda_set) => {
Cacheable(Ok(lambda_set.full_layout), criteria)
}
ClosureDataKind::Erased => Cacheable(Ok(Layout::ERASED), criteria),
}
}
}
Record(fields, ext_var) => {
@ -4461,7 +4577,7 @@ fn layout_from_num_content<'a>(
Alias(_, _, _, _) => {
todo!("TODO recursively resolve type aliases in num_from_content");
}
Structure(_) | RangedNumber(..) | LambdaSet(_) => {
Structure(_) | RangedNumber(..) | LambdaSet(_) | ErasedLambda => {
panic!("Invalid Num.Num type application: {content:?}");
}
Error => Err(LayoutProblem::Erroneous),

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)
16, STR, pub, nosema!(LayoutRepr::STR)
17, OPAQUE_PTR, pub, nosema!(LayoutRepr::OPAQUE_PTR)
18, NAKED_RECURSIVE_PTR, pub(super), nosema!(LayoutRepr::RecursivePointer(Layout::VOID))
19, STR_PTR, pub, nosema!(LayoutRepr::Ptr(Layout::STR))
20, LIST_U8, pub, nosema!(LayoutRepr::Builtin(crate::layout::Builtin::List(Layout::U8)))
18, ERASED, pub, nosema!(LayoutRepr::ERASED)
19, NAKED_RECURSIVE_PTR, pub(super), nosema!(LayoutRepr::RecursivePointer(Layout::VOID))
20, STR_PTR, pub, nosema!(LayoutRepr::Ptr(Layout::STR))
21, LIST_U8, pub, nosema!(LayoutRepr::Builtin(crate::layout::Builtin::List(Layout::U8)))
; 21
; 22
}
macro_rules! impl_to_from_int_width {
@ -354,6 +355,8 @@ pub trait LayoutInterner<'a>: Sized {
.text("Ptr(")
.append(self.to_doc(inner, alloc, seen_rec, parens))
.append(")"),
FunctionPointer(fp) => fp.to_doc(alloc, self, seen_rec, parens),
Erased(e) => e.to_doc(alloc),
}
}
@ -1076,7 +1079,9 @@ mod reify {
use bumpalo::{collections::Vec, Bump};
use roc_module::symbol::Symbol;
use crate::layout::{Builtin, LambdaSet, Layout, LayoutRepr, LayoutWrapper, UnionLayout};
use crate::layout::{
Builtin, FunctionPointer, LambdaSet, Layout, LayoutRepr, LayoutWrapper, UnionLayout,
};
use super::{InLayout, LayoutInterner, NeedsRecursionPointerFixup};
@ -1121,6 +1126,13 @@ mod reify {
// another recursive union's layout, do not change it.
LayoutRepr::RecursivePointer(if l == Layout::VOID { slot } else { l })
}
LayoutRepr::FunctionPointer(FunctionPointer { args, ret }) => {
LayoutRepr::FunctionPointer(FunctionPointer {
args: reify_layout_slice(arena, interner, slot, args),
ret: reify_layout(arena, interner, slot, ret),
})
}
LayoutRepr::Erased(e) => LayoutRepr::Erased(e),
}
}
@ -1394,7 +1406,7 @@ mod equiv {
pub mod dbg_deep {
use roc_module::symbol::Symbol;
use crate::layout::{Builtin, LambdaSet, LayoutRepr, UnionLayout};
use crate::layout::{Builtin, Erased, LambdaSet, LayoutRepr, UnionLayout};
use super::{InLayout, LayoutInterner};
@ -1447,6 +1459,12 @@ pub mod dbg_deep {
LayoutRepr::RecursivePointer(rp) => {
f.debug_tuple("RecursivePointer").field(&rp.0).finish()
}
LayoutRepr::FunctionPointer(fp) => f
.debug_struct("FunctionPointer")
.field("args", &fp.args)
.field("ret", &fp.ret)
.finish(),
LayoutRepr::Erased(Erased) => f.debug_struct("?Erased").finish(),
}
}
}
@ -1559,7 +1577,7 @@ pub mod dbg_deep {
pub mod dbg_stable {
use roc_module::symbol::Symbol;
use crate::layout::{Builtin, LambdaSet, LayoutRepr, SemanticRepr, UnionLayout};
use crate::layout::{Builtin, Erased, LambdaSet, LayoutRepr, SemanticRepr, UnionLayout};
use super::{InLayout, LayoutInterner};
@ -1620,6 +1638,12 @@ pub mod dbg_stable {
LayoutRepr::RecursivePointer(rp) => {
f.debug_tuple("RecursivePointer").field(&rp.0).finish()
}
LayoutRepr::FunctionPointer(fp) => f
.debug_struct("FunctionPointer")
.field("args", &fp.args)
.field("ret", &fp.ret)
.finish(),
LayoutRepr::Erased(Erased) => f.debug_struct("?Erased").finish(),
}
}
}

View File

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

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

View File

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

View File

@ -10,10 +10,15 @@ use roc_types::{
types::{RecordField, Uls},
};
use crate::env::Env;
use crate::env::SolveEnv;
// TODO: eventually, we could possibly use the arena in Env instead.
pub(crate) fn deep_copy_var_in(env: &mut Env, rank: Rank, var: Variable, arena: &Bump) -> Variable {
pub(crate) fn deep_copy_var_in(
env: &mut SolveEnv,
rank: Rank,
var: Variable,
arena: &Bump,
) -> Variable {
let mut visited = bumpalo::collections::Vec::with_capacity_in(256, arena);
let pool = env.pools.get_mut(rank);
@ -257,7 +262,7 @@ fn deep_copy_var_help(
subs.set_content_unchecked(copy, Structure(new_flat_type));
}
FlexVar(_) | FlexAbleVar(_, _) | Error => {
FlexVar(_) | FlexAbleVar(_, _) | Error | ErasedLambda => {
subs.set_content_unchecked(copy, content);
}

View File

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

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 deep_copy;
mod env;
mod kinds;
mod pools;
mod to_var;
pub use aliases::Aliases;
pub use env::{DerivedEnv, Env};
pub use env::{DerivedEnv, SolveEnv};
pub use kinds::FunctionKind;
pub use pools::Pools;

View File

@ -1,3 +1,4 @@
use crate::FunctionKind;
use crate::{aliases::Aliases, solve};
use roc_can::abilities::{AbilitiesStore, ResolvedImpl};
use roc_can::constraint::{Constraint, Constraints};
@ -61,6 +62,8 @@ pub struct SolveConfig<'a> {
/// All types introduced in the module. Canonicalized, but not necessarily yet associated with
/// a variable substitution.
pub types: Types,
/// How functions should be kinded.
pub function_kind: FunctionKind,
/// Table of types introduced in this module that claim to derive an ability implementation.
/// Due for checking and instantiation after the solver runs over the module.
pub pending_derives: PendingDerives,

View File

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

View File

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

View File

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

View File

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

View File

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

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_packaging::cache::RocCacheDir;
use roc_region::all::LineInfo;
use roc_solve::FunctionKind;
use tempfile::tempdir;
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
@ -58,6 +59,7 @@ pub fn helper(
palette: roc_reporting::report::DEFAULT_PALETTE,
threading: Threading::Single,
exec_mode: ExecutionMode::Executable,
function_kind: FunctionKind::LambdaSet,
};
let loaded = roc_load::load_and_monomorphize_from_str(
arena,

View File

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

View File

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

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_definitions;
pub mod gen_dict;
pub mod gen_erased;
pub mod gen_list;
pub mod gen_num;
pub mod gen_panic;

View File

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

View File

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

View File

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

View File

@ -51,20 +51,6 @@ procedure List.71 (#Attr.2, #Attr.3):
ret List.541;
procedure List.83 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2):
joinpoint List.535 List.123 List.124 List.125:
let List.543 : U64 = 0i64;
let List.537 : Int1 = CallByName Num.24 List.124 List.543;
if List.537 then
let List.542 : U64 = 1i64;
let List.539 : U64 = CallByName Num.20 List.124 List.542;
let List.540 : List U64 = CallByName List.71 List.125 List.123;
jump List.535 List.123 List.539 List.540;
else
ret List.125;
in
jump List.535 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2;
procedure List.83 (#Derived_gen.3, #Derived_gen.4, #Derived_gen.5):
joinpoint List.523 List.123 List.124 List.125:
let List.531 : U64 = 0i64;
let List.525 : Int1 = CallByName Num.24 List.124 List.531;
@ -76,7 +62,21 @@ procedure List.83 (#Derived_gen.3, #Derived_gen.4, #Derived_gen.5):
else
ret List.125;
in
jump List.523 #Derived_gen.3 #Derived_gen.4 #Derived_gen.5;
jump List.523 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2;
procedure List.83 (#Derived_gen.3, #Derived_gen.4, #Derived_gen.5):
joinpoint List.535 List.123 List.124 List.125:
let List.543 : U64 = 0i64;
let List.537 : Int1 = CallByName Num.24 List.124 List.543;
if List.537 then
let List.542 : U64 = 1i64;
let List.539 : U64 = CallByName Num.20 List.124 List.542;
let List.540 : List U64 = CallByName List.71 List.125 List.123;
jump List.535 List.123 List.539 List.540;
else
ret List.125;
in
jump List.535 #Derived_gen.3 #Derived_gen.4 #Derived_gen.5;
procedure Num.20 (#Attr.2, #Attr.3):
let Num.293 : U64 = lowlevel NumSub #Attr.2 #Attr.3;

View File

@ -235,7 +235,7 @@ procedure List.8 (#Attr.2, #Attr.3):
let List.668 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
ret List.668;
procedure List.80 (#Derived_gen.23, #Derived_gen.24, #Derived_gen.25, #Derived_gen.26, #Derived_gen.27):
procedure List.80 (#Derived_gen.30, #Derived_gen.31, #Derived_gen.32, #Derived_gen.33, #Derived_gen.34):
joinpoint List.715 List.439 List.440 List.441 List.442 List.443:
let List.717 : Int1 = CallByName Num.22 List.442 List.443;
if List.717 then
@ -259,9 +259,9 @@ procedure List.80 (#Derived_gen.23, #Derived_gen.24, #Derived_gen.25, #Derived_g
let List.716 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) List.440;
ret List.716;
in
jump List.715 #Derived_gen.23 #Derived_gen.24 #Derived_gen.25 #Derived_gen.26 #Derived_gen.27;
jump List.715 #Derived_gen.30 #Derived_gen.31 #Derived_gen.32 #Derived_gen.33 #Derived_gen.34;
procedure List.80 (#Derived_gen.31, #Derived_gen.32, #Derived_gen.33, #Derived_gen.34, #Derived_gen.35):
procedure List.80 (#Derived_gen.36, #Derived_gen.37, #Derived_gen.38, #Derived_gen.39, #Derived_gen.40):
joinpoint List.644 List.439 List.440 List.441 List.442 List.443:
let List.646 : Int1 = CallByName Num.22 List.442 List.443;
if List.646 then
@ -274,9 +274,9 @@ procedure List.80 (#Derived_gen.31, #Derived_gen.32, #Derived_gen.33, #Derived_g
dec List.439;
ret List.440;
in
jump List.644 #Derived_gen.31 #Derived_gen.32 #Derived_gen.33 #Derived_gen.34 #Derived_gen.35;
jump List.644 #Derived_gen.36 #Derived_gen.37 #Derived_gen.38 #Derived_gen.39 #Derived_gen.40;
procedure List.80 (#Derived_gen.36, #Derived_gen.37, #Derived_gen.38, #Derived_gen.39, #Derived_gen.40):
procedure List.80 (#Derived_gen.41, #Derived_gen.42, #Derived_gen.43, #Derived_gen.44, #Derived_gen.45):
joinpoint List.624 List.439 List.440 List.441 List.442 List.443:
let List.626 : Int1 = CallByName Num.22 List.442 List.443;
if List.626 then
@ -290,9 +290,9 @@ procedure List.80 (#Derived_gen.36, #Derived_gen.37, #Derived_gen.38, #Derived_g
dec List.439;
ret List.440;
in
jump List.624 #Derived_gen.36 #Derived_gen.37 #Derived_gen.38 #Derived_gen.39 #Derived_gen.40;
jump List.624 #Derived_gen.41 #Derived_gen.42 #Derived_gen.43 #Derived_gen.44 #Derived_gen.45;
procedure List.80 (#Derived_gen.42, #Derived_gen.43, #Derived_gen.44, #Derived_gen.45, #Derived_gen.46):
procedure List.80 (#Derived_gen.52, #Derived_gen.53, #Derived_gen.54, #Derived_gen.55, #Derived_gen.56):
joinpoint List.556 List.439 List.440 List.441 List.442 List.443:
let List.558 : Int1 = CallByName Num.22 List.442 List.443;
if List.558 then
@ -306,7 +306,7 @@ procedure List.80 (#Derived_gen.42, #Derived_gen.43, #Derived_gen.44, #Derived_g
dec List.439;
ret List.440;
in
jump List.556 #Derived_gen.42 #Derived_gen.43 #Derived_gen.44 #Derived_gen.45 #Derived_gen.46;
jump List.556 #Derived_gen.52 #Derived_gen.53 #Derived_gen.54 #Derived_gen.55 #Derived_gen.56;
procedure List.93 (List.436, List.437, List.438):
let List.554 : U64 = 0i64;
@ -388,8 +388,8 @@ procedure Str.9 (Str.79):
else
let Str.300 : U8 = StructAtIndex 3 Str.80;
let Str.301 : U64 = StructAtIndex 0 Str.80;
let #Derived_gen.58 : Str = StructAtIndex 1 Str.80;
dec #Derived_gen.58;
let #Derived_gen.57 : Str = StructAtIndex 1 Str.80;
dec #Derived_gen.57;
let Str.299 : {U64, U8} = Struct {Str.301, Str.300};
let Str.298 : [C {U64, U8}, C Str] = TagId(0) Str.299;
ret Str.298;
@ -1331,14 +1331,14 @@ procedure TotallyNotJson.82 (TotallyNotJson.802, TotallyNotJson.803):
procedure TotallyNotJson.832 (TotallyNotJson.1493):
let TotallyNotJson.1845 : List Str = StructAtIndex 1 TotallyNotJson.1493;
let #Derived_gen.59 : List Str = StructAtIndex 0 TotallyNotJson.1493;
dec #Derived_gen.59;
let #Derived_gen.58 : List Str = StructAtIndex 0 TotallyNotJson.1493;
dec #Derived_gen.58;
ret TotallyNotJson.1845;
procedure TotallyNotJson.840 (TotallyNotJson.1214):
let TotallyNotJson.1566 : List Str = StructAtIndex 1 TotallyNotJson.1214;
let #Derived_gen.57 : List Str = StructAtIndex 0 TotallyNotJson.1214;
dec #Derived_gen.57;
let #Derived_gen.59 : List Str = StructAtIndex 0 TotallyNotJson.1214;
dec #Derived_gen.59;
ret TotallyNotJson.1566;
procedure TotallyNotJson.87 (TotallyNotJson.809):
@ -1393,7 +1393,7 @@ procedure TotallyNotJson.95 (TotallyNotJson.829):
dec TotallyNotJson.1841;
ret TotallyNotJson.1839;
procedure TotallyNotJson.96 (#Derived_gen.41):
procedure TotallyNotJson.96 (#Derived_gen.29):
joinpoint TotallyNotJson.1847 TotallyNotJson.1168:
let TotallyNotJson.834 : List Str = StructAtIndex 0 TotallyNotJson.1168;
let TotallyNotJson.833 : List Str = StructAtIndex 1 TotallyNotJson.1168;
@ -1429,7 +1429,7 @@ procedure TotallyNotJson.96 (#Derived_gen.41):
let TotallyNotJson.1848 : {List Str, List Str} = Struct {TotallyNotJson.834, TotallyNotJson.833};
ret TotallyNotJson.1848;
in
jump TotallyNotJson.1847 #Derived_gen.41;
jump TotallyNotJson.1847 #Derived_gen.29;
procedure TotallyNotJson.97 (TotallyNotJson.837):
let TotallyNotJson.838 : List Str = CallByName Str.55 TotallyNotJson.837;
@ -1446,7 +1446,7 @@ procedure TotallyNotJson.97 (TotallyNotJson.837):
dec TotallyNotJson.1562;
ret TotallyNotJson.1560;
procedure TotallyNotJson.98 (#Derived_gen.47):
procedure TotallyNotJson.98 (#Derived_gen.35):
joinpoint TotallyNotJson.1568 TotallyNotJson.1169:
let TotallyNotJson.842 : List Str = StructAtIndex 0 TotallyNotJson.1169;
let TotallyNotJson.841 : List Str = StructAtIndex 1 TotallyNotJson.1169;
@ -1482,7 +1482,7 @@ procedure TotallyNotJson.98 (#Derived_gen.47):
let TotallyNotJson.1569 : {List Str, List Str} = Struct {TotallyNotJson.842, TotallyNotJson.841};
ret TotallyNotJson.1569;
in
jump TotallyNotJson.1568 #Derived_gen.47;
jump TotallyNotJson.1568 #Derived_gen.35;
procedure Test.0 ():
let Test.12 : Str = "bar";

View File

@ -192,23 +192,7 @@ procedure List.8 (#Attr.2, #Attr.3):
let List.600 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
ret List.600;
procedure List.80 (#Derived_gen.11, #Derived_gen.12, #Derived_gen.13, #Derived_gen.14, #Derived_gen.15):
joinpoint List.556 List.439 List.440 List.441 List.442 List.443:
let List.558 : Int1 = CallByName Num.22 List.442 List.443;
if List.558 then
let List.565 : {Str, Str} = CallByName List.66 List.439 List.442;
inc List.565;
let List.559 : {List U8, U64} = CallByName List.145 List.440 List.565 List.441;
let List.562 : U64 = 1i64;
let List.561 : U64 = CallByName Num.19 List.442 List.562;
jump List.556 List.439 List.559 List.441 List.561 List.443;
else
dec List.439;
ret List.440;
in
jump List.556 #Derived_gen.11 #Derived_gen.12 #Derived_gen.13 #Derived_gen.14 #Derived_gen.15;
procedure List.80 (#Derived_gen.16, #Derived_gen.17, #Derived_gen.18, #Derived_gen.19, #Derived_gen.20):
procedure List.80 (#Derived_gen.10, #Derived_gen.11, #Derived_gen.12, #Derived_gen.13, #Derived_gen.14):
joinpoint List.647 List.439 List.440 List.441 List.442 List.443:
let List.649 : Int1 = CallByName Num.22 List.442 List.443;
if List.649 then
@ -232,9 +216,9 @@ procedure List.80 (#Derived_gen.16, #Derived_gen.17, #Derived_gen.18, #Derived_g
let List.648 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) List.440;
ret List.648;
in
jump List.647 #Derived_gen.16 #Derived_gen.17 #Derived_gen.18 #Derived_gen.19 #Derived_gen.20;
jump List.647 #Derived_gen.10 #Derived_gen.11 #Derived_gen.12 #Derived_gen.13 #Derived_gen.14;
procedure List.80 (#Derived_gen.21, #Derived_gen.22, #Derived_gen.23, #Derived_gen.24, #Derived_gen.25):
procedure List.80 (#Derived_gen.18, #Derived_gen.19, #Derived_gen.20, #Derived_gen.21, #Derived_gen.22):
joinpoint List.576 List.439 List.440 List.441 List.442 List.443:
let List.578 : Int1 = CallByName Num.22 List.442 List.443;
if List.578 then
@ -247,7 +231,23 @@ procedure List.80 (#Derived_gen.21, #Derived_gen.22, #Derived_gen.23, #Derived_g
dec List.439;
ret List.440;
in
jump List.576 #Derived_gen.21 #Derived_gen.22 #Derived_gen.23 #Derived_gen.24 #Derived_gen.25;
jump List.576 #Derived_gen.18 #Derived_gen.19 #Derived_gen.20 #Derived_gen.21 #Derived_gen.22;
procedure List.80 (#Derived_gen.31, #Derived_gen.32, #Derived_gen.33, #Derived_gen.34, #Derived_gen.35):
joinpoint List.556 List.439 List.440 List.441 List.442 List.443:
let List.558 : Int1 = CallByName Num.22 List.442 List.443;
if List.558 then
let List.565 : {Str, Str} = CallByName List.66 List.439 List.442;
inc List.565;
let List.559 : {List U8, U64} = CallByName List.145 List.440 List.565 List.441;
let List.562 : U64 = 1i64;
let List.561 : U64 = CallByName Num.19 List.442 List.562;
jump List.556 List.439 List.559 List.441 List.561 List.443;
else
dec List.439;
ret List.440;
in
jump List.556 #Derived_gen.31 #Derived_gen.32 #Derived_gen.33 #Derived_gen.34 #Derived_gen.35;
procedure List.93 (List.436, List.437, List.438):
let List.554 : U64 = 0i64;
@ -323,8 +323,8 @@ procedure Str.9 (Str.79):
else
let Str.300 : U8 = StructAtIndex 3 Str.80;
let Str.301 : U64 = StructAtIndex 0 Str.80;
let #Derived_gen.38 : Str = StructAtIndex 1 Str.80;
dec #Derived_gen.38;
let #Derived_gen.36 : Str = StructAtIndex 1 Str.80;
dec #Derived_gen.36;
let Str.299 : {U64, U8} = Struct {Str.301, Str.300};
let Str.298 : [C {U64, U8}, C Str] = TagId(0) Str.299;
ret Str.298;
@ -1215,8 +1215,8 @@ procedure TotallyNotJson.82 (TotallyNotJson.802, TotallyNotJson.803):
procedure TotallyNotJson.832 (TotallyNotJson.1493):
let TotallyNotJson.1494 : List Str = StructAtIndex 1 TotallyNotJson.1493;
let #Derived_gen.36 : List Str = StructAtIndex 0 TotallyNotJson.1493;
dec #Derived_gen.36;
let #Derived_gen.38 : List Str = StructAtIndex 0 TotallyNotJson.1493;
dec #Derived_gen.38;
ret TotallyNotJson.1494;
procedure TotallyNotJson.840 (TotallyNotJson.1214):
@ -1277,7 +1277,7 @@ procedure TotallyNotJson.95 (TotallyNotJson.829):
dec TotallyNotJson.1489;
ret TotallyNotJson.1488;
procedure TotallyNotJson.96 (#Derived_gen.32):
procedure TotallyNotJson.96 (#Derived_gen.26):
joinpoint TotallyNotJson.1496 TotallyNotJson.1168:
let TotallyNotJson.834 : List Str = StructAtIndex 0 TotallyNotJson.1168;
let TotallyNotJson.833 : List Str = StructAtIndex 1 TotallyNotJson.1168;
@ -1313,7 +1313,7 @@ procedure TotallyNotJson.96 (#Derived_gen.32):
let TotallyNotJson.1497 : {List Str, List Str} = Struct {TotallyNotJson.834, TotallyNotJson.833};
ret TotallyNotJson.1497;
in
jump TotallyNotJson.1496 #Derived_gen.32;
jump TotallyNotJson.1496 #Derived_gen.26;
procedure TotallyNotJson.97 (TotallyNotJson.837):
let TotallyNotJson.838 : List Str = CallByName Str.55 TotallyNotJson.837;
@ -1330,7 +1330,7 @@ procedure TotallyNotJson.97 (TotallyNotJson.837):
dec TotallyNotJson.1210;
ret TotallyNotJson.1209;
procedure TotallyNotJson.98 (#Derived_gen.10):
procedure TotallyNotJson.98 (#Derived_gen.30):
joinpoint TotallyNotJson.1217 TotallyNotJson.1169:
let TotallyNotJson.842 : List Str = StructAtIndex 0 TotallyNotJson.1169;
let TotallyNotJson.841 : List Str = StructAtIndex 1 TotallyNotJson.1169;
@ -1366,7 +1366,7 @@ procedure TotallyNotJson.98 (#Derived_gen.10):
let TotallyNotJson.1218 : {List Str, List Str} = Struct {TotallyNotJson.842, TotallyNotJson.841};
ret TotallyNotJson.1218;
in
jump TotallyNotJson.1217 #Derived_gen.10;
jump TotallyNotJson.1217 #Derived_gen.30;
procedure Test.0 ():
let Test.11 : Str = "foo";

View File

@ -199,23 +199,7 @@ procedure List.8 (#Attr.2, #Attr.3):
let List.600 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
ret List.600;
procedure List.80 (#Derived_gen.15, #Derived_gen.16, #Derived_gen.17, #Derived_gen.18, #Derived_gen.19):
joinpoint List.556 List.439 List.440 List.441 List.442 List.443:
let List.558 : Int1 = CallByName Num.22 List.442 List.443;
if List.558 then
let List.565 : {Str, Str} = CallByName List.66 List.439 List.442;
inc List.565;
let List.559 : {List U8, U64} = CallByName List.145 List.440 List.565 List.441;
let List.562 : U64 = 1i64;
let List.561 : U64 = CallByName Num.19 List.442 List.562;
jump List.556 List.439 List.559 List.441 List.561 List.443;
else
dec List.439;
ret List.440;
in
jump List.556 #Derived_gen.15 #Derived_gen.16 #Derived_gen.17 #Derived_gen.18 #Derived_gen.19;
procedure List.80 (#Derived_gen.20, #Derived_gen.21, #Derived_gen.22, #Derived_gen.23, #Derived_gen.24):
procedure List.80 (#Derived_gen.14, #Derived_gen.15, #Derived_gen.16, #Derived_gen.17, #Derived_gen.18):
joinpoint List.647 List.439 List.440 List.441 List.442 List.443:
let List.649 : Int1 = CallByName Num.22 List.442 List.443;
if List.649 then
@ -239,9 +223,9 @@ procedure List.80 (#Derived_gen.20, #Derived_gen.21, #Derived_gen.22, #Derived_g
let List.648 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) List.440;
ret List.648;
in
jump List.647 #Derived_gen.20 #Derived_gen.21 #Derived_gen.22 #Derived_gen.23 #Derived_gen.24;
jump List.647 #Derived_gen.14 #Derived_gen.15 #Derived_gen.16 #Derived_gen.17 #Derived_gen.18;
procedure List.80 (#Derived_gen.25, #Derived_gen.26, #Derived_gen.27, #Derived_gen.28, #Derived_gen.29):
procedure List.80 (#Derived_gen.22, #Derived_gen.23, #Derived_gen.24, #Derived_gen.25, #Derived_gen.26):
joinpoint List.576 List.439 List.440 List.441 List.442 List.443:
let List.578 : Int1 = CallByName Num.22 List.442 List.443;
if List.578 then
@ -254,7 +238,23 @@ procedure List.80 (#Derived_gen.25, #Derived_gen.26, #Derived_gen.27, #Derived_g
dec List.439;
ret List.440;
in
jump List.576 #Derived_gen.25 #Derived_gen.26 #Derived_gen.27 #Derived_gen.28 #Derived_gen.29;
jump List.576 #Derived_gen.22 #Derived_gen.23 #Derived_gen.24 #Derived_gen.25 #Derived_gen.26;
procedure List.80 (#Derived_gen.35, #Derived_gen.36, #Derived_gen.37, #Derived_gen.38, #Derived_gen.39):
joinpoint List.556 List.439 List.440 List.441 List.442 List.443:
let List.558 : Int1 = CallByName Num.22 List.442 List.443;
if List.558 then
let List.565 : {Str, Str} = CallByName List.66 List.439 List.442;
inc List.565;
let List.559 : {List U8, U64} = CallByName List.145 List.440 List.565 List.441;
let List.562 : U64 = 1i64;
let List.561 : U64 = CallByName Num.19 List.442 List.562;
jump List.556 List.439 List.559 List.441 List.561 List.443;
else
dec List.439;
ret List.440;
in
jump List.556 #Derived_gen.35 #Derived_gen.36 #Derived_gen.37 #Derived_gen.38 #Derived_gen.39;
procedure List.93 (List.436, List.437, List.438):
let List.554 : U64 = 0i64;
@ -330,8 +330,8 @@ procedure Str.9 (Str.79):
else
let Str.300 : U8 = StructAtIndex 3 Str.80;
let Str.301 : U64 = StructAtIndex 0 Str.80;
let #Derived_gen.42 : Str = StructAtIndex 1 Str.80;
dec #Derived_gen.42;
let #Derived_gen.40 : Str = StructAtIndex 1 Str.80;
dec #Derived_gen.40;
let Str.299 : {U64, U8} = Struct {Str.301, Str.300};
let Str.298 : [C {U64, U8}, C Str] = TagId(0) Str.299;
ret Str.298;
@ -1222,8 +1222,8 @@ procedure TotallyNotJson.82 (TotallyNotJson.802, TotallyNotJson.803):
procedure TotallyNotJson.832 (TotallyNotJson.1493):
let TotallyNotJson.1494 : List Str = StructAtIndex 1 TotallyNotJson.1493;
let #Derived_gen.40 : List Str = StructAtIndex 0 TotallyNotJson.1493;
dec #Derived_gen.40;
let #Derived_gen.42 : List Str = StructAtIndex 0 TotallyNotJson.1493;
dec #Derived_gen.42;
ret TotallyNotJson.1494;
procedure TotallyNotJson.840 (TotallyNotJson.1214):
@ -1284,7 +1284,7 @@ procedure TotallyNotJson.95 (TotallyNotJson.829):
dec TotallyNotJson.1489;
ret TotallyNotJson.1488;
procedure TotallyNotJson.96 (#Derived_gen.36):
procedure TotallyNotJson.96 (#Derived_gen.30):
joinpoint TotallyNotJson.1496 TotallyNotJson.1168:
let TotallyNotJson.834 : List Str = StructAtIndex 0 TotallyNotJson.1168;
let TotallyNotJson.833 : List Str = StructAtIndex 1 TotallyNotJson.1168;
@ -1320,7 +1320,7 @@ procedure TotallyNotJson.96 (#Derived_gen.36):
let TotallyNotJson.1497 : {List Str, List Str} = Struct {TotallyNotJson.834, TotallyNotJson.833};
ret TotallyNotJson.1497;
in
jump TotallyNotJson.1496 #Derived_gen.36;
jump TotallyNotJson.1496 #Derived_gen.30;
procedure TotallyNotJson.97 (TotallyNotJson.837):
let TotallyNotJson.838 : List Str = CallByName Str.55 TotallyNotJson.837;
@ -1337,7 +1337,7 @@ procedure TotallyNotJson.97 (TotallyNotJson.837):
dec TotallyNotJson.1210;
ret TotallyNotJson.1209;
procedure TotallyNotJson.98 (#Derived_gen.14):
procedure TotallyNotJson.98 (#Derived_gen.34):
joinpoint TotallyNotJson.1217 TotallyNotJson.1169:
let TotallyNotJson.842 : List Str = StructAtIndex 0 TotallyNotJson.1169;
let TotallyNotJson.841 : List Str = StructAtIndex 1 TotallyNotJson.1169;
@ -1373,7 +1373,7 @@ procedure TotallyNotJson.98 (#Derived_gen.14):
let TotallyNotJson.1218 : {List Str, List Str} = Struct {TotallyNotJson.842, TotallyNotJson.841};
ret TotallyNotJson.1218;
in
jump TotallyNotJson.1217 #Derived_gen.14;
jump TotallyNotJson.1217 #Derived_gen.34;
procedure Test.0 ():
let Test.11 : Str = "foo";

View File

@ -88,7 +88,22 @@ procedure List.8 (#Attr.2, #Attr.3):
let List.529 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
ret List.529;
procedure List.80 (#Derived_gen.3, #Derived_gen.4, #Derived_gen.5, #Derived_gen.6, #Derived_gen.7):
procedure List.80 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4):
joinpoint List.540 List.439 List.440 List.441 List.442 List.443:
let List.542 : Int1 = CallByName Num.22 List.442 List.443;
if List.542 then
let List.549 : U8 = CallByName List.66 List.439 List.442;
let List.543 : List U8 = CallByName List.145 List.440 List.549 List.441;
let List.546 : U64 = 1i64;
let List.545 : U64 = CallByName Num.19 List.442 List.546;
jump List.540 List.439 List.543 List.441 List.545 List.443;
else
dec List.439;
ret List.440;
in
jump List.540 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4;
procedure List.80 (#Derived_gen.8, #Derived_gen.9, #Derived_gen.10, #Derived_gen.11, #Derived_gen.12):
joinpoint List.578 List.439 List.440 List.441 List.442 List.443:
let List.580 : Int1 = CallByName Num.22 List.442 List.443;
if List.580 then
@ -112,22 +127,7 @@ procedure List.80 (#Derived_gen.3, #Derived_gen.4, #Derived_gen.5, #Derived_gen.
let List.579 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) List.440;
ret List.579;
in
jump List.578 #Derived_gen.3 #Derived_gen.4 #Derived_gen.5 #Derived_gen.6 #Derived_gen.7;
procedure List.80 (#Derived_gen.8, #Derived_gen.9, #Derived_gen.10, #Derived_gen.11, #Derived_gen.12):
joinpoint List.540 List.439 List.440 List.441 List.442 List.443:
let List.542 : Int1 = CallByName Num.22 List.442 List.443;
if List.542 then
let List.549 : U8 = CallByName List.66 List.439 List.442;
let List.543 : List U8 = CallByName List.145 List.440 List.549 List.441;
let List.546 : U64 = 1i64;
let List.545 : U64 = CallByName Num.19 List.442 List.546;
jump List.540 List.439 List.543 List.441 List.545 List.443;
else
dec List.439;
ret List.440;
in
jump List.540 #Derived_gen.8 #Derived_gen.9 #Derived_gen.10 #Derived_gen.11 #Derived_gen.12;
jump List.578 #Derived_gen.8 #Derived_gen.9 #Derived_gen.10 #Derived_gen.11 #Derived_gen.12;
procedure List.93 (List.436, List.437, List.438):
let List.538 : U64 = 0i64;

View File

@ -147,38 +147,7 @@ procedure List.8 (#Attr.2, #Attr.3):
let List.599 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
ret List.599;
procedure List.80 (#Derived_gen.10, #Derived_gen.11, #Derived_gen.12, #Derived_gen.13, #Derived_gen.14):
joinpoint List.554 List.439 List.440 List.441 List.442 List.443:
let List.556 : Int1 = CallByName Num.22 List.442 List.443;
if List.556 then
let List.563 : Str = CallByName List.66 List.439 List.442;
inc List.563;
let List.557 : {List U8, U64} = CallByName List.145 List.440 List.563 List.441;
let List.560 : U64 = 1i64;
let List.559 : U64 = CallByName Num.19 List.442 List.560;
jump List.554 List.439 List.557 List.441 List.559 List.443;
else
dec List.439;
ret List.440;
in
jump List.554 #Derived_gen.10 #Derived_gen.11 #Derived_gen.12 #Derived_gen.13 #Derived_gen.14;
procedure List.80 (#Derived_gen.18, #Derived_gen.19, #Derived_gen.20, #Derived_gen.21, #Derived_gen.22):
joinpoint List.574 List.439 List.440 List.441 List.442 List.443:
let List.576 : Int1 = CallByName Num.22 List.442 List.443;
if List.576 then
let List.583 : U8 = CallByName List.66 List.439 List.442;
let List.577 : List U8 = CallByName List.145 List.440 List.583 List.441;
let List.580 : U64 = 1i64;
let List.579 : U64 = CallByName Num.19 List.442 List.580;
jump List.574 List.439 List.577 List.441 List.579 List.443;
else
dec List.439;
ret List.440;
in
jump List.574 #Derived_gen.18 #Derived_gen.19 #Derived_gen.20 #Derived_gen.21 #Derived_gen.22;
procedure List.80 (#Derived_gen.29, #Derived_gen.30, #Derived_gen.31, #Derived_gen.32, #Derived_gen.33):
procedure List.80 (#Derived_gen.16, #Derived_gen.17, #Derived_gen.18, #Derived_gen.19, #Derived_gen.20):
joinpoint List.627 List.439 List.440 List.441 List.442 List.443:
let List.629 : Int1 = CallByName Num.22 List.442 List.443;
if List.629 then
@ -202,7 +171,38 @@ procedure List.80 (#Derived_gen.29, #Derived_gen.30, #Derived_gen.31, #Derived_g
let List.628 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) List.440;
ret List.628;
in
jump List.627 #Derived_gen.29 #Derived_gen.30 #Derived_gen.31 #Derived_gen.32 #Derived_gen.33;
jump List.627 #Derived_gen.16 #Derived_gen.17 #Derived_gen.18 #Derived_gen.19 #Derived_gen.20;
procedure List.80 (#Derived_gen.24, #Derived_gen.25, #Derived_gen.26, #Derived_gen.27, #Derived_gen.28):
joinpoint List.574 List.439 List.440 List.441 List.442 List.443:
let List.576 : Int1 = CallByName Num.22 List.442 List.443;
if List.576 then
let List.583 : U8 = CallByName List.66 List.439 List.442;
let List.577 : List U8 = CallByName List.145 List.440 List.583 List.441;
let List.580 : U64 = 1i64;
let List.579 : U64 = CallByName Num.19 List.442 List.580;
jump List.574 List.439 List.577 List.441 List.579 List.443;
else
dec List.439;
ret List.440;
in
jump List.574 #Derived_gen.24 #Derived_gen.25 #Derived_gen.26 #Derived_gen.27 #Derived_gen.28;
procedure List.80 (#Derived_gen.29, #Derived_gen.30, #Derived_gen.31, #Derived_gen.32, #Derived_gen.33):
joinpoint List.554 List.439 List.440 List.441 List.442 List.443:
let List.556 : Int1 = CallByName Num.22 List.442 List.443;
if List.556 then
let List.563 : Str = CallByName List.66 List.439 List.442;
inc List.563;
let List.557 : {List U8, U64} = CallByName List.145 List.440 List.563 List.441;
let List.560 : U64 = 1i64;
let List.559 : U64 = CallByName Num.19 List.442 List.560;
jump List.554 List.439 List.557 List.441 List.559 List.443;
else
dec List.439;
ret List.440;
in
jump List.554 #Derived_gen.29 #Derived_gen.30 #Derived_gen.31 #Derived_gen.32 #Derived_gen.33;
procedure List.93 (List.436, List.437, List.438):
let List.552 : U64 = 0i64;

View File

@ -150,22 +150,7 @@ procedure List.8 (#Attr.2, #Attr.3):
let List.599 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
ret List.599;
procedure List.80 (#Derived_gen.20, #Derived_gen.21, #Derived_gen.22, #Derived_gen.23, #Derived_gen.24):
joinpoint List.574 List.439 List.440 List.441 List.442 List.443:
let List.576 : Int1 = CallByName Num.22 List.442 List.443;
if List.576 then
let List.583 : U8 = CallByName List.66 List.439 List.442;
let List.577 : List U8 = CallByName List.145 List.440 List.583 List.441;
let List.580 : U64 = 1i64;
let List.579 : U64 = CallByName Num.19 List.442 List.580;
jump List.574 List.439 List.577 List.441 List.579 List.443;
else
dec List.439;
ret List.440;
in
jump List.574 #Derived_gen.20 #Derived_gen.21 #Derived_gen.22 #Derived_gen.23 #Derived_gen.24;
procedure List.80 (#Derived_gen.25, #Derived_gen.26, #Derived_gen.27, #Derived_gen.28, #Derived_gen.29):
procedure List.80 (#Derived_gen.11, #Derived_gen.12, #Derived_gen.13, #Derived_gen.14, #Derived_gen.15):
joinpoint List.627 List.439 List.440 List.441 List.442 List.443:
let List.629 : Int1 = CallByName Num.22 List.442 List.443;
if List.629 then
@ -189,9 +174,9 @@ procedure List.80 (#Derived_gen.25, #Derived_gen.26, #Derived_gen.27, #Derived_g
let List.628 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) List.440;
ret List.628;
in
jump List.627 #Derived_gen.25 #Derived_gen.26 #Derived_gen.27 #Derived_gen.28 #Derived_gen.29;
jump List.627 #Derived_gen.11 #Derived_gen.12 #Derived_gen.13 #Derived_gen.14 #Derived_gen.15;
procedure List.80 (#Derived_gen.30, #Derived_gen.31, #Derived_gen.32, #Derived_gen.33, #Derived_gen.34):
procedure List.80 (#Derived_gen.19, #Derived_gen.20, #Derived_gen.21, #Derived_gen.22, #Derived_gen.23):
joinpoint List.554 List.439 List.440 List.441 List.442 List.443:
let List.556 : Int1 = CallByName Num.22 List.442 List.443;
if List.556 then
@ -205,7 +190,22 @@ procedure List.80 (#Derived_gen.30, #Derived_gen.31, #Derived_gen.32, #Derived_g
dec List.439;
ret List.440;
in
jump List.554 #Derived_gen.30 #Derived_gen.31 #Derived_gen.32 #Derived_gen.33 #Derived_gen.34;
jump List.554 #Derived_gen.19 #Derived_gen.20 #Derived_gen.21 #Derived_gen.22 #Derived_gen.23;
procedure List.80 (#Derived_gen.24, #Derived_gen.25, #Derived_gen.26, #Derived_gen.27, #Derived_gen.28):
joinpoint List.574 List.439 List.440 List.441 List.442 List.443:
let List.576 : Int1 = CallByName Num.22 List.442 List.443;
if List.576 then
let List.583 : U8 = CallByName List.66 List.439 List.442;
let List.577 : List U8 = CallByName List.145 List.440 List.583 List.441;
let List.580 : U64 = 1i64;
let List.579 : U64 = CallByName Num.19 List.442 List.580;
jump List.574 List.439 List.577 List.441 List.579 List.443;
else
dec List.439;
ret List.440;
in
jump List.574 #Derived_gen.24 #Derived_gen.25 #Derived_gen.26 #Derived_gen.27 #Derived_gen.28;
procedure List.93 (List.436, List.437, List.438):
let List.552 : U64 = 0i64;

View File

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

View File

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

View File

@ -6,10 +6,10 @@ procedure List.5 (#Attr.2, #Attr.3):
procedure Test.2 (Test.5):
let Test.6 : List [<rnnu>C List *self] = UnionAtIndex (Id 0) (Index 0) Test.5;
inc Test.6;
let #Derived_gen.1 : [<rnnu>C List *self] = Reset { symbol: Test.5, id: UpdateModeId { id: 0 } };
let #Derived_gen.2 : [<rnnu>C List *self] = Reset { symbol: Test.5, id: UpdateModeId { id: 1 } };
let Test.15 : {} = Struct {};
let Test.7 : List [<rnnu>C List *self] = CallByName List.5 Test.6 Test.15;
let Test.14 : [<rnnu>C List *self] = Reuse #Derived_gen.1 UpdateModeId { id: 0 } TagId(0) Test.7;
let Test.14 : [<rnnu>C List *self] = Reuse #Derived_gen.2 UpdateModeId { id: 1 } TagId(0) Test.7;
ret Test.14;
procedure Test.0 ():

View File

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

View File

@ -136,7 +136,23 @@ procedure List.8 (#Attr.2, #Attr.3):
let List.598 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
ret List.598;
procedure List.80 (#Derived_gen.11, #Derived_gen.12, #Derived_gen.13, #Derived_gen.14, #Derived_gen.15):
procedure List.80 (#Derived_gen.19, #Derived_gen.20, #Derived_gen.21, #Derived_gen.22, #Derived_gen.23):
joinpoint List.553 List.439 List.440 List.441 List.442 List.443:
let List.555 : Int1 = CallByName Num.22 List.442 List.443;
if List.555 then
let List.562 : Str = CallByName List.66 List.439 List.442;
inc List.562;
let List.556 : {List U8, U64} = CallByName List.145 List.440 List.562 List.441;
let List.559 : U64 = 1i64;
let List.558 : U64 = CallByName Num.19 List.442 List.559;
jump List.553 List.439 List.556 List.441 List.558 List.443;
else
dec List.439;
ret List.440;
in
jump List.553 #Derived_gen.19 #Derived_gen.20 #Derived_gen.21 #Derived_gen.22 #Derived_gen.23;
procedure List.80 (#Derived_gen.3, #Derived_gen.4, #Derived_gen.5, #Derived_gen.6, #Derived_gen.7):
joinpoint List.573 List.439 List.440 List.441 List.442 List.443:
let List.575 : Int1 = CallByName Num.22 List.442 List.443;
if List.575 then
@ -149,9 +165,9 @@ procedure List.80 (#Derived_gen.11, #Derived_gen.12, #Derived_gen.13, #Derived_g
dec List.439;
ret List.440;
in
jump List.573 #Derived_gen.11 #Derived_gen.12 #Derived_gen.13 #Derived_gen.14 #Derived_gen.15;
jump List.573 #Derived_gen.3 #Derived_gen.4 #Derived_gen.5 #Derived_gen.6 #Derived_gen.7;
procedure List.80 (#Derived_gen.16, #Derived_gen.17, #Derived_gen.18, #Derived_gen.19, #Derived_gen.20):
procedure List.80 (#Derived_gen.8, #Derived_gen.9, #Derived_gen.10, #Derived_gen.11, #Derived_gen.12):
joinpoint List.626 List.439 List.440 List.441 List.442 List.443:
let List.628 : Int1 = CallByName Num.22 List.442 List.443;
if List.628 then
@ -175,23 +191,7 @@ procedure List.80 (#Derived_gen.16, #Derived_gen.17, #Derived_gen.18, #Derived_g
let List.627 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) List.440;
ret List.627;
in
jump List.626 #Derived_gen.16 #Derived_gen.17 #Derived_gen.18 #Derived_gen.19 #Derived_gen.20;
procedure List.80 (#Derived_gen.6, #Derived_gen.7, #Derived_gen.8, #Derived_gen.9, #Derived_gen.10):
joinpoint List.553 List.439 List.440 List.441 List.442 List.443:
let List.555 : Int1 = CallByName Num.22 List.442 List.443;
if List.555 then
let List.562 : Str = CallByName List.66 List.439 List.442;
inc List.562;
let List.556 : {List U8, U64} = CallByName List.145 List.440 List.562 List.441;
let List.559 : U64 = 1i64;
let List.558 : U64 = CallByName Num.19 List.442 List.559;
jump List.553 List.439 List.556 List.441 List.558 List.443;
else
dec List.439;
ret List.440;
in
jump List.553 #Derived_gen.6 #Derived_gen.7 #Derived_gen.8 #Derived_gen.9 #Derived_gen.10;
jump List.626 #Derived_gen.8 #Derived_gen.9 #Derived_gen.10 #Derived_gen.11 #Derived_gen.12;
procedure List.93 (List.436, List.437, List.438):
let List.551 : U64 = 0i64;

View File

@ -131,7 +131,7 @@ procedure List.8 (#Attr.2, #Attr.3):
let List.616 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
ret List.616;
procedure List.80 (#Derived_gen.21, #Derived_gen.22, #Derived_gen.23, #Derived_gen.24, #Derived_gen.25):
procedure List.80 (#Derived_gen.18, #Derived_gen.19, #Derived_gen.20, #Derived_gen.21, #Derived_gen.22):
joinpoint List.601 List.439 List.440 List.441 List.442 List.443:
let List.603 : Int1 = CallByName Num.22 List.442 List.443;
if List.603 then
@ -144,9 +144,9 @@ procedure List.80 (#Derived_gen.21, #Derived_gen.22, #Derived_gen.23, #Derived_g
dec List.439;
ret List.440;
in
jump List.601 #Derived_gen.21 #Derived_gen.22 #Derived_gen.23 #Derived_gen.24 #Derived_gen.25;
jump List.601 #Derived_gen.18 #Derived_gen.19 #Derived_gen.20 #Derived_gen.21 #Derived_gen.22;
procedure List.80 (#Derived_gen.29, #Derived_gen.30, #Derived_gen.31, #Derived_gen.32, #Derived_gen.33):
procedure List.80 (#Derived_gen.38, #Derived_gen.39, #Derived_gen.40, #Derived_gen.41, #Derived_gen.42):
joinpoint List.553 List.439 List.440 List.441 List.442 List.443:
let List.555 : Int1 = CallByName Num.22 List.442 List.443;
if List.555 then
@ -159,7 +159,7 @@ procedure List.80 (#Derived_gen.29, #Derived_gen.30, #Derived_gen.31, #Derived_g
dec List.439;
ret List.440;
in
jump List.553 #Derived_gen.29 #Derived_gen.30 #Derived_gen.31 #Derived_gen.32 #Derived_gen.33;
jump List.553 #Derived_gen.38 #Derived_gen.39 #Derived_gen.40 #Derived_gen.41 #Derived_gen.42;
procedure List.93 (List.436, List.437, List.438):
let List.551 : U64 = 0i64;

View File

@ -16,6 +16,7 @@ const EXPANDED_STACK_SIZE: usize = 8 * 1024 * 1024;
use bumpalo::Bump;
use roc_collections::all::MutMap;
use roc_load::ExecutionMode;
use roc_load::FunctionKind;
use roc_load::LoadConfig;
use roc_load::LoadMonomorphizedError;
use roc_load::Threading;
@ -104,6 +105,8 @@ fn compiles_to_ir(test_name: &str, src: &str, mode: &str, allow_type_errors: boo
let load_config = LoadConfig {
target_info: TARGET_INFO,
// TODO parameterize
function_kind: FunctionKind::LambdaSet,
threading: Threading::Single,
render: roc_reporting::report::RenderTarget::Generic,
palette: roc_reporting::report::DEFAULT_PALETTE,

View File

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

View File

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

View File

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

View File

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

View File

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

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"
f = \{} ->
#^{-1} <1599><116>{} -<119>[[f(1)]]-> <115>[Ok <1607>{}]<79>*
#^{-1} <1600><117>{} -<120>[[f(1)]]-> <116>[Ok <1608>{}]<80>*
when g {} is
# ^ <1589><1607>{} -<1597>[[g(2)]]-> <71>[Ok <1607>{}]<101>*
# ^ <1590><1608>{} -<1598>[[g(2)]]-> <72>[Ok <1608>{}]<102>*
_ -> Ok {}
g = \{} ->
#^{-1} <1589><1607>{} -<1597>[[g(2)]]-> <71>[Ok <1607>{}]<101>*
#^{-1} <1590><1608>{} -<1598>[[g(2)]]-> <72>[Ok <1608>{}]<102>*
when h {} is
# ^ <1594><1607>{} -<1602>[[h(3)]]-> <93>[Ok <1607>{}]<123>*
# ^ <1595><1608>{} -<1603>[[h(3)]]-> <94>[Ok <1608>{}]<124>*
_ -> Ok {}
h = \{} ->
#^{-1} <1594><1607>{} -<1602>[[h(3)]]-> <93>[Ok <1607>{}]<123>*
#^{-1} <1595><1608>{} -<1603>[[h(3)]]-> <94>[Ok <1608>{}]<124>*
when f {} is
# ^ <1599><116>{} -<119>[[f(1)]]-> <115>[Ok <1607>{}]<79>*
# ^ <1600><117>{} -<120>[[f(1)]]-> <116>[Ok <1608>{}]<80>*
_ -> Ok {}
main = f {}
# ^ <1609><132>{} -<135>[[f(1)]]-> <137>[Ok <1607>{}]<1608>w_a
# ^ <1610><133>{} -<136>[[f(1)]]-> <138>[Ok <1608>{}]<1609>w_a

View File

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

View File

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

View File

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

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

View File

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

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