diff --git a/ast/src/constrain.rs b/ast/src/constrain.rs index 3dc4c9dbd0..201152ef3e 100644 --- a/ast/src/constrain.rs +++ b/ast/src/constrain.rs @@ -2051,7 +2051,7 @@ pub mod test_constrain { let subs = solved.inner_mut(); // name type vars - name_all_type_vars(var, subs); + let named_result = name_all_type_vars(var, subs); let content = subs.get_content_without_compacting(var); @@ -2063,7 +2063,7 @@ pub mod test_constrain { all_ident_ids: dep_idents, }; - let actual_str = content_to_string(content, subs, mod_id, &interns); + let actual_str = content_to_string(content, subs, mod_id, &interns, named_result); assert_eq!(actual_str, expected_str); } diff --git a/compiler/load_internal/tests/test_load.rs b/compiler/load_internal/tests/test_load.rs index de77f89913..f3c8484c51 100644 --- a/compiler/load_internal/tests/test_load.rs +++ b/compiler/load_internal/tests/test_load.rs @@ -237,10 +237,10 @@ mod test_load { expected_types: &mut HashMap<&str, &str>, ) { for (symbol, expr_var) in &def.pattern_vars { - name_all_type_vars(*expr_var, subs); + let named_result = name_all_type_vars(*expr_var, subs); let content = subs.get_content_without_compacting(*expr_var); - let actual_str = content_to_string(content, subs, home, interns); + let actual_str = content_to_string(content, subs, home, interns, named_result); let fully_qualified = symbol.fully_qualified(interns, home).to_string(); let expected_type = expected_types .remove(fully_qualified.as_str()) diff --git a/compiler/solve/tests/solve_expr.rs b/compiler/solve/tests/solve_expr.rs index 5de69110d7..dbbd513baa 100644 --- a/compiler/solve/tests/solve_expr.rs +++ b/compiler/solve/tests/solve_expr.rs @@ -172,18 +172,12 @@ mod solve_expr { let subs = solved.inner_mut(); - // name type vars - for var in exposed_to_host.values() { - name_all_type_vars(*var, subs); - } + debug_assert!(exposed_to_host.len() == 1); + let (_symbol, variable) = exposed_to_host.into_iter().next().unwrap(); + let named_result = name_all_type_vars(variable, subs); + let content = subs.get_content_without_compacting(variable); - let content = { - debug_assert!(exposed_to_host.len() == 1); - let (_symbol, variable) = exposed_to_host.into_iter().next().unwrap(); - subs.get_content_without_compacting(variable) - }; - - let actual_str = content_to_string(content, subs, home, &interns); + let actual_str = content_to_string(content, subs, home, &interns, named_result); Ok((type_problems, can_problems, actual_str)) } @@ -282,9 +276,9 @@ mod solve_expr { let var = find_type_at(region, &decls) .expect(&format!("No type for {} ({:?})!", &text, region)); - name_all_type_vars(var, subs); + let named_result = name_all_type_vars(var, subs); let content = subs.get_content_without_compacting(var); - let actual_str = content_to_string(content, subs, home, &interns); + let actual_str = content_to_string(content, subs, home, &interns, named_result); solved_queries.push(format!("{} : {}", text, actual_str)); } @@ -5445,26 +5439,19 @@ mod solve_expr { } #[test] - fn copy_vars_referencing_copied_vars_specialized() { + fn generalize_and_specialize_recursion_var() { infer_eq_without_problem( indoc!( r#" - Job a : [ Job [ Command ] (Job a) (List (Job a)) a ] + Job a : [ Job (List (Job a)) a ] job : Job Str when job is - Job _ j lst _ -> - when j is - Job _ _ _ s -> - { j, lst, s } + Job lst s -> P lst s "# ), - // TODO: this means that we're doing our job correctly, as now both `Job a`s have been - // specialized to the same type, and the second destructuring proves the reified type - // is `Job Str`. But we should just print the structure of the recursive type directly. - // See https://github.com/rtfeldman/roc/issues/2513 - "{ j : a, lst : List a, s : Str }", + "[ P (List [ Job (List a) Str ] as a) Str ]*", ) } diff --git a/compiler/types/src/pretty_print.rs b/compiler/types/src/pretty_print.rs index 4e2d8feb8b..acd1eee299 100644 --- a/compiler/types/src/pretty_print.rs +++ b/compiler/types/src/pretty_print.rs @@ -233,7 +233,11 @@ fn find_names_needed( } } -pub fn name_all_type_vars(variable: Variable, subs: &mut Subs) { +pub struct NamedResult { + recursion_structs_to_expand: Vec, +} + +pub fn name_all_type_vars(variable: Variable, subs: &mut Subs) -> NamedResult { let mut roots = Vec::new(); let mut letters_used = 0; let mut appearances = MutMap::default(); @@ -242,13 +246,30 @@ pub fn name_all_type_vars(variable: Variable, subs: &mut Subs) { // Populate names_needed find_names_needed(variable, subs, &mut roots, &mut appearances, &mut taken); + let mut recursion_structs_to_expand = vec![]; + for root in roots { // show the type variable number instead of `*`. useful for debugging // set_root_name(root, (format!("<{:?}>", root).into()), subs); - if let Some(Appearances::Multiple) = appearances.get(&root) { - letters_used = name_root(letters_used, root, subs, &mut taken); + match appearances.get(&root) { + Some(Appearances::Multiple) => { + letters_used = name_root(letters_used, root, subs, &mut taken); + } + Some(Appearances::Single) => { + if let Content::RecursionVar { structure, .. } = + subs.get_content_without_compacting(root) + { + recursion_structs_to_expand.push(*structure); + letters_used = name_root(letters_used, root, subs, &mut taken); + } + } + _ => {} } } + + NamedResult { + recursion_structs_to_expand, + } } fn name_root( @@ -312,6 +333,7 @@ fn set_root_name(root: Variable, name: Lowercase, subs: &mut Subs) { #[derive(Default)] struct Context<'a> { able_variables: Vec<(&'a str, Symbol)>, + recursion_structs_to_expand: Vec, } pub fn content_to_string( @@ -319,10 +341,14 @@ pub fn content_to_string( subs: &Subs, home: ModuleId, interns: &Interns, + named_result: NamedResult, ) -> String { let mut buf = String::new(); let env = Env { home, interns }; - let mut ctx = Context::default(); + let mut ctx = Context { + able_variables: vec![], + recursion_structs_to_expand: named_result.recursion_structs_to_expand, + }; write_content(&env, &mut ctx, content, subs, &mut buf, Parens::Unnecessary); @@ -381,12 +407,34 @@ fn write_content<'a>( ctx.able_variables.push((name, *ability)); buf.push_str(name); } - RecursionVar { opt_name, .. } => match opt_name { + RecursionVar { + opt_name, + structure, + } => match opt_name { Some(name_index) => { - let name = &subs.field_names[name_index.index as usize]; - buf.push_str(name.as_str()) + if let Some(idx) = ctx + .recursion_structs_to_expand + .iter() + .position(|v| v == structure) + { + ctx.recursion_structs_to_expand.swap_remove(idx); + + write_content( + env, + ctx, + subs.get_content_without_compacting(*structure), + subs, + buf, + parens, + ); + } else { + let name = &subs.field_names[name_index.index as usize]; + buf.push_str(name.as_str()) + } + } + None => { + unreachable!("This should always be filled in!") } - None => buf.push_str(WILDCARD), }, Structure(flat_type) => write_flat_type(env, ctx, flat_type, subs, buf, parens), Alias(symbol, args, _actual, _kind) => { diff --git a/compiler/types/src/subs.rs b/compiler/types/src/subs.rs index fbb4f54cf7..621115362d 100644 --- a/compiler/types/src/subs.rs +++ b/compiler/types/src/subs.rs @@ -2172,23 +2172,6 @@ impl Content { Content::Structure(FlatType::Apply(Symbol::NUM_NUM, _)) ) } - - #[cfg(debug_assertions)] - #[allow(dead_code)] - pub fn dbg(self, subs: &Subs) -> Self { - let home = roc_module::symbol::ModuleIds::default().get_or_insert(&"#Dbg".into()); - let interns = roc_module::symbol::Interns { - all_ident_ids: roc_module::symbol::IdentIds::exposed_builtins(0), - ..Default::default() - }; - - eprintln!( - "{}", - crate::pretty_print::content_to_string(&self, subs, home, &interns) - ); - - self - } } #[derive(Clone, Copy, Debug)] diff --git a/editor/src/editor/mvc/ed_update.rs b/editor/src/editor/mvc/ed_update.rs index bbe05560c6..bd2f8aee8e 100644 --- a/editor/src/editor/mvc/ed_update.rs +++ b/editor/src/editor/mvc/ed_update.rs @@ -59,6 +59,7 @@ use roc_collections::all::MutMap; use roc_module::ident::Lowercase; use roc_module::symbol::Symbol; use roc_region::all::Region; +use roc_types::pretty_print::name_all_type_vars; use roc_types::solved_types::Solved; use roc_types::subs::{Subs, Variable}; use roc_types::{pretty_print::content_to_string, subs::VarStore}; @@ -462,6 +463,8 @@ impl<'a> EdModel<'a> { let subs = solved.inner_mut(); + let named_result = name_all_type_vars(var, subs); + let content = subs.get_content_without_compacting(var); PoolStr::new( @@ -470,6 +473,7 @@ impl<'a> EdModel<'a> { subs, self.module.env.home, &self.loaded_module.interns, + named_result, ), self.module.env.pool, ) diff --git a/repl_cli/src/lib.rs b/repl_cli/src/lib.rs index 40837ce8ac..b823a50cd2 100644 --- a/repl_cli/src/lib.rs +++ b/repl_cli/src/lib.rs @@ -227,9 +227,9 @@ fn gen_and_eval_llvm<'a>( let main_fn_var = *main_fn_var; // pretty-print the expr type string for later. - name_all_type_vars(main_fn_var, &mut subs); + let named_result = name_all_type_vars(main_fn_var, &mut subs); let content = subs.get_content_without_compacting(main_fn_var); - let expr_type_str = content_to_string(content, &subs, home, &interns); + let expr_type_str = content_to_string(content, &subs, home, &interns, named_result); let (_, main_fn_layout) = match procedures.keys().find(|(s, _)| *s == main_fn_symbol) { Some(layout) => *layout, diff --git a/repl_wasm/src/repl.rs b/repl_wasm/src/repl.rs index 7ca51ebc57..012ecd6a5c 100644 --- a/repl_wasm/src/repl.rs +++ b/repl_wasm/src/repl.rs @@ -184,9 +184,9 @@ pub async fn entrypoint_from_js(src: String) -> Result { let main_fn_var = *main_fn_var; // pretty-print the expr type string for later. - name_all_type_vars(main_fn_var, &mut subs); + let named_result = name_all_type_vars(main_fn_var, &mut subs); let content = subs.get_content_without_compacting(main_fn_var); - let expr_type_str = content_to_string(content, &subs, module_id, &interns); + let expr_type_str = content_to_string(content, &subs, module_id, &interns, named_result); let (_, main_fn_layout) = match procedures.keys().find(|(s, _)| *s == main_fn_symbol) { Some(layout) => *layout,