fix problem with empty closures still considered closures

This commit is contained in:
Folkert 2020-12-10 23:33:53 +01:00
parent 84421ad06d
commit f5c267acf4
7 changed files with 71 additions and 15 deletions

View File

@ -1571,4 +1571,28 @@ mod gen_primitives {
i64
);
}
#[test]
fn unified_empty_closure() {
// none of the Closure tags will have a payload
// this was not handled correctly in the past
assert_non_opt_evals_to!(
indoc!(
r#"
app "test" provides [ main ] to "./platform"
foo = \{} ->
when A is
A -> (\_ -> 3.14)
B -> (\_ -> 3.14)
main : F64
main =
(foo {}) 0
"#
),
3.14,
f64
);
}
}

View File

@ -157,7 +157,7 @@ impl Dependencies {
output
}
pub fn add_platform_module(
pub fn add_effect_module(
&mut self,
module_id: ModuleId,
dependencies: &MutSet<ModuleId>,
@ -1580,7 +1580,7 @@ fn update<'a>(
.constrained
.insert(module_id, constrained_module);
let mut work = state.dependencies.add_platform_module(
let mut work = state.dependencies.add_effect_module(
module_id,
&MutSet::default(),
state.goal_phase,

View File

@ -1636,8 +1636,11 @@ fn specialize_external<'a>(
Layout::Struct(field_layouts) => {
// NOTE closure unions do not store the tag!
let field_layouts = &field_layouts[1..];
// TODO check for field_layouts.len() == 1 and do a rename in that case?
for (index, (symbol, _variable)) in captured.iter().enumerate()
{
// TODO therefore should the wrapped here not be RecordOrSingleTagUnion?
let expr = Expr::AccessAtIndex {
index: index as _,
field_layouts,

View File

@ -86,6 +86,9 @@ impl<'a> ClosureLayout<'a> {
fn from_tag_union(arena: &'a Bump, tags: &'a [(TagName, &'a [Layout<'a>])]) -> Self {
debug_assert!(tags.len() > 1);
// if the closed-over value is actually a layout, it should be wrapped in a 1-element record
debug_assert!(matches!(tags[0].0, TagName::Closure(_)));
let mut tag_arguments = Vec::with_capacity_in(tags.len(), arena);
for (_, tag_args) in tags.iter() {
@ -117,7 +120,15 @@ impl<'a> ClosureLayout<'a> {
let mut tags = std::vec::Vec::new();
match roc_types::pretty_print::chase_ext_tag_union(subs, closure_var, &mut tags) {
Ok(()) | Err((_, Content::FlexVar(_))) if !tags.is_empty() => {
// this is a closure
// special-case the `[ Closure1, Closure2, Closure3 ]` case, where none of
// the tags have a payload
let all_no_payload = tags.iter().all(|(_, arguments)| arguments.is_empty());
if all_no_payload {
return Ok(None);
}
// otherwise, this is a closure with a payload
let variant = union_sorted_tags_help(arena, tags, None, subs);
use UnionVariant::*;
@ -147,9 +158,9 @@ impl<'a> ClosureLayout<'a> {
Ok(Some(closure_layout))
}
Wrapped(tags) => {
// Wrapped(Vec<'a, (TagName, &'a [Layout<'a>])>),
let closure_layout =
ClosureLayout::from_tag_union(arena, tags.into_bump_slice());
Ok(Some(closure_layout))
}
}
@ -266,7 +277,13 @@ impl<'a> ClosureLayout<'a> {
}
_ => {
debug_assert_eq!(symbols.len(), 1);
debug_assert_eq!(
symbols.len(),
1,
"symbols {:?} for layout {:?}",
&symbols,
&self.layout
);
Err(symbols[0])
}

View File

@ -1,10 +1,20 @@
app "effect-example"
packages { base: "thing/platform-dir" }
imports [ base.Task.{ Task, after } ]
imports [ base.Task ]
provides [ main ] to base
main : Task.Task {} F64
main =
Task.after (Task.putLine "foo") \{} ->
Task.putLine "bar"
# main : Task.Task {} F64
main =
Task.after (Task.putLine "foo") \{} -> Task.putLine "bar"
# main =
# # Task.after (Task.putLine "foo") \{} -> Task.putLine "bar"
# y = Task.ealways 3.14
#
# a = y |> Task.emap (\x -> x)
# b = y |> Task.emap (\x -> x)
#
# if 1 == 1 then a else b

View File

@ -6,10 +6,12 @@ platform folkertdev/foo
provides [ mainForHost ]
effects Effect
{
putChar : I64 -> Effect {},
putLine : Str -> Effect {},
getLine : Effect Str
putLine : Str -> Effect {}
}
# putChar : I64 -> Effect {},
# getLine : Effect Str
mainForHost : Task.Task {} F64 as Fx
mainForHost = main

View File

@ -1,5 +1,5 @@
interface Task
exposes [ Task, putLine, after, always, map, after, fail ]
exposes [ Task, putLine, after, always, map, after, fail]
imports [ Effect ]
Task a err : Effect.Effect (Result a err)
@ -7,7 +7,7 @@ Task a err : Effect.Effect (Result a err)
always : a -> Task a *
always = \x -> Effect.always (Ok x)
fail : err -> Task * err
fail : err -> Task * err
fail = \x -> Effect.always (Err x)
putLine : Str -> Task {} *