Handle recursive variables in building lambda set representations

This commit is contained in:
Ayaz Hafiz 2022-08-09 14:11:02 -07:00
parent e1fb21fc59
commit 549b00d327
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
2 changed files with 66 additions and 11 deletions

View file

@ -10,7 +10,8 @@ use roc_problem::can::RuntimeError;
use roc_target::{PtrWidth, TargetInfo}; use roc_target::{PtrWidth, TargetInfo};
use roc_types::num::NumericRange; use roc_types::num::NumericRange;
use roc_types::subs::{ use roc_types::subs::{
self, Content, FlatType, Label, RecordFields, Subs, UnionTags, UnsortedUnionLabels, Variable, self, Content, FlatType, Label, OptVariable, RecordFields, Subs, UnionTags,
UnsortedUnionLabels, Variable,
}; };
use roc_types::types::{gather_fields_unsorted_iter, RecordField, RecordFieldsError}; use roc_types::types::{gather_fields_unsorted_iter, RecordField, RecordFieldsError};
use std::cmp::Ordering; use std::cmp::Ordering;
@ -866,7 +867,14 @@ impl<'a> LambdaSet<'a> {
.set .set
.iter() .iter()
.find(|(name, layouts)| comparator(*name, layouts)) .find(|(name, layouts)| comparator(*name, layouts))
.expect("no lambda set found"); .unwrap_or_else(|| {
internal_error!(
"no lambda set found for ({:?}, {:#?}): {:#?}",
function_symbol,
captures_layouts,
self
)
});
LambdaName { LambdaName {
name: *name, name: *name,
@ -971,7 +979,7 @@ impl<'a> LambdaSet<'a> {
target_info: TargetInfo, target_info: TargetInfo,
) -> Result<Self, LayoutProblem> { ) -> Result<Self, LayoutProblem> {
match resolve_lambda_set(subs, closure_var) { match resolve_lambda_set(subs, closure_var) {
ResolvedLambdaSet::Set(mut lambdas) => { ResolvedLambdaSet::Set(mut lambdas, opt_recursion_var) => {
// sort the tags; make sure ordering stays intact! // sort the tags; make sure ordering stays intact!
lambdas.sort_by_key(|(sym, _)| *sym); lambdas.sort_by_key(|(sym, _)| *sym);
@ -992,6 +1000,9 @@ impl<'a> LambdaSet<'a> {
seen: Vec::new_in(arena), seen: Vec::new_in(arena),
target_info, target_info,
}; };
if let Some(rec_var) = opt_recursion_var.into_variable() {
env.insert_seen(rec_var);
}
for var in variables { for var in variables {
arguments.push(Layout::from_var(&mut env, *var)?); arguments.push(Layout::from_var(&mut env, *var)?);
@ -1048,6 +1059,7 @@ impl<'a> LambdaSet<'a> {
arena, arena,
subs, subs,
set_with_variables, set_with_variables,
opt_recursion_var.into_variable(),
target_info, target_info,
)); ));
@ -1071,10 +1083,11 @@ impl<'a> LambdaSet<'a> {
arena: &'a Bump, arena: &'a Bump,
subs: &Subs, subs: &Subs,
tags: std::vec::Vec<(Symbol, std::vec::Vec<Variable>)>, tags: std::vec::Vec<(Symbol, std::vec::Vec<Variable>)>,
opt_rec_var: Option<Variable>,
target_info: TargetInfo, target_info: TargetInfo,
) -> Layout<'a> { ) -> Layout<'a> {
// otherwise, this is a closure with a payload // otherwise, this is a closure with a payload
let variant = union_sorted_tags_help(arena, tags, None, subs, target_info); let variant = union_sorted_tags_help(arena, tags, opt_rec_var, subs, target_info);
use UnionVariant::*; use UnionVariant::*;
match variant { match variant {
@ -1108,7 +1121,20 @@ impl<'a> LambdaSet<'a> {
Layout::Union(UnionLayout::NonRecursive(tag_arguments.into_bump_slice())) Layout::Union(UnionLayout::NonRecursive(tag_arguments.into_bump_slice()))
} }
_ => panic!("handle recursive layouts"), Recursive {
sorted_tag_layouts: tags,
} => {
debug_assert!(tags.len() > 1);
let mut tag_arguments = Vec::with_capacity_in(tags.len(), arena);
for (_, tag_args) in tags.iter() {
tag_arguments.push(&tag_args[0..]);
}
Layout::Union(UnionLayout::Recursive(tag_arguments.into_bump_slice()))
}
layout => panic!("handle recursive layout: {:?}", layout),
} }
} }
} }
@ -1130,7 +1156,10 @@ impl<'a> LambdaSet<'a> {
} }
enum ResolvedLambdaSet { enum ResolvedLambdaSet {
Set(std::vec::Vec<(Symbol, std::vec::Vec<Variable>)>), Set(
std::vec::Vec<(Symbol, std::vec::Vec<Variable>)>,
OptVariable,
),
/// TODO: figure out if this can happen in a correct program, or is the result of a bug in our /// TODO: figure out if this can happen in a correct program, or is the result of a bug in our
/// compiler. See https://github.com/rtfeldman/roc/issues/3163. /// compiler. See https://github.com/rtfeldman/roc/issues/3163.
Unbound, Unbound,
@ -1142,7 +1171,7 @@ fn resolve_lambda_set(subs: &Subs, mut var: Variable) -> ResolvedLambdaSet {
match subs.get_content_without_compacting(var) { match subs.get_content_without_compacting(var) {
Content::LambdaSet(subs::LambdaSet { Content::LambdaSet(subs::LambdaSet {
solved, solved,
recursion_var: _, recursion_var,
unspecialized, unspecialized,
ambient_function: _, ambient_function: _,
}) => { }) => {
@ -1153,7 +1182,7 @@ fn resolve_lambda_set(subs: &Subs, mut var: Variable) -> ResolvedLambdaSet {
subs.uls_of_var subs.uls_of_var
); );
roc_types::pretty_print::push_union(subs, solved, &mut set); roc_types::pretty_print::push_union(subs, solved, &mut set);
return ResolvedLambdaSet::Set(set); return ResolvedLambdaSet::Set(set, *recursion_var);
} }
Content::RecursionVar { structure, .. } => { Content::RecursionVar { structure, .. } => {
var = *structure; var = *structure;
@ -2130,11 +2159,15 @@ fn layout_from_flat_type<'a>(
} }
} }
Func(_, closure_var, _) => { Func(_, closure_var, _) => {
if env.is_seen(closure_var) {
Ok(Layout::RecursivePointer)
} else {
let lambda_set = let lambda_set =
LambdaSet::from_var(env.arena, env.subs, closure_var, env.target_info)?; LambdaSet::from_var(env.arena, env.subs, closure_var, env.target_info)?;
Ok(Layout::LambdaSet(lambda_set)) Ok(Layout::LambdaSet(lambda_set))
} }
}
Record(fields, ext_var) => { Record(fields, ext_var) => {
// extract any values from the ext_var // extract any values from the ext_var
@ -2686,6 +2719,9 @@ where
seen: Vec::new_in(arena), seen: Vec::new_in(arena),
target_info, target_info,
}; };
if let Some(rec_var) = opt_rec_var {
env.insert_seen(rec_var);
}
match tags_vec.len() { match tags_vec.len() {
0 => { 0 => {

View file

@ -1922,3 +1922,22 @@ fn issue_3669() {
"# "#
) )
} }
#[mono_test]
fn issue_3444() {
indoc!(
r#"
app "test" provides [main] to "./platform"
combine = \a, b -> (\x -> x |> a |> b )
const = \x -> (\_y -> x)
list = []
res : Str -> Str
res = List.walk list (const "z") (\c1, c2 -> combine c1 c2)
main = res "hello"
"#
)
}