Merge pull request #3738 from rtfeldman/i3444

Layout generation for recursive lambda sets
This commit is contained in:
Folkert de Vries 2022-08-11 10:22:07 +02:00 committed by GitHub
commit ae0e90c8f3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 441 additions and 110 deletions

View file

@ -3130,7 +3130,12 @@ fn specialize_external<'a>(
tag_id, tag_id,
.. ..
} => { } => {
debug_assert!(matches!(union_layout, UnionLayout::NonRecursive(_))); debug_assert!(matches!(
union_layout,
UnionLayout::NonRecursive(_)
| UnionLayout::Recursive(_)
| UnionLayout::NullableUnwrapped { .. }
));
debug_assert_eq!(field_layouts.len(), captured.len()); debug_assert_eq!(field_layouts.len(), captured.len());
// captured variables are in symbol-alphabetic order, but now we want // captured variables are in symbol-alphabetic order, but now we want
@ -3149,7 +3154,9 @@ fn specialize_external<'a>(
size2.cmp(&size1) size2.cmp(&size1)
}); });
for (index, (symbol, layout)) in combined.iter().enumerate() { for (index, (symbol, _)) in combined.iter().enumerate() {
let layout = union_layout.layout_at(tag_id, index);
let expr = Expr::UnionAtIndex { let expr = Expr::UnionAtIndex {
tag_id, tag_id,
structure: Symbol::ARG_CLOSURE, structure: Symbol::ARG_CLOSURE,
@ -3162,7 +3169,7 @@ fn specialize_external<'a>(
specialized_body = Stmt::Let( specialized_body = Stmt::Let(
symbol, symbol,
expr, expr,
**layout, layout,
env.arena.alloc(specialized_body), env.arena.alloc(specialized_body),
); );
} }

View file

@ -10,7 +10,8 @@ use roc_problem::can::RuntimeError;
use roc_target::{PtrWidth, TargetInfo}; use roc_target::{PtrWidth, TargetInfo};
use roc_types::num::NumericRange; use roc_types::num::NumericRange;
use roc_types::subs::{ use roc_types::subs::{
self, Content, FlatType, Label, RecordFields, Subs, UnionTags, UnsortedUnionLabels, Variable, self, Content, FlatType, Label, OptVariable, RecordFields, Subs, UnionTags,
UnsortedUnionLabels, Variable,
}; };
use roc_types::types::{gather_fields_unsorted_iter, RecordField, RecordFieldsError}; use roc_types::types::{gather_fields_unsorted_iter, RecordField, RecordFieldsError};
use std::cmp::Ordering; use std::cmp::Ordering;
@ -842,6 +843,7 @@ impl<'a> LambdaSet<'a> {
let comparator = |other_name: Symbol, other_captures_layouts: &[Layout]| { let comparator = |other_name: Symbol, other_captures_layouts: &[Layout]| {
other_name == lambda_name.name other_name == lambda_name.name
// Make sure all captures are equal
&& other_captures_layouts && other_captures_layouts
.iter() .iter()
.eq(lambda_name.captures_niche.0) .eq(lambda_name.captures_niche.0)
@ -859,14 +861,25 @@ impl<'a> LambdaSet<'a> {
debug_assert!(self.contains(function_symbol), "function symbol not in set"); debug_assert!(self.contains(function_symbol), "function symbol not in set");
let comparator = |other_name: Symbol, other_captures_layouts: &[Layout]| { let comparator = |other_name: Symbol, other_captures_layouts: &[Layout]| {
other_name == function_symbol && other_captures_layouts.iter().eq(captures_layouts) other_name == function_symbol
&& other_captures_layouts
.iter()
.zip(captures_layouts)
.all(|(other_layout, layout)| self.capture_layouts_eq(other_layout, layout))
}; };
let (name, layouts) = self let (name, layouts) = self
.set .set
.iter() .iter()
.find(|(name, layouts)| comparator(*name, layouts)) .find(|(name, layouts)| comparator(*name, layouts))
.expect("no lambda set found"); .unwrap_or_else(|| {
internal_error!(
"no lambda set found for ({:?}, {:#?}): {:#?}",
function_symbol,
captures_layouts,
self
)
});
LambdaName { LambdaName {
name: *name, name: *name,
@ -874,6 +887,38 @@ impl<'a> LambdaSet<'a> {
} }
} }
/// Checks if two captured layouts are equivalent under the current lambda set.
/// Resolves recursive pointers to the layout of the lambda set.
fn capture_layouts_eq(&self, left: &Layout, right: &Layout) -> bool {
if left == right {
return true;
}
let left = if left == &Layout::RecursivePointer {
let runtime_repr = self.runtime_representation();
debug_assert!(matches!(
runtime_repr,
Layout::Union(UnionLayout::Recursive(_) | UnionLayout::NullableUnwrapped { .. })
));
Layout::LambdaSet(*self)
} else {
*left
};
let right = if right == &Layout::RecursivePointer {
let runtime_repr = self.runtime_representation();
debug_assert!(matches!(
runtime_repr,
Layout::Union(UnionLayout::Recursive(_) | UnionLayout::NullableUnwrapped { .. })
));
Layout::LambdaSet(*self)
} else {
*right
};
left == right
}
fn layout_for_member<F>(&self, comparator: F) -> ClosureRepresentation<'a> fn layout_for_member<F>(&self, comparator: F) -> ClosureRepresentation<'a>
where where
F: Fn(Symbol, &[Layout]) -> bool, F: Fn(Symbol, &[Layout]) -> bool,
@ -902,16 +947,48 @@ impl<'a> LambdaSet<'a> {
union_layout: *union, union_layout: *union,
} }
} }
UnionLayout::Recursive(_) => todo!("recursive closures"), UnionLayout::Recursive(_) => {
let (index, (name, fields)) = self
.set
.iter()
.enumerate()
.find(|(_, (s, layouts))| comparator(*s, layouts))
.unwrap();
let closure_name = *name;
ClosureRepresentation::Union {
tag_id: index as TagIdIntType,
alphabetic_order_fields: fields,
closure_name,
union_layout: *union,
}
}
UnionLayout::NullableUnwrapped {
nullable_id: _,
other_fields: _,
} => {
let (index, (name, fields)) = self
.set
.iter()
.enumerate()
.find(|(_, (s, layouts))| comparator(*s, layouts))
.unwrap();
let closure_name = *name;
ClosureRepresentation::Union {
tag_id: index as TagIdIntType,
alphabetic_order_fields: fields,
closure_name,
union_layout: *union,
}
}
UnionLayout::NonNullableUnwrapped(_) => todo!("recursive closures"), UnionLayout::NonNullableUnwrapped(_) => todo!("recursive closures"),
UnionLayout::NullableWrapped { UnionLayout::NullableWrapped {
nullable_id: _, nullable_id: _,
other_tags: _, other_tags: _,
} => todo!("recursive closures"), } => todo!("recursive closures"),
UnionLayout::NullableUnwrapped {
nullable_id: _,
other_fields: _,
} => todo!("recursive closures"),
} }
} }
Layout::Struct { .. } => { Layout::Struct { .. } => {
@ -971,7 +1048,7 @@ impl<'a> LambdaSet<'a> {
target_info: TargetInfo, target_info: TargetInfo,
) -> Result<Self, LayoutProblem> { ) -> Result<Self, LayoutProblem> {
match resolve_lambda_set(subs, closure_var) { match resolve_lambda_set(subs, closure_var) {
ResolvedLambdaSet::Set(mut lambdas) => { ResolvedLambdaSet::Set(mut lambdas, opt_recursion_var) => {
// sort the tags; make sure ordering stays intact! // sort the tags; make sure ordering stays intact!
lambdas.sort_by_key(|(sym, _)| *sym); lambdas.sort_by_key(|(sym, _)| *sym);
@ -992,6 +1069,9 @@ impl<'a> LambdaSet<'a> {
seen: Vec::new_in(arena), seen: Vec::new_in(arena),
target_info, target_info,
}; };
if let Some(rec_var) = opt_recursion_var.into_variable() {
env.insert_seen(rec_var);
}
for var in variables { for var in variables {
arguments.push(Layout::from_var(&mut env, *var)?); arguments.push(Layout::from_var(&mut env, *var)?);
@ -1048,6 +1128,7 @@ impl<'a> LambdaSet<'a> {
arena, arena,
subs, subs,
set_with_variables, set_with_variables,
opt_recursion_var.into_variable(),
target_info, target_info,
)); ));
@ -1071,10 +1152,28 @@ impl<'a> LambdaSet<'a> {
arena: &'a Bump, arena: &'a Bump,
subs: &Subs, subs: &Subs,
tags: std::vec::Vec<(Symbol, std::vec::Vec<Variable>)>, tags: std::vec::Vec<(Symbol, std::vec::Vec<Variable>)>,
opt_rec_var: Option<Variable>,
target_info: TargetInfo, target_info: TargetInfo,
) -> Layout<'a> { ) -> Layout<'a> {
if let Some(rec_var) = opt_rec_var {
let tags: std::vec::Vec<_> = tags
.iter()
.map(|(sym, vars)| (sym, vars.as_slice()))
.collect();
let tags = UnsortedUnionLabels { tags };
let mut env = Env {
seen: Vec::new_in(arena),
target_info,
arena,
subs,
};
return layout_from_recursive_union(&mut env, rec_var, &tags)
.expect("unable to create lambda set representation");
}
// otherwise, this is a closure with a payload // otherwise, this is a closure with a payload
let variant = union_sorted_tags_help(arena, tags, None, subs, target_info); let variant = union_sorted_tags_help(arena, tags, opt_rec_var, subs, target_info);
use UnionVariant::*; use UnionVariant::*;
match variant { match variant {
@ -1108,7 +1207,12 @@ impl<'a> LambdaSet<'a> {
Layout::Union(UnionLayout::NonRecursive(tag_arguments.into_bump_slice())) Layout::Union(UnionLayout::NonRecursive(tag_arguments.into_bump_slice()))
} }
_ => panic!("handle recursive layouts"), Recursive { .. }
| NullableUnwrapped { .. }
| NullableWrapped { .. }
| NonNullableUnwrapped { .. } => {
internal_error!("Recursive layouts should be produced in an earlier branch")
}
} }
} }
} }
@ -1130,7 +1234,10 @@ impl<'a> LambdaSet<'a> {
} }
enum ResolvedLambdaSet { enum ResolvedLambdaSet {
Set(std::vec::Vec<(Symbol, std::vec::Vec<Variable>)>), Set(
std::vec::Vec<(Symbol, std::vec::Vec<Variable>)>,
OptVariable,
),
/// TODO: figure out if this can happen in a correct program, or is the result of a bug in our /// TODO: figure out if this can happen in a correct program, or is the result of a bug in our
/// compiler. See https://github.com/rtfeldman/roc/issues/3163. /// compiler. See https://github.com/rtfeldman/roc/issues/3163.
Unbound, Unbound,
@ -1142,7 +1249,7 @@ fn resolve_lambda_set(subs: &Subs, mut var: Variable) -> ResolvedLambdaSet {
match subs.get_content_without_compacting(var) { match subs.get_content_without_compacting(var) {
Content::LambdaSet(subs::LambdaSet { Content::LambdaSet(subs::LambdaSet {
solved, solved,
recursion_var: _, recursion_var,
unspecialized, unspecialized,
ambient_function: _, ambient_function: _,
}) => { }) => {
@ -1153,7 +1260,7 @@ fn resolve_lambda_set(subs: &Subs, mut var: Variable) -> ResolvedLambdaSet {
subs.uls_of_var subs.uls_of_var
); );
roc_types::pretty_print::push_union(subs, solved, &mut set); roc_types::pretty_print::push_union(subs, solved, &mut set);
return ResolvedLambdaSet::Set(set); return ResolvedLambdaSet::Set(set, *recursion_var);
} }
Content::RecursionVar { structure, .. } => { Content::RecursionVar { structure, .. } => {
var = *structure; var = *structure;
@ -2130,11 +2237,15 @@ fn layout_from_flat_type<'a>(
} }
} }
Func(_, closure_var, _) => { Func(_, closure_var, _) => {
if env.is_seen(closure_var) {
Ok(Layout::RecursivePointer)
} else {
let lambda_set = let lambda_set =
LambdaSet::from_var(env.arena, env.subs, closure_var, env.target_info)?; LambdaSet::from_var(env.arena, env.subs, closure_var, env.target_info)?;
Ok(Layout::LambdaSet(lambda_set)) Ok(Layout::LambdaSet(lambda_set))
} }
}
Record(fields, ext_var) => { Record(fields, ext_var) => {
// extract any values from the ext_var // extract any values from the ext_var

View file

@ -713,12 +713,13 @@ fn solve(
new_env.insert_symbol_var_if_vacant(*symbol, loc_var.value); new_env.insert_symbol_var_if_vacant(*symbol, loc_var.value);
} }
stack.push(Work::CheckForInfiniteTypes(local_def_vars));
stack.push(Work::Constraint { stack.push(Work::Constraint {
env: arena.alloc(new_env), env: arena.alloc(new_env),
rank, rank,
constraint: ret_constraint, constraint: ret_constraint,
}); });
// Check for infinite types first
stack.push(Work::CheckForInfiniteTypes(local_def_vars));
continue; continue;
} }
@ -831,12 +832,13 @@ fn solve(
// Now solve the body, using the new vars_by_symbol which includes // Now solve the body, using the new vars_by_symbol which includes
// the assignments' name-to-variable mappings. // the assignments' name-to-variable mappings.
stack.push(Work::CheckForInfiniteTypes(local_def_vars));
stack.push(Work::Constraint { stack.push(Work::Constraint {
env: arena.alloc(new_env), env: arena.alloc(new_env),
rank, rank,
constraint: ret_constraint, constraint: ret_constraint,
}); });
// Check for infinite types first
stack.push(Work::CheckForInfiniteTypes(local_def_vars));
state = state_for_ret_con; state = state_for_ret_con;
@ -2874,28 +2876,34 @@ fn check_for_infinite_type(
) { ) {
let var = loc_var.value; let var = loc_var.value;
while let Err((recursive, _chain)) = subs.occurs(var) { 'next_occurs_check: while let Err((_, chain)) = subs.occurs(var) {
// try to make a union recursive, see if that helps // walk the chain till we find a tag union or lambda set, starting from the variable that
match subs.get_content_without_compacting(recursive) { // occurred recursively, which is always at the end of the chain.
&Content::Structure(FlatType::TagUnion(tags, ext_var)) => { for &var in chain.iter().rev() {
subs.mark_tag_union_recursive(recursive, tags, ext_var); match *subs.get_content_without_compacting(var) {
Content::Structure(FlatType::TagUnion(tags, ext_var)) => {
subs.mark_tag_union_recursive(var, tags, ext_var);
continue 'next_occurs_check;
} }
&Content::LambdaSet(subs::LambdaSet { Content::LambdaSet(subs::LambdaSet {
solved, solved,
recursion_var: _, recursion_var: _,
unspecialized, unspecialized,
ambient_function: ambient_function_var, ambient_function: ambient_function_var,
}) => { }) => {
subs.mark_lambda_set_recursive( subs.mark_lambda_set_recursive(
recursive, var,
solved, solved,
unspecialized, unspecialized,
ambient_function_var, ambient_function_var,
); );
continue 'next_occurs_check;
}
_ => { /* fall through */ }
}
} }
_other => circular_error(subs, problems, symbol, &loc_var), circular_error(subs, problems, symbol, &loc_var);
}
} }
} }

View file

@ -7657,4 +7657,39 @@ mod solve_expr {
"Num *", "Num *",
); );
} }
#[test]
fn issue_3444() {
infer_queries!(
indoc!(
r#"
compose = \f, g ->
closCompose = \x -> g (f x)
closCompose
const = \x ->
closConst = \_ -> x
closConst
list = []
res : Str -> Str
res = List.walk list (const "z") (\c1, c2 -> compose c1 c2)
# ^^^^^ ^^^^^^^
# ^^^^^^^^^^^^^^^^^^^^^^^^
#^^^{-1}
res "hello"
#^^^{-1}
"#
),
@r###"
const : Str -[[const(2)]]-> (Str -[[closCompose(7) (Str -a-> Str) (Str -[[]]-> Str), closConst(10) Str] as a]-> Str)
compose : (Str -a-> Str), (Str -[[]]-> Str) -[[compose(1)]]-> (Str -a-> Str)
\c1, c2 -> compose c1 c2 : (Str -a-> Str), (Str -[[]]-> Str) -[[11(11)]]-> (Str -a-> Str)
res : Str -[[closCompose(7) (Str -a-> Str) (Str -[[]]-> Str), closConst(10) Str] as a]-> Str
res : Str -[[closCompose(7) (Str -a-> Str) (Str -[[]]-> Str), closConst(10) Str] as a]-> Str
"###
);
}
} }

View file

@ -3705,3 +3705,136 @@ fn runtime_error_when_degenerate_pattern_reached() {
true // allow errors true // allow errors
); );
} }
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn recursive_lambda_set_issue_3444() {
assert_evals_to!(
indoc!(
r#"
combine = \f, g -> \x -> g (f x)
const = \x -> (\_y -> x)
list = [const "a", const "b", const "c"]
res : Str -> Str
res = List.walk list (const "z") (\c1, c2 -> combine c1 c2)
res "hello"
"#
),
RocStr::from("c"),
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn recursive_lambda_set_toplevel_issue_3444() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
combine = \f, g -> \x -> g (f x)
const = \x -> (\_y -> x)
list = [const "a", const "b", const "c"]
res : Str -> Str
res = List.walk list (const "z") (\c1, c2 -> combine c1 c2)
main = res "hello"
"#
),
RocStr::from("c"),
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn recursive_lambda_set_issue_3444_inferred() {
assert_evals_to!(
indoc!(
r#"
combine = \f, g -> \x -> g (f x)
const = \x -> (\_y -> x)
list = [const "a", const "b", const "c"]
res = List.walk list (const "z") (\c1, c2 -> combine c1 c2)
res "hello"
"#
),
RocStr::from("c"),
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn compose_recursive_lambda_set_productive_toplevel() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
compose = \f, g -> \x -> g (f x)
identity = \x -> x
exclaim = \s -> "\(s)!"
whisper = \s -> "(\(s))"
main =
res: Str -> Str
res = List.walk [ exclaim, whisper ] identity compose
res "hello"
"#
),
RocStr::from("(hello!)"),
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn compose_recursive_lambda_set_productive_nested() {
assert_evals_to!(
indoc!(
r#"
compose = \f, g -> \x -> g (f x)
identity = \x -> x
exclaim = \s -> "\(s)!"
whisper = \s -> "(\(s))"
res: Str -> Str
res = List.walk [ exclaim, whisper ] identity compose
res "hello"
"#
),
RocStr::from("(hello!)"),
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn compose_recursive_lambda_set_productive_inferred() {
assert_evals_to!(
indoc!(
r#"
compose = \f, g -> \x -> g (f x)
identity = \x -> x
exclaim = \s -> "\(s)!"
whisper = \s -> "(\(s))"
res = List.walk [ exclaim, whisper ] identity compose
res "hello"
"#
),
RocStr::from("(hello!)"),
RocStr
);
}

View file

@ -2518,10 +2518,12 @@ fn unify_flat_type<M: MetaCollector>(
outcome.union(arg_outcome); outcome.union(arg_outcome);
if outcome.mismatches.is_empty() { if outcome.mismatches.is_empty() {
let merged_closure_var = choose_merged_var(env.subs, *l_closure, *r_closure);
outcome.union(merge( outcome.union(merge(
env, env,
ctx, ctx,
Structure(Func(*r_args, *r_closure, *r_ret)), Structure(Func(*r_args, merged_closure_var, *r_ret)),
)); ));
} }

View file

@ -503,8 +503,9 @@ fn addr_to_ast<'a, M: ReplAppMemory>(
unroll_newtypes_and_aliases(env, content); unroll_newtypes_and_aliases(env, content);
let expr = match (raw_content, layout) { let expr = match (raw_content, layout) {
(Content::Structure(FlatType::Func(_, _, _)), _) (Content::Structure(FlatType::Func(_, _, _)), _) | (_, Layout::LambdaSet(_)) => {
| (_, Layout::LambdaSet(_)) => OPAQUE_FUNCTION, OPAQUE_FUNCTION
}
(_, Layout::Builtin(Builtin::Bool)) => { (_, Layout::Builtin(Builtin::Bool)) => {
// TODO: bits are not as expected here. // TODO: bits are not as expected here.
// num is always false at the moment. // num is always false at the moment.
@ -549,7 +550,7 @@ fn addr_to_ast<'a, M: ReplAppMemory>(
let arena_str = env.arena.alloc_str(string); let arena_str = env.arena.alloc_str(string);
Expr::Str(StrLiteral::PlainLine(arena_str)) Expr::Str(StrLiteral::PlainLine(arena_str))
} }
(_, Layout::Struct{field_layouts, ..}) => match raw_content { (_, Layout::Struct { field_layouts, .. }) => match raw_content {
Content::Structure(FlatType::Record(fields, _)) => { Content::Structure(FlatType::Record(fields, _)) => {
struct_to_ast(env, mem, addr, *fields) struct_to_ast(env, mem, addr, *fields)
} }
@ -573,18 +574,37 @@ fn addr_to_ast<'a, M: ReplAppMemory>(
); );
} }
}, },
(_, Layout::RecursivePointer) => { (_, Layout::RecursivePointer) => match (raw_content, when_recursive) {
match (raw_content, when_recursive) { (
(Content::RecursionVar { Content::RecursionVar {
structure, structure,
opt_name: _, opt_name: _,
}, WhenRecursive::Loop(union_layout)) => { },
WhenRecursive::Loop(union_layout),
) => {
let content = env.subs.get_content_without_compacting(*structure); let content = env.subs.get_content_without_compacting(*structure);
addr_to_ast(env, mem, addr, &union_layout, when_recursive, content) addr_to_ast(env, mem, addr, &union_layout, when_recursive, content)
} }
(
Content::RecursionVar {
structure,
opt_name: _,
},
WhenRecursive::Unreachable,
) => {
// It's possible to hit a recursive pointer before the full type layout; just
// figure out the actual recursive structure layout at this point.
let content = env.subs.get_content_without_compacting(*structure);
let union_layout = LayoutCache::new(env.target_info)
.from_var(env.arena, *structure, env.subs)
.expect("no layout for structure");
debug_assert!(matches!(union_layout, Layout::Union(..)));
let when_recursive = WhenRecursive::Loop(union_layout);
addr_to_ast(env, mem, addr, &union_layout, when_recursive, content)
}
other => unreachable!("Something had a RecursivePointer layout, but instead of being a RecursionVar and having a known recursive layout, I found {:?}", other), other => unreachable!("Something had a RecursivePointer layout, but instead of being a RecursionVar and having a known recursive layout, I found {:?}", other),
} },
}
(_, Layout::Union(UnionLayout::NonRecursive(union_layouts))) => { (_, Layout::Union(UnionLayout::NonRecursive(union_layouts))) => {
let union_layout = UnionLayout::NonRecursive(union_layouts); let union_layout = UnionLayout::NonRecursive(union_layouts);
@ -608,8 +628,7 @@ fn addr_to_ast<'a, M: ReplAppMemory>(
let tag_id = tag_id_from_data(env, mem, union_layout, addr); let tag_id = tag_id_from_data(env, mem, union_layout, addr);
// use the tag ID as an index, to get its name and layout of any arguments // use the tag ID as an index, to get its name and layout of any arguments
let (tag_name, arg_layouts) = let (tag_name, arg_layouts) = &tags_and_layouts[tag_id as usize];
&tags_and_layouts[tag_id as usize];
expr_of_tag( expr_of_tag(
env, env,
@ -623,18 +642,19 @@ fn addr_to_ast<'a, M: ReplAppMemory>(
} }
(_, Layout::Union(union_layout @ UnionLayout::Recursive(union_layouts))) => { (_, Layout::Union(union_layout @ UnionLayout::Recursive(union_layouts))) => {
let (rec_var, tags) = match raw_content { let (rec_var, tags) = match raw_content {
Content::Structure(FlatType::RecursiveTagUnion(rec_var, tags, _)) => (rec_var, tags), Content::Structure(FlatType::RecursiveTagUnion(rec_var, tags, _)) => {
(rec_var, tags)
}
_ => unreachable!("any other content would have a different layout"), _ => unreachable!("any other content would have a different layout"),
}; };
debug_assert_eq!(union_layouts.len(), tags.len()); debug_assert_eq!(union_layouts.len(), tags.len());
let (vars_of_tag, union_variant) = let (vars_of_tag, union_variant) = get_tags_vars_and_variant(env, tags, Some(*rec_var));
get_tags_vars_and_variant(env, tags, Some(*rec_var));
let tags_and_layouts = match union_variant { let tags_and_layouts = match union_variant {
UnionVariant::Wrapped(WrappedVariant::Recursive { UnionVariant::Wrapped(WrappedVariant::Recursive { sorted_tag_layouts }) => {
sorted_tag_layouts sorted_tag_layouts
}) => sorted_tag_layouts, }
_ => unreachable!("any other variant would have a different layout"), _ => unreachable!("any other variant would have a different layout"),
}; };
@ -653,7 +673,9 @@ fn addr_to_ast<'a, M: ReplAppMemory>(
} }
(_, Layout::Union(UnionLayout::NonNullableUnwrapped(_))) => { (_, Layout::Union(UnionLayout::NonNullableUnwrapped(_))) => {
let (rec_var, tags) = match unroll_recursion_var(env, raw_content) { let (rec_var, tags) = match unroll_recursion_var(env, raw_content) {
Content::Structure(FlatType::RecursiveTagUnion(rec_var, tags, _)) => (rec_var, tags), Content::Structure(FlatType::RecursiveTagUnion(rec_var, tags, _)) => {
(rec_var, tags)
}
other => unreachable!("Unexpected content for NonNullableUnwrapped: {:?}", other), other => unreachable!("Unexpected content for NonNullableUnwrapped: {:?}", other),
}; };
debug_assert_eq!(tags.len(), 1); debug_assert_eq!(tags.len(), 1);
@ -662,7 +684,8 @@ fn addr_to_ast<'a, M: ReplAppMemory>(
let (tag_name, arg_layouts) = match union_variant { let (tag_name, arg_layouts) = match union_variant {
UnionVariant::Wrapped(WrappedVariant::NonNullableUnwrapped { UnionVariant::Wrapped(WrappedVariant::NonNullableUnwrapped {
tag_name, fields, tag_name,
fields,
}) => (tag_name.expect_tag(), fields), }) => (tag_name.expect_tag(), fields),
_ => unreachable!("any other variant would have a different layout"), _ => unreachable!("any other variant would have a different layout"),
}; };
@ -681,7 +704,9 @@ fn addr_to_ast<'a, M: ReplAppMemory>(
} }
(_, Layout::Union(UnionLayout::NullableUnwrapped { .. })) => { (_, Layout::Union(UnionLayout::NullableUnwrapped { .. })) => {
let (rec_var, tags) = match unroll_recursion_var(env, raw_content) { let (rec_var, tags) = match unroll_recursion_var(env, raw_content) {
Content::Structure(FlatType::RecursiveTagUnion(rec_var, tags, _)) => (rec_var, tags), Content::Structure(FlatType::RecursiveTagUnion(rec_var, tags, _)) => {
(rec_var, tags)
}
other => unreachable!("Unexpected content for NonNullableUnwrapped: {:?}", other), other => unreachable!("Unexpected content for NonNullableUnwrapped: {:?}", other),
}; };
debug_assert!(tags.len() <= 2); debug_assert!(tags.len() <= 2);
@ -694,7 +719,11 @@ fn addr_to_ast<'a, M: ReplAppMemory>(
nullable_name, nullable_name,
other_name, other_name,
other_fields, other_fields,
}) => (nullable_name.expect_tag(), other_name.expect_tag(), other_fields), }) => (
nullable_name.expect_tag(),
other_name.expect_tag(),
other_fields,
),
_ => unreachable!("any other variant would have a different layout"), _ => unreachable!("any other variant would have a different layout"),
}; };
@ -715,7 +744,9 @@ fn addr_to_ast<'a, M: ReplAppMemory>(
} }
(_, Layout::Union(union_layout @ UnionLayout::NullableWrapped { .. })) => { (_, Layout::Union(union_layout @ UnionLayout::NullableWrapped { .. })) => {
let (rec_var, tags) = match unroll_recursion_var(env, raw_content) { let (rec_var, tags) = match unroll_recursion_var(env, raw_content) {
Content::Structure(FlatType::RecursiveTagUnion(rec_var, tags, _)) => (rec_var, tags), Content::Structure(FlatType::RecursiveTagUnion(rec_var, tags, _)) => {
(rec_var, tags)
}
other => unreachable!("Unexpected content for NonNullableUnwrapped: {:?}", other), other => unreachable!("Unexpected content for NonNullableUnwrapped: {:?}", other),
}; };
@ -736,7 +767,11 @@ fn addr_to_ast<'a, M: ReplAppMemory>(
} else { } else {
let (tag_id, data_addr) = tag_id_from_recursive_ptr(env, mem, *union_layout, addr); let (tag_id, data_addr) = tag_id_from_recursive_ptr(env, mem, *union_layout, addr);
let tag_id = if tag_id > nullable_id.into() { tag_id - 1 } else { tag_id }; let tag_id = if tag_id > nullable_id.into() {
tag_id - 1
} else {
tag_id
};
let (tag_name, arg_layouts) = &tags_and_layouts[tag_id as usize]; let (tag_name, arg_layouts) = &tags_and_layouts[tag_id as usize];
expr_of_tag( expr_of_tag(
@ -750,7 +785,10 @@ fn addr_to_ast<'a, M: ReplAppMemory>(
) )
} }
} }
(Content::Structure(FlatType::Apply(Symbol::BOX_BOX_TYPE, args)), Layout::Boxed(inner_layout)) => { (
Content::Structure(FlatType::Apply(Symbol::BOX_BOX_TYPE, args)),
Layout::Boxed(inner_layout),
) => {
debug_assert_eq!(args.len(), 1); debug_assert_eq!(args.len(), 1);
let inner_var_index = args.into_iter().next().unwrap(); let inner_var_index = args.into_iter().next().unwrap();
@ -758,17 +796,27 @@ fn addr_to_ast<'a, M: ReplAppMemory>(
let inner_content = env.subs.get_content_without_compacting(inner_var); let inner_content = env.subs.get_content_without_compacting(inner_var);
let addr_of_inner = mem.deref_usize(addr); let addr_of_inner = mem.deref_usize(addr);
let inner_expr = addr_to_ast(env, mem, addr_of_inner, inner_layout, WhenRecursive::Unreachable, inner_content); let inner_expr = addr_to_ast(
env,
mem,
addr_of_inner,
inner_layout,
WhenRecursive::Unreachable,
inner_content,
);
let box_box = env.arena.alloc(Loc::at_zero(Expr::Var { let box_box = env.arena.alloc(Loc::at_zero(Expr::Var {
module_name: "Box", ident: "box" module_name: "Box",
ident: "box",
})); }));
let box_box_arg = &*env.arena.alloc(Loc::at_zero(inner_expr)); let box_box_arg = &*env.arena.alloc(Loc::at_zero(inner_expr));
let box_box_args = env.arena.alloc([box_box_arg]); let box_box_args = env.arena.alloc([box_box_arg]);
Expr::Apply(box_box, box_box_args, CalledVia::Space) Expr::Apply(box_box, box_box_args, CalledVia::Space)
} }
(_, Layout::Boxed(_)) => unreachable!("Box layouts can only be behind a `Box.Box` application"), (_, Layout::Boxed(_)) => {
unreachable!("Box layouts can only be behind a `Box.Box` application")
}
other => { other => {
todo!( todo!(
"TODO add support for rendering pointer to {:?} in the REPL", "TODO add support for rendering pointer to {:?} in the REPL",

View file

@ -1004,7 +1004,7 @@ mod test_reporting {
@r###" @r###"
CIRCULAR TYPE /code/proj/Main.roc CIRCULAR TYPE /code/proj/Main.roc
I'm inferring a weird self-referential type for `g`: I'm inferring a weird self-referential type for `f`:
4 f = \g -> g g 4 f = \g -> g g
^ ^
@ -1013,7 +1013,7 @@ mod test_reporting {
parts of the type that repeat something already printed out parts of the type that repeat something already printed out
infinitely. infinitely.
-> a ( -> a) -> a
"### "###
); );
@ -1188,10 +1188,10 @@ mod test_reporting {
f f
"# "#
), ),
@r#" @r###"
CIRCULAR TYPE /code/proj/Main.roc CIRCULAR TYPE /code/proj/Main.roc
I'm inferring a weird self-referential type for `x`: I'm inferring a weird self-referential type for `f`:
5 f = \x -> f [x] 5 f = \x -> f [x]
^ ^
@ -1200,8 +1200,8 @@ mod test_reporting {
parts of the type that repeat something already printed out parts of the type that repeat something already printed out
infinitely. infinitely.
List List -> List a
"# "###
); );
test_report!( test_report!(
@ -1247,32 +1247,6 @@ mod test_reporting {
6 g = \x -> f [x] 6 g = \x -> f [x]
^ ^
Here is my best effort at writing down the type. You will see for
parts of the type that repeat something already printed out
infinitely.
List -> List a
CIRCULAR TYPE /code/proj/Main.roc
I'm inferring a weird self-referential type for `f`:
5 f = \x -> g x
^
Here is my best effort at writing down the type. You will see for
parts of the type that repeat something already printed out
infinitely.
List -> List a
CIRCULAR TYPE /code/proj/Main.roc
I'm inferring a weird self-referential type for `main`:
3 main =
^^^^
Here is my best effort at writing down the type. You will see for Here is my best effort at writing down the type. You will see for
parts of the type that repeat something already printed out parts of the type that repeat something already printed out
infinitely. infinitely.
@ -1292,10 +1266,23 @@ mod test_reporting {
f f
"# "#
), ),
@r#" @r###"
CIRCULAR TYPE /code/proj/Main.roc CIRCULAR TYPE /code/proj/Main.roc
I'm inferring a weird self-referential type for `x`: I'm inferring a weird self-referential type for `f`:
4 f = \x -> g x
^
Here is my best effort at writing down the type. You will see for
parts of the type that repeat something already printed out
infinitely.
List -> List a
CIRCULAR TYPE /code/proj/Main.roc
I'm inferring a weird self-referential type for `g`:
6 g = \x -> f [x] 6 g = \x -> f [x]
^ ^
@ -1304,8 +1291,8 @@ mod test_reporting {
parts of the type that repeat something already printed out parts of the type that repeat something already printed out
infinitely. infinitely.
List List -> List a
"# "###
); );
test_report!( test_report!(