Compile lambdas that have an empty lambda set

Closes #3224

This should also fix a number of other issues often observed when a
lambda is never actually called, but we fail to codegen because it has
an empty lambda set.
This commit is contained in:
ayazhafiz 2022-07-05 23:35:41 -04:00 committed by Ayaz Hafiz
parent 7365da6f69
commit 937b73b797
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
6 changed files with 89 additions and 14 deletions

View file

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

View file

@ -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,10 +2808,12 @@ 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());
@ -2878,6 +2881,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 +2934,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 +2978,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 +4717,7 @@ pub fn with_hole<'a>(
result = match_on_lambda_set(
env,
procs,
lambda_set,
closure_data_symbol,
arg_symbols,
@ -4752,6 +4758,7 @@ pub fn with_hole<'a>(
result = match_on_lambda_set(
env,
procs,
lambda_set,
closure_data_symbol,
arg_symbols,
@ -4809,6 +4816,7 @@ pub fn with_hole<'a>(
result = match_on_lambda_set(
env,
procs,
lambda_set,
closure_data_symbol,
arg_symbols,
@ -7485,6 +7493,7 @@ fn call_by_name<'a>(
let result = match_on_lambda_set(
env,
procs,
lambda_set,
closure_data_symbol,
arg_symbols,
@ -8045,6 +8054,7 @@ fn call_specialized_proc<'a>(
let new_hole = match_on_lambda_set(
env,
procs,
lambda_set,
closure_data_symbol,
field_symbols,
@ -9118,6 +9128,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 +9172,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,

View file

@ -2958,3 +2958,19 @@ fn with_capacity() {
RocList<u64>
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn call_function_in_empty_list() {
assert_evals_to!(
indoc!(
r#"
lst : List ({} -> {})
lst = []
List.map lst \f -> f {}
"#
),
RocList::from_slice(&[]),
RocList<()>
)
}

View file

@ -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, #Attr.12):
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;

View file

@ -1682,3 +1682,14 @@ fn choose_u128_layout() {
"#
)
}
#[mono_test]
fn call_function_in_empty_list() {
indoc!(
r#"
lst : List ({} -> {})
lst = []
List.map lst \f -> f {}
"#
)
}