Support printing weak type variables in tests

Unbound type variables that are not at the generalization rank will now
be printed as `w_a` in solve tests.
This commit is contained in:
Ayaz Hafiz 2023-01-04 16:24:19 -06:00
parent 2b993a29f9
commit 3b0e2429e6
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
5 changed files with 49 additions and 24 deletions

View file

@ -520,7 +520,7 @@ pub fn find_type_def_symbols(
} }
fn find_fresh_var_name(introduced_variables: &IntroducedVariables) -> Lowercase { fn find_fresh_var_name(introduced_variables: &IntroducedVariables) -> Lowercase {
name_type_var(0, &mut introduced_variables.iter_named(), |var, str| { name_type_var("", 0, &mut introduced_variables.iter_named(), |var, str| {
var.name().as_str() == str var.name().as_str() == str
}) })
.0 .0

View file

@ -332,6 +332,7 @@ mod solve_expr {
print_lambda_sets: true, print_lambda_sets: true,
print_only_under_alias: options.print_only_under_alias, print_only_under_alias: options.print_only_under_alias,
ignore_polarity: true, ignore_polarity: true,
print_weakened_vars: true,
}, },
); );
subs.rollback_to(snapshot); subs.rollback_to(snapshot);

View file

@ -56,6 +56,7 @@ pub struct DebugPrint {
pub print_lambda_sets: bool, pub print_lambda_sets: bool,
pub print_only_under_alias: bool, pub print_only_under_alias: bool,
pub ignore_polarity: bool, pub ignore_polarity: bool,
pub print_weakened_vars: bool,
} }
impl DebugPrint { impl DebugPrint {
@ -63,6 +64,7 @@ impl DebugPrint {
print_lambda_sets: false, print_lambda_sets: false,
print_only_under_alias: false, print_only_under_alias: false,
ignore_polarity: false, ignore_polarity: false,
print_weakened_vars: false,
}; };
} }
@ -380,11 +382,7 @@ struct NamedResult {
recursion_structs_to_expand: Vec<Variable>, recursion_structs_to_expand: Vec<Variable>,
} }
fn name_all_type_vars( fn name_all_type_vars(variable: Variable, subs: &mut Subs, debug_print: DebugPrint) -> NamedResult {
variable: Variable,
subs: &mut Subs,
find_names_under_alias: bool,
) -> NamedResult {
let mut roots = Vec::new(); let mut roots = Vec::new();
let mut letters_used = 0; let mut letters_used = 0;
let mut appearances = MutMap::default(); let mut appearances = MutMap::default();
@ -397,7 +395,7 @@ fn name_all_type_vars(
&mut roots, &mut roots,
&mut appearances, &mut appearances,
&mut taken, &mut taken,
find_names_under_alias, debug_print.print_only_under_alias,
); );
let mut recursion_structs_to_expand = vec![]; let mut recursion_structs_to_expand = vec![];
@ -407,14 +405,14 @@ fn name_all_type_vars(
// set_root_name(root, (format!("<{:?}>", root).into()), subs); // set_root_name(root, (format!("<{:?}>", root).into()), subs);
match appearances.get(&root) { match appearances.get(&root) {
Some(Appearances::Multiple) => { Some(Appearances::Multiple) => {
letters_used = name_root(letters_used, root, subs, &mut taken); letters_used = name_root(letters_used, root, subs, &mut taken, debug_print);
} }
Some(Appearances::Single) => { Some(Appearances::Single) => {
if let Content::RecursionVar { structure, .. } = if let Content::RecursionVar { structure, .. } =
subs.get_content_without_compacting(root) subs.get_content_without_compacting(root)
{ {
recursion_structs_to_expand.push(*structure); recursion_structs_to_expand.push(*structure);
letters_used = name_root(letters_used, root, subs, &mut taken); letters_used = name_root(letters_used, root, subs, &mut taken, debug_print);
} }
} }
_ => {} _ => {}
@ -426,12 +424,29 @@ fn name_all_type_vars(
} }
} }
fn is_weakened_unbound(subs: &Subs, var: Variable) -> bool {
use Content::*;
let desc = subs.get_without_compacting(var);
!desc.rank.is_none()
&& !matches!(
desc.content,
FlexVar(_) | RigidVar(_) | FlexAbleVar(..) | RigidAbleVar(..)
)
}
fn name_root( fn name_root(
letters_used: u32, letters_used: u32,
root: Variable, root: Variable,
subs: &mut Subs, subs: &mut Subs,
taken: &mut MutMap<Lowercase, Variable>, taken: &mut MutMap<Lowercase, Variable>,
debug_print: DebugPrint,
) -> u32 { ) -> u32 {
let prefix = if debug_print.print_weakened_vars && is_weakened_unbound(subs, root) {
"w_" // weakened variable
} else {
""
};
let (generated_name, new_letters_used) = match subs.get_content_unchecked(root) { let (generated_name, new_letters_used) = match subs.get_content_unchecked(root) {
Content::FlexVar(Some(name)) Content::FlexVar(Some(name))
| Content::RigidVar(name) | Content::RigidVar(name)
@ -444,19 +459,21 @@ fn name_root(
let name_hint = &subs[*name]; let name_hint = &subs[*name];
if name_hint.as_str() == "*" { if name_hint.as_str() == "*" {
// Give a proper name to named wildcards! // Give a proper name to named wildcards!
name_type_var(letters_used, &mut taken.keys(), |var, str| { name_type_var(prefix, letters_used, &mut taken.keys(), |var, str| {
var.as_str() == str var.as_str() == str
}) })
} else { } else {
let generated = let generated = name_type_var_with_hint(
name_type_var_with_hint(name_hint.as_str(), &mut taken.keys(), |var, str| { prefix,
var.as_str() == str name_hint.as_str(),
}); &mut taken.keys(),
|var, str| var.as_str() == str,
);
(generated, letters_used) (generated, letters_used)
} }
} }
_ => name_type_var(letters_used, &mut taken.keys(), |var, str| { _ => name_type_var(prefix, letters_used, &mut taken.keys(), |var, str| {
var.as_str() == str var.as_str() == str
}), }),
}; };
@ -568,7 +585,7 @@ pub fn name_and_print_var(
interns: &Interns, interns: &Interns,
debug_print: DebugPrint, debug_print: DebugPrint,
) -> String { ) -> String {
let named_result = name_all_type_vars(var, subs, debug_print.print_only_under_alias); let named_result = name_all_type_vars(var, subs, debug_print);
let content = subs.get_content_without_compacting(var); let content = subs.get_content_without_compacting(var);
content_to_string( content_to_string(
content, content,

View file

@ -2185,6 +2185,7 @@ pub struct Rank(u32);
impl Rank { impl Rank {
pub const NONE: Rank = Rank(0); pub const NONE: Rank = Rank(0);
/// The generalized rank
pub fn is_none(&self) -> bool { pub fn is_none(&self) -> bool {
*self == Self::NONE *self == Self::NONE
} }
@ -4095,10 +4096,12 @@ fn get_fresh_error_var_name(state: &mut ErrorTypeState) -> Lowercase {
// //
// We want to claim both the "#name" and "name" forms, because if "#name" appears multiple // We want to claim both the "#name" and "name" forms, because if "#name" appears multiple
// times during error type reporting, we'll use "name" for display. // times during error type reporting, we'll use "name" for display.
let (name, new_index) = let (name, new_index) = name_type_var(
name_type_var(state.letters_used, &mut state.taken.iter(), |var, str| { "",
var.as_str() == str state.letters_used,
}); &mut state.taken.iter(),
|var, str| var.as_str() == str,
);
state.letters_used = new_index; state.letters_used = new_index;

View file

@ -4095,13 +4095,16 @@ static THE_LETTER_A: u32 = 'a' as u32;
/// Generates a fresh type variable name, composed of lowercase alphabetic characters in sequence. /// Generates a fresh type variable name, composed of lowercase alphabetic characters in sequence.
pub fn name_type_var<I, F: FnMut(&I, &str) -> bool>( pub fn name_type_var<I, F: FnMut(&I, &str) -> bool>(
prefix: &str,
letters_used: u32, letters_used: u32,
taken: &mut impl Iterator<Item = I>, taken: &mut impl Iterator<Item = I>,
mut predicate: F, mut predicate: F,
) -> (Lowercase, u32) { ) -> (Lowercase, u32) {
// TODO we should arena-allocate this String, // TODO we should arena-allocate this String,
// so all the strings in the entire pass only require ~1 allocation. // so all the strings in the entire pass only require ~1 allocation.
let mut buf = String::with_capacity((letters_used as usize) / 26 + 1); let mut buf = String::with_capacity(prefix.len() + (letters_used as usize) / 26 + 1);
buf.push_str(prefix);
let is_taken = { let is_taken = {
let mut remaining = letters_used as i32; let mut remaining = letters_used as i32;
@ -4118,7 +4121,7 @@ pub fn name_type_var<I, F: FnMut(&I, &str) -> bool>(
if is_taken { if is_taken {
// If the generated name is already taken, try again. // If the generated name is already taken, try again.
name_type_var(letters_used + 1, taken, predicate) name_type_var(prefix, letters_used + 1, taken, predicate)
} else { } else {
(buf.into(), letters_used + 1) (buf.into(), letters_used + 1)
} }
@ -4127,18 +4130,19 @@ pub fn name_type_var<I, F: FnMut(&I, &str) -> bool>(
/// Generates a fresh type variable name given a hint, composed of the hint as a prefix and a /// Generates a fresh type variable name given a hint, composed of the hint as a prefix and a
/// number as a suffix. For example, given hint `a` we'll name the variable `a`, `a1`, or `a27`. /// number as a suffix. For example, given hint `a` we'll name the variable `a`, `a1`, or `a27`.
pub fn name_type_var_with_hint<I, F: FnMut(&I, &str) -> bool>( pub fn name_type_var_with_hint<I, F: FnMut(&I, &str) -> bool>(
prefix: &str,
hint: &str, hint: &str,
taken: &mut impl Iterator<Item = I>, taken: &mut impl Iterator<Item = I>,
mut predicate: F, mut predicate: F,
) -> Lowercase { ) -> Lowercase {
if !taken.any(|item| predicate(&item, hint)) { if !taken.any(|item| predicate(&item, hint)) {
return hint.into(); return format!("{prefix}{hint}").into();
} }
let mut i = 0; let mut i = 0;
loop { loop {
i += 1; i += 1;
let cand = format!("{}{}", hint, i); let cand = format!("{prefix}{hint}{i}");
if !taken.any(|item| predicate(&item, &cand)) { if !taken.any(|item| predicate(&item, &cand)) {
return cand.into(); return cand.into();