mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-03 03:42:17 +00:00
Merge pull request #3738 from rtfeldman/i3444
Layout generation for recursive lambda sets
This commit is contained in:
commit
ae0e90c8f3
8 changed files with 441 additions and 110 deletions
|
@ -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),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
"###
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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)),
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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!(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue