mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-22 00:09:33 +03:00
fix problem with empty closures still considered closures
This commit is contained in:
parent
84421ad06d
commit
f5c267acf4
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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])
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 {} *
|
||||
|
Loading…
Reference in New Issue
Block a user