mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 05:49:08 +00:00
Print multiple abilities in error types
This commit is contained in:
parent
94fc58a508
commit
49e19d96e5
3 changed files with 62 additions and 30 deletions
|
@ -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 {
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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,14 +2423,21 @@ 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"));
|
||||||
|
|
||||||
|
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.space());
|
||||||
doc.push(alloc.symbol_foreign_qualified(ability));
|
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) => {
|
||||||
|
let mut abilities = abilities.into_sorted_iter();
|
||||||
|
if abilities.len() == 1 {
|
||||||
|
alloc.concat([
|
||||||
alloc.reflow("any value implementing the "),
|
alloc.reflow("any value implementing the "),
|
||||||
alloc.symbol_unqualified(ability),
|
alloc.symbol_unqualified(abilities.next().unwrap()),
|
||||||
alloc.reflow(" ability"),
|
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();
|
||||||
|
let msg = if abilities.len() == 1 {
|
||||||
alloc.concat([
|
alloc.concat([
|
||||||
alloc.reflow("an instance of the ability "),
|
alloc.reflow("an instance of the ability "),
|
||||||
alloc.symbol_unqualified(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")),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue