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,
..
} => {
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());
// captured variables are in symbol-alphabetic order, but now we want
@ -3149,7 +3154,9 @@ fn specialize_external<'a>(
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 {
tag_id,
structure: Symbol::ARG_CLOSURE,
@ -3162,7 +3169,7 @@ fn specialize_external<'a>(
specialized_body = Stmt::Let(
symbol,
expr,
**layout,
layout,
env.arena.alloc(specialized_body),
);
}

View file

@ -10,7 +10,8 @@ use roc_problem::can::RuntimeError;
use roc_target::{PtrWidth, TargetInfo};
use roc_types::num::NumericRange;
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 std::cmp::Ordering;
@ -842,6 +843,7 @@ impl<'a> LambdaSet<'a> {
let comparator = |other_name: Symbol, other_captures_layouts: &[Layout]| {
other_name == lambda_name.name
// Make sure all captures are equal
&& other_captures_layouts
.iter()
.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");
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
.set
.iter()
.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 {
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>
where
F: Fn(Symbol, &[Layout]) -> bool,
@ -902,16 +947,48 @@ impl<'a> LambdaSet<'a> {
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::NullableWrapped {
nullable_id: _,
other_tags: _,
} => todo!("recursive closures"),
UnionLayout::NullableUnwrapped {
nullable_id: _,
other_fields: _,
} => todo!("recursive closures"),
}
}
Layout::Struct { .. } => {
@ -971,7 +1048,7 @@ impl<'a> LambdaSet<'a> {
target_info: TargetInfo,
) -> Result<Self, LayoutProblem> {
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!
lambdas.sort_by_key(|(sym, _)| *sym);
@ -992,6 +1069,9 @@ impl<'a> LambdaSet<'a> {
seen: Vec::new_in(arena),
target_info,
};
if let Some(rec_var) = opt_recursion_var.into_variable() {
env.insert_seen(rec_var);
}
for var in variables {
arguments.push(Layout::from_var(&mut env, *var)?);
@ -1048,6 +1128,7 @@ impl<'a> LambdaSet<'a> {
arena,
subs,
set_with_variables,
opt_recursion_var.into_variable(),
target_info,
));
@ -1071,10 +1152,28 @@ impl<'a> LambdaSet<'a> {
arena: &'a Bump,
subs: &Subs,
tags: std::vec::Vec<(Symbol, std::vec::Vec<Variable>)>,
opt_rec_var: Option<Variable>,
target_info: TargetInfo,
) -> 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
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::*;
match variant {
@ -1108,7 +1207,12 @@ impl<'a> LambdaSet<'a> {
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 {
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
/// compiler. See https://github.com/rtfeldman/roc/issues/3163.
Unbound,
@ -1142,7 +1249,7 @@ fn resolve_lambda_set(subs: &Subs, mut var: Variable) -> ResolvedLambdaSet {
match subs.get_content_without_compacting(var) {
Content::LambdaSet(subs::LambdaSet {
solved,
recursion_var: _,
recursion_var,
unspecialized,
ambient_function: _,
}) => {
@ -1153,7 +1260,7 @@ fn resolve_lambda_set(subs: &Subs, mut var: Variable) -> ResolvedLambdaSet {
subs.uls_of_var
);
roc_types::pretty_print::push_union(subs, solved, &mut set);
return ResolvedLambdaSet::Set(set);
return ResolvedLambdaSet::Set(set, *recursion_var);
}
Content::RecursionVar { structure, .. } => {
var = *structure;
@ -2130,10 +2237,14 @@ fn layout_from_flat_type<'a>(
}
}
Func(_, closure_var, _) => {
let lambda_set =
LambdaSet::from_var(env.arena, env.subs, closure_var, env.target_info)?;
if env.is_seen(closure_var) {
Ok(Layout::RecursivePointer)
} else {
let lambda_set =
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) => {
// 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);
}
stack.push(Work::CheckForInfiniteTypes(local_def_vars));
stack.push(Work::Constraint {
env: arena.alloc(new_env),
rank,
constraint: ret_constraint,
});
// Check for infinite types first
stack.push(Work::CheckForInfiniteTypes(local_def_vars));
continue;
}
@ -831,12 +832,13 @@ fn solve(
// Now solve the body, using the new vars_by_symbol which includes
// the assignments' name-to-variable mappings.
stack.push(Work::CheckForInfiniteTypes(local_def_vars));
stack.push(Work::Constraint {
env: arena.alloc(new_env),
rank,
constraint: ret_constraint,
});
// Check for infinite types first
stack.push(Work::CheckForInfiniteTypes(local_def_vars));
state = state_for_ret_con;
@ -2874,28 +2876,34 @@ fn check_for_infinite_type(
) {
let var = loc_var.value;
while let Err((recursive, _chain)) = subs.occurs(var) {
// try to make a union recursive, see if that helps
match subs.get_content_without_compacting(recursive) {
&Content::Structure(FlatType::TagUnion(tags, ext_var)) => {
subs.mark_tag_union_recursive(recursive, tags, ext_var);
}
&Content::LambdaSet(subs::LambdaSet {
solved,
recursion_var: _,
unspecialized,
ambient_function: ambient_function_var,
}) => {
subs.mark_lambda_set_recursive(
recursive,
'next_occurs_check: while let Err((_, chain)) = subs.occurs(var) {
// walk the chain till we find a tag union or lambda set, starting from the variable that
// occurred recursively, which is always at the end of the chain.
for &var in chain.iter().rev() {
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 {
solved,
recursion_var: _,
unspecialized,
ambient_function_var,
);
ambient_function: ambient_function_var,
}) => {
subs.mark_lambda_set_recursive(
var,
solved,
unspecialized,
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 *",
);
}
#[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
);
}
#[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);
if outcome.mismatches.is_empty() {
let merged_closure_var = choose_merged_var(env.subs, *l_closure, *r_closure);
outcome.union(merge(
env,
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);
let expr = match (raw_content, layout) {
(Content::Structure(FlatType::Func(_, _, _)), _)
| (_, Layout::LambdaSet(_)) => OPAQUE_FUNCTION,
(Content::Structure(FlatType::Func(_, _, _)), _) | (_, Layout::LambdaSet(_)) => {
OPAQUE_FUNCTION
}
(_, Layout::Builtin(Builtin::Bool)) => {
// TODO: bits are not as expected here.
// 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);
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, _)) => {
struct_to_ast(env, mem, addr, *fields)
}
@ -573,18 +574,37 @@ fn addr_to_ast<'a, M: ReplAppMemory>(
);
}
},
(_, Layout::RecursivePointer) => {
match (raw_content, when_recursive) {
(Content::RecursionVar {
(_, Layout::RecursivePointer) => match (raw_content, when_recursive) {
(
Content::RecursionVar {
structure,
opt_name: _,
}, WhenRecursive::Loop(union_layout)) => {
let content = env.subs.get_content_without_compacting(*structure);
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),
},
WhenRecursive::Loop(union_layout),
) => {
let content = env.subs.get_content_without_compacting(*structure);
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),
},
(_, Layout::Union(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);
// use the tag ID as an index, to get its name and layout of any arguments
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(
env,
@ -623,18 +642,19 @@ fn addr_to_ast<'a, M: ReplAppMemory>(
}
(_, Layout::Union(union_layout @ UnionLayout::Recursive(union_layouts))) => {
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"),
};
debug_assert_eq!(union_layouts.len(), tags.len());
let (vars_of_tag, union_variant) =
get_tags_vars_and_variant(env, tags, Some(*rec_var));
let (vars_of_tag, union_variant) = get_tags_vars_and_variant(env, tags, Some(*rec_var));
let tags_and_layouts = match union_variant {
UnionVariant::Wrapped(WrappedVariant::Recursive {
UnionVariant::Wrapped(WrappedVariant::Recursive { sorted_tag_layouts }) => {
sorted_tag_layouts
}) => sorted_tag_layouts,
}
_ => unreachable!("any other variant would have a different layout"),
};
@ -653,7 +673,9 @@ fn addr_to_ast<'a, M: ReplAppMemory>(
}
(_, Layout::Union(UnionLayout::NonNullableUnwrapped(_))) => {
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),
};
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 {
UnionVariant::Wrapped(WrappedVariant::NonNullableUnwrapped {
tag_name, fields,
tag_name,
fields,
}) => (tag_name.expect_tag(), fields),
_ => unreachable!("any other variant would have a different layout"),
};
@ -681,7 +704,9 @@ fn addr_to_ast<'a, M: ReplAppMemory>(
}
(_, Layout::Union(UnionLayout::NullableUnwrapped { .. })) => {
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),
};
debug_assert!(tags.len() <= 2);
@ -694,7 +719,11 @@ fn addr_to_ast<'a, M: ReplAppMemory>(
nullable_name,
other_name,
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"),
};
@ -715,7 +744,9 @@ fn addr_to_ast<'a, M: ReplAppMemory>(
}
(_, Layout::Union(union_layout @ UnionLayout::NullableWrapped { .. })) => {
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),
};
@ -736,7 +767,11 @@ fn addr_to_ast<'a, M: ReplAppMemory>(
} else {
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];
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);
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 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 {
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_args = env.arena.alloc([box_box_arg]);
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 => {
todo!(
"TODO add support for rendering pointer to {:?} in the REPL",

View file

@ -1004,16 +1004,16 @@ mod test_reporting {
@r###"
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
^
^
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.
-> a
( -> a) -> a
"###
);
@ -1188,20 +1188,20 @@ mod test_reporting {
f
"#
),
@r#"
@r###"
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]
^
^
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 -> List a
"###
);
test_report!(
@ -1247,32 +1247,6 @@ mod test_reporting {
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
parts of the type that repeat something already printed out
infinitely.
@ -1292,20 +1266,33 @@ mod test_reporting {
f
"#
),
@r#"
@r###"
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`:
6 g = \x -> f [x]
^
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 -> List a
CIRCULAR TYPE /code/proj/Main.roc
I'm inferring a weird self-referential type for `g`:
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
"###
);
test_report!(