mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 13:59:08 +00:00
Getting closer. Checking in for a break.
This commit is contained in:
parent
790eb8e20c
commit
2e70c9aba4
12 changed files with 534 additions and 203 deletions
|
@ -1408,7 +1408,10 @@ fn adjust_rank_content(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LambdaSet(subs::LambdaSet { solved }) => {
|
LambdaSet(subs::LambdaSet {
|
||||||
|
solved,
|
||||||
|
recursion_var,
|
||||||
|
}) => {
|
||||||
let mut rank = group_rank;
|
let mut rank = group_rank;
|
||||||
|
|
||||||
for (_, index) in solved.iter_all() {
|
for (_, index) in solved.iter_all() {
|
||||||
|
@ -1419,6 +1422,15 @@ fn adjust_rank_content(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(rec_var) = recursion_var.into_variable() {
|
||||||
|
// THEORY: the recursion var has the same rank as the tag union itself
|
||||||
|
// all types it uses are also in the tags already, so it cannot influence the
|
||||||
|
// rank
|
||||||
|
debug_assert!(
|
||||||
|
rank >= adjust_rank(subs, young_mark, visit_mark, group_rank, rec_var)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
rank
|
rank
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1596,7 +1608,14 @@ fn instantiate_rigids_help(
|
||||||
instantiate_rigids_help(subs, max_rank, pools, real_type_var);
|
instantiate_rigids_help(subs, max_rank, pools, real_type_var);
|
||||||
}
|
}
|
||||||
|
|
||||||
LambdaSet(subs::LambdaSet { solved }) => {
|
LambdaSet(subs::LambdaSet {
|
||||||
|
solved,
|
||||||
|
recursion_var,
|
||||||
|
}) => {
|
||||||
|
if let Some(rec_var) = recursion_var.into_variable() {
|
||||||
|
instantiate_rigids_help(subs, max_rank, pools, rec_var);
|
||||||
|
}
|
||||||
|
|
||||||
for (_, index) in solved.iter_all() {
|
for (_, index) in solved.iter_all() {
|
||||||
let slice = subs[index];
|
let slice = subs[index];
|
||||||
for var_index in slice {
|
for var_index in slice {
|
||||||
|
@ -1872,7 +1891,10 @@ fn deep_copy_var_help(
|
||||||
copy
|
copy
|
||||||
}
|
}
|
||||||
|
|
||||||
LambdaSet(subs::LambdaSet { solved }) => {
|
LambdaSet(subs::LambdaSet {
|
||||||
|
solved,
|
||||||
|
recursion_var,
|
||||||
|
}) => {
|
||||||
let mut new_variable_slices = Vec::with_capacity(solved.len());
|
let mut new_variable_slices = Vec::with_capacity(solved.len());
|
||||||
|
|
||||||
let mut new_variables = Vec::new();
|
let mut new_variables = Vec::new();
|
||||||
|
@ -1898,7 +1920,13 @@ fn deep_copy_var_help(
|
||||||
};
|
};
|
||||||
|
|
||||||
let new_solved = UnionTags::from_slices(solved.tag_names(), new_variables);
|
let new_solved = UnionTags::from_slices(solved.tag_names(), new_variables);
|
||||||
let new_content = LambdaSet(subs::LambdaSet { solved: new_solved });
|
let new_rec_var =
|
||||||
|
recursion_var.map(|rec_var| deep_copy_var_help(subs, max_rank, pools, rec_var));
|
||||||
|
|
||||||
|
let new_content = LambdaSet(subs::LambdaSet {
|
||||||
|
solved: new_solved,
|
||||||
|
recursion_var: new_rec_var,
|
||||||
|
});
|
||||||
|
|
||||||
subs.set(copy, make_descriptor(new_content));
|
subs.set(copy, make_descriptor(new_content));
|
||||||
|
|
||||||
|
|
|
@ -118,9 +118,10 @@ fn add_type_help<'a>(
|
||||||
fields,
|
fields,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Content::LambdaSet(LambdaSet { solved }) => {
|
Content::LambdaSet(LambdaSet {
|
||||||
add_tag_union(env, opt_name, solved, var, types)
|
solved,
|
||||||
}
|
recursion_var: _,
|
||||||
|
}) => add_tag_union(env, opt_name, solved, var, types),
|
||||||
Content::Structure(FlatType::TagUnion(tags, ext_var)) => {
|
Content::Structure(FlatType::TagUnion(tags, ext_var)) => {
|
||||||
debug_assert!(ext_var_is_empty_tag_union(subs, *ext_var));
|
debug_assert!(ext_var_is_empty_tag_union(subs, *ext_var));
|
||||||
|
|
||||||
|
|
|
@ -607,7 +607,11 @@ fn deep_copy_type_vars<'a>(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
LambdaSet(subs::LambdaSet { solved }) => {
|
LambdaSet(subs::LambdaSet {
|
||||||
|
solved,
|
||||||
|
recursion_var,
|
||||||
|
}) => {
|
||||||
|
let new_rec_var = recursion_var.map(|var| descend_var!(var));
|
||||||
for variables_slice_index in solved.variables() {
|
for variables_slice_index in solved.variables() {
|
||||||
let variables_slice = subs[variables_slice_index];
|
let variables_slice = subs[variables_slice_index];
|
||||||
descend_slice!(variables_slice);
|
descend_slice!(variables_slice);
|
||||||
|
@ -626,7 +630,10 @@ fn deep_copy_type_vars<'a>(
|
||||||
let new_solved =
|
let new_solved =
|
||||||
UnionTags::from_slices(solved.tag_names(), new_variable_slices);
|
UnionTags::from_slices(solved.tag_names(), new_variable_slices);
|
||||||
|
|
||||||
LambdaSet(subs::LambdaSet { solved: new_solved })
|
LambdaSet(subs::LambdaSet {
|
||||||
|
solved: new_solved,
|
||||||
|
recursion_var: new_rec_var,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2739,7 +2739,10 @@ fn generate_runtime_error_function<'a>(
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
eprintln!("emitted runtime error function {:?}", &msg);
|
eprintln!(
|
||||||
|
"emitted runtime error function {:?} for layout {:?}",
|
||||||
|
&msg, layout
|
||||||
|
);
|
||||||
|
|
||||||
let runtime_error = Stmt::RuntimeError(msg.into_bump_str());
|
let runtime_error = Stmt::RuntimeError(msg.into_bump_str());
|
||||||
|
|
||||||
|
@ -2911,6 +2914,7 @@ fn specialize_external<'a>(
|
||||||
fn_var,
|
fn_var,
|
||||||
roc_unify::unify::Mode::EQ,
|
roc_unify::unify::Mode::EQ,
|
||||||
);
|
);
|
||||||
|
dbg!(&_unified);
|
||||||
|
|
||||||
// This will not hold for programs with type errors
|
// This will not hold for programs with type errors
|
||||||
// let is_valid = matches!(unified, roc_unify::unify::Unified::Success(_));
|
// let is_valid = matches!(unified, roc_unify::unify::Unified::Success(_));
|
||||||
|
@ -2928,8 +2932,13 @@ fn specialize_external<'a>(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let specialized =
|
let specialized = dbg!(build_specialized_proc_from_var(
|
||||||
build_specialized_proc_from_var(env, layout_cache, proc_name, pattern_symbols, fn_var)?;
|
env,
|
||||||
|
layout_cache,
|
||||||
|
proc_name,
|
||||||
|
pattern_symbols,
|
||||||
|
fn_var
|
||||||
|
))?;
|
||||||
|
|
||||||
// determine the layout of aliases/rigids exposed to the host
|
// determine the layout of aliases/rigids exposed to the host
|
||||||
let host_exposed_layouts = if host_exposed_variables.is_empty() {
|
let host_exposed_layouts = if host_exposed_variables.is_empty() {
|
||||||
|
@ -3537,6 +3546,7 @@ where
|
||||||
Ok((proc, raw))
|
Ok((proc, raw))
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
|
dbg!(&error);
|
||||||
env.subs.rollback_to(snapshot);
|
env.subs.rollback_to(snapshot);
|
||||||
layout_cache.rollback_to(cache_snapshot);
|
layout_cache.rollback_to(cache_snapshot);
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ impl<'a> RawFunctionLayout<'a> {
|
||||||
content: Content,
|
content: Content,
|
||||||
) -> Result<Self, LayoutProblem> {
|
) -> Result<Self, LayoutProblem> {
|
||||||
use roc_types::subs::Content::*;
|
use roc_types::subs::Content::*;
|
||||||
match content {
|
match dbg!(content) {
|
||||||
FlexVar(_) | RigidVar(_) => Err(LayoutProblem::UnresolvedTypeVar(var)),
|
FlexVar(_) | RigidVar(_) => Err(LayoutProblem::UnresolvedTypeVar(var)),
|
||||||
FlexAbleVar(_, _) | RigidAbleVar(_, _) => todo_abilities!("Not reachable yet"),
|
FlexAbleVar(_, _) | RigidAbleVar(_, _) => todo_abilities!("Not reachable yet"),
|
||||||
RecursionVar { structure, .. } => {
|
RecursionVar { structure, .. } => {
|
||||||
|
@ -157,8 +157,9 @@ impl<'a> RawFunctionLayout<'a> {
|
||||||
lset: subs::LambdaSet,
|
lset: subs::LambdaSet,
|
||||||
) -> Result<Self, LayoutProblem> {
|
) -> Result<Self, LayoutProblem> {
|
||||||
// Lambda set is just a tag union from the layout's perspective.
|
// Lambda set is just a tag union from the layout's perspective.
|
||||||
let subs::LambdaSet { solved } = lset;
|
dbg!(
|
||||||
Self::layout_from_flat_type(env, FlatType::TagUnion(solved, Variable::EMPTY_TAG_UNION))
|
Self::layout_from_flat_type(env, dbg!(lset.as_tag_union()))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout_from_flat_type(
|
fn layout_from_flat_type(
|
||||||
|
@ -1696,8 +1697,7 @@ fn layout_from_lambda_set<'a>(
|
||||||
lset: subs::LambdaSet,
|
lset: subs::LambdaSet,
|
||||||
) -> Result<Layout<'a>, LayoutProblem> {
|
) -> Result<Layout<'a>, LayoutProblem> {
|
||||||
// Lambda set is just a tag union from the layout's perspective.
|
// Lambda set is just a tag union from the layout's perspective.
|
||||||
let subs::LambdaSet { solved } = lset;
|
layout_from_flat_type(env, lset.as_tag_union())
|
||||||
layout_from_flat_type(env, FlatType::TagUnion(solved, Variable::EMPTY_TAG_UNION))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout_from_flat_type<'a>(
|
fn layout_from_flat_type<'a>(
|
||||||
|
|
|
@ -156,12 +156,7 @@ impl FunctionLayout {
|
||||||
lset: subs::LambdaSet,
|
lset: subs::LambdaSet,
|
||||||
) -> Result<Self, LayoutError> {
|
) -> Result<Self, LayoutError> {
|
||||||
// Lambda set is just a tag union from the layout's perspective.
|
// Lambda set is just a tag union from the layout's perspective.
|
||||||
let subs::LambdaSet { solved } = lset;
|
Self::from_flat_type(layouts, subs, &lset.as_tag_union())
|
||||||
Self::from_flat_type(
|
|
||||||
layouts,
|
|
||||||
subs,
|
|
||||||
&FlatType::TagUnion(solved, Variable::EMPTY_TAG_UNION),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_flat_type(
|
fn from_flat_type(
|
||||||
|
@ -281,12 +276,7 @@ impl LambdaSet {
|
||||||
lset: subs::LambdaSet,
|
lset: subs::LambdaSet,
|
||||||
) -> Result<Self, LayoutError> {
|
) -> Result<Self, LayoutError> {
|
||||||
// Lambda set is just a tag union from the layout's perspective.
|
// Lambda set is just a tag union from the layout's perspective.
|
||||||
let subs::LambdaSet { solved } = lset;
|
Self::from_flat_type(layouts, subs, &lset.as_tag_union())
|
||||||
Self::from_flat_type(
|
|
||||||
layouts,
|
|
||||||
subs,
|
|
||||||
&FlatType::TagUnion(solved, Variable::EMPTY_TAG_UNION),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_flat_type(
|
fn from_flat_type(
|
||||||
|
@ -730,12 +720,7 @@ impl Layout {
|
||||||
lset: subs::LambdaSet,
|
lset: subs::LambdaSet,
|
||||||
) -> Result<Layout, LayoutError> {
|
) -> Result<Layout, LayoutError> {
|
||||||
// Lambda set is just a tag union from the layout's perspective.
|
// Lambda set is just a tag union from the layout's perspective.
|
||||||
let subs::LambdaSet { solved } = lset;
|
Self::from_flat_type(layouts, subs, &lset.as_tag_union())
|
||||||
Self::from_flat_type(
|
|
||||||
layouts,
|
|
||||||
subs,
|
|
||||||
&FlatType::TagUnion(solved, Variable::EMPTY_TAG_UNION),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_flat_type(
|
fn from_flat_type(
|
||||||
|
|
|
@ -1893,7 +1893,12 @@ fn type_to_variable<'a>(
|
||||||
Content::Structure(FlatType::EmptyTagUnion),
|
Content::Structure(FlatType::EmptyTagUnion),
|
||||||
));
|
));
|
||||||
|
|
||||||
let content = Content::LambdaSet(subs::LambdaSet { solved });
|
let content = Content::LambdaSet(subs::LambdaSet {
|
||||||
|
solved,
|
||||||
|
// We may figure out the lambda set is recursive during solving, but it never
|
||||||
|
// is to begin with.
|
||||||
|
recursion_var: OptVariable::NONE,
|
||||||
|
});
|
||||||
|
|
||||||
register_with_known_var(subs, destination, rank, pools, content)
|
register_with_known_var(subs, destination, rank, pools, content)
|
||||||
}
|
}
|
||||||
|
@ -2528,13 +2533,24 @@ fn check_for_infinite_type(
|
||||||
let var = loc_var.value;
|
let var = loc_var.value;
|
||||||
|
|
||||||
while let Err((recursive, _chain)) = subs.occurs(var) {
|
while let Err((recursive, _chain)) = subs.occurs(var) {
|
||||||
// try to make a tag union recursive, see if that helps
|
// try to make a union recursive, see if that helps
|
||||||
match subs.get_content_without_compacting(recursive) {
|
match subs.get_content_without_compacting(recursive) {
|
||||||
&Content::Structure(FlatType::TagUnion(tags, ext_var)) => {
|
&Content::Structure(FlatType::TagUnion(tags, ext_var)) => {
|
||||||
|
dbg!(1);
|
||||||
subs.mark_tag_union_recursive(recursive, tags, ext_var);
|
subs.mark_tag_union_recursive(recursive, tags, ext_var);
|
||||||
}
|
}
|
||||||
|
&Content::LambdaSet(subs::LambdaSet {
|
||||||
|
solved,
|
||||||
|
recursion_var: _,
|
||||||
|
}) => {
|
||||||
|
dbg!(2);
|
||||||
|
subs.mark_lambda_set_recursive(recursive, solved);
|
||||||
|
}
|
||||||
|
|
||||||
_other => circular_error(subs, problems, symbol, &loc_var),
|
_other => {
|
||||||
|
dbg!(3);
|
||||||
|
circular_error(subs, problems, symbol, &loc_var)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2875,7 +2891,10 @@ fn adjust_rank_content(
|
||||||
rank
|
rank
|
||||||
}
|
}
|
||||||
|
|
||||||
LambdaSet(subs::LambdaSet { solved }) => {
|
LambdaSet(subs::LambdaSet {
|
||||||
|
solved,
|
||||||
|
recursion_var,
|
||||||
|
}) => {
|
||||||
let mut rank = group_rank;
|
let mut rank = group_rank;
|
||||||
|
|
||||||
for (_, index) in solved.iter_all() {
|
for (_, index) in solved.iter_all() {
|
||||||
|
@ -2886,6 +2905,26 @@ fn adjust_rank_content(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let (true, Some(rec_var)) = (cfg!(debug_assertions), recursion_var.into_variable()) {
|
||||||
|
// THEORY: unlike the situation for recursion vars under recursive tag unions,
|
||||||
|
// recursive vars inside lambda sets can't escape into higher let-generalized regions
|
||||||
|
// because lambda sets aren't user-facing.
|
||||||
|
//
|
||||||
|
// So the recursion var should be fully accounted by everything else in the lambda set
|
||||||
|
// (since it appears in the lambda set), and if the rank is higher, it's either a
|
||||||
|
// bug or our theory is wrong and indeed they can escape into higher regions.
|
||||||
|
let rec_var_rank = adjust_rank(subs, young_mark, visit_mark, group_rank, rec_var);
|
||||||
|
|
||||||
|
debug_assert!(
|
||||||
|
rank >= rec_var_rank,
|
||||||
|
"rank was {:?} but recursion var <{:?}>{:?} has higher rank {:?}",
|
||||||
|
rank,
|
||||||
|
rec_var,
|
||||||
|
subs.get_content_without_compacting(rec_var),
|
||||||
|
rec_var_rank
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
rank
|
rank
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3041,11 +3080,18 @@ fn instantiate_rigids_help(subs: &mut Subs, max_rank: Rank, initial: Variable) {
|
||||||
|
|
||||||
stack.push(var);
|
stack.push(var);
|
||||||
}
|
}
|
||||||
LambdaSet(subs::LambdaSet { solved }) => {
|
LambdaSet(subs::LambdaSet {
|
||||||
|
solved,
|
||||||
|
recursion_var,
|
||||||
|
}) => {
|
||||||
for slice_index in solved.variables() {
|
for slice_index in solved.variables() {
|
||||||
let slice = subs.variable_slices[slice_index.index as usize];
|
let slice = subs.variable_slices[slice_index.index as usize];
|
||||||
stack.extend(var_slice!(slice));
|
stack.extend(var_slice!(slice));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(rec_var) = recursion_var.into_variable() {
|
||||||
|
stack.push(rec_var);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
&RangedNumber(typ, _) => {
|
&RangedNumber(typ, _) => {
|
||||||
stack.push(typ);
|
stack.push(typ);
|
||||||
|
@ -3306,10 +3352,20 @@ fn deep_copy_var_help(
|
||||||
subs.set_content_unchecked(copy, new_content);
|
subs.set_content_unchecked(copy, new_content);
|
||||||
}
|
}
|
||||||
|
|
||||||
LambdaSet(subs::LambdaSet { solved }) => {
|
LambdaSet(subs::LambdaSet {
|
||||||
|
solved,
|
||||||
|
recursion_var,
|
||||||
|
}) => {
|
||||||
let new_solved = copy_union_tags!(solved);
|
let new_solved = copy_union_tags!(solved);
|
||||||
|
let new_rec_var = recursion_var.map(|v| work!(v));
|
||||||
|
|
||||||
subs.set_content_unchecked(copy, LambdaSet(subs::LambdaSet { solved: new_solved }));
|
subs.set_content_unchecked(
|
||||||
|
copy,
|
||||||
|
LambdaSet(subs::LambdaSet {
|
||||||
|
solved: new_solved,
|
||||||
|
recursion_var: new_rec_var,
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
RangedNumber(typ, range) => {
|
RangedNumber(typ, range) => {
|
||||||
|
|
|
@ -6500,7 +6500,37 @@ mod solve_expr {
|
||||||
r#"
|
r#"
|
||||||
app "test" provides [main] to "./platform"
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
main = Result.mapErr
|
greeting =
|
||||||
|
hi = "Hello"
|
||||||
|
name = "World"
|
||||||
|
|
||||||
|
"\(hi), \(name)!"
|
||||||
|
|
||||||
|
main =
|
||||||
|
when nestHelp 4 is
|
||||||
|
_ -> greeting
|
||||||
|
|
||||||
|
nestHelp : I64 -> XEffect {}
|
||||||
|
nestHelp = \m ->
|
||||||
|
when m is
|
||||||
|
0 ->
|
||||||
|
always {}
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
always {} |> after \_ -> nestHelp (m - 1)
|
||||||
|
|
||||||
|
|
||||||
|
XEffect a := {} -> a
|
||||||
|
|
||||||
|
always : a -> XEffect a
|
||||||
|
always = \x -> @XEffect (\{} -> x)
|
||||||
|
|
||||||
|
after : XEffect a, (a -> XEffect b) -> XEffect b
|
||||||
|
after = \(@XEffect e), toB ->
|
||||||
|
@XEffect \{} ->
|
||||||
|
when toB (e {}) is
|
||||||
|
@XEffect e2 ->
|
||||||
|
e2 {}
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"",
|
"",
|
||||||
|
|
|
@ -3195,7 +3195,6 @@ fn alias_defined_out_of_order() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore = "recursive lambda set"]
|
|
||||||
#[cfg(any(feature = "gen-llvm"))]
|
#[cfg(any(feature = "gen-llvm"))]
|
||||||
fn recursively_build_effect() {
|
fn recursively_build_effect() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
|
|
|
@ -221,7 +221,22 @@ fn find_names_needed(
|
||||||
// TODO should we also look in the actual variable?
|
// TODO should we also look in the actual variable?
|
||||||
// find_names_needed(_actual, subs, roots, root_appearances, names_taken);
|
// find_names_needed(_actual, subs, roots, root_appearances, names_taken);
|
||||||
}
|
}
|
||||||
LambdaSet(subs::LambdaSet { solved: _ }) => {}
|
LambdaSet(subs::LambdaSet {
|
||||||
|
solved,
|
||||||
|
recursion_var,
|
||||||
|
}) => {
|
||||||
|
for slice_index in solved.variables() {
|
||||||
|
let slice = subs[slice_index];
|
||||||
|
for var_index in slice {
|
||||||
|
let var = subs[var_index];
|
||||||
|
find_names_needed(var, subs, roots, root_appearances, names_taken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(rec_var) = recursion_var.into_variable() {
|
||||||
|
find_names_needed(rec_var, subs, roots, root_appearances, names_taken);
|
||||||
|
}
|
||||||
|
}
|
||||||
&RangedNumber(typ, _) => {
|
&RangedNumber(typ, _) => {
|
||||||
find_names_needed(typ, subs, roots, root_appearances, names_taken);
|
find_names_needed(typ, subs, roots, root_appearances, names_taken);
|
||||||
}
|
}
|
||||||
|
@ -944,7 +959,10 @@ pub fn resolve_lambda_set<'a>(
|
||||||
fields: &mut Vec<(TagName, Vec<Variable>)>,
|
fields: &mut Vec<(TagName, Vec<Variable>)>,
|
||||||
) {
|
) {
|
||||||
match subs.get_content_without_compacting(var) {
|
match subs.get_content_without_compacting(var) {
|
||||||
Content::LambdaSet(subs::LambdaSet { solved }) => {
|
Content::LambdaSet(subs::LambdaSet {
|
||||||
|
solved,
|
||||||
|
recursion_var: _,
|
||||||
|
}) => {
|
||||||
push_union_tags(subs, solved, fields);
|
push_union_tags(subs, solved, fields);
|
||||||
}
|
}
|
||||||
c => internal_error!("called with a non-lambda set {:?}", c),
|
c => internal_error!("called with a non-lambda set {:?}", c),
|
||||||
|
|
|
@ -718,11 +718,15 @@ fn subs_fmt_content(this: &Content, subs: &Subs, f: &mut fmt::Formatter) -> fmt:
|
||||||
SubsFmtContent(subs.get_content_without_compacting(*actual), subs)
|
SubsFmtContent(subs.get_content_without_compacting(*actual), subs)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Content::LambdaSet(LambdaSet { solved }) => {
|
Content::LambdaSet(LambdaSet {
|
||||||
|
solved,
|
||||||
|
recursion_var,
|
||||||
|
}) => {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"LambdaSet({:?})",
|
"LambdaSet({:?}, <{:?}>)",
|
||||||
SubsFmtUnionTags(solved, Variable::EMPTY_TAG_UNION, subs)
|
SubsFmtUnionTags(solved, Variable::EMPTY_TAG_UNION, subs),
|
||||||
|
recursion_var
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Content::RangedNumber(typ, range) => {
|
Content::RangedNumber(typ, range) => {
|
||||||
|
@ -784,7 +788,9 @@ fn subs_fmt_flat_type(this: &FlatType, subs: &Subs, f: &mut fmt::Formatter) -> f
|
||||||
let lambda_content = subs.get_content_without_compacting(*lambda_set);
|
let lambda_content = subs.get_content_without_compacting(*lambda_set);
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"], {:?}, <{:?}>{:?})",
|
"], <{:?}={:?}>{:?}, <{:?}>{:?})",
|
||||||
|
lambda_set,
|
||||||
|
subs.get_root_key_without_compacting(*lambda_set),
|
||||||
SubsFmtContent(lambda_content, subs),
|
SubsFmtContent(lambda_content, subs),
|
||||||
*result,
|
*result,
|
||||||
SubsFmtContent(result_content, subs)
|
SubsFmtContent(result_content, subs)
|
||||||
|
@ -908,6 +914,16 @@ impl OptVariable {
|
||||||
Variable(self.0)
|
Variable(self.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn map<F>(self, f: F) -> OptVariable
|
||||||
|
where
|
||||||
|
F: FnOnce(Variable) -> Variable,
|
||||||
|
{
|
||||||
|
self.into_variable()
|
||||||
|
.map(f)
|
||||||
|
.map(OptVariable::from)
|
||||||
|
.unwrap_or(OptVariable::NONE)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for OptVariable {
|
impl fmt::Debug for OptVariable {
|
||||||
|
@ -1759,6 +1775,30 @@ impl Subs {
|
||||||
tags: UnionTags,
|
tags: UnionTags,
|
||||||
ext_var: Variable,
|
ext_var: Variable,
|
||||||
) {
|
) {
|
||||||
|
let (rec_var, new_tags) = self.mark_union_recursive_help(recursive, tags);
|
||||||
|
|
||||||
|
let new_ext_var = self.explicit_substitute(recursive, rec_var, ext_var);
|
||||||
|
let flat_type = FlatType::RecursiveTagUnion(rec_var, new_tags, new_ext_var);
|
||||||
|
|
||||||
|
self.set_content(recursive, Content::Structure(flat_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mark_lambda_set_recursive(&mut self, recursive: Variable, solved_lambdas: UnionTags) {
|
||||||
|
let (rec_var, new_tags) = self.mark_union_recursive_help(recursive, solved_lambdas);
|
||||||
|
|
||||||
|
let new_lambda_set = Content::LambdaSet(LambdaSet {
|
||||||
|
solved: new_tags,
|
||||||
|
recursion_var: OptVariable::from(rec_var),
|
||||||
|
});
|
||||||
|
|
||||||
|
self.set_content(recursive, new_lambda_set);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mark_union_recursive_help(
|
||||||
|
&mut self,
|
||||||
|
recursive: Variable,
|
||||||
|
tags: UnionTags,
|
||||||
|
) -> (Variable, UnionTags) {
|
||||||
let description = self.get(recursive);
|
let description = self.get(recursive);
|
||||||
|
|
||||||
let rec_var = self.fresh_unnamed_flex_var();
|
let rec_var = self.fresh_unnamed_flex_var();
|
||||||
|
@ -1786,13 +1826,9 @@ impl Subs {
|
||||||
self.variable_slices[variable_slice_index] = new_variables;
|
self.variable_slices[variable_slice_index] = new_variables;
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_ext_var = self.explicit_substitute(recursive, rec_var, ext_var);
|
|
||||||
|
|
||||||
let new_tags = UnionTags::from_slices(tags.tag_names(), new_variable_slices);
|
let new_tags = UnionTags::from_slices(tags.tag_names(), new_variable_slices);
|
||||||
|
|
||||||
let flat_type = FlatType::RecursiveTagUnion(rec_var, new_tags, new_ext_var);
|
(rec_var, new_tags)
|
||||||
|
|
||||||
self.set_content(recursive, Content::Structure(flat_type));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn explicit_substitute(
|
pub fn explicit_substitute(
|
||||||
|
@ -2020,6 +2056,45 @@ pub enum Content {
|
||||||
pub struct LambdaSet {
|
pub struct LambdaSet {
|
||||||
/// The resolved lambda symbols we know.
|
/// The resolved lambda symbols we know.
|
||||||
pub solved: UnionTags,
|
pub solved: UnionTags,
|
||||||
|
/// Lambda sets may be recursive. For example, consider the annotated program
|
||||||
|
///
|
||||||
|
/// ```text
|
||||||
|
/// XEffect : A -> B
|
||||||
|
///
|
||||||
|
/// after : ({} -> XEffect) -> XEffect
|
||||||
|
/// after =
|
||||||
|
/// \cont ->
|
||||||
|
/// f = \A -[`f (typeof cont)]-> when cont {} is A -> B
|
||||||
|
/// f
|
||||||
|
///
|
||||||
|
/// nestForever : {} -> XEffect
|
||||||
|
/// nestForever = \{} -[`nestForever]-> after nestForever
|
||||||
|
/// ^^^^^^^^^^^ {} -[`nestForever]-> A -[`f ({} -[`nestForever]-> A -[`f ...]-> B)]-> B
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// where [`nestForever] and [`f ...] refer to the lambda sets of their respective arrows. `f`
|
||||||
|
/// captures `cont`. The usage of `after` in `nestForever` means that `nestForever` has type
|
||||||
|
/// ``nestForever : {} -[`nestForever]-> A -[`f (typeof cont)]-> B``. But also, `after` is called
|
||||||
|
/// with ``nestForever`, which means in this case `typeof cont = typeof nestForever``. So we see
|
||||||
|
/// that ``nestForever : {} -[`nestForever]-> A -[`f (typeof nestForever)]-> B``, and the lambda
|
||||||
|
/// set ``[`f (typeof nestForever)]`` is recursive.
|
||||||
|
///
|
||||||
|
/// However, we don't know if a lambda set is recursive or not until type inference.
|
||||||
|
pub recursion_var: OptVariable,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LambdaSet {
|
||||||
|
/// For things like layout generation, we don't care about differentiating betweent lambda sets
|
||||||
|
/// and tag unions - this function normalizes lambda sets appropriately as a possibly-recursive
|
||||||
|
/// tag union.
|
||||||
|
pub fn as_tag_union(&self) -> FlatType {
|
||||||
|
match self.recursion_var.into_variable() {
|
||||||
|
Some(rec_var) => {
|
||||||
|
FlatType::RecursiveTagUnion(rec_var, self.solved, Variable::EMPTY_TAG_UNION)
|
||||||
|
}
|
||||||
|
None => FlatType::TagUnion(self.solved, Variable::EMPTY_TAG_UNION),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
@ -2803,10 +2878,19 @@ fn occurs(
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
LambdaSet(self::LambdaSet { solved }) => {
|
LambdaSet(self::LambdaSet {
|
||||||
|
solved,
|
||||||
|
recursion_var,
|
||||||
|
}) => {
|
||||||
let mut new_seen = seen.to_owned();
|
let mut new_seen = seen.to_owned();
|
||||||
new_seen.push(root_var);
|
new_seen.push(root_var);
|
||||||
|
|
||||||
|
if include_recursion_var {
|
||||||
|
if let Some(v) = recursion_var.into_variable() {
|
||||||
|
new_seen.push(subs.get_root_key_without_compacting(v));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
occurs_union_tags(subs, root_var, &new_seen, include_recursion_var, solved)
|
occurs_union_tags(subs, root_var, &new_seen, include_recursion_var, solved)
|
||||||
}
|
}
|
||||||
RangedNumber(typ, _range_vars) => {
|
RangedNumber(typ, _range_vars) => {
|
||||||
|
@ -2884,14 +2968,13 @@ fn explicit_substitute(
|
||||||
use self::Content::*;
|
use self::Content::*;
|
||||||
use self::FlatType::*;
|
use self::FlatType::*;
|
||||||
let in_root = subs.get_root_key(in_var);
|
let in_root = subs.get_root_key(in_var);
|
||||||
if seen.contains(&in_root) {
|
if subs.get_root_key(from) == in_root {
|
||||||
|
to
|
||||||
|
} else if seen.contains(&in_root) {
|
||||||
in_var
|
in_var
|
||||||
} else {
|
} else {
|
||||||
seen.insert(in_root);
|
seen.insert(in_root);
|
||||||
|
|
||||||
if subs.get_root_key(from) == subs.get_root_key(in_var) {
|
|
||||||
to
|
|
||||||
} else {
|
|
||||||
match subs.get(in_var).content {
|
match subs.get(in_var).content {
|
||||||
FlexVar(_)
|
FlexVar(_)
|
||||||
| RigidVar(_)
|
| RigidVar(_)
|
||||||
|
@ -2930,8 +3013,7 @@ fn explicit_substitute(
|
||||||
TagUnion(tags, ext_var) => {
|
TagUnion(tags, ext_var) => {
|
||||||
let new_ext_var = explicit_substitute(subs, from, to, ext_var, seen);
|
let new_ext_var = explicit_substitute(subs, from, to, ext_var, seen);
|
||||||
|
|
||||||
let union_tags =
|
let union_tags = explicit_substitute_union_tags(subs, from, to, tags, seen);
|
||||||
explicit_substitute_union_tags(subs, from, to, tags, seen);
|
|
||||||
|
|
||||||
subs.set_content(in_var, Structure(TagUnion(union_tags, new_ext_var)));
|
subs.set_content(in_var, Structure(TagUnion(union_tags, new_ext_var)));
|
||||||
}
|
}
|
||||||
|
@ -2946,8 +3028,7 @@ fn explicit_substitute(
|
||||||
// NOTE rec_var is not substituted, verify that this is correct!
|
// NOTE rec_var is not substituted, verify that this is correct!
|
||||||
let new_ext_var = explicit_substitute(subs, from, to, ext_var, seen);
|
let new_ext_var = explicit_substitute(subs, from, to, ext_var, seen);
|
||||||
|
|
||||||
let union_tags =
|
let union_tags = explicit_substitute_union_tags(subs, from, to, tags, seen);
|
||||||
explicit_substitute_union_tags(subs, from, to, tags, seen);
|
|
||||||
|
|
||||||
subs.set_content(
|
subs.set_content(
|
||||||
in_var,
|
in_var,
|
||||||
|
@ -2984,10 +3065,20 @@ fn explicit_substitute(
|
||||||
|
|
||||||
in_var
|
in_var
|
||||||
}
|
}
|
||||||
LambdaSet(self::LambdaSet { solved }) => {
|
LambdaSet(self::LambdaSet {
|
||||||
|
solved,
|
||||||
|
recursion_var,
|
||||||
|
}) => {
|
||||||
|
// NOTE recursion_var is not substituted, verify that this is correct!
|
||||||
let new_solved = explicit_substitute_union_tags(subs, from, to, solved, seen);
|
let new_solved = explicit_substitute_union_tags(subs, from, to, solved, seen);
|
||||||
|
|
||||||
subs.set_content(in_var, LambdaSet(self::LambdaSet { solved: new_solved }));
|
subs.set_content(
|
||||||
|
in_var,
|
||||||
|
LambdaSet(self::LambdaSet {
|
||||||
|
solved: new_solved,
|
||||||
|
recursion_var,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
in_var
|
in_var
|
||||||
}
|
}
|
||||||
|
@ -3001,7 +3092,6 @@ fn explicit_substitute(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn explicit_substitute_union_tags(
|
fn explicit_substitute_union_tags(
|
||||||
|
@ -3092,8 +3182,15 @@ fn get_var_names(
|
||||||
get_var_names(subs, subs[arg_var], answer)
|
get_var_names(subs, subs[arg_var], answer)
|
||||||
}),
|
}),
|
||||||
|
|
||||||
LambdaSet(self::LambdaSet { solved }) => {
|
LambdaSet(self::LambdaSet {
|
||||||
get_var_names_union_tags(subs, solved, taken_names)
|
solved,
|
||||||
|
recursion_var,
|
||||||
|
}) => {
|
||||||
|
let taken_names = get_var_names_union_tags(subs, solved, taken_names);
|
||||||
|
match recursion_var.into_variable() {
|
||||||
|
Some(v) => get_var_names(subs, v, taken_names),
|
||||||
|
None => taken_names,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RangedNumber(typ, _) => get_var_names(subs, typ, taken_names),
|
RangedNumber(typ, _) => get_var_names(subs, typ, taken_names),
|
||||||
|
@ -3332,9 +3429,19 @@ fn content_to_err_type(
|
||||||
ErrorType::Alias(symbol, err_args, Box::new(err_type), kind)
|
ErrorType::Alias(symbol, err_args, Box::new(err_type), kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
LambdaSet(self::LambdaSet { solved }) => {
|
LambdaSet(self::LambdaSet {
|
||||||
|
solved,
|
||||||
|
recursion_var,
|
||||||
|
}) => match recursion_var.into_variable() {
|
||||||
|
None => {
|
||||||
ErrorType::TagUnion(union_tags_to_err_tags(subs, state, solved), TypeExt::Closed)
|
ErrorType::TagUnion(union_tags_to_err_tags(subs, state, solved), TypeExt::Closed)
|
||||||
}
|
}
|
||||||
|
Some(v) => ErrorType::RecursiveTagUnion(
|
||||||
|
Box::new(var_to_err_type(subs, state, v)),
|
||||||
|
union_tags_to_err_tags(subs, state, solved),
|
||||||
|
TypeExt::Closed,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
|
||||||
RangedNumber(typ, range) => {
|
RangedNumber(typ, range) => {
|
||||||
let err_type = var_to_err_type(subs, state, typ);
|
let err_type = var_to_err_type(subs, state, typ);
|
||||||
|
@ -3640,11 +3747,16 @@ fn restore_help(subs: &mut Subs, initial: Variable) {
|
||||||
stack.push(*var);
|
stack.push(*var);
|
||||||
}
|
}
|
||||||
|
|
||||||
LambdaSet(self::LambdaSet { solved }) => {
|
LambdaSet(self::LambdaSet {
|
||||||
|
solved,
|
||||||
|
recursion_var,
|
||||||
|
}) => {
|
||||||
for slice_index in solved.variables() {
|
for slice_index in solved.variables() {
|
||||||
let slice = variable_slices[slice_index.index as usize];
|
let slice = variable_slices[slice_index.index as usize];
|
||||||
stack.extend(var_slice(slice));
|
stack.extend(var_slice(slice));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
recursion_var.into_variable().map(|v| stack.push(v));
|
||||||
}
|
}
|
||||||
|
|
||||||
RangedNumber(typ, _vars) => {
|
RangedNumber(typ, _vars) => {
|
||||||
|
@ -3834,8 +3946,12 @@ impl StorageSubs {
|
||||||
Self::offset_variable(offsets, *actual),
|
Self::offset_variable(offsets, *actual),
|
||||||
*kind,
|
*kind,
|
||||||
),
|
),
|
||||||
LambdaSet(self::LambdaSet { solved }) => LambdaSet(self::LambdaSet {
|
LambdaSet(self::LambdaSet {
|
||||||
|
solved,
|
||||||
|
recursion_var,
|
||||||
|
}) => LambdaSet(self::LambdaSet {
|
||||||
solved: Self::offset_union_tags(offsets, *solved),
|
solved: Self::offset_union_tags(offsets, *solved),
|
||||||
|
recursion_var: recursion_var.map(|v| Self::offset_variable(offsets, v)),
|
||||||
}),
|
}),
|
||||||
RangedNumber(typ, range) => RangedNumber(Self::offset_variable(offsets, *typ), *range),
|
RangedNumber(typ, range) => RangedNumber(Self::offset_variable(offsets, *typ), *range),
|
||||||
Error => Content::Error,
|
Error => Content::Error,
|
||||||
|
@ -4236,10 +4352,17 @@ fn deep_copy_var_to_help(env: &mut DeepCopyVarToEnv<'_>, var: Variable) -> Varia
|
||||||
copy
|
copy
|
||||||
}
|
}
|
||||||
|
|
||||||
LambdaSet(self::LambdaSet { solved }) => {
|
LambdaSet(self::LambdaSet {
|
||||||
|
solved,
|
||||||
|
recursion_var,
|
||||||
|
}) => {
|
||||||
let new_solved = deep_copy_union_tags(env, solved);
|
let new_solved = deep_copy_union_tags(env, solved);
|
||||||
|
let new_rec_var = recursion_var.map(|v| deep_copy_var_to_help(env, v));
|
||||||
|
|
||||||
let new_content = LambdaSet(self::LambdaSet { solved: new_solved });
|
let new_content = LambdaSet(self::LambdaSet {
|
||||||
|
solved: new_solved,
|
||||||
|
recursion_var: new_rec_var,
|
||||||
|
});
|
||||||
env.target.set(copy, make_descriptor(new_content));
|
env.target.set(copy, make_descriptor(new_content));
|
||||||
copy
|
copy
|
||||||
}
|
}
|
||||||
|
@ -4680,10 +4803,18 @@ fn copy_import_to_help(env: &mut CopyImportEnv<'_>, max_rank: Rank, var: Variabl
|
||||||
copy
|
copy
|
||||||
}
|
}
|
||||||
|
|
||||||
LambdaSet(self::LambdaSet { solved }) => {
|
LambdaSet(self::LambdaSet {
|
||||||
|
solved,
|
||||||
|
recursion_var,
|
||||||
|
}) => {
|
||||||
let new_solved = copy_union_tags(env, max_rank, solved);
|
let new_solved = copy_union_tags(env, max_rank, solved);
|
||||||
|
let new_rec_var =
|
||||||
|
recursion_var.map(|rec_var| copy_import_to_help(env, max_rank, rec_var));
|
||||||
|
|
||||||
let new_content = LambdaSet(self::LambdaSet { solved: new_solved });
|
let new_content = LambdaSet(self::LambdaSet {
|
||||||
|
solved: new_solved,
|
||||||
|
recursion_var: new_rec_var,
|
||||||
|
});
|
||||||
|
|
||||||
env.target.set(copy, make_descriptor(new_content));
|
env.target.set(copy, make_descriptor(new_content));
|
||||||
|
|
||||||
|
|
|
@ -343,8 +343,8 @@ fn debug_print_unified_types(subs: &mut Subs, ctx: &Context, opt_outcome: Option
|
||||||
"{}{}({:?}-{:?}): {:?} {:?} {} {:?} {:?}",
|
"{}{}({:?}-{:?}): {:?} {:?} {} {:?} {:?}",
|
||||||
" ".repeat(use_depth),
|
" ".repeat(use_depth),
|
||||||
prefix,
|
prefix,
|
||||||
ctx.first,
|
subs.get_root_key_without_compacting(ctx.first),
|
||||||
ctx.second,
|
subs.get_root_key_without_compacting(ctx.second),
|
||||||
ctx.first,
|
ctx.first,
|
||||||
SubsFmtContent(&content_1, subs),
|
SubsFmtContent(&content_1, subs),
|
||||||
mode,
|
mode,
|
||||||
|
@ -801,11 +801,14 @@ fn unify_lambda_set(
|
||||||
Content::LambdaSet(other_lambda_set) => {
|
Content::LambdaSet(other_lambda_set) => {
|
||||||
unify_lambda_set_help(subs, pool, ctx, lambda_set, *other_lambda_set)
|
unify_lambda_set_help(subs, pool, ctx, lambda_set, *other_lambda_set)
|
||||||
}
|
}
|
||||||
|
RecursionVar { structure, .. } => {
|
||||||
|
// suppose that the recursion var is a lambda set
|
||||||
|
unify_pool(subs, pool, ctx.first, *structure, ctx.mode)
|
||||||
|
}
|
||||||
RigidVar(..) | RigidAbleVar(..) => mismatch!("Lambda sets never unify with rigid"),
|
RigidVar(..) | RigidAbleVar(..) => mismatch!("Lambda sets never unify with rigid"),
|
||||||
FlexAbleVar(..) => mismatch!("Lambda sets should never have abilities attached to them"),
|
FlexAbleVar(..) => mismatch!("Lambda sets should never have abilities attached to them"),
|
||||||
Structure(..) => mismatch!("Lambda set cannot unify with non-lambda set structure"),
|
Structure(..) => mismatch!("Lambda set cannot unify with non-lambda set structure"),
|
||||||
RangedNumber(..) => mismatch!("Lambda sets are never numbers"),
|
RangedNumber(..) => mismatch!("Lambda sets are never numbers"),
|
||||||
RecursionVar { .. } => mismatch!("Lambda set not expected to be recursive!"),
|
|
||||||
Alias(..) => mismatch!("Lambda set can never be directly under an alias!"),
|
Alias(..) => mismatch!("Lambda set can never be directly under an alias!"),
|
||||||
Error => merge(subs, ctx, Error),
|
Error => merge(subs, ctx, Error),
|
||||||
}
|
}
|
||||||
|
@ -822,8 +825,21 @@ fn unify_lambda_set_help(
|
||||||
// LambdaSets unify like TagUnions, but can grow unbounded regardless of the extension
|
// LambdaSets unify like TagUnions, but can grow unbounded regardless of the extension
|
||||||
// variable.
|
// variable.
|
||||||
|
|
||||||
let LambdaSet { solved: solved1 } = lset1;
|
let LambdaSet {
|
||||||
let LambdaSet { solved: solved2 } = lset2;
|
solved: solved1,
|
||||||
|
recursion_var: rec1,
|
||||||
|
} = lset1;
|
||||||
|
let LambdaSet {
|
||||||
|
solved: solved2,
|
||||||
|
recursion_var: rec2,
|
||||||
|
} = lset2;
|
||||||
|
|
||||||
|
debug_assert!(
|
||||||
|
(rec1.into_variable().into_iter())
|
||||||
|
.chain(rec2.into_variable().into_iter())
|
||||||
|
.all(|v| is_recursion_var(subs, v)),
|
||||||
|
"Recursion var is present, but it doesn't have a recursive content!"
|
||||||
|
);
|
||||||
|
|
||||||
let (separate_solved, _, _) = separate_union_tags(
|
let (separate_solved, _, _) = separate_union_tags(
|
||||||
subs,
|
subs,
|
||||||
|
@ -843,7 +859,7 @@ fn unify_lambda_set_help(
|
||||||
|
|
||||||
let mut joined_lambdas = vec![];
|
let mut joined_lambdas = vec![];
|
||||||
for (tag_name, (vars1, vars2)) in in_both {
|
for (tag_name, (vars1, vars2)) in in_both {
|
||||||
let mut joined_vars = vec![];
|
let mut matching_vars = vec![];
|
||||||
|
|
||||||
if vars1.len() != vars2.len() {
|
if vars1.len() != vars2.len() {
|
||||||
continue; // this is a type mismatch; not adding the tag will trigger it below.
|
continue; // this is a type mismatch; not adding the tag will trigger it below.
|
||||||
|
@ -853,16 +869,32 @@ fn unify_lambda_set_help(
|
||||||
for (var1, var2) in (vars1.into_iter()).zip(vars2.into_iter()) {
|
for (var1, var2) in (vars1.into_iter()).zip(vars2.into_iter()) {
|
||||||
let (var1, var2) = (subs[var1], subs[var2]);
|
let (var1, var2) = (subs[var1], subs[var2]);
|
||||||
|
|
||||||
|
// Lambda sets are effectively tags under another name, and their usage can also result
|
||||||
|
// in the arguments of a lambda name being recursive. It very well may happen that
|
||||||
|
// during unification, a lambda set previously marked as not recursive becomes
|
||||||
|
// recursive. See the docs of [LambdaSet] for one example, or https://github.com/rtfeldman/roc/pull/2307.
|
||||||
|
//
|
||||||
|
// Like with tag unions, if it has, we'll always pass through this branch. So, take
|
||||||
|
// this opportunity to promote the lambda set to recursive if need be.
|
||||||
|
maybe_mark_union_recursive(subs, var1);
|
||||||
|
maybe_mark_union_recursive(subs, var2);
|
||||||
|
|
||||||
let outcome = unify_pool(subs, pool, var1, var2, ctx.mode);
|
let outcome = unify_pool(subs, pool, var1, var2, ctx.mode);
|
||||||
|
|
||||||
|
// TODO: i think we can get rid of this
|
||||||
|
// clearly, this is very suspicious: these variables have just been unified. And yet,
|
||||||
|
// not doing this leads to stack overflows
|
||||||
|
if rec2.is_some() {
|
||||||
if outcome.mismatches.is_empty() {
|
if outcome.mismatches.is_empty() {
|
||||||
// otherwise this is a type mismatch; not adding the variable will trigger it below.
|
matching_vars.push(var2);
|
||||||
joined_vars.push(var1);
|
}
|
||||||
|
} else if outcome.mismatches.is_empty() {
|
||||||
|
matching_vars.push(var1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if joined_vars.len() == num_vars {
|
if matching_vars.len() == num_vars {
|
||||||
joined_lambdas.push((tag_name, joined_vars));
|
joined_lambdas.push((tag_name, matching_vars));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -885,8 +917,17 @@ fn unify_lambda_set_help(
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let recursion_var = match (rec1.into_variable(), rec2.into_variable()) {
|
||||||
|
// Prefer left when it's available.
|
||||||
|
(Some(rec), _) | (_, Some(rec)) => OptVariable::from(rec),
|
||||||
|
(None, None) => OptVariable::NONE,
|
||||||
|
};
|
||||||
|
|
||||||
let new_solved = UnionTags::insert_into_subs(subs, all_lambdas);
|
let new_solved = UnionTags::insert_into_subs(subs, all_lambdas);
|
||||||
let new_lambda_set = Content::LambdaSet(LambdaSet { solved: new_solved });
|
let new_lambda_set = Content::LambdaSet(LambdaSet {
|
||||||
|
solved: new_solved,
|
||||||
|
recursion_var,
|
||||||
|
});
|
||||||
|
|
||||||
merge(subs, ctx, new_lambda_set)
|
merge(subs, ctx, new_lambda_set)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1483,19 +1524,39 @@ enum OtherTags2 {
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn maybe_mark_tag_union_recursive(subs: &mut Subs, tag_union_var: Variable) {
|
/// Promotes a non-recursive tag union or lambda set to its recursive variant, if it is found to be
|
||||||
'outer: while let Err((recursive, chain)) = subs.occurs(tag_union_var) {
|
/// recursive.
|
||||||
|
fn maybe_mark_union_recursive(subs: &mut Subs, union_var: Variable) {
|
||||||
|
'outer: while let Err((recursive, chain)) = subs.occurs(union_var) {
|
||||||
let description = subs.get(recursive);
|
let description = subs.get(recursive);
|
||||||
if let Content::Structure(FlatType::TagUnion(tags, ext_var)) = description.content {
|
match description.content {
|
||||||
|
Content::Structure(FlatType::TagUnion(tags, ext_var)) => {
|
||||||
subs.mark_tag_union_recursive(recursive, tags, ext_var);
|
subs.mark_tag_union_recursive(recursive, tags, ext_var);
|
||||||
} else {
|
}
|
||||||
// walk the chain till we find a tag union
|
LambdaSet(self::LambdaSet {
|
||||||
|
solved,
|
||||||
|
recursion_var: OptVariable::NONE,
|
||||||
|
}) => {
|
||||||
|
subs.mark_lambda_set_recursive(recursive, solved);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// walk the chain till we find a tag union or lambda set
|
||||||
for v in &chain[..chain.len() - 1] {
|
for v in &chain[..chain.len() - 1] {
|
||||||
let description = subs.get(*v);
|
let description = subs.get(*v);
|
||||||
if let Content::Structure(FlatType::TagUnion(tags, ext_var)) = description.content {
|
match description.content {
|
||||||
|
Content::Structure(FlatType::TagUnion(tags, ext_var)) => {
|
||||||
subs.mark_tag_union_recursive(*v, tags, ext_var);
|
subs.mark_tag_union_recursive(*v, tags, ext_var);
|
||||||
continue 'outer;
|
continue 'outer;
|
||||||
}
|
}
|
||||||
|
LambdaSet(self::LambdaSet {
|
||||||
|
solved,
|
||||||
|
recursion_var: OptVariable::NONE,
|
||||||
|
}) => {
|
||||||
|
subs.mark_lambda_set_recursive(recursive, solved);
|
||||||
|
continue 'outer;
|
||||||
|
}
|
||||||
|
_ => { /* fall through */ }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Might not be any tag union if we only pass through `Apply`s. Otherwise, we have a bug!
|
// Might not be any tag union if we only pass through `Apply`s. Otherwise, we have a bug!
|
||||||
|
@ -1512,6 +1573,7 @@ fn maybe_mark_tag_union_recursive(subs: &mut Subs, tag_union_var: Variable) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn unify_shared_tags_new(
|
fn unify_shared_tags_new(
|
||||||
subs: &mut Subs,
|
subs: &mut Subs,
|
||||||
|
@ -1563,8 +1625,8 @@ fn unify_shared_tags_new(
|
||||||
// since we're expanding tag unions to equal depths as described above,
|
// since we're expanding tag unions to equal depths as described above,
|
||||||
// we'll always pass through this branch. So, we promote tag unions to recursive
|
// we'll always pass through this branch. So, we promote tag unions to recursive
|
||||||
// ones here if it turns out they are that.
|
// ones here if it turns out they are that.
|
||||||
maybe_mark_tag_union_recursive(subs, actual);
|
maybe_mark_union_recursive(subs, actual);
|
||||||
maybe_mark_tag_union_recursive(subs, expected);
|
maybe_mark_union_recursive(subs, expected);
|
||||||
|
|
||||||
let mut outcome = Outcome::default();
|
let mut outcome = Outcome::default();
|
||||||
|
|
||||||
|
@ -2094,7 +2156,8 @@ fn unify_recursion(
|
||||||
),
|
),
|
||||||
|
|
||||||
LambdaSet(..) => {
|
LambdaSet(..) => {
|
||||||
mismatch!("RecursionVar {:?} with LambdaSet {:?}", ctx.first, &other)
|
// suppose that the recursion var is a lambda set
|
||||||
|
unify_pool(subs, pool, structure, ctx.second, ctx.mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
Error => merge(subs, ctx, Error),
|
Error => merge(subs, ctx, Error),
|
||||||
|
@ -2172,7 +2235,10 @@ fn unify_function_or_tag_union_and_func(
|
||||||
{
|
{
|
||||||
let tag_name = TagName::Closure(tag_symbol);
|
let tag_name = TagName::Closure(tag_symbol);
|
||||||
let union_tags = UnionTags::tag_without_arguments(subs, tag_name);
|
let union_tags = UnionTags::tag_without_arguments(subs, tag_name);
|
||||||
let lambda_set_content = LambdaSet(self::LambdaSet { solved: union_tags });
|
let lambda_set_content = LambdaSet(self::LambdaSet {
|
||||||
|
solved: union_tags,
|
||||||
|
recursion_var: OptVariable::NONE,
|
||||||
|
});
|
||||||
|
|
||||||
let tag_lambda_set = register(
|
let tag_lambda_set = register(
|
||||||
subs,
|
subs,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue