Print multiple abilities in error types

This commit is contained in:
Ayaz Hafiz 2022-10-12 15:38:16 -05:00
parent 94fc58a508
commit 49e19d96e5
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
3 changed files with 62 additions and 30 deletions

View file

@ -3738,14 +3738,14 @@ fn content_to_err_type(
} }
}; };
// TODO(multi-abilities) let ability_set = AbilitySet::from_iter(subs.get_subs_slice(abilities).iter().copied());
ErrorType::FlexAbleVar(name, subs.get_subs_slice(abilities)[0]) ErrorType::FlexAbleVar(name, ability_set)
} }
RigidAbleVar(name_index, abilities) => { RigidAbleVar(name_index, abilities) => {
let name = subs.field_names[name_index.index as usize].clone(); let name = subs.field_names[name_index.index as usize].clone();
// TODO(multi-abilities) let ability_set = AbilitySet::from_iter(subs.get_subs_slice(abilities).iter().copied());
ErrorType::RigidAbleVar(name, subs.get_subs_slice(abilities)[0]) ErrorType::RigidAbleVar(name, ability_set)
} }
RecursionVar { RecursionVar {

View file

@ -256,7 +256,7 @@ pub struct AliasCommon {
/// ///
/// In the future we might want to do some small-vec optimizations, though that may be trivialized /// In the future we might want to do some small-vec optimizations, though that may be trivialized
/// away with a SoA representation of canonicalized types. /// away with a SoA representation of canonicalized types.
#[derive(Clone, Debug, Default, PartialEq, PartialOrd, Eq, Ord)] #[derive(Clone, Debug, Default, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct AbilitySet(Vec<Symbol>); pub struct AbilitySet(Vec<Symbol>);
impl AbilitySet { impl AbilitySet {
@ -282,11 +282,11 @@ impl AbilitySet {
self.0.contains(ability) self.0.contains(ability)
} }
pub fn sorted_iter(&self) -> impl Iterator<Item = &Symbol> { pub fn sorted_iter(&self) -> impl ExactSizeIterator<Item = &Symbol> {
self.0.iter() self.0.iter()
} }
pub fn into_sorted_iter(self) -> impl Iterator<Item = Symbol> { pub fn into_sorted_iter(self) -> impl ExactSizeIterator<Item = Symbol> {
self.0.into_iter() self.0.into_iter()
} }
} }
@ -2266,8 +2266,8 @@ pub enum ErrorType {
Type(Symbol, Vec<ErrorType>), Type(Symbol, Vec<ErrorType>),
FlexVar(Lowercase), FlexVar(Lowercase),
RigidVar(Lowercase), RigidVar(Lowercase),
FlexAbleVar(Lowercase, Symbol), FlexAbleVar(Lowercase, AbilitySet),
RigidAbleVar(Lowercase, Symbol), RigidAbleVar(Lowercase, AbilitySet),
Record(SendMap<Lowercase, RecordField<ErrorType>>, TypeExt), Record(SendMap<Lowercase, RecordField<ErrorType>>, TypeExt),
TagUnion(SendMap<TagName, Vec<ErrorType>>, TypeExt), TagUnion(SendMap<TagName, Vec<ErrorType>>, TypeExt),
RecursiveTagUnion(Box<ErrorType>, SendMap<TagName, Vec<ErrorType>>, TypeExt), RecursiveTagUnion(Box<ErrorType>, SendMap<TagName, Vec<ErrorType>>, TypeExt),

View file

@ -15,7 +15,7 @@ use roc_solve_problem::{
use roc_std::RocDec; use roc_std::RocDec;
use roc_types::pretty_print::{Parens, WILDCARD}; use roc_types::pretty_print::{Parens, WILDCARD};
use roc_types::types::{ use roc_types::types::{
AliasKind, Category, ErrorType, PatternCategory, Reason, RecordField, TypeExt, AbilitySet, AliasKind, Category, ErrorType, PatternCategory, Reason, RecordField, TypeExt,
}; };
use std::path::PathBuf; use std::path::PathBuf;
use ven_pretty::DocAllocator; use ven_pretty::DocAllocator;
@ -2050,7 +2050,7 @@ pub enum Problem {
FieldsMissing(Vec<Lowercase>), FieldsMissing(Vec<Lowercase>),
TagTypo(TagName, Vec<TagName>), TagTypo(TagName, Vec<TagName>),
TagsMissing(Vec<TagName>), TagsMissing(Vec<TagName>),
BadRigidVar(Lowercase, ErrorType, Option<Symbol>), BadRigidVar(Lowercase, ErrorType, Option<AbilitySet>),
OptionalRequiredMismatch(Lowercase), OptionalRequiredMismatch(Lowercase),
OpaqueComparedToNonOpaque, OpaqueComparedToNonOpaque,
BoolVsBoolTag(TagName), BoolVsBoolTag(TagName),
@ -2208,7 +2208,7 @@ fn ext_to_doc<'b>(alloc: &'b RocDocAllocator<'b>, ext: TypeExt) -> Option<RocDoc
} }
} }
type AbleVariables = Vec<(Lowercase, Symbol)>; type AbleVariables = Vec<(Lowercase, AbilitySet)>;
#[derive(Default)] #[derive(Default)]
struct Context { struct Context {
@ -2249,7 +2249,6 @@ fn to_doc_help<'b>(
FlexVar(lowercase) | RigidVar(lowercase) => alloc.type_variable(lowercase), FlexVar(lowercase) | RigidVar(lowercase) => alloc.type_variable(lowercase),
FlexAbleVar(lowercase, ability) | RigidAbleVar(lowercase, ability) => { FlexAbleVar(lowercase, ability) | RigidAbleVar(lowercase, ability) => {
// TODO we should be putting able variables on the toplevel of the type, not here
ctx.able_variables.push((lowercase.clone(), ability)); ctx.able_variables.push((lowercase.clone(), ability));
alloc.type_variable(lowercase) alloc.type_variable(lowercase)
} }
@ -2424,13 +2423,20 @@ fn type_with_able_vars<'b>(
let mut doc = Vec::with_capacity(1 + 6 * able.len()); let mut doc = Vec::with_capacity(1 + 6 * able.len());
doc.push(typ); doc.push(typ);
for (i, (var, ability)) in able.into_iter().enumerate() { for (i, (var, abilities)) in able.into_iter().enumerate() {
doc.push(alloc.string(if i == 0 { " | " } else { ", " }.to_string())); doc.push(alloc.string(if i == 0 { " | " } else { ", " }.to_string()));
doc.push(alloc.type_variable(var)); doc.push(alloc.type_variable(var));
doc.push(alloc.space()); doc.push(alloc.space());
doc.push(alloc.keyword("has")); doc.push(alloc.keyword("has"));
doc.push(alloc.space());
doc.push(alloc.symbol_foreign_qualified(ability)); for (i, ability) in abilities.into_sorted_iter().enumerate() {
if i > 0 {
doc.push(alloc.space());
doc.push(alloc.text("&"));
}
doc.push(alloc.space());
doc.push(alloc.symbol_foreign_qualified(ability));
}
} }
alloc.concat(doc) alloc.concat(doc)
@ -2501,14 +2507,14 @@ fn to_diff<'b>(
} }
} }
(RigidAbleVar(x, ab), other) | (other, RigidAbleVar(x, ab)) => { (RigidAbleVar(x, abs), other) | (other, RigidAbleVar(x, abs)) => {
let (left, left_able) = to_doc(alloc, Parens::InFn, type1); let (left, left_able) = to_doc(alloc, Parens::InFn, type1);
let (right, right_able) = to_doc(alloc, Parens::InFn, type2); let (right, right_able) = to_doc(alloc, Parens::InFn, type2);
Diff { Diff {
left, left,
right, right,
status: Status::Different(vec![Problem::BadRigidVar(x, other, Some(ab))]), status: Status::Different(vec![Problem::BadRigidVar(x, other, Some(abs))]),
left_able, left_able,
right_able, right_able,
} }
@ -3554,11 +3560,25 @@ fn type_problem_to_pretty<'b>(
let bad_rigid_var = |name: Lowercase, a_thing| { let bad_rigid_var = |name: Lowercase, a_thing| {
let kind_of_value = match opt_ability { let kind_of_value = match opt_ability {
Some(ability) => alloc.concat([ Some(abilities) => {
alloc.reflow("any value implementing the "), let mut abilities = abilities.into_sorted_iter();
alloc.symbol_unqualified(ability), if abilities.len() == 1 {
alloc.reflow(" ability"), alloc.concat([
]), alloc.reflow("any value implementing the "),
alloc.symbol_unqualified(abilities.next().unwrap()),
alloc.reflow(" ability"),
])
} else {
alloc.concat([
alloc.reflow("any value implementing the "),
alloc.intersperse(
abilities.map(|ab| alloc.symbol_unqualified(ab)),
alloc.reflow(", "),
),
alloc.reflow(" abilities"),
])
}
}
None => alloc.reflow("any type of value"), None => alloc.reflow("any type of value"),
}; };
alloc alloc
@ -3609,13 +3629,25 @@ fn type_problem_to_pretty<'b>(
match tipe { match tipe {
Infinite | Error | FlexVar(_) => alloc.nil(), Infinite | Error | FlexVar(_) => alloc.nil(),
FlexAbleVar(_, ability) => bad_rigid_var( FlexAbleVar(_, abilities) => {
x, let mut abilities = abilities.into_sorted_iter();
alloc.concat([ let msg = if abilities.len() == 1 {
alloc.reflow("an instance of the ability "), alloc.concat([
alloc.symbol_unqualified(ability), alloc.reflow("an instance of the ability "),
]), alloc.symbol_unqualified(abilities.next().unwrap()),
), ])
} else {
alloc.concat([
alloc.reflow("an instance of the "),
alloc.intersperse(
abilities.map(|ab| alloc.symbol_unqualified(ab)),
alloc.reflow(", "),
),
alloc.reflow(" abilities"),
])
};
bad_rigid_var(x, msg)
}
RigidVar(y) | RigidAbleVar(y, _) => bad_double_rigid(x, y), RigidVar(y) | RigidAbleVar(y, _) => bad_double_rigid(x, y),
Function(_, _, _) => bad_rigid_var(x, alloc.reflow("a function value")), Function(_, _, _) => bad_rigid_var(x, alloc.reflow("a function value")),
Record(_, _) => bad_rigid_var(x, alloc.reflow("a record value")), Record(_, _) => bad_rigid_var(x, alloc.reflow("a record value")),