mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 13:29:12 +00:00
156 lines
6.4 KiB
Rust
156 lines
6.4 KiB
Rust
use roc_can::abilities::AbilitiesStore;
|
|
use roc_region::all::{Loc, Region};
|
|
use roc_types::subs::Subs;
|
|
use roc_types::subs::Variable;
|
|
use roc_types::types::{Category, PatternCategory};
|
|
use roc_unify::unify::MustImplementAbility;
|
|
|
|
use crate::solve::{IncompleteAbilityImplementation, TypeError};
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum AbilityImplError {
|
|
/// Promote this to an error that the type does not fully implement an ability
|
|
IncompleteAbility,
|
|
/// Promote this error to a `TypeError::BadExpr` from elsewhere
|
|
BadExpr(Region, Category, Variable),
|
|
/// Promote this error to a `TypeError::BadPattern` from elsewhere
|
|
BadPattern(Region, PatternCategory, Variable),
|
|
}
|
|
|
|
#[derive(Default)]
|
|
pub struct DeferredMustImplementAbility(Vec<(Vec<MustImplementAbility>, AbilityImplError)>);
|
|
|
|
impl DeferredMustImplementAbility {
|
|
pub fn add(&mut self, must_implement: Vec<MustImplementAbility>, on_error: AbilityImplError) {
|
|
self.0.push((must_implement, on_error));
|
|
}
|
|
|
|
pub fn check(self, subs: &mut Subs, abilities_store: &AbilitiesStore) -> Vec<TypeError> {
|
|
// Two passes here. First up let's build up records of what types fully implement
|
|
// abilities, and what specializations are available/missing for the ones that don't.
|
|
// Use a vec since these lists should usually be pretty small.
|
|
let mut good = vec![];
|
|
let mut bad = vec![];
|
|
|
|
macro_rules! is_good {
|
|
($e:expr) => {
|
|
good.contains($e)
|
|
};
|
|
}
|
|
macro_rules! get_bad {
|
|
($e:expr) => {
|
|
bad.iter()
|
|
.find(|(cand, _)| $e == *cand)
|
|
.map(|(_, incomplete)| incomplete)
|
|
};
|
|
}
|
|
|
|
for (mias, _) in self.0.iter() {
|
|
for &mia @ MustImplementAbility { typ, ability } in mias {
|
|
if is_good!(&mia) || get_bad!(mia).is_some() {
|
|
continue;
|
|
}
|
|
|
|
let members_of_ability = abilities_store.members_of_ability(ability).unwrap();
|
|
let mut specialized_members = Vec::with_capacity(members_of_ability.len());
|
|
let mut missing_members = Vec::with_capacity(members_of_ability.len());
|
|
for &member in members_of_ability {
|
|
match abilities_store.get_specialization(member, typ) {
|
|
None => {
|
|
let root_data = abilities_store.member_def(member).unwrap();
|
|
missing_members.push(Loc::at(root_data.region, member));
|
|
}
|
|
Some(specialization) => {
|
|
specialized_members.push(Loc::at(specialization.region, member));
|
|
}
|
|
}
|
|
}
|
|
|
|
if missing_members.is_empty() {
|
|
good.push(mia);
|
|
} else {
|
|
bad.push((
|
|
mia,
|
|
IncompleteAbilityImplementation {
|
|
typ,
|
|
ability,
|
|
specialized_members,
|
|
missing_members,
|
|
},
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now figure out what errors we need to report.
|
|
let mut problems = vec![];
|
|
|
|
// Keep track of which types that have an incomplete ability were reported as part of
|
|
// another type error (from an expression or pattern). If we reported an error for a type
|
|
// that doesn't implement an ability in that context, we don't want to repeat the error
|
|
// message.
|
|
let mut reported_in_context = vec![];
|
|
let mut incomplete_not_in_context = vec![];
|
|
|
|
for (must_implement, on_error) in self.0.into_iter() {
|
|
use AbilityImplError::*;
|
|
match on_error {
|
|
IncompleteAbility => {
|
|
incomplete_not_in_context.extend(must_implement);
|
|
}
|
|
BadExpr(region, category, var) => {
|
|
let incomplete_types = must_implement
|
|
.iter()
|
|
.filter_map(|e| get_bad!(*e))
|
|
.cloned()
|
|
.collect::<Vec<_>>();
|
|
if !incomplete_types.is_empty() {
|
|
// Demote the bad variable that exposed this problem to an error, both so
|
|
// that we have an ErrorType to report and so that codegen knows to deal
|
|
// with the error later.
|
|
let (error_type, _moar_ghosts_n_stuff) = subs.var_to_error_type(var);
|
|
problems.push(TypeError::BadExprMissingAbility(
|
|
region,
|
|
category,
|
|
error_type,
|
|
incomplete_types,
|
|
));
|
|
reported_in_context.extend(must_implement);
|
|
}
|
|
}
|
|
BadPattern(region, category, var) => {
|
|
let incomplete_types = must_implement
|
|
.iter()
|
|
.filter_map(|e| get_bad!(*e))
|
|
.cloned()
|
|
.collect::<Vec<_>>();
|
|
if !incomplete_types.is_empty() {
|
|
// Demote the bad variable that exposed this problem to an error, both so
|
|
// that we have an ErrorType to report and so that codegen knows to deal
|
|
// with the error later.
|
|
let (error_type, _moar_ghosts_n_stuff) = subs.var_to_error_type(var);
|
|
problems.push(TypeError::BadPatternMissingAbility(
|
|
region,
|
|
category,
|
|
error_type,
|
|
incomplete_types,
|
|
));
|
|
reported_in_context.extend(must_implement);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
for mia in incomplete_not_in_context.into_iter() {
|
|
if let Some(must_implement) = get_bad!(mia) {
|
|
if !reported_in_context.contains(&mia) {
|
|
problems.push(TypeError::IncompleteAbilityImplementation(
|
|
must_implement.clone(),
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
problems
|
|
}
|
|
}
|