mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-03 11:52:19 +00:00
Merge pull request #4819 from roc-lang/i4717
Compile anonymous closures with multiple specializations, and various fixes to lambda set compilation
This commit is contained in:
commit
fe2bab56f9
6 changed files with 391 additions and 270 deletions
|
@ -2,8 +2,8 @@
|
|||
|
||||
use crate::layout::{
|
||||
self, Builtin, CapturesNiche, ClosureCallOptions, ClosureRepresentation, EnumDispatch,
|
||||
LambdaName, LambdaSet, Layout, LayoutCache, LayoutInterner, LayoutProblem, RawFunctionLayout,
|
||||
STLayoutInterner, TagIdIntType, UnionLayout, WrappedVariant,
|
||||
LambdaName, LambdaSet, Layout, LayoutCache, LayoutProblem, RawFunctionLayout, STLayoutInterner,
|
||||
TagIdIntType, UnionLayout, WrappedVariant,
|
||||
};
|
||||
use bumpalo::collections::{CollectIn, Vec};
|
||||
use bumpalo::Bump;
|
||||
|
@ -1089,12 +1089,7 @@ impl<'a> Procs<'a> {
|
|||
.raw_from_var(env.arena, annotation, env.subs)
|
||||
.unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err));
|
||||
|
||||
let top_level = ProcLayout::from_raw(
|
||||
env.arena,
|
||||
&layout_cache.interner,
|
||||
raw_layout,
|
||||
name.captures_niche(),
|
||||
);
|
||||
let top_level = ProcLayout::from_raw_named(env.arena, name, raw_layout);
|
||||
|
||||
// anonymous functions cannot reference themselves, therefore cannot be tail-recursive
|
||||
// EXCEPT when the closure conversion makes it tail-recursive.
|
||||
|
@ -1169,16 +1164,8 @@ impl<'a> Procs<'a> {
|
|||
let partial_proc_id = if let Some(partial_proc_id) =
|
||||
self.partial_procs.symbol_to_id(name.name())
|
||||
{
|
||||
let existing = self.partial_procs.get_id(partial_proc_id);
|
||||
// if we're adding the same partial proc twice, they must be the actual same!
|
||||
//
|
||||
// NOTE we can't skip extra work! we still need to make the specialization for this
|
||||
// invocation. The content of the `annotation` can be different, even if the variable
|
||||
// number is the same
|
||||
debug_assert_eq!(annotation, existing.annotation);
|
||||
debug_assert_eq!(captured_symbols, existing.captured_symbols);
|
||||
debug_assert_eq!(is_self_recursive, existing.is_self_recursive);
|
||||
|
||||
// NOTE we can't skip extra work! We still need to make the specialization for this
|
||||
// invocation.
|
||||
partial_proc_id
|
||||
} else {
|
||||
let pattern_symbols = pattern_symbols.into_bump_slice();
|
||||
|
@ -1204,29 +1191,13 @@ impl<'a> Procs<'a> {
|
|||
&[],
|
||||
partial_proc_id,
|
||||
) {
|
||||
Ok((proc, _ignore_layout)) => {
|
||||
// the `layout` is a function pointer, while `_ignore_layout` can be a
|
||||
// closure. We only specialize functions, storing this value with a closure
|
||||
// layout will give trouble.
|
||||
let arguments = Vec::from_iter_in(
|
||||
proc.args.iter().map(|(l, _)| *l),
|
||||
env.arena,
|
||||
)
|
||||
.into_bump_slice();
|
||||
|
||||
let proper_layout = ProcLayout {
|
||||
arguments,
|
||||
result: proc.ret_layout,
|
||||
captures_niche: proc.name.captures_niche(),
|
||||
};
|
||||
|
||||
// NOTE: some functions are specialized to have a closure, but don't actually
|
||||
// need any closure argument. Here is where we correct this sort of thing,
|
||||
// by trusting the layout of the Proc, not of what we specialize for
|
||||
self.specialized.remove_specialized(name.name(), &layout);
|
||||
Ok((proc, layout)) => {
|
||||
let proc_name = proc.name;
|
||||
let function_layout =
|
||||
ProcLayout::from_raw_named(env.arena, proc_name, layout);
|
||||
self.specialized.insert_specialized(
|
||||
name.name(),
|
||||
proper_layout,
|
||||
proc_name.name(),
|
||||
function_layout,
|
||||
proc,
|
||||
);
|
||||
}
|
||||
|
@ -1282,17 +1253,20 @@ impl<'a> Procs<'a> {
|
|||
suspended.specialization(env.subs, name, layout, fn_var);
|
||||
}
|
||||
(PendingSpecializations::Making(_), false) => {
|
||||
let symbol = name;
|
||||
let proc_name = name;
|
||||
|
||||
let partial_proc_id = match self.partial_procs.symbol_to_id(symbol.name()) {
|
||||
let partial_proc_id = match self.partial_procs.symbol_to_id(proc_name.name()) {
|
||||
Some(p) => p,
|
||||
None => panic!("no partial_proc for {:?} in module {:?}", symbol, env.home),
|
||||
None => panic!(
|
||||
"no partial_proc for {:?} in module {:?}",
|
||||
proc_name, env.home
|
||||
),
|
||||
};
|
||||
|
||||
// Mark this proc as in-progress, so if we're dealing with
|
||||
// mutually recursive functions, we don't loop forever.
|
||||
// (We had a bug around this before this system existed!)
|
||||
self.specialized.mark_in_progress(symbol.name(), layout);
|
||||
self.specialized.mark_in_progress(proc_name.name(), layout);
|
||||
|
||||
// See https://github.com/roc-lang/roc/issues/1600
|
||||
//
|
||||
|
@ -1301,39 +1275,21 @@ impl<'a> Procs<'a> {
|
|||
//
|
||||
// fn_var is the variable representing the type that we actually need for the
|
||||
// function right here.
|
||||
//
|
||||
// For some reason, it matters that we unify with the original variable. Extracting
|
||||
// that variable into a SolvedType and then introducing it again severs some
|
||||
// connection that turns out to be important
|
||||
match specialize_variable(
|
||||
env,
|
||||
self,
|
||||
symbol,
|
||||
proc_name,
|
||||
layout_cache,
|
||||
fn_var,
|
||||
Default::default(),
|
||||
partial_proc_id,
|
||||
) {
|
||||
Ok((proc, _ignore_layout)) => {
|
||||
// the `layout` is a function pointer, while `_ignore_layout` can be a
|
||||
// closure. We only specialize functions, storing this value with a closure
|
||||
// layout will give trouble.
|
||||
let arguments =
|
||||
Vec::from_iter_in(proc.args.iter().map(|(l, _)| *l), env.arena)
|
||||
.into_bump_slice();
|
||||
Ok((proc, raw_layout)) => {
|
||||
let proc_layout =
|
||||
ProcLayout::from_raw_named(env.arena, proc_name, raw_layout);
|
||||
|
||||
let proper_layout = ProcLayout {
|
||||
arguments,
|
||||
result: proc.ret_layout,
|
||||
captures_niche: proc.name.captures_niche(),
|
||||
};
|
||||
|
||||
// NOTE: some functions are specialized to have a closure, but don't actually
|
||||
// need any closure argument. Here is where we correct this sort of thing,
|
||||
// by trusting the layout of the Proc, not of what we specialize for
|
||||
self.specialized.remove_specialized(symbol.name(), &layout);
|
||||
self.specialized
|
||||
.insert_specialized(symbol.name(), proper_layout, proc);
|
||||
.insert_specialized(proc_name.name(), proc_layout, proc);
|
||||
}
|
||||
Err(error) => {
|
||||
panic!("TODO generate a RuntimeError message for {:?}", error);
|
||||
|
@ -2463,17 +2419,6 @@ fn from_can_let<'a>(
|
|||
|
||||
lower_rest!(variable, cont.value)
|
||||
}
|
||||
Accessor(accessor_data) => {
|
||||
let fresh_record_symbol = env.unique_symbol();
|
||||
register_noncapturing_closure(
|
||||
env,
|
||||
procs,
|
||||
*symbol,
|
||||
accessor_data.to_closure_data(fresh_record_symbol),
|
||||
);
|
||||
|
||||
lower_rest!(variable, cont.value)
|
||||
}
|
||||
Var(original, _) | AbilityMember(original, _, _)
|
||||
if procs.get_partial_proc(original).is_none() =>
|
||||
{
|
||||
|
@ -3040,55 +2985,18 @@ fn specialize_suspended<'a>(
|
|||
};
|
||||
|
||||
match specialize_variable(env, procs, name, layout_cache, var, &[], partial_proc) {
|
||||
Ok((proc, _layout)) => {
|
||||
// TODO this code is duplicated elsewhere
|
||||
|
||||
// the `layout` is a function pointer, while `_ignore_layout` can be a
|
||||
// closure. We only specialize functions, storing this value with a closure
|
||||
// layout will give trouble.
|
||||
let arguments = Vec::from_iter_in(proc.args.iter().map(|(l, _)| *l), env.arena)
|
||||
.into_bump_slice();
|
||||
|
||||
let proper_layout = ProcLayout {
|
||||
arguments,
|
||||
result: proc.ret_layout,
|
||||
captures_niche: proc.name.captures_niche(),
|
||||
};
|
||||
if procs.is_module_thunk(proc.name.name()) {
|
||||
debug_assert!(
|
||||
proper_layout.arguments.is_empty(),
|
||||
"{:?} from {:?}",
|
||||
name,
|
||||
proper_layout
|
||||
);
|
||||
}
|
||||
|
||||
// NOTE: some functions are specialized to have a closure, but don't actually
|
||||
// need any closure argument. Here is where we correct this sort of thing,
|
||||
// by trusting the layout of the Proc, not of what we specialize for
|
||||
Ok((proc, raw_layout)) => {
|
||||
let proc_layout = ProcLayout::from_raw_named(env.arena, name, raw_layout);
|
||||
procs
|
||||
.specialized
|
||||
.remove_specialized(name.name(), &outside_layout);
|
||||
procs
|
||||
.specialized
|
||||
.insert_specialized(name.name(), proper_layout, proc);
|
||||
.insert_specialized(name.name(), proc_layout, proc);
|
||||
}
|
||||
Err(SpecializeFailure {
|
||||
attempted_layout, ..
|
||||
}) => {
|
||||
let proc = generate_runtime_error_function(
|
||||
env,
|
||||
layout_cache,
|
||||
name.name(),
|
||||
attempted_layout,
|
||||
);
|
||||
let proc = generate_runtime_error_function(env, name, attempted_layout);
|
||||
|
||||
let top_level = ProcLayout::from_raw(
|
||||
env.arena,
|
||||
&layout_cache.interner,
|
||||
attempted_layout,
|
||||
CapturesNiche::no_niche(),
|
||||
);
|
||||
let top_level = ProcLayout::from_raw_named(env.arena, name, attempted_layout);
|
||||
|
||||
procs
|
||||
.specialized
|
||||
|
@ -3234,12 +3142,7 @@ fn specialize_external_help<'a>(
|
|||
|
||||
match specialization_result {
|
||||
Ok((proc, layout)) => {
|
||||
let top_level = ProcLayout::from_raw(
|
||||
env.arena,
|
||||
&layout_cache.interner,
|
||||
layout,
|
||||
proc.name.captures_niche(),
|
||||
);
|
||||
let top_level = ProcLayout::from_raw_named(env.arena, name, layout);
|
||||
|
||||
if procs.is_module_thunk(name.name()) {
|
||||
debug_assert!(top_level.arguments.is_empty());
|
||||
|
@ -3250,15 +3153,9 @@ fn specialize_external_help<'a>(
|
|||
.insert_specialized(name.name(), top_level, proc);
|
||||
}
|
||||
Err(SpecializeFailure { attempted_layout }) => {
|
||||
let proc =
|
||||
generate_runtime_error_function(env, layout_cache, name.name(), attempted_layout);
|
||||
let proc = generate_runtime_error_function(env, name, attempted_layout);
|
||||
|
||||
let top_level = ProcLayout::from_raw(
|
||||
env.arena,
|
||||
&layout_cache.interner,
|
||||
attempted_layout,
|
||||
proc.name.captures_niche(),
|
||||
);
|
||||
let top_level = ProcLayout::from_raw_named(env.arena, name, attempted_layout);
|
||||
|
||||
procs
|
||||
.specialized
|
||||
|
@ -3269,8 +3166,7 @@ fn specialize_external_help<'a>(
|
|||
|
||||
fn generate_runtime_error_function<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
layout_cache: &LayoutCache<'a>,
|
||||
name: Symbol,
|
||||
lambda_name: LambdaName<'a>,
|
||||
layout: RawFunctionLayout<'a>,
|
||||
) -> Proc<'a> {
|
||||
let mut msg = bumpalo::collections::string::String::with_capacity_in(80, env.arena);
|
||||
|
@ -3278,7 +3174,7 @@ fn generate_runtime_error_function<'a>(
|
|||
write!(
|
||||
&mut msg,
|
||||
"The {:?} function could not be generated, likely due to a type error.",
|
||||
name
|
||||
lambda_name.name(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
@ -3294,7 +3190,7 @@ fn generate_runtime_error_function<'a>(
|
|||
let (args, ret_layout) = match layout {
|
||||
RawFunctionLayout::Function(arg_layouts, lambda_set, ret_layout) => {
|
||||
let real_arg_layouts =
|
||||
lambda_set.extend_argument_list(env.arena, &layout_cache.interner, arg_layouts);
|
||||
lambda_set.extend_argument_list_for_named(env.arena, lambda_name, arg_layouts);
|
||||
let mut args = Vec::with_capacity_in(real_arg_layouts.len(), env.arena);
|
||||
|
||||
for arg in arg_layouts {
|
||||
|
@ -3310,7 +3206,7 @@ fn generate_runtime_error_function<'a>(
|
|||
};
|
||||
|
||||
Proc {
|
||||
name: LambdaName::no_niche(name),
|
||||
name: lambda_name,
|
||||
args,
|
||||
body: runtime_error,
|
||||
closure_data_layout: None,
|
||||
|
@ -3508,8 +3404,9 @@ fn specialize_proc_help<'a>(
|
|||
let hole = env.arena.alloc(Stmt::Ret(assigned));
|
||||
let forced = force_thunk(env, lambda_name.name(), result, assigned, hole);
|
||||
|
||||
let lambda_name = LambdaName::no_niche(name);
|
||||
let proc = Proc {
|
||||
name: LambdaName::no_niche(name),
|
||||
name: lambda_name,
|
||||
args: &[],
|
||||
body: forced,
|
||||
closure_data_layout: None,
|
||||
|
@ -3519,12 +3416,7 @@ fn specialize_proc_help<'a>(
|
|||
host_exposed_layouts: HostExposedLayouts::NotHostExposed,
|
||||
};
|
||||
|
||||
let top_level = ProcLayout::from_raw(
|
||||
env.arena,
|
||||
&layout_cache.interner,
|
||||
layout,
|
||||
CapturesNiche::no_niche(),
|
||||
);
|
||||
let top_level = ProcLayout::from_raw_named(env.arena, lambda_name, layout);
|
||||
|
||||
procs.specialized.insert_specialized(name, top_level, proc);
|
||||
|
||||
|
@ -4062,7 +3954,7 @@ pub struct ProcLayout<'a> {
|
|||
}
|
||||
|
||||
impl<'a> ProcLayout<'a> {
|
||||
pub fn new(
|
||||
pub(crate) fn new(
|
||||
arena: &'a Bump,
|
||||
old_arguments: &'a [Layout<'a>],
|
||||
old_captures_niche: CapturesNiche<'a>,
|
||||
|
@ -4085,16 +3977,16 @@ impl<'a> ProcLayout<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn from_raw(
|
||||
fn from_raw_named(
|
||||
arena: &'a Bump,
|
||||
interner: &LayoutInterner<'a>,
|
||||
lambda_name: LambdaName<'a>,
|
||||
raw: RawFunctionLayout<'a>,
|
||||
captures_niche: CapturesNiche<'a>,
|
||||
) -> Self {
|
||||
match raw {
|
||||
RawFunctionLayout::Function(arguments, lambda_set, result) => {
|
||||
let arguments = lambda_set.extend_argument_list(arena, interner, arguments);
|
||||
ProcLayout::new(arena, arguments, captures_niche, *result)
|
||||
let arguments =
|
||||
lambda_set.extend_argument_list_for_named(arena, lambda_name, arguments);
|
||||
ProcLayout::new(arena, arguments, lambda_name.captures_niche(), *result)
|
||||
}
|
||||
RawFunctionLayout::ZeroArgumentThunk(result) => {
|
||||
ProcLayout::new(arena, &[], CapturesNiche::no_niche(), result)
|
||||
|
@ -5567,15 +5459,8 @@ pub fn with_hole<'a>(
|
|||
"match_on_closure_argument"
|
||||
);
|
||||
|
||||
// NB: I don't think the top_level here can have a captures niche?
|
||||
let top_level_capture_niche = CapturesNiche::no_niche();
|
||||
let top_level = ProcLayout::from_raw(env.arena, &layout_cache.interner, closure_data_layout, top_level_capture_niche);
|
||||
|
||||
let arena = env.arena;
|
||||
|
||||
let arg_layouts = top_level.arguments;
|
||||
let ret_layout = top_level.result;
|
||||
|
||||
match closure_data_layout {
|
||||
RawFunctionLayout::Function(_, lambda_set, _) => {
|
||||
lowlevel_match_on_lambda_set(
|
||||
|
@ -5584,9 +5469,14 @@ pub fn with_hole<'a>(
|
|||
lambda_set,
|
||||
op,
|
||||
closure_data_symbol,
|
||||
|(top_level_function, closure_data, closure_env_layout, specialization_id, update_mode)| {
|
||||
|(lambda_name, closure_data, closure_env_layout, specialization_id, update_mode)| {
|
||||
// Build a call for a specific lambda in the set
|
||||
let top_level = ProcLayout::from_raw_named(env.arena, lambda_name, closure_data_layout);
|
||||
let arg_layouts = top_level.arguments;
|
||||
let ret_layout = top_level.result;
|
||||
|
||||
let passed_function = PassedFunction {
|
||||
name: top_level_function,
|
||||
name: lambda_name,
|
||||
captured_environment: closure_data_symbol,
|
||||
owns_captured_environment: false,
|
||||
specialization_id,
|
||||
|
@ -5594,7 +5484,6 @@ pub fn with_hole<'a>(
|
|||
return_layout: ret_layout,
|
||||
};
|
||||
|
||||
|
||||
let higher_order = HigherOrderLowLevel {
|
||||
op: crate::low_level::HigherOrder::$ho { $($x,)* },
|
||||
closure_env_layout,
|
||||
|
@ -5604,7 +5493,7 @@ pub fn with_hole<'a>(
|
|||
|
||||
self::Call {
|
||||
call_type: CallType::HigherOrder(arena.alloc(higher_order)),
|
||||
arguments: arena.alloc([$($x,)* top_level_function.name(), closure_data]),
|
||||
arguments: arena.alloc([$($x,)* lambda_name.name(), closure_data]),
|
||||
}
|
||||
},
|
||||
layout,
|
||||
|
@ -8334,12 +8223,8 @@ fn specialize_symbol<'a>(
|
|||
"imported functions are top-level and should never capture"
|
||||
);
|
||||
|
||||
let function_ptr_layout = ProcLayout::from_raw(
|
||||
env.arena,
|
||||
&layout_cache.interner,
|
||||
raw,
|
||||
lambda_name.captures_niche(),
|
||||
);
|
||||
let function_ptr_layout =
|
||||
ProcLayout::from_raw_named(env.arena, lambda_name, raw);
|
||||
procs.insert_passed_by_name(
|
||||
env,
|
||||
arg_var,
|
||||
|
@ -8370,17 +8255,13 @@ fn specialize_symbol<'a>(
|
|||
};
|
||||
|
||||
let raw = RawFunctionLayout::ZeroArgumentThunk(layout);
|
||||
let top_level = ProcLayout::from_raw(
|
||||
env.arena,
|
||||
&layout_cache.interner,
|
||||
raw,
|
||||
CapturesNiche::no_niche(),
|
||||
);
|
||||
let lambda_name = LambdaName::no_niche(original);
|
||||
let top_level = ProcLayout::from_raw_named(env.arena, lambda_name, raw);
|
||||
|
||||
procs.insert_passed_by_name(
|
||||
env,
|
||||
arg_var,
|
||||
LambdaName::no_niche(original),
|
||||
lambda_name,
|
||||
top_level,
|
||||
layout_cache,
|
||||
);
|
||||
|
@ -8447,12 +8328,8 @@ fn specialize_symbol<'a>(
|
|||
);
|
||||
|
||||
// define the function pointer
|
||||
let function_ptr_layout = ProcLayout::from_raw(
|
||||
env.arena,
|
||||
&layout_cache.interner,
|
||||
res_layout,
|
||||
lambda_name.captures_niche(),
|
||||
);
|
||||
let function_ptr_layout =
|
||||
ProcLayout::from_raw_named(env.arena, lambda_name, res_layout);
|
||||
|
||||
// this is a closure by capture, meaning it itself captures local variables.
|
||||
procs.insert_passed_by_name(
|
||||
|
@ -8502,12 +8379,8 @@ fn specialize_symbol<'a>(
|
|||
debug_assert!(lambda_name.no_captures());
|
||||
|
||||
// define the function pointer
|
||||
let function_ptr_layout = ProcLayout::from_raw(
|
||||
env.arena,
|
||||
&layout_cache.interner,
|
||||
res_layout,
|
||||
lambda_name.captures_niche(),
|
||||
);
|
||||
let function_ptr_layout =
|
||||
ProcLayout::from_raw_named(env.arena, lambda_name, res_layout);
|
||||
|
||||
procs.insert_passed_by_name(
|
||||
env,
|
||||
|
@ -8859,7 +8732,7 @@ fn call_by_name_help<'a>(
|
|||
// number of arguments actually passed.
|
||||
let top_level_layout = {
|
||||
let argument_layouts =
|
||||
lambda_set.extend_argument_list(env.arena, &layout_cache.interner, argument_layouts);
|
||||
lambda_set.extend_argument_list_for_named(env.arena, proc_name, argument_layouts);
|
||||
ProcLayout::new(
|
||||
env.arena,
|
||||
argument_layouts,
|
||||
|
@ -9041,12 +8914,8 @@ fn call_by_name_help<'a>(
|
|||
) {
|
||||
Ok((proc, layout)) => {
|
||||
let proc_name = proc.name;
|
||||
let function_layout = ProcLayout::from_raw(
|
||||
env.arena,
|
||||
&layout_cache.interner,
|
||||
layout,
|
||||
proc_name.captures_niche(),
|
||||
);
|
||||
let function_layout =
|
||||
ProcLayout::from_raw_named(env.arena, proc_name, layout);
|
||||
procs.specialized.insert_specialized(
|
||||
proc_name.name(),
|
||||
function_layout,
|
||||
|
@ -9071,17 +8940,15 @@ fn call_by_name_help<'a>(
|
|||
Err(SpecializeFailure { attempted_layout }) => {
|
||||
let proc = generate_runtime_error_function(
|
||||
env,
|
||||
layout_cache,
|
||||
proc_name.name(),
|
||||
proc_name,
|
||||
attempted_layout,
|
||||
);
|
||||
|
||||
let proc_name = proc.name;
|
||||
let function_layout = ProcLayout::from_raw(
|
||||
let function_layout = ProcLayout::from_raw_named(
|
||||
env.arena,
|
||||
&layout_cache.interner,
|
||||
proc_name,
|
||||
attempted_layout,
|
||||
proc_name.captures_niche(),
|
||||
);
|
||||
procs.specialized.insert_specialized(
|
||||
proc_name.name(),
|
||||
|
@ -9216,8 +9083,7 @@ fn call_by_name_module_thunk<'a>(
|
|||
Err(SpecializeFailure { attempted_layout }) => {
|
||||
let proc = generate_runtime_error_function(
|
||||
env,
|
||||
layout_cache,
|
||||
proc_name,
|
||||
LambdaName::no_niche(proc_name),
|
||||
attempted_layout,
|
||||
);
|
||||
|
||||
|
@ -10175,15 +10041,6 @@ fn from_can_pattern_help<'a>(
|
|||
// it must be an optional field, and we will use the default
|
||||
match &destruct.value.typ {
|
||||
roc_can::pattern::DestructType::Optional(field_var, loc_expr) => {
|
||||
// TODO these don't match up in the uniqueness inference; when we remove
|
||||
// that, reinstate this assert!
|
||||
//
|
||||
// dbg!(&env.subs.get_content_without_compacting(*field_var));
|
||||
// dbg!(&env.subs.get_content_without_compacting(destruct.var).content);
|
||||
// debug_assert_eq!(
|
||||
// env.subs.get_root_key_without_compacting(*field_var),
|
||||
// env.subs.get_root_key_without_compacting(destruct.value.var)
|
||||
// );
|
||||
assignments.push((
|
||||
destruct.value.symbol,
|
||||
// destruct.value.var,
|
||||
|
@ -10582,7 +10439,6 @@ fn match_on_lambda_set<'a>(
|
|||
|
||||
let result = union_lambda_set_to_switch(
|
||||
env,
|
||||
layout_cache,
|
||||
lambda_set,
|
||||
closure_tag_id_symbol,
|
||||
union_layout.tag_id_layout(),
|
||||
|
@ -10617,19 +10473,16 @@ fn match_on_lambda_set<'a>(
|
|||
// Lambda set is empty, so this function is never called; synthesize a function
|
||||
// that always yields a runtime error.
|
||||
let name = env.unique_symbol();
|
||||
let lambda_name = LambdaName::no_niche(name);
|
||||
let function_layout =
|
||||
RawFunctionLayout::Function(argument_layouts, lambda_set, return_layout);
|
||||
let proc =
|
||||
generate_runtime_error_function(env, layout_cache, name, function_layout);
|
||||
let top_level = ProcLayout::from_raw(
|
||||
env.arena,
|
||||
&layout_cache.interner,
|
||||
function_layout,
|
||||
CapturesNiche::no_niche(),
|
||||
);
|
||||
let proc = generate_runtime_error_function(env, lambda_name, function_layout);
|
||||
let top_level =
|
||||
ProcLayout::from_raw_named(env.arena, lambda_name, function_layout);
|
||||
|
||||
procs.specialized.insert_specialized(name, top_level, proc);
|
||||
LambdaName::no_niche(name)
|
||||
|
||||
lambda_name
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -10643,7 +10496,6 @@ fn match_on_lambda_set<'a>(
|
|||
|
||||
union_lambda_set_branch_help(
|
||||
env,
|
||||
layout_cache,
|
||||
function_symbol,
|
||||
closure_info,
|
||||
argument_symbols,
|
||||
|
@ -10666,7 +10518,6 @@ fn match_on_lambda_set<'a>(
|
|||
|
||||
union_lambda_set_branch_help(
|
||||
env,
|
||||
layout_cache,
|
||||
function_symbol,
|
||||
closure_info,
|
||||
argument_symbols,
|
||||
|
@ -10714,7 +10565,6 @@ fn match_on_lambda_set<'a>(
|
|||
#[allow(clippy::too_many_arguments)]
|
||||
fn union_lambda_set_to_switch<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
layout_cache: &LayoutCache<'a>,
|
||||
lambda_set: LambdaSet<'a>,
|
||||
closure_tag_id_symbol: Symbol,
|
||||
closure_tag_id_layout: Layout<'a>,
|
||||
|
@ -10749,7 +10599,6 @@ fn union_lambda_set_to_switch<'a>(
|
|||
|
||||
let stmt = union_lambda_set_branch(
|
||||
env,
|
||||
layout_cache,
|
||||
join_point_id,
|
||||
lambda_name,
|
||||
closure_info,
|
||||
|
@ -10791,7 +10640,6 @@ fn union_lambda_set_to_switch<'a>(
|
|||
#[allow(clippy::too_many_arguments)]
|
||||
fn union_lambda_set_branch<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
layout_cache: &LayoutCache<'a>,
|
||||
join_point_id: JoinPointId,
|
||||
lambda_name: LambdaName<'a>,
|
||||
closure_info: ClosureInfo<'a>,
|
||||
|
@ -10805,7 +10653,6 @@ fn union_lambda_set_branch<'a>(
|
|||
|
||||
union_lambda_set_branch_help(
|
||||
env,
|
||||
layout_cache,
|
||||
lambda_name,
|
||||
closure_info,
|
||||
argument_symbols_slice,
|
||||
|
@ -10829,7 +10676,6 @@ enum ClosureInfo<'a> {
|
|||
#[allow(clippy::too_many_arguments)]
|
||||
fn union_lambda_set_branch_help<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
layout_cache: &LayoutCache<'a>,
|
||||
lambda_name: LambdaName<'a>,
|
||||
closure_info: ClosureInfo<'a>,
|
||||
argument_symbols_slice: &'a [Symbol],
|
||||
|
@ -10843,22 +10689,21 @@ fn union_lambda_set_branch_help<'a>(
|
|||
lambda_set,
|
||||
closure_data_symbol,
|
||||
} => {
|
||||
let argument_layouts = lambda_set.extend_argument_list(
|
||||
let argument_layouts = lambda_set.extend_argument_list_for_named(
|
||||
env.arena,
|
||||
&layout_cache.interner,
|
||||
lambda_name,
|
||||
argument_layouts_slice,
|
||||
);
|
||||
let argument_symbols = if argument_layouts.len() > argument_layouts_slice.len() {
|
||||
// extend symbols with the symbol of the closure environment
|
||||
let mut argument_symbols =
|
||||
Vec::with_capacity_in(argument_symbols_slice.len() + 1, env.arena);
|
||||
argument_symbols.extend(argument_symbols_slice);
|
||||
argument_symbols.push(closure_data_symbol);
|
||||
argument_symbols.into_bump_slice()
|
||||
} else {
|
||||
argument_symbols_slice
|
||||
};
|
||||
(argument_layouts, argument_symbols)
|
||||
|
||||
// Since this lambda captures, the arguments must have been extended.
|
||||
debug_assert!(argument_layouts.len() > argument_layouts_slice.len());
|
||||
// Extend symbols with the symbol of the closure environment.
|
||||
let mut argument_symbols =
|
||||
Vec::with_capacity_in(argument_symbols_slice.len() + 1, env.arena);
|
||||
argument_symbols.extend(argument_symbols_slice);
|
||||
argument_symbols.push(closure_data_symbol);
|
||||
|
||||
(argument_layouts, argument_symbols.into_bump_slice())
|
||||
}
|
||||
ClosureInfo::DoesNotCapture => {
|
||||
// sometimes unification causes a function that does not itself capture anything
|
||||
|
|
|
@ -1610,37 +1610,31 @@ impl<'a> LambdaSet<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn extend_argument_list<I>(
|
||||
/// If `lambda_name` captures, extend the arguments to the lambda with the lambda set, from
|
||||
/// which the lambda should extract its captures from.
|
||||
///
|
||||
/// If `lambda_name` doesn't capture, the arguments are unaffected.
|
||||
pub(crate) fn extend_argument_list_for_named(
|
||||
&self,
|
||||
arena: &'a Bump,
|
||||
interner: &I,
|
||||
lambda_name: LambdaName<'a>,
|
||||
argument_layouts: &'a [Layout<'a>],
|
||||
) -> &'a [Layout<'a>]
|
||||
where
|
||||
I: Interner<'a, Layout<'a>>,
|
||||
{
|
||||
match self.call_by_name_options(interner) {
|
||||
ClosureCallOptions::Void => argument_layouts,
|
||||
ClosureCallOptions::Struct {
|
||||
field_layouts: &[], ..
|
||||
} => {
|
||||
// this function does not have anything in its closure, and the lambda set is a
|
||||
// singleton, so we pass no extra argument
|
||||
argument_layouts
|
||||
}
|
||||
ClosureCallOptions::Struct { .. }
|
||||
| ClosureCallOptions::Union(_)
|
||||
| ClosureCallOptions::UnwrappedCapture(_) => {
|
||||
let mut arguments = Vec::with_capacity_in(argument_layouts.len() + 1, arena);
|
||||
arguments.extend(argument_layouts);
|
||||
arguments.push(Layout::LambdaSet(*self));
|
||||
) -> &'a [Layout<'a>] {
|
||||
debug_assert!(
|
||||
self.set
|
||||
.contains(&(lambda_name.name, lambda_name.captures_niche.0)),
|
||||
"{:?}",
|
||||
(self, lambda_name)
|
||||
);
|
||||
// If we don't capture, there is nothing to extend.
|
||||
if lambda_name.captures_niche.0.is_empty() {
|
||||
argument_layouts
|
||||
} else {
|
||||
let mut arguments = Vec::with_capacity_in(argument_layouts.len() + 1, arena);
|
||||
arguments.extend(argument_layouts);
|
||||
arguments.push(Layout::LambdaSet(*self));
|
||||
|
||||
arguments.into_bump_slice()
|
||||
}
|
||||
ClosureCallOptions::EnumDispatch(_) => {
|
||||
// No captures, don't pass this along
|
||||
argument_layouts
|
||||
}
|
||||
arguments.into_bump_slice()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,170 @@
|
|||
procedure Bool.11 (#Attr.2, #Attr.3):
|
||||
let Bool.23 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
|
||||
ret Bool.23;
|
||||
|
||||
procedure Bool.11 (#Attr.2, #Attr.3):
|
||||
let Bool.24 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
|
||||
ret Bool.24;
|
||||
|
||||
procedure List.26 (List.152, List.153, List.154):
|
||||
let List.493 : [C U64, C U64] = CallByName List.90 List.152 List.153 List.154;
|
||||
let List.496 : U8 = 1i64;
|
||||
let List.497 : U8 = GetTagId List.493;
|
||||
let List.498 : Int1 = lowlevel Eq List.496 List.497;
|
||||
if List.498 then
|
||||
let List.155 : U64 = UnionAtIndex (Id 1) (Index 0) List.493;
|
||||
ret List.155;
|
||||
else
|
||||
let List.156 : U64 = UnionAtIndex (Id 0) (Index 0) List.493;
|
||||
ret List.156;
|
||||
|
||||
procedure List.26 (List.152, List.153, List.154):
|
||||
let List.515 : [C I64, C I64] = CallByName List.90 List.152 List.153 List.154;
|
||||
let List.518 : U8 = 1i64;
|
||||
let List.519 : U8 = GetTagId List.515;
|
||||
let List.520 : Int1 = lowlevel Eq List.518 List.519;
|
||||
if List.520 then
|
||||
let List.155 : I64 = UnionAtIndex (Id 1) (Index 0) List.515;
|
||||
ret List.155;
|
||||
else
|
||||
let List.156 : I64 = UnionAtIndex (Id 0) (Index 0) List.515;
|
||||
ret List.156;
|
||||
|
||||
procedure List.29 (List.294, List.295):
|
||||
let List.492 : U64 = CallByName List.6 List.294;
|
||||
let List.296 : U64 = CallByName Num.77 List.492 List.295;
|
||||
let List.478 : List U8 = CallByName List.43 List.294 List.296;
|
||||
ret List.478;
|
||||
|
||||
procedure List.43 (List.292, List.293):
|
||||
let List.490 : U64 = CallByName List.6 List.292;
|
||||
let List.489 : U64 = CallByName Num.77 List.490 List.293;
|
||||
let List.480 : {U64, U64} = Struct {List.293, List.489};
|
||||
let List.479 : List U8 = CallByName List.49 List.292 List.480;
|
||||
ret List.479;
|
||||
|
||||
procedure List.49 (List.366, List.367):
|
||||
let List.487 : U64 = StructAtIndex 0 List.367;
|
||||
let List.488 : U64 = 0i64;
|
||||
let List.485 : Int1 = CallByName Bool.11 List.487 List.488;
|
||||
if List.485 then
|
||||
dec List.366;
|
||||
let List.486 : List U8 = Array [];
|
||||
ret List.486;
|
||||
else
|
||||
let List.482 : U64 = StructAtIndex 1 List.367;
|
||||
let List.483 : U64 = StructAtIndex 0 List.367;
|
||||
let List.481 : List U8 = CallByName List.72 List.366 List.482 List.483;
|
||||
ret List.481;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.491 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.491;
|
||||
|
||||
procedure List.66 (#Attr.2, #Attr.3):
|
||||
let List.514 : U8 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.514;
|
||||
|
||||
procedure List.72 (#Attr.2, #Attr.3, #Attr.4):
|
||||
let List.484 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4;
|
||||
ret List.484;
|
||||
|
||||
procedure List.90 (List.426, List.427, List.428):
|
||||
let List.500 : U64 = 0i64;
|
||||
let List.501 : U64 = CallByName List.6 List.426;
|
||||
let List.499 : [C U64, C U64] = CallByName List.91 List.426 List.427 List.428 List.500 List.501;
|
||||
ret List.499;
|
||||
|
||||
procedure List.90 (List.426, List.427, List.428):
|
||||
let List.522 : U64 = 0i64;
|
||||
let List.523 : U64 = CallByName List.6 List.426;
|
||||
let List.521 : [C I64, C I64] = CallByName List.91 List.426 List.427 List.428 List.522 List.523;
|
||||
ret List.521;
|
||||
|
||||
procedure List.91 (List.549, List.550, List.551, List.552, List.553):
|
||||
joinpoint List.502 List.429 List.430 List.431 List.432 List.433:
|
||||
let List.504 : Int1 = CallByName Num.22 List.432 List.433;
|
||||
if List.504 then
|
||||
let List.513 : U8 = CallByName List.66 List.429 List.432;
|
||||
let List.505 : [C U64, C U64] = CallByName Test.4 List.430 List.513;
|
||||
let List.510 : U8 = 1i64;
|
||||
let List.511 : U8 = GetTagId List.505;
|
||||
let List.512 : Int1 = lowlevel Eq List.510 List.511;
|
||||
if List.512 then
|
||||
let List.434 : U64 = UnionAtIndex (Id 1) (Index 0) List.505;
|
||||
let List.508 : U64 = 1i64;
|
||||
let List.507 : U64 = CallByName Num.19 List.432 List.508;
|
||||
jump List.502 List.429 List.434 List.431 List.507 List.433;
|
||||
else
|
||||
let List.435 : U64 = UnionAtIndex (Id 0) (Index 0) List.505;
|
||||
let List.509 : [C U64, C U64] = TagId(0) List.435;
|
||||
ret List.509;
|
||||
else
|
||||
let List.503 : [C U64, C U64] = TagId(1) List.430;
|
||||
ret List.503;
|
||||
in
|
||||
jump List.502 List.549 List.550 List.551 List.552 List.553;
|
||||
|
||||
procedure List.91 (List.562, List.563, List.564, List.565, List.566):
|
||||
joinpoint List.524 List.429 List.430 List.431 List.432 List.433:
|
||||
let List.526 : Int1 = CallByName Num.22 List.432 List.433;
|
||||
if List.526 then
|
||||
let List.535 : U8 = CallByName List.66 List.429 List.432;
|
||||
let List.527 : [C I64, C I64] = CallByName Test.4 List.430 List.535;
|
||||
let List.532 : U8 = 1i64;
|
||||
let List.533 : U8 = GetTagId List.527;
|
||||
let List.534 : Int1 = lowlevel Eq List.532 List.533;
|
||||
if List.534 then
|
||||
let List.434 : I64 = UnionAtIndex (Id 1) (Index 0) List.527;
|
||||
let List.530 : U64 = 1i64;
|
||||
let List.529 : U64 = CallByName Num.19 List.432 List.530;
|
||||
jump List.524 List.429 List.434 List.431 List.529 List.433;
|
||||
else
|
||||
let List.435 : I64 = UnionAtIndex (Id 0) (Index 0) List.527;
|
||||
let List.531 : [C I64, C I64] = TagId(0) List.435;
|
||||
ret List.531;
|
||||
else
|
||||
let List.525 : [C I64, C I64] = TagId(1) List.430;
|
||||
ret List.525;
|
||||
in
|
||||
jump List.524 List.562 List.563 List.564 List.565 List.566;
|
||||
|
||||
procedure Num.19 (#Attr.2, #Attr.3):
|
||||
let Num.259 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.259;
|
||||
|
||||
procedure Num.22 (#Attr.2, #Attr.3):
|
||||
let Num.261 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
ret Num.261;
|
||||
|
||||
procedure Num.77 (#Attr.2, #Attr.3):
|
||||
let Num.257 : U64 = lowlevel NumSubSaturated #Attr.2 #Attr.3;
|
||||
ret Num.257;
|
||||
|
||||
procedure Test.1 (Test.2):
|
||||
let Test.18 : I64 = 0i64;
|
||||
let Test.19 : {} = Struct {};
|
||||
let Test.12 : I64 = CallByName List.26 Test.2 Test.18 Test.19;
|
||||
let Test.14 : U64 = 0i64;
|
||||
let Test.15 : {} = Struct {};
|
||||
let Test.3 : U64 = CallByName List.26 Test.2 Test.14 Test.15;
|
||||
let Test.13 : I64 = 0i64;
|
||||
let Test.10 : Int1 = CallByName Bool.11 Test.12 Test.13;
|
||||
if Test.10 then
|
||||
ret Test.2;
|
||||
else
|
||||
let Test.9 : List U8 = CallByName List.29 Test.2 Test.3;
|
||||
ret Test.9;
|
||||
|
||||
procedure Test.4 (Test.5, Test.16):
|
||||
let Test.17 : [C U64, C U64] = TagId(0) Test.5;
|
||||
ret Test.17;
|
||||
|
||||
procedure Test.4 (Test.5, Test.16):
|
||||
let Test.21 : [C U64, C U64] = TagId(0) Test.5;
|
||||
ret Test.21;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.8 : List U8 = Array [1i64, 2i64, 3i64];
|
||||
let Test.7 : List U8 = CallByName Test.1 Test.8;
|
||||
ret Test.7;
|
|
@ -62,7 +62,7 @@ procedure Decode.26 (Decode.100, Decode.101):
|
|||
let Decode.116 : [C [C List U8, C ], C Str] = TagId(0) Decode.117;
|
||||
ret Decode.116;
|
||||
|
||||
procedure Json.139 (Json.450, Json.451):
|
||||
procedure Json.139 (Json.452, Json.453):
|
||||
joinpoint Json.421 Json.418 Json.138:
|
||||
let Json.141 : List U8 = StructAtIndex 0 Json.418;
|
||||
inc Json.141;
|
||||
|
@ -91,7 +91,7 @@ procedure Json.139 (Json.450, Json.451):
|
|||
let Json.435 : {List U8, List U8} = Struct {Json.141, Json.140};
|
||||
ret Json.435;
|
||||
in
|
||||
jump Json.421 Json.450 Json.451;
|
||||
jump Json.421 Json.452 Json.453;
|
||||
|
||||
procedure Json.143 (Json.432):
|
||||
let Json.433 : List U8 = StructAtIndex 1 Json.432;
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
procedure List.5 (#Attr.2, #Attr.3):
|
||||
let List.479 : U8 = GetTagId #Attr.3;
|
||||
joinpoint List.480 List.478:
|
||||
inc List.478;
|
||||
ret List.478;
|
||||
in
|
||||
switch List.479:
|
||||
case 0:
|
||||
let List.481 : List U8 = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.4 #Attr.3;
|
||||
decref #Attr.2;
|
||||
jump List.480 List.481;
|
||||
|
||||
case 1:
|
||||
let List.482 : List U8 = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.6 #Attr.3;
|
||||
decref #Attr.2;
|
||||
jump List.480 List.482;
|
||||
|
||||
default:
|
||||
let List.483 : List U8 = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.8 #Attr.3;
|
||||
decref #Attr.2;
|
||||
jump List.480 List.483;
|
||||
|
||||
|
||||
procedure Num.19 (#Attr.2, #Attr.3):
|
||||
let Num.258 : U8 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.258;
|
||||
|
||||
procedure Test.4 (Test.5, #Attr.12):
|
||||
let Test.1 : U8 = UnionAtIndex (Id 0) (Index 0) #Attr.12;
|
||||
let Test.15 : U8 = CallByName Num.19 Test.5 Test.1;
|
||||
ret Test.15;
|
||||
|
||||
procedure Test.6 (Test.7, #Attr.12):
|
||||
let Test.2 : U8 = UnionAtIndex (Id 1) (Index 0) #Attr.12;
|
||||
let Test.17 : U8 = CallByName Num.19 Test.7 Test.2;
|
||||
ret Test.17;
|
||||
|
||||
procedure Test.8 (Test.9):
|
||||
let Test.19 : U8 = CallByName Num.19 Test.9 Test.9;
|
||||
ret Test.19;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.1 : U8 = 1i64;
|
||||
let Test.2 : U8 = 2i64;
|
||||
joinpoint Test.13 Test.3:
|
||||
let Test.11 : List U8 = Array [1i64, 2i64, 3i64];
|
||||
let Test.10 : List U8 = CallByName List.5 Test.11 Test.3;
|
||||
ret Test.10;
|
||||
in
|
||||
let Test.12 : Str = "";
|
||||
let Test.22 : Str = "A";
|
||||
let Test.23 : Int1 = lowlevel Eq Test.22 Test.12;
|
||||
dec Test.22;
|
||||
if Test.23 then
|
||||
dec Test.12;
|
||||
let Test.14 : [C U8, C U8, C ] = TagId(0) Test.1;
|
||||
jump Test.13 Test.14;
|
||||
else
|
||||
let Test.20 : Str = "B";
|
||||
let Test.21 : Int1 = lowlevel Eq Test.20 Test.12;
|
||||
dec Test.12;
|
||||
dec Test.20;
|
||||
if Test.21 then
|
||||
let Test.16 : [C U8, C U8, C ] = TagId(1) Test.2;
|
||||
jump Test.13 Test.16;
|
||||
else
|
||||
let Test.18 : [C U8, C U8, C ] = TagId(2) ;
|
||||
jump Test.13 Test.18;
|
|
@ -2243,6 +2243,50 @@ fn lambda_set_with_imported_toplevels_issue_4733() {
|
|||
)
|
||||
}
|
||||
|
||||
#[mono_test]
|
||||
fn anonymous_closure_in_polymorphic_expression_issue_4717() {
|
||||
indoc!(
|
||||
r###"
|
||||
app "test" provides [main] to "platform"
|
||||
|
||||
chompWhile : (List U8) -> (List U8)
|
||||
chompWhile = \input ->
|
||||
index = List.walkUntil input 0 \i, _ -> Break i
|
||||
|
||||
if index == 0 then
|
||||
input
|
||||
else
|
||||
List.drop input index
|
||||
|
||||
main = chompWhile [1u8, 2u8, 3u8]
|
||||
"###
|
||||
)
|
||||
}
|
||||
|
||||
#[mono_test]
|
||||
fn list_map_take_capturing_or_noncapturing() {
|
||||
indoc!(
|
||||
r###"
|
||||
app "test" provides [main] to "platform"
|
||||
|
||||
main =
|
||||
x = 1u8
|
||||
y = 2u8
|
||||
f = when "" is
|
||||
"A" ->
|
||||
g = \n -> n + x
|
||||
g
|
||||
"B" ->
|
||||
h = \n -> n + y
|
||||
h
|
||||
_ ->
|
||||
k = \n -> n + n
|
||||
k
|
||||
List.map [1u8, 2u8, 3u8] f
|
||||
"###
|
||||
)
|
||||
}
|
||||
|
||||
#[mono_test]
|
||||
fn issue_4557() {
|
||||
indoc!(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue