mirror of
https://github.com/roc-lang/roc.git
synced 2025-07-23 06:25:10 +00:00
Merge pull request #3409 from rtfeldman/i3224
Compile lambdas that have an empty lambda set
This commit is contained in:
commit
957c4258b1
7 changed files with 128 additions and 17 deletions
|
@ -104,6 +104,9 @@ flags! {
|
|||
/// Prints debug information during the alias analysis pass.
|
||||
ROC_DEBUG_ALIAS_ANALYSIS
|
||||
|
||||
/// Print to stderr when a runtime error function is generated.
|
||||
ROC_PRINT_RUNTIME_ERROR_GEN
|
||||
|
||||
// ===LLVM Gen===
|
||||
|
||||
/// Prints LLVM function verification output.
|
||||
|
|
|
@ -15,6 +15,7 @@ use roc_debug_flags::dbg_do;
|
|||
#[cfg(debug_assertions)]
|
||||
use roc_debug_flags::{
|
||||
ROC_PRINT_IR_AFTER_REFCOUNT, ROC_PRINT_IR_AFTER_RESET_REUSE, ROC_PRINT_IR_AFTER_SPECIALIZATION,
|
||||
ROC_PRINT_RUNTIME_ERROR_GEN,
|
||||
};
|
||||
use roc_derive_key::GlobalDerivedSymbols;
|
||||
use roc_error_macros::{internal_error, todo_abilities};
|
||||
|
@ -2807,22 +2808,26 @@ fn generate_runtime_error_function<'a>(
|
|||
)
|
||||
.unwrap();
|
||||
|
||||
eprintln!(
|
||||
"emitted runtime error function {:?} for layout {:?}",
|
||||
&msg, layout
|
||||
);
|
||||
dbg_do!(ROC_PRINT_RUNTIME_ERROR_GEN, {
|
||||
eprintln!(
|
||||
"emitted runtime error function {:?} for layout {:?}",
|
||||
&msg, layout
|
||||
);
|
||||
});
|
||||
|
||||
let runtime_error = Stmt::RuntimeError(msg.into_bump_str());
|
||||
|
||||
let (args, ret_layout) = match layout {
|
||||
RawFunctionLayout::Function(arg_layouts, lambda_set, ret_layout) => {
|
||||
let mut args = Vec::with_capacity_in(arg_layouts.len(), env.arena);
|
||||
let real_arg_layouts = lambda_set.extend_argument_list(env.arena, arg_layouts);
|
||||
let mut args = Vec::with_capacity_in(real_arg_layouts.len(), env.arena);
|
||||
|
||||
for arg in arg_layouts {
|
||||
args.push((*arg, env.unique_symbol()));
|
||||
}
|
||||
|
||||
args.push((Layout::LambdaSet(lambda_set), Symbol::ARG_CLOSURE));
|
||||
if real_arg_layouts.len() != arg_layouts.len() {
|
||||
args.push((Layout::LambdaSet(lambda_set), Symbol::ARG_CLOSURE));
|
||||
}
|
||||
|
||||
(args.into_bump_slice(), *ret_layout)
|
||||
}
|
||||
|
@ -2878,6 +2883,15 @@ fn specialize_external<'a>(
|
|||
let specialized =
|
||||
build_specialized_proc_from_var(env, layout_cache, lambda_name, pattern_symbols, fn_var)?;
|
||||
|
||||
let recursivity = if partial_proc.is_self_recursive {
|
||||
SelfRecursive::SelfRecursive(JoinPointId(env.unique_symbol()))
|
||||
} else {
|
||||
SelfRecursive::NotSelfRecursive
|
||||
};
|
||||
|
||||
let body = partial_proc.body.clone();
|
||||
let body_var = partial_proc.body_var;
|
||||
|
||||
// determine the layout of aliases/rigids exposed to the host
|
||||
let host_exposed_layouts = if host_exposed_variables.is_empty() {
|
||||
HostExposedLayouts::NotHostExposed
|
||||
|
@ -2922,6 +2936,7 @@ fn specialize_external<'a>(
|
|||
|
||||
let body = match_on_lambda_set(
|
||||
env,
|
||||
procs,
|
||||
lambda_set,
|
||||
Symbol::ARG_CLOSURE,
|
||||
argument_symbols.into_bump_slice(),
|
||||
|
@ -2965,15 +2980,7 @@ fn specialize_external<'a>(
|
|||
}
|
||||
};
|
||||
|
||||
let recursivity = if partial_proc.is_self_recursive {
|
||||
SelfRecursive::SelfRecursive(JoinPointId(env.unique_symbol()))
|
||||
} else {
|
||||
SelfRecursive::NotSelfRecursive
|
||||
};
|
||||
|
||||
let body = partial_proc.body.clone();
|
||||
|
||||
let mut specialized_body = from_can(env, partial_proc.body_var, body, procs, layout_cache);
|
||||
let mut specialized_body = from_can(env, body_var, body, procs, layout_cache);
|
||||
|
||||
match specialized {
|
||||
SpecializedLayout::FunctionPointerBody {
|
||||
|
@ -4712,6 +4719,7 @@ pub fn with_hole<'a>(
|
|||
|
||||
result = match_on_lambda_set(
|
||||
env,
|
||||
procs,
|
||||
lambda_set,
|
||||
closure_data_symbol,
|
||||
arg_symbols,
|
||||
|
@ -4752,6 +4760,7 @@ pub fn with_hole<'a>(
|
|||
|
||||
result = match_on_lambda_set(
|
||||
env,
|
||||
procs,
|
||||
lambda_set,
|
||||
closure_data_symbol,
|
||||
arg_symbols,
|
||||
|
@ -4809,6 +4818,7 @@ pub fn with_hole<'a>(
|
|||
|
||||
result = match_on_lambda_set(
|
||||
env,
|
||||
procs,
|
||||
lambda_set,
|
||||
closure_data_symbol,
|
||||
arg_symbols,
|
||||
|
@ -7485,6 +7495,7 @@ fn call_by_name<'a>(
|
|||
|
||||
let result = match_on_lambda_set(
|
||||
env,
|
||||
procs,
|
||||
lambda_set,
|
||||
closure_data_symbol,
|
||||
arg_symbols,
|
||||
|
@ -8045,6 +8056,7 @@ fn call_specialized_proc<'a>(
|
|||
|
||||
let new_hole = match_on_lambda_set(
|
||||
env,
|
||||
procs,
|
||||
lambda_set,
|
||||
closure_data_symbol,
|
||||
field_symbols,
|
||||
|
@ -9118,6 +9130,7 @@ where
|
|||
#[allow(clippy::too_many_arguments)]
|
||||
fn match_on_lambda_set<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
procs: &mut Procs<'a>,
|
||||
lambda_set: LambdaSet<'a>,
|
||||
closure_data_symbol: Symbol,
|
||||
argument_symbols: &'a [Symbol],
|
||||
|
@ -9161,7 +9174,22 @@ fn match_on_lambda_set<'a>(
|
|||
field_layouts,
|
||||
field_order_hash,
|
||||
} => {
|
||||
let function_symbol = lambda_set.iter_set().next().unwrap();
|
||||
let function_symbol = match lambda_set.iter_set().next() {
|
||||
Some(function_symbol) => function_symbol,
|
||||
None => {
|
||||
// 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 function_layout =
|
||||
RawFunctionLayout::Function(argument_layouts, lambda_set, return_layout);
|
||||
let proc = generate_runtime_error_function(env, name, function_layout);
|
||||
let top_level =
|
||||
ProcLayout::from_raw(env.arena, function_layout, CapturesNiche::no_niche());
|
||||
|
||||
procs.specialized.insert_specialized(name, top_level, proc);
|
||||
LambdaName::no_niche(name)
|
||||
}
|
||||
};
|
||||
|
||||
let closure_info = match field_layouts {
|
||||
[] => ClosureInfo::DoesNotCapture,
|
||||
|
|
|
@ -2958,3 +2958,37 @@ fn with_capacity() {
|
|||
RocList<u64>
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn call_function_in_empty_list() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
lst : List ({} -> {})
|
||||
lst = []
|
||||
List.map lst \f -> f {}
|
||||
"#
|
||||
),
|
||||
RocList::from_slice(&[]),
|
||||
RocList<()>
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
// TODO to be improved, if we can generate the void function type for the function in the list,
|
||||
// this should succeed.
|
||||
#[should_panic(expected = r#"Roc failed with message: "#)]
|
||||
fn call_function_in_empty_list_unbound() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
lst = []
|
||||
List.map lst \f -> f {}
|
||||
"#
|
||||
),
|
||||
RocList::from_slice(&[]),
|
||||
RocList<()>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
procedure List.5 (#Attr.2, #Attr.3):
|
||||
let List.279 : List {} = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.2 #Attr.3;
|
||||
decref #Attr.2;
|
||||
ret List.279;
|
||||
|
||||
procedure Test.2 (Test.3):
|
||||
let Test.7 : {} = Struct {};
|
||||
let Test.6 : {} = CallByName Test.8 Test.7;
|
||||
ret Test.6;
|
||||
|
||||
procedure Test.8 (Test.9):
|
||||
Error The `#UserApp.IdentId(8)` function could not be generated, likely due to a type error.
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.1 : List {} = Array [];
|
||||
let Test.5 : {} = Struct {};
|
||||
let Test.4 : List {} = CallByName List.5 Test.1 Test.5;
|
||||
ret Test.4;
|
|
@ -0,0 +1,6 @@
|
|||
procedure List.5 (#Attr.2, #Attr.3):
|
||||
Error UnresolvedTypeVar crates/compiler/mono/src/ir.rs line 5030
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.1 : List {} = Array [];
|
||||
Error UnresolvedTypeVar crates/compiler/mono/src/ir.rs line 4557
|
|
@ -1682,3 +1682,24 @@ fn choose_u128_layout() {
|
|||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[mono_test]
|
||||
fn call_function_in_empty_list() {
|
||||
indoc!(
|
||||
r#"
|
||||
lst : List ({} -> {})
|
||||
lst = []
|
||||
List.map lst \f -> f {}
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[mono_test]
|
||||
fn call_function_in_empty_list_unbound() {
|
||||
indoc!(
|
||||
r#"
|
||||
lst = []
|
||||
List.map lst \f -> f {}
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue