mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 23:04:49 +00:00
Merge pull request #2857 from rtfeldman/abilities-mono
Codegen for abilities
This commit is contained in:
commit
718b999751
15 changed files with 442 additions and 103 deletions
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue