Merge pull request #2857 from rtfeldman/abilities-mono

Codegen for abilities
This commit is contained in:
Richard Feldman 2022-04-16 22:59:16 -04:00 committed by GitHub
commit 718b999751
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 442 additions and 103 deletions

View file

@ -1,9 +1,11 @@
use roc_can::abilities::AbilitiesStore;
use roc_module::symbol::Symbol;
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 roc_unify::unify::MustImplementConstraints;
use crate::solve::{IncompleteAbilityImplementation, TypeError};
@ -18,17 +20,14 @@ pub enum AbilityImplError {
}
#[derive(Default)]
pub struct DeferredMustImplementAbility(Vec<(Vec<MustImplementAbility>, AbilityImplError)>);
pub struct DeferredMustImplementAbility(Vec<(MustImplementConstraints, AbilityImplError)>);
impl DeferredMustImplementAbility {
pub fn add(&mut self, must_implement: Vec<MustImplementAbility>, on_error: AbilityImplError) {
pub fn add(&mut self, must_implement: MustImplementConstraints, 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![];
@ -45,8 +44,21 @@ impl DeferredMustImplementAbility {
};
}
for (mias, _) in self.0.iter() {
for &mia @ MustImplementAbility { typ, ability } in mias {
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 (constraints, on_error) in self.0.into_iter() {
let must_implement = constraints.get_unique();
// First off, make sure we populate information about which of the "must implement"
// constraints are met, and which aren't.
for &mia @ MustImplementAbility { typ, ability } in must_implement.iter() {
if is_good!(&mia) || get_bad!(mia).is_some() {
continue;
}
@ -80,22 +92,16 @@ impl DeferredMustImplementAbility {
));
}
}
}
// 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() {
// Now, figure out what errors we need to report.
use AbilityImplError::*;
match on_error {
IncompleteAbility => {
// These aren't attached to another type error, so if these must_implement
// constraints aren't met, we'll emit a generic "this type doesn't implement an
// ability" error message at the end. We only want to do this if it turns out
// the "must implement" constraint indeed wasn't part of a more specific type
// error.
incomplete_not_in_context.extend(must_implement);
}
BadExpr(region, category, var) => {
@ -138,9 +144,11 @@ impl DeferredMustImplementAbility {
reported_in_context.extend(must_implement);
}
}
};
}
}
// Go through and attach generic "type does not implement ability" errors, if they were not
// part of a larger context.
for mia in incomplete_not_in_context.into_iter() {
if let Some(must_implement) = get_bad!(mia) {
if !reported_in_context.contains(&mia) {
@ -154,3 +162,29 @@ impl DeferredMustImplementAbility {
problems
}
}
/// Determines what type implements an ability member of a specialized signature, given the
/// [MustImplementAbility] constraints of the signature.
pub fn type_implementing_member(
specialization_must_implement_constraints: &MustImplementConstraints,
ability: Symbol,
) -> Symbol {
debug_assert_eq!({
let ability_implementations_for_specialization =
specialization_must_implement_constraints
.clone()
.get_unique();
ability_implementations_for_specialization.len()
},
1,
"Multiple variables bound to an ability - this is ambiguous and should have been caught in canonicalization: {:?}",
specialization_must_implement_constraints
);
specialization_must_implement_constraints
.iter_for_ability(ability)
.next()
.unwrap()
.typ
}

View file

@ -2,6 +2,6 @@
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
#![allow(clippy::large_enum_variant)]
mod ability;
pub mod ability;
pub mod module;
pub mod solve;

View file

@ -1,4 +1,4 @@
use crate::ability::{AbilityImplError, DeferredMustImplementAbility};
use crate::ability::{type_implementing_member, AbilityImplError, DeferredMustImplementAbility};
use bumpalo::Bump;
use roc_can::abilities::{AbilitiesStore, MemberSpecialization};
use roc_can::constraint::Constraint::{self, *};
@ -703,7 +703,6 @@ fn solve(
check_ability_specialization(
arena,
subs,
&new_env,
pools,
rank,
abilities_store,
@ -812,7 +811,6 @@ fn solve(
check_ability_specialization(
arena,
subs,
&new_env,
pools,
rank,
abilities_store,
@ -1282,7 +1280,6 @@ fn solve(
fn check_ability_specialization(
arena: &Bump,
subs: &mut Subs,
env: &Env,
pools: &mut Pools,
rank: Rank,
abilities_store: &mut AbilitiesStore,
@ -1295,9 +1292,7 @@ fn check_ability_specialization(
// inferred type for the specialization actually aligns with the expected
// implementation.
if let Some((root_symbol, root_data)) = abilities_store.root_name_and_def(symbol) {
let root_signature_var = env
.get_var_by_symbol(&root_symbol)
.expect("Ability should be registered in env by now!");
let root_signature_var = root_data.signature_var;
// Check if they unify - if they don't, then the claimed specialization isn't really one,
// and that's a type error!
@ -1351,16 +1346,8 @@ fn check_ability_specialization(
// First, figure out and register for what type does this symbol specialize
// the ability member.
let mut ability_implementations_for_specialization = must_implement_ability
.iter()
.filter(|mia| mia.ability == root_data.parent_ability)
.collect::<Vec<_>>();
ability_implementations_for_specialization.dedup();
debug_assert!(ability_implementations_for_specialization.len() == 1, "Multiple variables bound to an ability - this is ambiguous and should have been caught in canonicalization");
// This is a valid specialization! Record it.
let specialization_type = ability_implementations_for_specialization[0].typ;
let specialization_type =
type_implementing_member(&must_implement_ability, root_data.parent_ability);
let specialization = MemberSpecialization {
symbol,
region: symbol_loc_var.region,

View file

@ -5913,4 +5913,23 @@ mod solve_expr {
"U64",
)
}
#[test]
fn alias_ability_member() {
infer_eq_without_problem(
indoc!(
r#"
app "test" provides [ thething ] to "./platform"
Hash has
hash : a -> U64 | a has Hash
thething =
itis = hash
itis
"#
),
"a -> U64 | a has Hash",
)
}
}