Fast-path for determining ability member impls for builtin opaques

This commit is contained in:
Ayaz Hafiz 2023-03-20 16:05:10 -04:00
parent 297a571b34
commit e6094df69b
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
8 changed files with 182 additions and 60 deletions

View file

@ -4,8 +4,9 @@ use roc_collections::{VecMap, VecSet};
use roc_debug_flags::dbg_do;
#[cfg(debug_assertions)]
use roc_debug_flags::ROC_PRINT_UNDERIVABLE;
use roc_derive_key::Derived;
use roc_error_macros::internal_error;
use roc_module::symbol::Symbol;
use roc_module::symbol::{ModuleId, Symbol};
use roc_region::all::{Loc, Region};
use roc_solve_problem::{
NotDerivableContext, NotDerivableDecode, NotDerivableEncode, NotDerivableEq, TypeError,
@ -1298,12 +1299,12 @@ pub fn type_implementing_specialization(
}
/// Result of trying to resolve an ability specialization.
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Debug)]
pub enum Resolved {
/// A user-defined specialization should be used.
Specialization(Symbol),
/// A specialization must be generated for the given type variable.
NeedsGenerated(Variable),
/// A specialization must be generated with the given derive key.
Derive(Derived),
}
/// An [`AbilityResolver`] is a shell of an abilities store that answers questions needed for
@ -1346,6 +1347,13 @@ impl AbilityResolver for AbilitiesStore {
}
}
/// Whether this a module whose types' ability implementations should be checked via derive_key,
/// because they do not explicitly list ability implementations due to circular dependencies.
#[inline]
pub(crate) fn builtin_module_with_unlisted_ability_impl(module_id: ModuleId) -> bool {
matches!(module_id, ModuleId::NUM | ModuleId::BOOL)
}
pub fn resolve_ability_specialization<R: AbilityResolver>(
subs: &mut Subs,
resolver: &R,
@ -1379,23 +1387,45 @@ pub fn resolve_ability_specialization<R: AbilityResolver>(
let resolved = match obligated {
Obligated::Opaque(symbol) => {
let impl_key = roc_can::abilities::ImplKey {
opaque: symbol,
ability_member,
};
if builtin_module_with_unlisted_ability_impl(symbol.module_id()) {
let derive_key = roc_derive_key::Derived::builtin_with_builtin_symbol(
ability_member
.try_into()
.expect("derived symbols must be builtins"),
symbol,
)
.expect("specialization var not derivable!");
match resolver.get_implementation(impl_key)? {
roc_types::types::MemberImpl::Impl(spec_symbol) => {
Resolved::Specialization(spec_symbol)
Resolved::Derive(derive_key)
} else {
let impl_key = roc_can::abilities::ImplKey {
opaque: symbol,
ability_member,
};
match resolver.get_implementation(impl_key)? {
roc_types::types::MemberImpl::Impl(spec_symbol) => {
Resolved::Specialization(spec_symbol)
}
// TODO this is not correct. We can replace `Resolved` with `MemberImpl` entirely,
// which will make this simpler.
roc_types::types::MemberImpl::Error => {
Resolved::Specialization(Symbol::UNDERSCORE)
}
}
// TODO this is not correct. We can replace `Resolved` with `MemberImpl` entirely,
// which will make this simpler.
roc_types::types::MemberImpl::Error => Resolved::Specialization(Symbol::UNDERSCORE),
}
}
Obligated::Adhoc(variable) => {
// TODO: more rules need to be validated here, like is this a builtin ability?
Resolved::NeedsGenerated(variable)
let derive_key = roc_derive_key::Derived::builtin(
ability_member
.try_into()
.expect("derived symbols must be builtins"),
subs,
variable,
)
.expect("specialization var not derivable!");
Resolved::Derive(derive_key)
}
};

View file

@ -24,7 +24,10 @@ use roc_types::{
};
use roc_unify::unify::{unify, Env as UEnv, Mode, MustImplementConstraints};
use crate::solve::{deep_copy_var_in, introduce, Pools};
use crate::{
ability::builtin_module_with_unlisted_ability_impl,
solve::{deep_copy_var_in, introduce, Pools},
};
/// What phase in the compiler is reaching out to specialize lambda sets?
/// This is important to distinguish subtle differences in the behavior of the solving algorithm.
@ -626,7 +629,7 @@ fn make_specialization_decision<P: Phase>(
use SpecializationTypeKey::*;
match subs.get_content_without_compacting(var) {
Alias(opaque, _, _, AliasKind::Opaque)
if !matches!(opaque.module_id(), ModuleId::NUM | ModuleId::BOOL) =>
if !builtin_module_with_unlisted_ability_impl(opaque.module_id()) =>
{
if P::IS_LATE {
SpecializeDecision::Specialize(Opaque(*opaque))