mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-04 12:18:19 +00:00
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:
parent
2b993a29f9
commit
3b0e2429e6
5 changed files with 49 additions and 24 deletions
|
@ -56,6 +56,7 @@ pub struct DebugPrint {
|
|||
pub print_lambda_sets: bool,
|
||||
pub print_only_under_alias: bool,
|
||||
pub ignore_polarity: bool,
|
||||
pub print_weakened_vars: bool,
|
||||
}
|
||||
|
||||
impl DebugPrint {
|
||||
|
@ -63,6 +64,7 @@ impl DebugPrint {
|
|||
print_lambda_sets: false,
|
||||
print_only_under_alias: false,
|
||||
ignore_polarity: false,
|
||||
print_weakened_vars: false,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -380,11 +382,7 @@ struct NamedResult {
|
|||
recursion_structs_to_expand: Vec<Variable>,
|
||||
}
|
||||
|
||||
fn name_all_type_vars(
|
||||
variable: Variable,
|
||||
subs: &mut Subs,
|
||||
find_names_under_alias: bool,
|
||||
) -> NamedResult {
|
||||
fn name_all_type_vars(variable: Variable, subs: &mut Subs, debug_print: DebugPrint) -> NamedResult {
|
||||
let mut roots = Vec::new();
|
||||
let mut letters_used = 0;
|
||||
let mut appearances = MutMap::default();
|
||||
|
@ -397,7 +395,7 @@ fn name_all_type_vars(
|
|||
&mut roots,
|
||||
&mut appearances,
|
||||
&mut taken,
|
||||
find_names_under_alias,
|
||||
debug_print.print_only_under_alias,
|
||||
);
|
||||
|
||||
let mut recursion_structs_to_expand = vec![];
|
||||
|
@ -407,14 +405,14 @@ fn name_all_type_vars(
|
|||
// set_root_name(root, (format!("<{:?}>", root).into()), subs);
|
||||
match appearances.get(&root) {
|
||||
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) => {
|
||||
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);
|
||||
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(
|
||||
letters_used: u32,
|
||||
root: Variable,
|
||||
subs: &mut Subs,
|
||||
taken: &mut MutMap<Lowercase, Variable>,
|
||||
debug_print: DebugPrint,
|
||||
) -> 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) {
|
||||
Content::FlexVar(Some(name))
|
||||
| Content::RigidVar(name)
|
||||
|
@ -444,19 +459,21 @@ fn name_root(
|
|||
let name_hint = &subs[*name];
|
||||
if name_hint.as_str() == "*" {
|
||||
// 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
|
||||
})
|
||||
} else {
|
||||
let generated =
|
||||
name_type_var_with_hint(name_hint.as_str(), &mut taken.keys(), |var, str| {
|
||||
var.as_str() == str
|
||||
});
|
||||
let generated = name_type_var_with_hint(
|
||||
prefix,
|
||||
name_hint.as_str(),
|
||||
&mut taken.keys(),
|
||||
|var, str| var.as_str() == str,
|
||||
);
|
||||
|
||||
(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
|
||||
}),
|
||||
};
|
||||
|
@ -568,7 +585,7 @@ pub fn name_and_print_var(
|
|||
interns: &Interns,
|
||||
debug_print: DebugPrint,
|
||||
) -> 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);
|
||||
content_to_string(
|
||||
content,
|
||||
|
|
|
@ -2185,6 +2185,7 @@ pub struct Rank(u32);
|
|||
impl Rank {
|
||||
pub const NONE: Rank = Rank(0);
|
||||
|
||||
/// The generalized rank
|
||||
pub fn is_none(&self) -> bool {
|
||||
*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
|
||||
// times during error type reporting, we'll use "name" for display.
|
||||
let (name, new_index) =
|
||||
name_type_var(state.letters_used, &mut state.taken.iter(), |var, str| {
|
||||
var.as_str() == str
|
||||
});
|
||||
let (name, new_index) = name_type_var(
|
||||
"",
|
||||
state.letters_used,
|
||||
&mut state.taken.iter(),
|
||||
|var, str| var.as_str() == str,
|
||||
);
|
||||
|
||||
state.letters_used = new_index;
|
||||
|
||||
|
|
|
@ -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.
|
||||
pub fn name_type_var<I, F: FnMut(&I, &str) -> bool>(
|
||||
prefix: &str,
|
||||
letters_used: u32,
|
||||
taken: &mut impl Iterator<Item = I>,
|
||||
mut predicate: F,
|
||||
) -> (Lowercase, u32) {
|
||||
// TODO we should arena-allocate this String,
|
||||
// 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 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 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 {
|
||||
(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
|
||||
/// 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>(
|
||||
prefix: &str,
|
||||
hint: &str,
|
||||
taken: &mut impl Iterator<Item = I>,
|
||||
mut predicate: F,
|
||||
) -> Lowercase {
|
||||
if !taken.any(|item| predicate(&item, hint)) {
|
||||
return hint.into();
|
||||
return format!("{prefix}{hint}").into();
|
||||
}
|
||||
|
||||
let mut i = 0;
|
||||
loop {
|
||||
i += 1;
|
||||
let cand = format!("{}{}", hint, i);
|
||||
let cand = format!("{prefix}{hint}{i}");
|
||||
|
||||
if !taken.any(|item| predicate(&item, &cand)) {
|
||||
return cand.into();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue