mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 13:29:12 +00:00
Store declared implementations, both custom and derived, in abilities store
This commit is contained in:
parent
6035e45f25
commit
e2454f497f
10 changed files with 249 additions and 171 deletions
|
@ -4,7 +4,10 @@ use roc_collections::{all::MutMap, VecMap, VecSet};
|
|||
use roc_error_macros::internal_error;
|
||||
use roc_module::symbol::{ModuleId, Symbol};
|
||||
use roc_region::all::Region;
|
||||
use roc_types::{subs::Variable, types::Type};
|
||||
use roc_types::{
|
||||
subs::Variable,
|
||||
types::{MemberImpl, Type},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct MemberVariables {
|
||||
|
@ -68,10 +71,7 @@ impl AbilityMemberData<Resolved> {
|
|||
}
|
||||
|
||||
/// (member, specialization type) -> specialization
|
||||
pub type SpecializationsMap<Phase> = VecMap<(Symbol, Symbol), MemberSpecialization<Phase>>;
|
||||
|
||||
pub type PendingSpecializations = SpecializationsMap<Pending>;
|
||||
pub type ResolvedSpecializations = SpecializationsMap<Resolved>;
|
||||
pub type ImplMap = VecMap<(Symbol, Symbol), MemberImpl>;
|
||||
|
||||
/// Solved lambda sets for an ability member specialization. For example, if we have
|
||||
///
|
||||
|
@ -86,15 +86,13 @@ pub type SpecializationLambdaSets = VecMap<u8, Variable>;
|
|||
|
||||
/// A particular specialization of an ability member.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MemberSpecialization<Phase: ResolvePhase> {
|
||||
pub struct MemberSpecializationInfo<Phase: ResolvePhase> {
|
||||
_phase: std::marker::PhantomData<Phase>,
|
||||
|
||||
pub symbol: Symbol,
|
||||
|
||||
pub specialization_lambda_sets: SpecializationLambdaSets,
|
||||
}
|
||||
|
||||
impl MemberSpecialization<Resolved> {
|
||||
impl MemberSpecializationInfo<Resolved> {
|
||||
pub fn new(symbol: Symbol, specialization_lambda_sets: SpecializationLambdaSets) -> Self {
|
||||
Self {
|
||||
_phase: Default::default(),
|
||||
|
@ -120,23 +118,26 @@ pub enum SpecializationLambdaSetError {}
|
|||
pub struct IAbilitiesStore<Phase: ResolvePhase> {
|
||||
/// Maps an ability to the members defining it.
|
||||
members_of_ability: MutMap<Symbol, Vec<Symbol>>,
|
||||
/// Map of symbols that specialize an ability member to the root ability symbol name.
|
||||
/// For example, in the program
|
||||
///
|
||||
/// Hash has hash : a -> U64 | a has Hash
|
||||
///
|
||||
/// Id := {} implements [Hash {hash: myHash}]
|
||||
/// myHash = \@Id n -> n
|
||||
///
|
||||
/// We keep the mapping myHash->hash
|
||||
specialization_to_root: MutMap<Symbol, Symbol>,
|
||||
|
||||
/// Information about all members composing abilities.
|
||||
ability_members: MutMap<Symbol, AbilityMemberData<Phase>>,
|
||||
|
||||
/// Map of symbols that specialize an ability member to the root ability symbol name.
|
||||
/// For example, for the program
|
||||
/// Hash has hash : a -> U64 | a has Hash
|
||||
/// ^^^^ gets the symbol "#hash"
|
||||
/// hash = \@Id n -> n
|
||||
/// ^^^^ gets the symbol "#hash1"
|
||||
///
|
||||
/// We keep the mapping #hash1->#hash
|
||||
specialization_to_root: MutMap<Symbol, Symbol>,
|
||||
/// Maps a tuple (member, type) specifying that `type` has an implementation of an ability
|
||||
/// member `member`, to how that implementation is defined.
|
||||
declared_implementations: ImplMap,
|
||||
|
||||
/// Maps a tuple (member, type) specifying that `type` declares an implementation of an ability
|
||||
/// member `member`, to the exact symbol that implements the ability.
|
||||
declared_specializations: SpecializationsMap<Phase>,
|
||||
/// Information about specialized ability member implementations for a type.
|
||||
specializations: MutMap<Symbol, MemberSpecializationInfo<Phase>>,
|
||||
|
||||
next_specialization_id: NonZeroU32,
|
||||
|
||||
|
@ -148,14 +149,15 @@ pub struct IAbilitiesStore<Phase: ResolvePhase> {
|
|||
impl<Phase: ResolvePhase> Default for IAbilitiesStore<Phase> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
members_of_ability: Default::default(),
|
||||
ability_members: Default::default(),
|
||||
specialization_to_root: Default::default(),
|
||||
declared_specializations: Default::default(),
|
||||
next_specialization_id:
|
||||
// Safety: 1 != 0
|
||||
unsafe { NonZeroU32::new_unchecked(1) },
|
||||
resolved_specializations: Default::default(),
|
||||
members_of_ability: Default::default(),
|
||||
specialization_to_root: Default::default(),
|
||||
ability_members: Default::default(),
|
||||
declared_implementations: Default::default(),
|
||||
specializations: Default::default(),
|
||||
next_specialization_id:
|
||||
// Safety: 1 != 0
|
||||
unsafe { NonZeroU32::new_unchecked(1) },
|
||||
resolved_specializations: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -207,22 +209,45 @@ impl<Phase: ResolvePhase> IAbilitiesStore<Phase> {
|
|||
&self.ability_members
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn register_one_declared_impl(
|
||||
&mut self,
|
||||
implementing_type: Symbol,
|
||||
member: Symbol,
|
||||
member_impl: MemberImpl,
|
||||
) {
|
||||
if let MemberImpl::Impl(specialization_symbol) = member_impl {
|
||||
self.specialization_to_root
|
||||
.insert(specialization_symbol, member);
|
||||
}
|
||||
self.declared_implementations
|
||||
.insert((member, implementing_type), member_impl);
|
||||
}
|
||||
|
||||
/// Records the implementations of an ability an opaque type declares to have.
|
||||
///
|
||||
/// Calling this function does not validate that the implementations are correctly specializing
|
||||
/// in their definition, nor does it store type information about the implementations.
|
||||
///
|
||||
/// It is expected that during type solving, the owner of the abilities store marks the claimed
|
||||
/// implementation as either a proper or erroring implementation using
|
||||
/// [`Self::mark_implementation`].
|
||||
pub fn register_declared_implementations(
|
||||
&mut self,
|
||||
implementing_type: Symbol,
|
||||
// (ability member, implementation)
|
||||
implementations: impl IntoIterator<Item = (Symbol, MemberImpl)>,
|
||||
) {
|
||||
for (member, member_impl) in implementations.into_iter() {
|
||||
self.register_one_declared_impl(implementing_type, member, member_impl);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether a symbol is declared to specialize an ability member.
|
||||
pub fn is_specialization_name(&self, symbol: Symbol) -> bool {
|
||||
self.specialization_to_root.contains_key(&symbol)
|
||||
}
|
||||
|
||||
/// Records that the symbol `specializing_symbol` claims to specialize `ability_member`; for
|
||||
/// example the symbol of `hash : Id -> U64` specializing `hash : a -> U64 | a has Hash`.
|
||||
pub fn register_specializing_symbol(
|
||||
&mut self,
|
||||
specializing_symbol: Symbol,
|
||||
ability_member: Symbol,
|
||||
) {
|
||||
self.specialization_to_root
|
||||
.insert(specializing_symbol, ability_member);
|
||||
}
|
||||
|
||||
pub fn members_of_ability(&self, ability: Symbol) -> Option<&[Symbol]> {
|
||||
self.members_of_ability.get(&ability).map(|v| v.as_ref())
|
||||
}
|
||||
|
@ -243,9 +268,10 @@ impl<Phase: ResolvePhase> IAbilitiesStore<Phase> {
|
|||
let Self {
|
||||
members_of_ability,
|
||||
ability_members,
|
||||
declared_specializations,
|
||||
declared_implementations,
|
||||
specializations,
|
||||
|
||||
// Covered by `declared_specializations`
|
||||
// Covered by `declared_implementations`
|
||||
specialization_to_root: _,
|
||||
|
||||
// Taking closure for a new module, so specialization IDs can be fresh
|
||||
|
@ -292,12 +318,17 @@ impl<Phase: ResolvePhase> IAbilitiesStore<Phase> {
|
|||
new.register_ability(ability, imported_member_data);
|
||||
|
||||
// Add any specializations of the ability's members we know about.
|
||||
declared_specializations
|
||||
declared_implementations
|
||||
.iter()
|
||||
.filter(|((member, _), _)| members.contains(member))
|
||||
.for_each(|(&(member, typ), specialization)| {
|
||||
new.register_specializing_symbol(specialization.symbol, member);
|
||||
new.import_specialization(member, typ, specialization);
|
||||
.for_each(|(&(member, typ), member_impl)| {
|
||||
new.register_one_declared_impl(typ, member, *member_impl);
|
||||
|
||||
if let MemberImpl::Impl(spec_symbol) = member_impl {
|
||||
if let Some(specialization_info) = specializations.get(spec_symbol) {
|
||||
new.import_specialization(specialization_info);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -305,6 +336,12 @@ impl<Phase: ResolvePhase> IAbilitiesStore<Phase> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum MarkError {
|
||||
NoDeclaredImpl,
|
||||
ImplIsNotCustom,
|
||||
}
|
||||
|
||||
impl IAbilitiesStore<Resolved> {
|
||||
/// Finds the symbol name and ability member definition for a symbol specializing the ability
|
||||
/// member, if it specializes any.
|
||||
|
@ -326,36 +363,59 @@ impl IAbilitiesStore<Resolved> {
|
|||
self.ability_members.get(&member)
|
||||
}
|
||||
|
||||
/// Returns an iterator over pairs ((ability member, type), specialization) specifying that
|
||||
/// "ability member" has a "specialization" for type "type".
|
||||
pub fn iter_specializations(
|
||||
/// Returns an iterator over pairs ((ability member, type), implementation) specifying that
|
||||
/// the give type has an implementation of an ability member.
|
||||
pub fn iter_declared_implementations(
|
||||
&self,
|
||||
) -> impl Iterator<Item = ((Symbol, Symbol), &MemberSpecialization<Resolved>)> + '_ {
|
||||
self.declared_specializations.iter().map(|(k, v)| (*k, v))
|
||||
) -> impl Iterator<Item = ((Symbol, Symbol), &MemberImpl)> + '_ {
|
||||
self.declared_implementations.iter().map(|(k, v)| (*k, v))
|
||||
}
|
||||
|
||||
/// Retrieves the specialization of `member` for `typ`, if it exists.
|
||||
pub fn get_specialization(
|
||||
&self,
|
||||
member: Symbol,
|
||||
typ: Symbol,
|
||||
) -> Option<&MemberSpecialization<Resolved>> {
|
||||
self.declared_specializations.get(&(member, typ))
|
||||
/// Retrieves the declared implementation of `member` for `typ`, if it exists.
|
||||
pub fn get_implementation(&self, member: Symbol, typ: Symbol) -> Option<&MemberImpl> {
|
||||
self.declared_implementations.get(&(member, typ))
|
||||
}
|
||||
|
||||
/// Records a specialization of `ability_member` with specialized type `implementing_type`.
|
||||
/// Entries via this function are considered a source of truth. It must be ensured that a
|
||||
/// specialization is validated before being registered here.
|
||||
pub fn register_specialization_for_type(
|
||||
/// Marks a declared implementation as either properly specializing, or as erroring.
|
||||
pub fn mark_implementation(
|
||||
&mut self,
|
||||
ability_member: Symbol,
|
||||
implementing_type: Symbol,
|
||||
specialization: MemberSpecialization<Resolved>,
|
||||
) {
|
||||
let old_spec = self
|
||||
.declared_specializations
|
||||
.insert((ability_member, implementing_type), specialization);
|
||||
debug_assert!(old_spec.is_none(), "Replacing existing specialization");
|
||||
typ: Symbol,
|
||||
mark: Result<MemberSpecializationInfo<Resolved>, ()>,
|
||||
) -> Result<(), MarkError> {
|
||||
match self
|
||||
.declared_implementations
|
||||
.get_mut(&(ability_member, typ))
|
||||
{
|
||||
Some(member_impl) => match *member_impl {
|
||||
MemberImpl::Impl(specialization_symbol) => {
|
||||
debug_assert!(!self.specializations.contains_key(&specialization_symbol));
|
||||
|
||||
match mark {
|
||||
Ok(specialization_info) => {
|
||||
self.specializations
|
||||
.insert(specialization_symbol, specialization_info);
|
||||
}
|
||||
Err(()) => {
|
||||
// Mark the member implementation as erroring, so we know to generate a
|
||||
// runtime error function as appropriate.
|
||||
*member_impl = MemberImpl::Error;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
MemberImpl::Derived | MemberImpl::Error => Err(MarkError::ImplIsNotCustom),
|
||||
},
|
||||
None => Err(MarkError::NoDeclaredImpl),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn specialization_info(
|
||||
&self,
|
||||
specialization_symbol: Symbol,
|
||||
) -> Option<&MemberSpecializationInfo<Resolved>> {
|
||||
self.specializations.get(&specialization_symbol)
|
||||
}
|
||||
|
||||
pub fn insert_resolved(&mut self, id: SpecializationId, specialization: Symbol) {
|
||||
|
@ -376,19 +436,17 @@ impl IAbilitiesStore<Resolved> {
|
|||
impl IAbilitiesStore<Pending> {
|
||||
pub fn import_specialization(
|
||||
&mut self,
|
||||
ability_member: Symbol,
|
||||
implementing_type: Symbol,
|
||||
specialization: &MemberSpecialization<impl ResolvePhase>,
|
||||
specialization: &MemberSpecializationInfo<impl ResolvePhase>,
|
||||
) {
|
||||
let MemberSpecialization {
|
||||
let MemberSpecializationInfo {
|
||||
_phase,
|
||||
symbol,
|
||||
specialization_lambda_sets,
|
||||
} = specialization;
|
||||
|
||||
let old_spec = self.declared_specializations.insert(
|
||||
(ability_member, implementing_type),
|
||||
MemberSpecialization {
|
||||
let old_spec = self.specializations.insert(
|
||||
*symbol,
|
||||
MemberSpecializationInfo {
|
||||
_phase: Default::default(),
|
||||
symbol: *symbol,
|
||||
specialization_lambda_sets: specialization_lambda_sets.clone(),
|
||||
|
@ -402,9 +460,10 @@ impl IAbilitiesStore<Pending> {
|
|||
members_of_ability: other_members_of_ability,
|
||||
ability_members: mut other_ability_members,
|
||||
specialization_to_root,
|
||||
declared_specializations,
|
||||
declared_implementations,
|
||||
next_specialization_id,
|
||||
resolved_specializations,
|
||||
specializations,
|
||||
} = other;
|
||||
|
||||
for (ability, members) in other_members_of_ability.into_iter() {
|
||||
|
@ -425,13 +484,18 @@ impl IAbilitiesStore<Pending> {
|
|||
debug_assert!(old_root.is_none() || old_root.unwrap() == member);
|
||||
}
|
||||
|
||||
for ((member, typ), specialization) in declared_specializations.into_iter() {
|
||||
for ((member, typ), impl_) in declared_implementations.into_iter() {
|
||||
let old_impl = self.declared_implementations.insert((member, typ), impl_);
|
||||
debug_assert!(old_impl.is_none() || old_impl.unwrap() == impl_);
|
||||
}
|
||||
|
||||
for (symbol, specialization_info) in specializations.into_iter() {
|
||||
let old_specialization = self
|
||||
.declared_specializations
|
||||
.insert((member, typ), specialization.clone());
|
||||
.specializations
|
||||
.insert(symbol, specialization_info.clone());
|
||||
debug_assert!(
|
||||
old_specialization.is_none()
|
||||
|| old_specialization.unwrap().symbol == specialization.symbol
|
||||
|| old_specialization.unwrap().symbol == specialization_info.symbol
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -456,9 +520,10 @@ impl IAbilitiesStore<Pending> {
|
|||
members_of_ability,
|
||||
ability_members,
|
||||
specialization_to_root,
|
||||
declared_specializations,
|
||||
declared_implementations,
|
||||
next_specialization_id,
|
||||
resolved_specializations,
|
||||
specializations,
|
||||
} = self;
|
||||
|
||||
let ability_members = ability_members
|
||||
|
@ -491,24 +556,22 @@ impl IAbilitiesStore<Pending> {
|
|||
})
|
||||
.collect();
|
||||
|
||||
let declared_specializations = declared_specializations
|
||||
let specializations = specializations
|
||||
.into_iter()
|
||||
.map(
|
||||
|(
|
||||
key,
|
||||
MemberSpecialization {
|
||||
|(symbol, specialization)| {
|
||||
let MemberSpecializationInfo {
|
||||
_phase,
|
||||
symbol,
|
||||
symbol: _,
|
||||
specialization_lambda_sets,
|
||||
},
|
||||
)| {
|
||||
} = specialization;
|
||||
let symbol_module = symbol.module_id();
|
||||
|
||||
// NOTE: this totally assumes we're dealing with subs that belong to an
|
||||
// individual module, things would be badly broken otherwise
|
||||
let member_specialization = if symbol_module == my_module {
|
||||
internal_error!("Ability store may only be pending before module solving, \
|
||||
so there shouldn't be any known module specializations at this point, but we found one for {:?}", symbol);
|
||||
// MemberSpecialization::new(symbol, specialization_lambda_sets)
|
||||
} else {
|
||||
let specialization_lambda_sets = specialization_lambda_sets
|
||||
.into_iter()
|
||||
|
@ -523,10 +586,11 @@ impl IAbilitiesStore<Pending> {
|
|||
)
|
||||
})
|
||||
.collect();
|
||||
MemberSpecialization::new(symbol, specialization_lambda_sets)
|
||||
|
||||
MemberSpecializationInfo::new(symbol, specialization_lambda_sets)
|
||||
};
|
||||
(key, member_specialization)
|
||||
},
|
||||
(symbol, member_specialization)
|
||||
}
|
||||
)
|
||||
.collect();
|
||||
|
||||
|
@ -534,9 +598,10 @@ impl IAbilitiesStore<Pending> {
|
|||
members_of_ability,
|
||||
ability_members,
|
||||
specialization_to_root,
|
||||
declared_specializations,
|
||||
declared_implementations,
|
||||
next_specialization_id,
|
||||
resolved_specializations,
|
||||
specializations,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ use roc_types::types::AliasCommon;
|
|||
use roc_types::types::AliasKind;
|
||||
use roc_types::types::AliasVar;
|
||||
use roc_types::types::LambdaSet;
|
||||
use roc_types::types::OpaqueSupports;
|
||||
use roc_types::types::MemberImpl;
|
||||
use roc_types::types::OptAbleType;
|
||||
use roc_types::types::{Alias, Type};
|
||||
use std::fmt::Debug;
|
||||
|
@ -616,7 +616,6 @@ fn canonicalize_opaque<'a>(
|
|||
let has_abilities = has_abilities.value.collection();
|
||||
|
||||
let mut derived_abilities = vec![];
|
||||
let mut supported_abilities = vec![];
|
||||
|
||||
for has_ability in has_abilities.items {
|
||||
let region = has_ability.region;
|
||||
|
@ -661,7 +660,7 @@ fn canonicalize_opaque<'a>(
|
|||
};
|
||||
|
||||
if let Some(impls) = opt_impls {
|
||||
let mut impl_map: VecMap<Symbol, Loc<Symbol>> = VecMap::default();
|
||||
let mut impl_map: VecMap<Symbol, Loc<MemberImpl>> = VecMap::default();
|
||||
|
||||
// First up canonicalize all the claimed implementations, building a map of ability
|
||||
// member -> implementation.
|
||||
|
@ -672,19 +671,16 @@ fn canonicalize_opaque<'a>(
|
|||
Err(()) => continue,
|
||||
};
|
||||
|
||||
match impl_map.insert(member, Loc::at(loc_impl.region, impl_symbol)) {
|
||||
None => {
|
||||
// TODO: get rid of register_specializing_symbol
|
||||
scope
|
||||
.abilities_store
|
||||
.register_specializing_symbol(impl_symbol, member);
|
||||
}
|
||||
Some(old_impl_symbol) => {
|
||||
env.problem(Problem::DuplicateImpl {
|
||||
original: old_impl_symbol.region,
|
||||
duplicate: loc_impl.region,
|
||||
});
|
||||
}
|
||||
let member_impl = MemberImpl::Impl(impl_symbol);
|
||||
|
||||
let opt_old_impl_symbol =
|
||||
impl_map.insert(member, Loc::at(loc_impl.region, member_impl));
|
||||
|
||||
if let Some(old_impl_symbol) = opt_old_impl_symbol {
|
||||
env.problem(Problem::DuplicateImpl {
|
||||
original: old_impl_symbol.region,
|
||||
duplicate: loc_impl.region,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -699,55 +695,56 @@ fn canonicalize_opaque<'a>(
|
|||
);
|
||||
|
||||
if !not_required.is_empty() {
|
||||
// Implementing something that's not required is a recoverable error, we don't
|
||||
// need to skip association of the implemented abilities. Just remove the
|
||||
// unneeded members.
|
||||
for sym in not_required.iter() {
|
||||
impl_map.remove(sym);
|
||||
}
|
||||
|
||||
env.problem(Problem::ImplementsNonRequired {
|
||||
region,
|
||||
ability,
|
||||
not_required,
|
||||
});
|
||||
// Implementing something that's not required is a recoverable error, we don't
|
||||
// need to skip association of the implemented abilities.
|
||||
}
|
||||
|
||||
if !not_implemented.is_empty() {
|
||||
// We'll generate runtime errors for the members that are needed but
|
||||
// unspecified.
|
||||
for sym in not_implemented.iter() {
|
||||
impl_map.insert(*sym, Loc::at_zero(MemberImpl::Error));
|
||||
}
|
||||
|
||||
env.problem(Problem::DoesNotImplementAbility {
|
||||
region,
|
||||
ability,
|
||||
not_implemented,
|
||||
});
|
||||
// However not implementing something that is required is not recoverable for
|
||||
// an ability, so skip association.
|
||||
// TODO: can we "partially" associate members of an ability and generate
|
||||
// RuntimeErrors for unimplemented members?
|
||||
// TODO: can we derive implementations of unimplemented members for builtin
|
||||
// abilities?
|
||||
continue;
|
||||
}
|
||||
|
||||
supported_abilities.push(OpaqueSupports::Implemented {
|
||||
ability_name: ability,
|
||||
impls: impl_map
|
||||
.into_iter()
|
||||
.map(|(member, def)| (member, def.value))
|
||||
.collect(),
|
||||
});
|
||||
} else if ability.is_derivable_ability() {
|
||||
derived_abilities.push(Loc::at(region, ability));
|
||||
supported_abilities.push(OpaqueSupports::Derived(ability));
|
||||
let impls = impl_map
|
||||
.into_iter()
|
||||
.map(|(member, def)| (member, def.value));
|
||||
|
||||
scope
|
||||
.abilities_store
|
||||
.register_declared_implementations(name.value, impls);
|
||||
} else if let Some((_, members)) = ability.derivable_ability() {
|
||||
let impls = members.iter().map(|member| (*member, MemberImpl::Derived));
|
||||
scope
|
||||
.abilities_store
|
||||
.register_declared_implementations(name.value, impls);
|
||||
|
||||
derived_abilities.push(Loc::at(ability_region, ability));
|
||||
} else {
|
||||
// There was no record specified of functions to use for
|
||||
// members, but also this isn't a builtin ability, so we don't
|
||||
// know how to auto-derive it.
|
||||
//
|
||||
// Register the problem but keep going, we may still be able to compile the
|
||||
// program even if a derive is missing.
|
||||
env.problem(Problem::IllegalDerivedAbility(region));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: properly validate all supported_abilities
|
||||
if !derived_abilities.is_empty() {
|
||||
// Fresh instance of this opaque to be checked for derivability during solving.
|
||||
let fresh_inst = Type::DelayedAlias(AliasCommon {
|
||||
|
@ -767,6 +764,7 @@ fn canonicalize_opaque<'a>(
|
|||
let old = output
|
||||
.pending_derives
|
||||
.insert(name.value, (fresh_inst, derived_abilities));
|
||||
|
||||
debug_assert!(old.is_none());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::abilities::{PendingAbilitiesStore, ResolvedSpecializations};
|
||||
use crate::abilities::{MemberSpecializationInfo, PendingAbilitiesStore, Resolved};
|
||||
use crate::annotation::canonicalize_annotation;
|
||||
use crate::def::{canonicalize_defs, Def};
|
||||
use crate::effect_module::HostedGeneratedFunctions;
|
||||
|
@ -103,6 +103,8 @@ impl ExposedForModule {
|
|||
}
|
||||
}
|
||||
|
||||
pub type ResolvedSpecializations = VecMap<Symbol, MemberSpecializationInfo<Resolved>>;
|
||||
|
||||
/// The types of all exposed values/functions of a module. This includes ability member
|
||||
/// specializations.
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use roc_module::{ident::Lowercase, symbol::Symbol};
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::subs::Variable;
|
||||
use roc_types::{subs::Variable, types::MemberImpl};
|
||||
|
||||
use crate::{
|
||||
abilities::AbilitiesStore,
|
||||
|
@ -587,8 +587,8 @@ pub fn find_ability_member_and_owning_type_at(
|
|||
abilities_store: &AbilitiesStore,
|
||||
) -> Option<Symbol> {
|
||||
abilities_store
|
||||
.iter_specializations()
|
||||
.find(|(_, ms)| ms.symbol == symbol)
|
||||
.iter_declared_implementations()
|
||||
.find(|(_, member_impl)| matches!(member_impl, MemberImpl::Impl(sym) if *sym == symbol))
|
||||
.map(|(spec, _)| spec.1)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4166,7 +4166,7 @@ fn run_solve_solve(
|
|||
|
||||
// Figure out what specializations belong to this module
|
||||
let solved_specializations: ResolvedSpecializations = abilities_store
|
||||
.iter_specializations()
|
||||
.iter_declared_implementations()
|
||||
.filter(|((member, typ), _)| {
|
||||
// This module solved this specialization if either the member or the type comes from the
|
||||
// module.
|
||||
|
|
|
@ -394,7 +394,7 @@ impl ObligationCache<'_> {
|
|||
for &member in members_of_ability {
|
||||
if self
|
||||
.abilities_store
|
||||
.get_specialization(member, opaque)
|
||||
.get_implementation(member, opaque)
|
||||
.is_none()
|
||||
{
|
||||
let root_data = self.abilities_store.member_def(member).unwrap();
|
||||
|
@ -671,9 +671,15 @@ pub fn resolve_ability_specialization(
|
|||
|
||||
let resolved = match obligated {
|
||||
Obligated::Opaque(symbol) => {
|
||||
let specialization = abilities_store.get_specialization(ability_member, symbol)?;
|
||||
|
||||
Resolved::Specialization(specialization.symbol)
|
||||
match abilities_store.get_implementation(ability_member, symbol)? {
|
||||
roc_types::types::MemberImpl::Impl(spec_symbol) => {
|
||||
Resolved::Specialization(*spec_symbol)
|
||||
}
|
||||
roc_types::types::MemberImpl::Derived => Resolved::NeedsGenerated,
|
||||
// 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(_) => {
|
||||
// TODO: more rules need to be validated here, like is this a builtin ability?
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use crate::solve::{self, Aliases};
|
||||
use roc_can::abilities::{AbilitiesStore, ResolvedSpecializations};
|
||||
use roc_can::abilities::AbilitiesStore;
|
||||
use roc_can::constraint::{Constraint as ConstraintSoa, Constraints};
|
||||
use roc_can::expr::PendingDerives;
|
||||
use roc_can::module::{ExposedByModule, RigidVariables};
|
||||
use roc_can::module::{ExposedByModule, ResolvedSpecializations, RigidVariables};
|
||||
use roc_collections::all::MutMap;
|
||||
use roc_collections::VecMap;
|
||||
use roc_derive::SharedDerivedModule;
|
||||
|
|
|
@ -4,7 +4,7 @@ use crate::ability::{
|
|||
};
|
||||
use crate::module::Solved;
|
||||
use bumpalo::Bump;
|
||||
use roc_can::abilities::{AbilitiesStore, MemberSpecialization};
|
||||
use roc_can::abilities::{AbilitiesStore, MemberSpecializationInfo};
|
||||
use roc_can::constraint::Constraint::{self, *};
|
||||
use roc_can::constraint::{Constraints, Cycle, LetConstraint, OpportunisticResolve};
|
||||
use roc_can::expected::{Expected, PExpected};
|
||||
|
@ -16,7 +16,7 @@ use roc_debug_flags::dbg_do;
|
|||
use roc_debug_flags::{ROC_TRACE_COMPACTION, ROC_VERIFY_RIGID_LET_GENERALIZED};
|
||||
use roc_derive::SharedDerivedModule;
|
||||
use roc_derive_key::{DeriveError, DeriveKey};
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_error_macros::{internal_error, todo_abilities};
|
||||
use roc_module::ident::TagName;
|
||||
use roc_module::symbol::{ModuleId, Symbol};
|
||||
use roc_problem::can::CycleEntry;
|
||||
|
@ -28,8 +28,8 @@ use roc_types::subs::{
|
|||
};
|
||||
use roc_types::types::Type::{self, *};
|
||||
use roc_types::types::{
|
||||
gather_fields_unsorted_iter, AliasCommon, AliasKind, Category, ErrorType, OptAbleType,
|
||||
OptAbleVar, PatternCategory, Reason, TypeExtension, Uls,
|
||||
gather_fields_unsorted_iter, AliasCommon, AliasKind, Category, ErrorType, MemberImpl,
|
||||
OptAbleType, OptAbleVar, PatternCategory, Reason, TypeExtension, Uls,
|
||||
};
|
||||
use roc_unify::unify::{
|
||||
unify, unify_introduced_ability_specialization, Mode, MustImplementConstraints, Obligated,
|
||||
|
@ -1755,12 +1755,11 @@ fn check_ability_specialization(
|
|||
|
||||
let specialization_region = symbol_loc_var.region;
|
||||
let specialization =
|
||||
MemberSpecialization::new(symbol, specialization_lambda_sets);
|
||||
abilities_store.register_specialization_for_type(
|
||||
ability_member,
|
||||
opaque,
|
||||
specialization,
|
||||
);
|
||||
MemberSpecializationInfo::new(symbol, specialization_lambda_sets);
|
||||
|
||||
abilities_store
|
||||
.mark_implementation(ability_member, opaque, Ok(specialization))
|
||||
.expect("marked as a custom implementation, but not recorded as such");
|
||||
|
||||
// Make sure we check that the opaque has specialized all members of the
|
||||
// ability, after we finish solving the module.
|
||||
|
@ -2301,7 +2300,7 @@ fn get_specialization_lambda_set_ambient_function<P: Phase>(
|
|||
let external_specialized_lset =
|
||||
phase.with_module_abilities_store(opaque_home, |abilities_store| {
|
||||
let opt_specialization =
|
||||
abilities_store.get_specialization(ability_member, opaque);
|
||||
abilities_store.get_implementation(ability_member, opaque);
|
||||
match (P::IS_LATE, opt_specialization) {
|
||||
(false, None) => {
|
||||
// doesn't specialize, we'll have reported an error for this
|
||||
|
@ -2314,13 +2313,20 @@ fn get_specialization_lambda_set_ambient_function<P: Phase>(
|
|||
ability_member,
|
||||
);
|
||||
}
|
||||
(_, Some(specialization)) => {
|
||||
let specialized_lambda_set = *specialization
|
||||
.specialization_lambda_sets
|
||||
.get(&lset_region)
|
||||
.expect("lambda set region not resolved");
|
||||
Ok(specialized_lambda_set)
|
||||
}
|
||||
(_, Some(member_impl)) => match member_impl {
|
||||
MemberImpl::Impl(spec_symbol) => {
|
||||
let specialization =
|
||||
abilities_store.specialization_info(*spec_symbol).expect("expected custom implementations to always have complete specialization info by this point");
|
||||
|
||||
let specialized_lambda_set = *specialization
|
||||
.specialization_lambda_sets
|
||||
.get(&lset_region)
|
||||
.expect("lambda set region not resolved");
|
||||
Ok(specialized_lambda_set)
|
||||
}
|
||||
MemberImpl::Derived => todo_abilities!(),
|
||||
MemberImpl::Error => todo_abilities!(),
|
||||
},
|
||||
}
|
||||
})?;
|
||||
|
||||
|
|
|
@ -361,7 +361,7 @@ mod solve_expr {
|
|||
if !type_problems.is_empty() {
|
||||
eprintln!("{:?}", type_problems);
|
||||
panic!();
|
||||
}
|
||||
}iter_declared_impls
|
||||
|
||||
let known_specializations = abilities_store.iter_specializations();
|
||||
use std::collections::HashSet;
|
||||
|
|
|
@ -4,7 +4,6 @@ use crate::subs::{
|
|||
GetSubsSlice, RecordFields, Subs, UnionTags, VarStore, Variable, VariableSubsSlice,
|
||||
};
|
||||
use roc_collections::all::{HumanIndex, ImMap, ImSet, MutMap, MutSet, SendMap};
|
||||
use roc_collections::VecMap;
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_module::called_via::CalledVia;
|
||||
use roc_module::ident::{ForeignSymbol, Ident, Lowercase, TagName};
|
||||
|
@ -2056,13 +2055,15 @@ impl From<&AliasVar> for OptAbleVar {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum OpaqueSupports {
|
||||
Derived(Symbol),
|
||||
Implemented {
|
||||
ability_name: Symbol,
|
||||
impls: VecMap<Symbol, Symbol>,
|
||||
},
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum MemberImpl {
|
||||
/// The implementation is claimed to be at the given symbol.
|
||||
/// During solving we validate that the impl is really there.
|
||||
Impl(Symbol),
|
||||
/// The implementation should be derived.
|
||||
Derived,
|
||||
/// The implementation is not present or does not match the expected member type.
|
||||
Error,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue