mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 22:09:09 +00:00
fix problem with empty closures still considered closures
This commit is contained in:
parent
84421ad06d
commit
f5c267acf4
7 changed files with 71 additions and 15 deletions
|
@ -1571,4 +1571,28 @@ mod gen_primitives {
|
||||||
i64
|
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
|
output
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_platform_module(
|
pub fn add_effect_module(
|
||||||
&mut self,
|
&mut self,
|
||||||
module_id: ModuleId,
|
module_id: ModuleId,
|
||||||
dependencies: &MutSet<ModuleId>,
|
dependencies: &MutSet<ModuleId>,
|
||||||
|
@ -1580,7 +1580,7 @@ fn update<'a>(
|
||||||
.constrained
|
.constrained
|
||||||
.insert(module_id, constrained_module);
|
.insert(module_id, constrained_module);
|
||||||
|
|
||||||
let mut work = state.dependencies.add_platform_module(
|
let mut work = state.dependencies.add_effect_module(
|
||||||
module_id,
|
module_id,
|
||||||
&MutSet::default(),
|
&MutSet::default(),
|
||||||
state.goal_phase,
|
state.goal_phase,
|
||||||
|
|
|
@ -1636,8 +1636,11 @@ fn specialize_external<'a>(
|
||||||
Layout::Struct(field_layouts) => {
|
Layout::Struct(field_layouts) => {
|
||||||
// NOTE closure unions do not store the tag!
|
// NOTE closure unions do not store the tag!
|
||||||
let field_layouts = &field_layouts[1..];
|
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()
|
for (index, (symbol, _variable)) in captured.iter().enumerate()
|
||||||
{
|
{
|
||||||
|
// TODO therefore should the wrapped here not be RecordOrSingleTagUnion?
|
||||||
let expr = Expr::AccessAtIndex {
|
let expr = Expr::AccessAtIndex {
|
||||||
index: index as _,
|
index: index as _,
|
||||||
field_layouts,
|
field_layouts,
|
||||||
|
|
|
@ -86,6 +86,9 @@ impl<'a> ClosureLayout<'a> {
|
||||||
fn from_tag_union(arena: &'a Bump, tags: &'a [(TagName, &'a [Layout<'a>])]) -> Self {
|
fn from_tag_union(arena: &'a Bump, tags: &'a [(TagName, &'a [Layout<'a>])]) -> Self {
|
||||||
debug_assert!(tags.len() > 1);
|
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);
|
let mut tag_arguments = Vec::with_capacity_in(tags.len(), arena);
|
||||||
|
|
||||||
for (_, tag_args) in tags.iter() {
|
for (_, tag_args) in tags.iter() {
|
||||||
|
@ -117,7 +120,15 @@ impl<'a> ClosureLayout<'a> {
|
||||||
let mut tags = std::vec::Vec::new();
|
let mut tags = std::vec::Vec::new();
|
||||||
match roc_types::pretty_print::chase_ext_tag_union(subs, closure_var, &mut tags) {
|
match roc_types::pretty_print::chase_ext_tag_union(subs, closure_var, &mut tags) {
|
||||||
Ok(()) | Err((_, Content::FlexVar(_))) if !tags.is_empty() => {
|
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);
|
let variant = union_sorted_tags_help(arena, tags, None, subs);
|
||||||
|
|
||||||
use UnionVariant::*;
|
use UnionVariant::*;
|
||||||
|
@ -147,9 +158,9 @@ impl<'a> ClosureLayout<'a> {
|
||||||
Ok(Some(closure_layout))
|
Ok(Some(closure_layout))
|
||||||
}
|
}
|
||||||
Wrapped(tags) => {
|
Wrapped(tags) => {
|
||||||
// Wrapped(Vec<'a, (TagName, &'a [Layout<'a>])>),
|
|
||||||
let closure_layout =
|
let closure_layout =
|
||||||
ClosureLayout::from_tag_union(arena, tags.into_bump_slice());
|
ClosureLayout::from_tag_union(arena, tags.into_bump_slice());
|
||||||
|
|
||||||
Ok(Some(closure_layout))
|
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])
|
Err(symbols[0])
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,20 @@
|
||||||
app "effect-example"
|
app "effect-example"
|
||||||
packages { base: "thing/platform-dir" }
|
packages { base: "thing/platform-dir" }
|
||||||
imports [ base.Task.{ Task, after } ]
|
imports [ base.Task ]
|
||||||
provides [ main ] to base
|
provides [ main ] to base
|
||||||
|
|
||||||
main : Task.Task {} F64
|
# main : Task.Task {} F64
|
||||||
main =
|
|
||||||
Task.after (Task.putLine "foo") \{} ->
|
|
||||||
Task.putLine "bar"
|
|
||||||
|
|
||||||
|
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 ]
|
provides [ mainForHost ]
|
||||||
effects Effect
|
effects Effect
|
||||||
{
|
{
|
||||||
putChar : I64 -> Effect {},
|
putLine : Str -> Effect {}
|
||||||
putLine : Str -> Effect {},
|
|
||||||
getLine : Effect Str
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# putChar : I64 -> Effect {},
|
||||||
|
# getLine : Effect Str
|
||||||
|
|
||||||
mainForHost : Task.Task {} F64 as Fx
|
mainForHost : Task.Task {} F64 as Fx
|
||||||
mainForHost = main
|
mainForHost = main
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
interface Task
|
interface Task
|
||||||
exposes [ Task, putLine, after, always, map, after, fail ]
|
exposes [ Task, putLine, after, always, map, after, fail]
|
||||||
imports [ Effect ]
|
imports [ Effect ]
|
||||||
|
|
||||||
Task a err : Effect.Effect (Result a err)
|
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 : a -> Task a *
|
||||||
always = \x -> Effect.always (Ok x)
|
always = \x -> Effect.always (Ok x)
|
||||||
|
|
||||||
fail : err -> Task * err
|
fail : err -> Task * err
|
||||||
fail = \x -> Effect.always (Err x)
|
fail = \x -> Effect.always (Err x)
|
||||||
|
|
||||||
putLine : Str -> Task {} *
|
putLine : Str -> Task {} *
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue