mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-03 03:42:17 +00:00
Merge pull request #3589 from rtfeldman/can-abilities3
Canonicalize syntactic abilities: Part 3 - solving
This commit is contained in:
commit
6b6f240acb
21 changed files with 594 additions and 274 deletions
|
@ -4,7 +4,10 @@ use roc_collections::{all::MutMap, VecMap, VecSet};
|
||||||
use roc_error_macros::internal_error;
|
use roc_error_macros::internal_error;
|
||||||
use roc_module::symbol::{ModuleId, Symbol};
|
use roc_module::symbol::{ModuleId, Symbol};
|
||||||
use roc_region::all::Region;
|
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)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct MemberVariables {
|
pub struct MemberVariables {
|
||||||
|
@ -68,10 +71,7 @@ impl AbilityMemberData<Resolved> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// (member, specialization type) -> specialization
|
/// (member, specialization type) -> specialization
|
||||||
pub type SpecializationsMap<Phase> = VecMap<(Symbol, Symbol), MemberSpecialization<Phase>>;
|
pub type ImplMap = VecMap<(Symbol, Symbol), MemberImpl>;
|
||||||
|
|
||||||
pub type PendingSpecializations = SpecializationsMap<Pending>;
|
|
||||||
pub type ResolvedSpecializations = SpecializationsMap<Resolved>;
|
|
||||||
|
|
||||||
/// Solved lambda sets for an ability member specialization. For example, if we have
|
/// 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.
|
/// A particular specialization of an ability member.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct MemberSpecialization<Phase: ResolvePhase> {
|
pub struct MemberSpecializationInfo<Phase: ResolvePhase> {
|
||||||
_phase: std::marker::PhantomData<Phase>,
|
_phase: std::marker::PhantomData<Phase>,
|
||||||
|
|
||||||
pub symbol: Symbol,
|
pub symbol: Symbol,
|
||||||
|
|
||||||
pub specialization_lambda_sets: SpecializationLambdaSets,
|
pub specialization_lambda_sets: SpecializationLambdaSets,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MemberSpecialization<Resolved> {
|
impl MemberSpecializationInfo<Resolved> {
|
||||||
pub fn new(symbol: Symbol, specialization_lambda_sets: SpecializationLambdaSets) -> Self {
|
pub fn new(symbol: Symbol, specialization_lambda_sets: SpecializationLambdaSets) -> Self {
|
||||||
Self {
|
Self {
|
||||||
_phase: Default::default(),
|
_phase: Default::default(),
|
||||||
|
@ -111,6 +109,22 @@ static_assertions::assert_eq_size!(SpecializationId, Option<SpecializationId>);
|
||||||
|
|
||||||
pub enum SpecializationLambdaSetError {}
|
pub enum SpecializationLambdaSetError {}
|
||||||
|
|
||||||
|
/// A key into a particular implementation of an ability member for an opaque type.
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||||
|
pub struct ImplKey {
|
||||||
|
pub opaque: Symbol,
|
||||||
|
pub ability_member: Symbol,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fully-resolved implementation of an ability member for an opaque type.
|
||||||
|
/// This is only fully known after type solving of the owning module.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum ResolvedImpl {
|
||||||
|
Impl(MemberSpecializationInfo<Resolved>),
|
||||||
|
Derived,
|
||||||
|
Error,
|
||||||
|
}
|
||||||
|
|
||||||
/// Stores information about what abilities exist in a scope, what it means to implement an
|
/// Stores information about what abilities exist in a scope, what it means to implement an
|
||||||
/// ability, and what types implement them.
|
/// ability, and what types implement them.
|
||||||
// TODO(abilities): this should probably go on the Scope, I don't put it there for now because we
|
// TODO(abilities): this should probably go on the Scope, I don't put it there for now because we
|
||||||
|
@ -120,23 +134,28 @@ pub enum SpecializationLambdaSetError {}
|
||||||
pub struct IAbilitiesStore<Phase: ResolvePhase> {
|
pub struct IAbilitiesStore<Phase: ResolvePhase> {
|
||||||
/// Maps an ability to the members defining it.
|
/// Maps an ability to the members defining it.
|
||||||
members_of_ability: MutMap<Symbol, Vec<Symbol>>,
|
members_of_ability: MutMap<Symbol, Vec<Symbol>>,
|
||||||
|
/// Map of symbols that specialize an ability member to the root ability symbol name,
|
||||||
|
/// and the type the specialization claims to implement the ability for.
|
||||||
|
///
|
||||||
|
/// 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, Id)
|
||||||
|
specialization_to_root: MutMap<Symbol, ImplKey>,
|
||||||
|
|
||||||
/// Information about all members composing abilities.
|
/// Information about all members composing abilities.
|
||||||
ability_members: MutMap<Symbol, AbilityMemberData<Phase>>,
|
ability_members: MutMap<Symbol, AbilityMemberData<Phase>>,
|
||||||
|
|
||||||
/// Map of symbols that specialize an ability member to the root ability symbol name.
|
/// Maps a tuple (member, type) specifying that `type` has an implementation of an ability
|
||||||
/// For example, for the program
|
/// member `member`, to how that implementation is defined.
|
||||||
/// Hash has hash : a -> U64 | a has Hash
|
declared_implementations: ImplMap,
|
||||||
/// ^^^^ 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` declares an implementation of an ability
|
/// Information about specialized ability member implementations for a type.
|
||||||
/// member `member`, to the exact symbol that implements the ability.
|
specializations: MutMap<Symbol, MemberSpecializationInfo<Phase>>,
|
||||||
declared_specializations: SpecializationsMap<Phase>,
|
|
||||||
|
|
||||||
next_specialization_id: NonZeroU32,
|
next_specialization_id: NonZeroU32,
|
||||||
|
|
||||||
|
@ -148,14 +167,15 @@ pub struct IAbilitiesStore<Phase: ResolvePhase> {
|
||||||
impl<Phase: ResolvePhase> Default for IAbilitiesStore<Phase> {
|
impl<Phase: ResolvePhase> Default for IAbilitiesStore<Phase> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
members_of_ability: Default::default(),
|
members_of_ability: Default::default(),
|
||||||
ability_members: Default::default(),
|
specialization_to_root: Default::default(),
|
||||||
specialization_to_root: Default::default(),
|
ability_members: Default::default(),
|
||||||
declared_specializations: Default::default(),
|
declared_implementations: Default::default(),
|
||||||
next_specialization_id:
|
specializations: Default::default(),
|
||||||
// Safety: 1 != 0
|
next_specialization_id:
|
||||||
unsafe { NonZeroU32::new_unchecked(1) },
|
// Safety: 1 != 0
|
||||||
resolved_specializations: Default::default(),
|
unsafe { NonZeroU32::new_unchecked(1) },
|
||||||
|
resolved_specializations: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -207,22 +227,44 @@ impl<Phase: ResolvePhase> IAbilitiesStore<Phase> {
|
||||||
&self.ability_members
|
&self.ability_members
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn register_one_declared_impl(&mut self, impl_key: ImplKey, member_impl: MemberImpl) {
|
||||||
|
if let MemberImpl::Impl(specialization_symbol) = member_impl {
|
||||||
|
self.specialization_to_root
|
||||||
|
.insert(specialization_symbol, impl_key);
|
||||||
|
}
|
||||||
|
self.declared_implementations
|
||||||
|
.insert((impl_key.ability_member, impl_key.opaque), 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() {
|
||||||
|
let impl_key = ImplKey {
|
||||||
|
opaque: implementing_type,
|
||||||
|
ability_member: member,
|
||||||
|
};
|
||||||
|
self.register_one_declared_impl(impl_key, member_impl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns whether a symbol is declared to specialize an ability member.
|
/// Returns whether a symbol is declared to specialize an ability member.
|
||||||
pub fn is_specialization_name(&self, symbol: Symbol) -> bool {
|
pub fn is_specialization_name(&self, symbol: Symbol) -> bool {
|
||||||
self.specialization_to_root.contains_key(&symbol)
|
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]> {
|
pub fn members_of_ability(&self, ability: Symbol) -> Option<&[Symbol]> {
|
||||||
self.members_of_ability.get(&ability).map(|v| v.as_ref())
|
self.members_of_ability.get(&ability).map(|v| v.as_ref())
|
||||||
}
|
}
|
||||||
|
@ -237,15 +279,23 @@ impl<Phase: ResolvePhase> IAbilitiesStore<Phase> {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Finds the implementation key for a symbol specializing the ability member, if it specializes any.
|
||||||
|
/// For example, suppose `hashId : Id -> U64` specializes `hash : a -> U64 | a has Hash`.
|
||||||
|
/// Calling this with `hashId` would retrieve (hash, hashId).
|
||||||
|
pub fn impl_key(&self, specializing_symbol: Symbol) -> Option<&ImplKey> {
|
||||||
|
self.specialization_to_root.get(&specializing_symbol)
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a store from [`self`] that closes over the abilities/members given by the
|
/// Creates a store from [`self`] that closes over the abilities/members given by the
|
||||||
/// imported `symbols`, and their specializations (if any).
|
/// imported `symbols`, and their specializations (if any).
|
||||||
pub fn closure_from_imported(&self, symbols: &VecSet<Symbol>) -> PendingAbilitiesStore {
|
pub fn closure_from_imported(&self, symbols: &VecSet<Symbol>) -> PendingAbilitiesStore {
|
||||||
let Self {
|
let Self {
|
||||||
members_of_ability,
|
members_of_ability,
|
||||||
ability_members,
|
ability_members,
|
||||||
declared_specializations,
|
declared_implementations,
|
||||||
|
specializations,
|
||||||
|
|
||||||
// Covered by `declared_specializations`
|
// Covered by `declared_implementations`
|
||||||
specialization_to_root: _,
|
specialization_to_root: _,
|
||||||
|
|
||||||
// Taking closure for a new module, so specialization IDs can be fresh
|
// Taking closure for a new module, so specialization IDs can be fresh
|
||||||
|
@ -292,12 +342,21 @@ impl<Phase: ResolvePhase> IAbilitiesStore<Phase> {
|
||||||
new.register_ability(ability, imported_member_data);
|
new.register_ability(ability, imported_member_data);
|
||||||
|
|
||||||
// Add any specializations of the ability's members we know about.
|
// Add any specializations of the ability's members we know about.
|
||||||
declared_specializations
|
declared_implementations
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|((member, _), _)| members.contains(member))
|
.filter(|((member, _), _)| members.contains(member))
|
||||||
.for_each(|(&(member, typ), specialization)| {
|
.for_each(|(&(member, typ), member_impl)| {
|
||||||
new.register_specializing_symbol(specialization.symbol, member);
|
let impl_key = ImplKey {
|
||||||
new.import_specialization(member, typ, specialization);
|
ability_member: member,
|
||||||
|
opaque: typ,
|
||||||
|
};
|
||||||
|
new.register_one_declared_impl(impl_key, *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,20 +364,29 @@ impl<Phase: ResolvePhase> IAbilitiesStore<Phase> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum MarkError {
|
||||||
|
NoDeclaredImpl,
|
||||||
|
ImplIsNotCustom,
|
||||||
|
}
|
||||||
|
|
||||||
impl IAbilitiesStore<Resolved> {
|
impl IAbilitiesStore<Resolved> {
|
||||||
/// Finds the symbol name and ability member definition for a symbol specializing the ability
|
/// Finds the symbol name and ability member definition for a symbol specializing the ability
|
||||||
/// member, if it specializes any.
|
/// member, if it specializes any.
|
||||||
/// For example, suppose `hash : Id -> U64` has symbol #hash1 and specializes
|
/// For example, suppose `hashId : Id -> U64` specializes `hash : a -> U64 | a has Hash`.
|
||||||
/// `hash : a -> U64 | a has Hash` with symbol #hash. Calling this with #hash1 would retrieve
|
/// Calling this with `hashId` would retrieve the ability member data for `hash`, and what type
|
||||||
/// the ability member data for #hash.
|
/// `hashId` is specializing for.
|
||||||
pub fn root_name_and_def(
|
pub fn impl_key_and_def(
|
||||||
&self,
|
&self,
|
||||||
specializing_symbol: Symbol,
|
specializing_symbol: Symbol,
|
||||||
) -> Option<(Symbol, &AbilityMemberData<Resolved>)> {
|
) -> Option<(ImplKey, &AbilityMemberData<Resolved>)> {
|
||||||
let root_symbol = self.specialization_to_root.get(&specializing_symbol)?;
|
let impl_key = self.impl_key(specializing_symbol)?;
|
||||||
debug_assert!(self.ability_members.contains_key(root_symbol));
|
debug_assert!(self.ability_members.contains_key(&impl_key.ability_member));
|
||||||
let root_data = self.ability_members.get(root_symbol).unwrap();
|
let root_data = self
|
||||||
Some((*root_symbol, root_data))
|
.ability_members
|
||||||
|
.get(&impl_key.ability_member)
|
||||||
|
.expect("impl keys can only exist for known ability members");
|
||||||
|
Some((*impl_key, root_data))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finds the ability member definition for a member name.
|
/// Finds the ability member definition for a member name.
|
||||||
|
@ -326,36 +394,59 @@ impl IAbilitiesStore<Resolved> {
|
||||||
self.ability_members.get(&member)
|
self.ability_members.get(&member)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator over pairs ((ability member, type), specialization) specifying that
|
/// Returns an iterator over pairs ((ability member, type), implementation) specifying that
|
||||||
/// "ability member" has a "specialization" for type "type".
|
/// the give type has an implementation of an ability member.
|
||||||
pub fn iter_specializations(
|
pub fn iter_declared_implementations(
|
||||||
&self,
|
&self,
|
||||||
) -> impl Iterator<Item = ((Symbol, Symbol), &MemberSpecialization<Resolved>)> + '_ {
|
) -> impl Iterator<Item = ((Symbol, Symbol), &MemberImpl)> + '_ {
|
||||||
self.declared_specializations.iter().map(|(k, v)| (*k, v))
|
self.declared_implementations.iter().map(|(k, v)| (*k, v))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves the specialization of `member` for `typ`, if it exists.
|
/// Retrieves the declared implementation of `member` for `typ`, if it exists.
|
||||||
pub fn get_specialization(
|
pub fn get_implementation(&self, member: Symbol, typ: Symbol) -> Option<&MemberImpl> {
|
||||||
&self,
|
self.declared_implementations.get(&(member, typ))
|
||||||
member: Symbol,
|
|
||||||
typ: Symbol,
|
|
||||||
) -> Option<&MemberSpecialization<Resolved>> {
|
|
||||||
self.declared_specializations.get(&(member, typ))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Records a specialization of `ability_member` with specialized type `implementing_type`.
|
/// Marks a declared implementation as either properly specializing, or as erroring.
|
||||||
/// Entries via this function are considered a source of truth. It must be ensured that a
|
pub fn mark_implementation(
|
||||||
/// specialization is validated before being registered here.
|
|
||||||
pub fn register_specialization_for_type(
|
|
||||||
&mut self,
|
&mut self,
|
||||||
ability_member: Symbol,
|
ability_member: Symbol,
|
||||||
implementing_type: Symbol,
|
typ: Symbol,
|
||||||
specialization: MemberSpecialization<Resolved>,
|
mark: Result<MemberSpecializationInfo<Resolved>, ()>,
|
||||||
) {
|
) -> Result<(), MarkError> {
|
||||||
let old_spec = self
|
match self
|
||||||
.declared_specializations
|
.declared_implementations
|
||||||
.insert((ability_member, implementing_type), specialization);
|
.get_mut(&(ability_member, typ))
|
||||||
debug_assert!(old_spec.is_none(), "Replacing existing specialization");
|
{
|
||||||
|
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) {
|
pub fn insert_resolved(&mut self, id: SpecializationId, specialization: Symbol) {
|
||||||
|
@ -374,21 +465,43 @@ impl IAbilitiesStore<Resolved> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IAbilitiesStore<Pending> {
|
impl IAbilitiesStore<Pending> {
|
||||||
pub fn import_specialization(
|
pub fn import_implementation(&mut self, impl_key: ImplKey, resolved_impl: &ResolvedImpl) {
|
||||||
|
let ImplKey {
|
||||||
|
opaque,
|
||||||
|
ability_member,
|
||||||
|
} = impl_key;
|
||||||
|
|
||||||
|
let member_impl = match resolved_impl {
|
||||||
|
ResolvedImpl::Impl(specialization) => {
|
||||||
|
self.import_specialization(specialization);
|
||||||
|
MemberImpl::Impl(specialization.symbol)
|
||||||
|
}
|
||||||
|
ResolvedImpl::Derived => MemberImpl::Derived,
|
||||||
|
ResolvedImpl::Error => MemberImpl::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
let old_declared_impl = self
|
||||||
|
.declared_implementations
|
||||||
|
.insert((ability_member, opaque), member_impl);
|
||||||
|
debug_assert!(
|
||||||
|
old_declared_impl.is_none(),
|
||||||
|
"Replacing existing declared impl!"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn import_specialization(
|
||||||
&mut self,
|
&mut self,
|
||||||
ability_member: Symbol,
|
specialization: &MemberSpecializationInfo<impl ResolvePhase>,
|
||||||
implementing_type: Symbol,
|
|
||||||
specialization: &MemberSpecialization<impl ResolvePhase>,
|
|
||||||
) {
|
) {
|
||||||
let MemberSpecialization {
|
let MemberSpecializationInfo {
|
||||||
_phase,
|
_phase,
|
||||||
symbol,
|
symbol,
|
||||||
specialization_lambda_sets,
|
specialization_lambda_sets,
|
||||||
} = specialization;
|
} = specialization;
|
||||||
|
|
||||||
let old_spec = self.declared_specializations.insert(
|
let old_spec = self.specializations.insert(
|
||||||
(ability_member, implementing_type),
|
*symbol,
|
||||||
MemberSpecialization {
|
MemberSpecializationInfo {
|
||||||
_phase: Default::default(),
|
_phase: Default::default(),
|
||||||
symbol: *symbol,
|
symbol: *symbol,
|
||||||
specialization_lambda_sets: specialization_lambda_sets.clone(),
|
specialization_lambda_sets: specialization_lambda_sets.clone(),
|
||||||
|
@ -402,9 +515,10 @@ impl IAbilitiesStore<Pending> {
|
||||||
members_of_ability: other_members_of_ability,
|
members_of_ability: other_members_of_ability,
|
||||||
ability_members: mut other_ability_members,
|
ability_members: mut other_ability_members,
|
||||||
specialization_to_root,
|
specialization_to_root,
|
||||||
declared_specializations,
|
declared_implementations,
|
||||||
next_specialization_id,
|
next_specialization_id,
|
||||||
resolved_specializations,
|
resolved_specializations,
|
||||||
|
specializations,
|
||||||
} = other;
|
} = other;
|
||||||
|
|
||||||
for (ability, members) in other_members_of_ability.into_iter() {
|
for (ability, members) in other_members_of_ability.into_iter() {
|
||||||
|
@ -425,13 +539,18 @@ impl IAbilitiesStore<Pending> {
|
||||||
debug_assert!(old_root.is_none() || old_root.unwrap() == member);
|
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
|
let old_specialization = self
|
||||||
.declared_specializations
|
.specializations
|
||||||
.insert((member, typ), specialization.clone());
|
.insert(symbol, specialization_info.clone());
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
old_specialization.is_none()
|
old_specialization.is_none()
|
||||||
|| old_specialization.unwrap().symbol == specialization.symbol
|
|| old_specialization.unwrap().symbol == specialization_info.symbol
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -456,9 +575,10 @@ impl IAbilitiesStore<Pending> {
|
||||||
members_of_ability,
|
members_of_ability,
|
||||||
ability_members,
|
ability_members,
|
||||||
specialization_to_root,
|
specialization_to_root,
|
||||||
declared_specializations,
|
declared_implementations,
|
||||||
next_specialization_id,
|
next_specialization_id,
|
||||||
resolved_specializations,
|
resolved_specializations,
|
||||||
|
specializations,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
let ability_members = ability_members
|
let ability_members = ability_members
|
||||||
|
@ -491,24 +611,22 @@ impl IAbilitiesStore<Pending> {
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let declared_specializations = declared_specializations
|
let specializations = specializations
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(
|
.map(
|
||||||
|(
|
|(symbol, specialization)| {
|
||||||
key,
|
let MemberSpecializationInfo {
|
||||||
MemberSpecialization {
|
|
||||||
_phase,
|
_phase,
|
||||||
symbol,
|
symbol: _,
|
||||||
specialization_lambda_sets,
|
specialization_lambda_sets,
|
||||||
},
|
} = specialization;
|
||||||
)| {
|
|
||||||
let symbol_module = symbol.module_id();
|
let symbol_module = symbol.module_id();
|
||||||
|
|
||||||
// NOTE: this totally assumes we're dealing with subs that belong to an
|
// NOTE: this totally assumes we're dealing with subs that belong to an
|
||||||
// individual module, things would be badly broken otherwise
|
// individual module, things would be badly broken otherwise
|
||||||
let member_specialization = if symbol_module == my_module {
|
let member_specialization = if symbol_module == my_module {
|
||||||
internal_error!("Ability store may only be pending before module solving, \
|
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);
|
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 {
|
} else {
|
||||||
let specialization_lambda_sets = specialization_lambda_sets
|
let specialization_lambda_sets = specialization_lambda_sets
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -523,10 +641,11 @@ impl IAbilitiesStore<Pending> {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
MemberSpecialization::new(symbol, specialization_lambda_sets)
|
|
||||||
|
MemberSpecializationInfo::new(symbol, specialization_lambda_sets)
|
||||||
};
|
};
|
||||||
(key, member_specialization)
|
(symbol, member_specialization)
|
||||||
},
|
}
|
||||||
)
|
)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
@ -534,9 +653,10 @@ impl IAbilitiesStore<Pending> {
|
||||||
members_of_ability,
|
members_of_ability,
|
||||||
ability_members,
|
ability_members,
|
||||||
specialization_to_root,
|
specialization_to_root,
|
||||||
declared_specializations,
|
declared_implementations,
|
||||||
next_specialization_id,
|
next_specialization_id,
|
||||||
resolved_specializations,
|
resolved_specializations,
|
||||||
|
specializations,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::abilities::AbilityMemberData;
|
use crate::abilities::AbilityMemberData;
|
||||||
|
use crate::abilities::ImplKey;
|
||||||
use crate::abilities::MemberVariables;
|
use crate::abilities::MemberVariables;
|
||||||
use crate::abilities::PendingMemberType;
|
use crate::abilities::PendingMemberType;
|
||||||
use crate::annotation::canonicalize_annotation;
|
use crate::annotation::canonicalize_annotation;
|
||||||
|
@ -42,7 +43,7 @@ use roc_types::types::AliasCommon;
|
||||||
use roc_types::types::AliasKind;
|
use roc_types::types::AliasKind;
|
||||||
use roc_types::types::AliasVar;
|
use roc_types::types::AliasVar;
|
||||||
use roc_types::types::LambdaSet;
|
use roc_types::types::LambdaSet;
|
||||||
use roc_types::types::OpaqueSupports;
|
use roc_types::types::MemberImpl;
|
||||||
use roc_types::types::OptAbleType;
|
use roc_types::types::OptAbleType;
|
||||||
use roc_types::types::{Alias, Type};
|
use roc_types::types::{Alias, Type};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
@ -616,7 +617,6 @@ fn canonicalize_opaque<'a>(
|
||||||
let has_abilities = has_abilities.value.collection();
|
let has_abilities = has_abilities.value.collection();
|
||||||
|
|
||||||
let mut derived_abilities = vec![];
|
let mut derived_abilities = vec![];
|
||||||
let mut supported_abilities = vec![];
|
|
||||||
|
|
||||||
for has_ability in has_abilities.items {
|
for has_ability in has_abilities.items {
|
||||||
let region = has_ability.region;
|
let region = has_ability.region;
|
||||||
|
@ -661,7 +661,7 @@ fn canonicalize_opaque<'a>(
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(impls) = opt_impls {
|
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
|
// First up canonicalize all the claimed implementations, building a map of ability
|
||||||
// member -> implementation.
|
// member -> implementation.
|
||||||
|
@ -672,19 +672,38 @@ fn canonicalize_opaque<'a>(
|
||||||
Err(()) => continue,
|
Err(()) => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
match impl_map.insert(member, Loc::at(loc_impl.region, impl_symbol)) {
|
// Did the user claim this implementation for a specialization of a different
|
||||||
None => {
|
// type? e.g.
|
||||||
// TODO: get rid of register_specializing_symbol
|
//
|
||||||
scope
|
// A has [Hash {hash: myHash}]
|
||||||
.abilities_store
|
// B has [Hash {hash: myHash}]
|
||||||
.register_specializing_symbol(impl_symbol, member);
|
//
|
||||||
}
|
// If so, that's an error and we drop the impl for this opaque type.
|
||||||
Some(old_impl_symbol) => {
|
let member_impl = match scope.abilities_store.impl_key(impl_symbol) {
|
||||||
env.problem(Problem::DuplicateImpl {
|
Some(ImplKey {
|
||||||
original: old_impl_symbol.region,
|
opaque,
|
||||||
duplicate: loc_impl.region,
|
ability_member,
|
||||||
|
}) => {
|
||||||
|
env.problem(Problem::OverloadedSpecialization {
|
||||||
|
overload: loc_impl.region,
|
||||||
|
original_opaque: *opaque,
|
||||||
|
ability_member: *ability_member,
|
||||||
});
|
});
|
||||||
|
MemberImpl::Error
|
||||||
}
|
}
|
||||||
|
None => MemberImpl::Impl(impl_symbol),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Did the user already claim an implementation for the ability member for this
|
||||||
|
// type previously? (e.g. Hash {hash: hash1, hash: hash2})
|
||||||
|
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 +718,56 @@ fn canonicalize_opaque<'a>(
|
||||||
);
|
);
|
||||||
|
|
||||||
if !not_required.is_empty() {
|
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() {
|
for sym in not_required.iter() {
|
||||||
impl_map.remove(sym);
|
impl_map.remove(sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
env.problem(Problem::ImplementsNonRequired {
|
env.problem(Problem::ImplementsNonRequired {
|
||||||
region,
|
region,
|
||||||
ability,
|
ability,
|
||||||
not_required,
|
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() {
|
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 {
|
env.problem(Problem::DoesNotImplementAbility {
|
||||||
region,
|
region,
|
||||||
ability,
|
ability,
|
||||||
not_implemented,
|
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 {
|
let impls = impl_map
|
||||||
ability_name: ability,
|
.into_iter()
|
||||||
impls: impl_map
|
.map(|(member, def)| (member, def.value));
|
||||||
.into_iter()
|
|
||||||
.map(|(member, def)| (member, def.value))
|
scope
|
||||||
.collect(),
|
.abilities_store
|
||||||
});
|
.register_declared_implementations(name.value, impls);
|
||||||
} else if ability.is_builtin_ability() {
|
} else if let Some((_, members)) = ability.derivable_ability() {
|
||||||
derived_abilities.push(Loc::at(region, ability));
|
let impls = members.iter().map(|member| (*member, MemberImpl::Derived));
|
||||||
supported_abilities.push(OpaqueSupports::Derived(ability));
|
scope
|
||||||
|
.abilities_store
|
||||||
|
.register_declared_implementations(name.value, impls);
|
||||||
|
|
||||||
|
derived_abilities.push(Loc::at(ability_region, ability));
|
||||||
} else {
|
} else {
|
||||||
// There was no record specified of functions to use for
|
// There was no record specified of functions to use for
|
||||||
// members, but also this isn't a builtin ability, so we don't
|
// members, but also this isn't a builtin ability, so we don't
|
||||||
// know how to auto-derive it.
|
// 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));
|
env.problem(Problem::IllegalDerivedAbility(region));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: properly validate all supported_abilities
|
|
||||||
if !derived_abilities.is_empty() {
|
if !derived_abilities.is_empty() {
|
||||||
// Fresh instance of this opaque to be checked for derivability during solving.
|
// Fresh instance of this opaque to be checked for derivability during solving.
|
||||||
let fresh_inst = Type::DelayedAlias(AliasCommon {
|
let fresh_inst = Type::DelayedAlias(AliasCommon {
|
||||||
|
@ -767,6 +787,7 @@ fn canonicalize_opaque<'a>(
|
||||||
let old = output
|
let old = output
|
||||||
.pending_derives
|
.pending_derives
|
||||||
.insert(name.value, (fresh_inst, derived_abilities));
|
.insert(name.value, (fresh_inst, derived_abilities));
|
||||||
|
|
||||||
debug_assert!(old.is_none());
|
debug_assert!(old.is_none());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1568,6 +1568,13 @@ fn canonicalize_var_lookup(
|
||||||
output.references.insert_value_lookup(symbol);
|
output.references.insert_value_lookup(symbol);
|
||||||
|
|
||||||
if scope.abilities_store.is_ability_member_name(symbol) {
|
if scope.abilities_store.is_ability_member_name(symbol) {
|
||||||
|
// Is there a shadow implementation with the same name? If so, we might be in
|
||||||
|
// the def for that shadow. In that case add a value lookup of the shadow impl,
|
||||||
|
// so that it's marked as possibly-recursive.
|
||||||
|
if let Some(shadow) = scope.get_member_shadow(symbol) {
|
||||||
|
output.references.insert_value_lookup(shadow.value);
|
||||||
|
}
|
||||||
|
|
||||||
AbilityMember(
|
AbilityMember(
|
||||||
symbol,
|
symbol,
|
||||||
Some(scope.abilities_store.fresh_specialization_id()),
|
Some(scope.abilities_store.fresh_specialization_id()),
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::abilities::{PendingAbilitiesStore, ResolvedSpecializations};
|
use crate::abilities::{ImplKey, PendingAbilitiesStore, ResolvedImpl};
|
||||||
use crate::annotation::canonicalize_annotation;
|
use crate::annotation::canonicalize_annotation;
|
||||||
use crate::def::{canonicalize_defs, Def};
|
use crate::def::{canonicalize_defs, Def};
|
||||||
use crate::effect_module::HostedGeneratedFunctions;
|
use crate::effect_module::HostedGeneratedFunctions;
|
||||||
|
@ -103,12 +103,20 @@ impl ExposedForModule {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// During type solving and monomorphization, a module must know how its imported ability
|
||||||
|
/// implementations are resolved - are they derived, or have a concrete implementation?
|
||||||
|
///
|
||||||
|
/// Unfortunately we cannot keep this information opaque, as it's important for properly
|
||||||
|
/// restoring specialization lambda sets. As such, we need to export implementation information,
|
||||||
|
/// which is the job of this structure.
|
||||||
|
pub type ResolvedImplementations = VecMap<ImplKey, ResolvedImpl>;
|
||||||
|
|
||||||
/// The types of all exposed values/functions of a module. This includes ability member
|
/// The types of all exposed values/functions of a module. This includes ability member
|
||||||
/// specializations.
|
/// specializations.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ExposedModuleTypes {
|
pub struct ExposedModuleTypes {
|
||||||
pub exposed_types_storage_subs: ExposedTypesStorageSubs,
|
pub exposed_types_storage_subs: ExposedTypesStorageSubs,
|
||||||
pub resolved_specializations: ResolvedSpecializations,
|
pub resolved_implementations: ResolvedImplementations,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
|
@ -205,6 +205,7 @@ pub fn canonicalize_def_header_pattern<'a>(
|
||||||
}
|
}
|
||||||
// Likely a specialization of an ability.
|
// Likely a specialization of an ability.
|
||||||
Some(ability_member_name) => {
|
Some(ability_member_name) => {
|
||||||
|
output.references.insert_bound(symbol);
|
||||||
output.references.insert_value_lookup(ability_member_name);
|
output.references.insert_value_lookup(ability_member_name);
|
||||||
Pattern::AbilityMemberSpecialization {
|
Pattern::AbilityMemberSpecialization {
|
||||||
ident: symbol,
|
ident: symbol,
|
||||||
|
|
|
@ -348,6 +348,10 @@ impl Scope {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_member_shadow(&self, ability_member: Symbol) -> Option<&Loc<Symbol>> {
|
||||||
|
self.shadows.get(&ability_member)
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new symbol, but don't add it to the scope (yet)
|
/// Create a new symbol, but don't add it to the scope (yet)
|
||||||
///
|
///
|
||||||
/// Used for record guards like { x: Just _ } where the `x` is not added to the scope,
|
/// Used for record guards like { x: Just _ } where the `x` is not added to the scope,
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use roc_module::{ident::Lowercase, symbol::Symbol};
|
use roc_module::{ident::Lowercase, symbol::Symbol};
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
use roc_types::subs::Variable;
|
use roc_types::{subs::Variable, types::MemberImpl};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
abilities::AbilitiesStore,
|
abilities::AbilitiesStore,
|
||||||
|
@ -591,8 +591,8 @@ pub fn find_ability_member_and_owning_type_at(
|
||||||
abilities_store: &AbilitiesStore,
|
abilities_store: &AbilitiesStore,
|
||||||
) -> Option<Symbol> {
|
) -> Option<Symbol> {
|
||||||
abilities_store
|
abilities_store
|
||||||
.iter_specializations()
|
.iter_declared_implementations()
|
||||||
.find(|(_, ms)| ms.symbol == symbol)
|
.find(|(_, member_impl)| matches!(member_impl, MemberImpl::Impl(sym) if *sym == symbol))
|
||||||
.map(|(spec, _)| spec.1)
|
.map(|(spec, _)| spec.1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,12 +5,13 @@ use crossbeam::deque::{Injector, Stealer, Worker};
|
||||||
use crossbeam::thread;
|
use crossbeam::thread;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use roc_builtins::roc::module_source;
|
use roc_builtins::roc::module_source;
|
||||||
use roc_can::abilities::{AbilitiesStore, PendingAbilitiesStore, ResolvedSpecializations};
|
use roc_can::abilities::{AbilitiesStore, PendingAbilitiesStore, ResolvedImpl};
|
||||||
use roc_can::constraint::{Constraint as ConstraintSoa, Constraints};
|
use roc_can::constraint::{Constraint as ConstraintSoa, Constraints};
|
||||||
use roc_can::expr::Declarations;
|
use roc_can::expr::Declarations;
|
||||||
use roc_can::expr::PendingDerives;
|
use roc_can::expr::PendingDerives;
|
||||||
use roc_can::module::{
|
use roc_can::module::{
|
||||||
canonicalize_module_defs, ExposedByModule, ExposedForModule, ExposedModuleTypes, Module,
|
canonicalize_module_defs, ExposedByModule, ExposedForModule, ExposedModuleTypes, Module,
|
||||||
|
ResolvedImplementations,
|
||||||
};
|
};
|
||||||
use roc_collections::{default_hasher, BumpMap, MutMap, MutSet, VecMap, VecSet};
|
use roc_collections::{default_hasher, BumpMap, MutMap, MutSet, VecMap, VecSet};
|
||||||
use roc_constrain::module::constrain_module;
|
use roc_constrain::module::constrain_module;
|
||||||
|
@ -41,7 +42,7 @@ use roc_parse::module::module_defs;
|
||||||
use roc_parse::parser::{FileError, Parser, SyntaxError};
|
use roc_parse::parser::{FileError, Parser, SyntaxError};
|
||||||
use roc_region::all::{LineInfo, Loc, Region};
|
use roc_region::all::{LineInfo, Loc, Region};
|
||||||
use roc_reporting::report::RenderTarget;
|
use roc_reporting::report::RenderTarget;
|
||||||
use roc_solve::module::{Solved, SolvedModule};
|
use roc_solve::module::{extract_module_owned_implementations, Solved, SolvedModule};
|
||||||
use roc_solve::solve;
|
use roc_solve::solve;
|
||||||
use roc_target::TargetInfo;
|
use roc_target::TargetInfo;
|
||||||
use roc_types::subs::{ExposedTypesStorageSubs, Subs, VarStore, Variable};
|
use roc_types::subs::{ExposedTypesStorageSubs, Subs, VarStore, Variable};
|
||||||
|
@ -562,7 +563,7 @@ pub struct LoadedModule {
|
||||||
pub exposed_aliases: MutMap<Symbol, Alias>,
|
pub exposed_aliases: MutMap<Symbol, Alias>,
|
||||||
pub exposed_values: Vec<Symbol>,
|
pub exposed_values: Vec<Symbol>,
|
||||||
pub exposed_types_storage: ExposedTypesStorageSubs,
|
pub exposed_types_storage: ExposedTypesStorageSubs,
|
||||||
pub resolved_specializations: ResolvedSpecializations,
|
pub resolved_implementations: ResolvedImplementations,
|
||||||
pub sources: MutMap<ModuleId, (PathBuf, Box<str>)>,
|
pub sources: MutMap<ModuleId, (PathBuf, Box<str>)>,
|
||||||
pub timings: MutMap<ModuleId, ModuleTiming>,
|
pub timings: MutMap<ModuleId, ModuleTiming>,
|
||||||
pub documentation: MutMap<ModuleId, ModuleDocumentation>,
|
pub documentation: MutMap<ModuleId, ModuleDocumentation>,
|
||||||
|
@ -754,7 +755,7 @@ enum Msg<'a> {
|
||||||
exposed_vars_by_symbol: Vec<(Symbol, Variable)>,
|
exposed_vars_by_symbol: Vec<(Symbol, Variable)>,
|
||||||
exposed_aliases_by_symbol: MutMap<Symbol, (bool, Alias)>,
|
exposed_aliases_by_symbol: MutMap<Symbol, (bool, Alias)>,
|
||||||
exposed_types_storage: ExposedTypesStorageSubs,
|
exposed_types_storage: ExposedTypesStorageSubs,
|
||||||
resolved_specializations: ResolvedSpecializations,
|
resolved_implementations: ResolvedImplementations,
|
||||||
dep_idents: IdentIdsByModule,
|
dep_idents: IdentIdsByModule,
|
||||||
documentation: MutMap<ModuleId, ModuleDocumentation>,
|
documentation: MutMap<ModuleId, ModuleDocumentation>,
|
||||||
abilities_store: AbilitiesStore,
|
abilities_store: AbilitiesStore,
|
||||||
|
@ -1513,7 +1514,7 @@ fn state_thread_step<'a>(
|
||||||
exposed_vars_by_symbol,
|
exposed_vars_by_symbol,
|
||||||
exposed_aliases_by_symbol,
|
exposed_aliases_by_symbol,
|
||||||
exposed_types_storage,
|
exposed_types_storage,
|
||||||
resolved_specializations,
|
resolved_implementations,
|
||||||
dep_idents,
|
dep_idents,
|
||||||
documentation,
|
documentation,
|
||||||
abilities_store,
|
abilities_store,
|
||||||
|
@ -1532,7 +1533,7 @@ fn state_thread_step<'a>(
|
||||||
exposed_aliases_by_symbol,
|
exposed_aliases_by_symbol,
|
||||||
exposed_vars_by_symbol,
|
exposed_vars_by_symbol,
|
||||||
exposed_types_storage,
|
exposed_types_storage,
|
||||||
resolved_specializations,
|
resolved_implementations,
|
||||||
dep_idents,
|
dep_idents,
|
||||||
documentation,
|
documentation,
|
||||||
abilities_store,
|
abilities_store,
|
||||||
|
@ -2362,7 +2363,7 @@ fn update<'a>(
|
||||||
exposed_vars_by_symbol: solved_module.exposed_vars_by_symbol,
|
exposed_vars_by_symbol: solved_module.exposed_vars_by_symbol,
|
||||||
exposed_aliases_by_symbol: solved_module.aliases,
|
exposed_aliases_by_symbol: solved_module.aliases,
|
||||||
exposed_types_storage: solved_module.exposed_types,
|
exposed_types_storage: solved_module.exposed_types,
|
||||||
resolved_specializations: solved_module.solved_specializations,
|
resolved_implementations: solved_module.solved_implementations,
|
||||||
dep_idents,
|
dep_idents,
|
||||||
documentation,
|
documentation,
|
||||||
abilities_store,
|
abilities_store,
|
||||||
|
@ -2381,7 +2382,7 @@ fn update<'a>(
|
||||||
module_id,
|
module_id,
|
||||||
ExposedModuleTypes {
|
ExposedModuleTypes {
|
||||||
exposed_types_storage_subs: solved_module.exposed_types,
|
exposed_types_storage_subs: solved_module.exposed_types,
|
||||||
resolved_specializations: solved_module.solved_specializations,
|
resolved_implementations: solved_module.solved_implementations,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -2843,7 +2844,7 @@ fn finish(
|
||||||
exposed_aliases_by_symbol: MutMap<Symbol, Alias>,
|
exposed_aliases_by_symbol: MutMap<Symbol, Alias>,
|
||||||
exposed_vars_by_symbol: Vec<(Symbol, Variable)>,
|
exposed_vars_by_symbol: Vec<(Symbol, Variable)>,
|
||||||
exposed_types_storage: ExposedTypesStorageSubs,
|
exposed_types_storage: ExposedTypesStorageSubs,
|
||||||
resolved_specializations: ResolvedSpecializations,
|
resolved_implementations: ResolvedImplementations,
|
||||||
dep_idents: IdentIdsByModule,
|
dep_idents: IdentIdsByModule,
|
||||||
documentation: MutMap<ModuleId, ModuleDocumentation>,
|
documentation: MutMap<ModuleId, ModuleDocumentation>,
|
||||||
abilities_store: AbilitiesStore,
|
abilities_store: AbilitiesStore,
|
||||||
|
@ -2890,7 +2891,7 @@ fn finish(
|
||||||
exposed_values,
|
exposed_values,
|
||||||
exposed_to_host: exposed_vars_by_symbol.into_iter().collect(),
|
exposed_to_host: exposed_vars_by_symbol.into_iter().collect(),
|
||||||
exposed_types_storage,
|
exposed_types_storage,
|
||||||
resolved_specializations,
|
resolved_implementations,
|
||||||
sources,
|
sources,
|
||||||
timings: state.timings,
|
timings: state.timings,
|
||||||
documentation,
|
documentation,
|
||||||
|
@ -3991,7 +3992,7 @@ pub fn add_imports(
|
||||||
match $exposed_by_module.get(&module_id) {
|
match $exposed_by_module.get(&module_id) {
|
||||||
Some(ExposedModuleTypes {
|
Some(ExposedModuleTypes {
|
||||||
exposed_types_storage_subs: exposed_types,
|
exposed_types_storage_subs: exposed_types,
|
||||||
resolved_specializations: _,
|
resolved_implementations: _,
|
||||||
}) => {
|
}) => {
|
||||||
let variable = match exposed_types.stored_vars_by_symbol.iter().find(|(s, _)| **s == $symbol) {
|
let variable = match exposed_types.stored_vars_by_symbol.iter().find(|(s, _)| **s == $symbol) {
|
||||||
None => {
|
None => {
|
||||||
|
@ -4050,8 +4051,8 @@ pub fn add_imports(
|
||||||
// One idea is to just always assume external modules fulfill their specialization obligations
|
// One idea is to just always assume external modules fulfill their specialization obligations
|
||||||
// and save lambda set resolution for mono.
|
// and save lambda set resolution for mono.
|
||||||
for (_, module_types) in exposed_for_module.exposed_by_module.iter_all() {
|
for (_, module_types) in exposed_for_module.exposed_by_module.iter_all() {
|
||||||
for ((member, typ), specialization) in module_types.resolved_specializations.iter() {
|
for (impl_key, resolved_impl) in module_types.resolved_implementations.iter() {
|
||||||
pending_abilities.import_specialization(*member, *typ, specialization)
|
pending_abilities.import_implementation(*impl_key, resolved_impl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4081,7 +4082,7 @@ pub fn add_imports(
|
||||||
|ctx, module, lset_var| match ctx.exposed_by_module.get(&module) {
|
|ctx, module, lset_var| match ctx.exposed_by_module.get(&module) {
|
||||||
Some(ExposedModuleTypes {
|
Some(ExposedModuleTypes {
|
||||||
exposed_types_storage_subs: exposed_types,
|
exposed_types_storage_subs: exposed_types,
|
||||||
resolved_specializations: _,
|
resolved_implementations: _,
|
||||||
}) => {
|
}) => {
|
||||||
let var = exposed_types
|
let var = exposed_types
|
||||||
.stored_specialization_lambda_set_vars
|
.stored_specialization_lambda_set_vars
|
||||||
|
@ -4112,7 +4113,7 @@ fn run_solve_solve(
|
||||||
derived_module: SharedDerivedModule,
|
derived_module: SharedDerivedModule,
|
||||||
) -> (
|
) -> (
|
||||||
Solved<Subs>,
|
Solved<Subs>,
|
||||||
ResolvedSpecializations,
|
ResolvedImplementations,
|
||||||
Vec<(Symbol, Variable)>,
|
Vec<(Symbol, Variable)>,
|
||||||
Vec<solve::TypeError>,
|
Vec<solve::TypeError>,
|
||||||
AbilitiesStore,
|
AbilitiesStore,
|
||||||
|
@ -4148,7 +4149,7 @@ fn run_solve_solve(
|
||||||
solve_aliases.insert(*name, alias.clone());
|
solve_aliases.insert(*name, alias.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
let (solved_subs, solved_specializations, exposed_vars_by_symbol, problems, abilities_store) = {
|
let (solved_subs, solved_implementations, exposed_vars_by_symbol, problems, abilities_store) = {
|
||||||
let module_id = module.module_id;
|
let module_id = module.module_id;
|
||||||
|
|
||||||
let (solved_subs, solved_env, problems, abilities_store) = roc_solve::module::run_solve(
|
let (solved_subs, solved_env, problems, abilities_store) = roc_solve::module::run_solve(
|
||||||
|
@ -4164,19 +4165,17 @@ fn run_solve_solve(
|
||||||
derived_module,
|
derived_module,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Figure out what specializations belong to this module
|
let solved_implementations =
|
||||||
let solved_specializations: ResolvedSpecializations = abilities_store
|
extract_module_owned_implementations(module_id, &abilities_store);
|
||||||
.iter_specializations()
|
|
||||||
.filter(|((member, typ), _)| {
|
|
||||||
// This module solved this specialization if either the member or the type comes from the
|
|
||||||
// module.
|
|
||||||
member.module_id() == module_id || typ.module_id() == module_id
|
|
||||||
})
|
|
||||||
.map(|(key, specialization)| (key, specialization.clone()))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let is_specialization_symbol =
|
let is_specialization_symbol = |sym| {
|
||||||
|sym| solved_specializations.values().any(|ms| ms.symbol == sym);
|
solved_implementations
|
||||||
|
.values()
|
||||||
|
.any(|resolved_impl| match resolved_impl {
|
||||||
|
ResolvedImpl::Impl(specialization) => specialization.symbol == sym,
|
||||||
|
ResolvedImpl::Derived | ResolvedImpl::Error => false,
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
// Expose anything that is explicitly exposed by the header, or is a specialization of an
|
// Expose anything that is explicitly exposed by the header, or is a specialization of an
|
||||||
// ability.
|
// ability.
|
||||||
|
@ -4187,7 +4186,7 @@ fn run_solve_solve(
|
||||||
|
|
||||||
(
|
(
|
||||||
solved_subs,
|
solved_subs,
|
||||||
solved_specializations,
|
solved_implementations,
|
||||||
exposed_vars_by_symbol,
|
exposed_vars_by_symbol,
|
||||||
problems,
|
problems,
|
||||||
abilities_store,
|
abilities_store,
|
||||||
|
@ -4196,7 +4195,7 @@ fn run_solve_solve(
|
||||||
|
|
||||||
(
|
(
|
||||||
solved_subs,
|
solved_subs,
|
||||||
solved_specializations,
|
solved_implementations,
|
||||||
exposed_vars_by_symbol,
|
exposed_vars_by_symbol,
|
||||||
problems,
|
problems,
|
||||||
abilities_store,
|
abilities_store,
|
||||||
|
@ -4229,7 +4228,7 @@ fn run_solve<'a>(
|
||||||
let loc_expects = std::mem::take(&mut module.loc_expects);
|
let loc_expects = std::mem::take(&mut module.loc_expects);
|
||||||
let module = module;
|
let module = module;
|
||||||
|
|
||||||
let (solved_subs, solved_specializations, exposed_vars_by_symbol, problems, abilities_store) = {
|
let (solved_subs, solved_implementations, exposed_vars_by_symbol, problems, abilities_store) = {
|
||||||
if module_id.is_builtin() {
|
if module_id.is_builtin() {
|
||||||
match cached_subs.lock().remove(&module_id) {
|
match cached_subs.lock().remove(&module_id) {
|
||||||
None => run_solve_solve(
|
None => run_solve_solve(
|
||||||
|
@ -4271,7 +4270,7 @@ fn run_solve<'a>(
|
||||||
module_id,
|
module_id,
|
||||||
&mut solved_subs,
|
&mut solved_subs,
|
||||||
&exposed_vars_by_symbol,
|
&exposed_vars_by_symbol,
|
||||||
&solved_specializations,
|
&solved_implementations,
|
||||||
&abilities_store,
|
&abilities_store,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -4279,7 +4278,7 @@ fn run_solve<'a>(
|
||||||
exposed_vars_by_symbol,
|
exposed_vars_by_symbol,
|
||||||
problems,
|
problems,
|
||||||
aliases,
|
aliases,
|
||||||
solved_specializations,
|
solved_implementations,
|
||||||
exposed_types,
|
exposed_types,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,8 @@ const SYMBOL_HAS_NICHE: () =
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
const PRETTY_PRINT_DEBUG_SYMBOLS: bool = true;
|
const PRETTY_PRINT_DEBUG_SYMBOLS: bool = true;
|
||||||
|
|
||||||
pub const BUILTIN_ABILITIES: &[Symbol] = &[Symbol::ENCODE_ENCODING];
|
pub const DERIVABLE_ABILITIES: &[(Symbol, &[Symbol])] =
|
||||||
|
&[(Symbol::ENCODE_ENCODING, &[Symbol::ENCODE_TO_ENCODER])];
|
||||||
|
|
||||||
/// In Debug builds only, Symbol has a name() method that lets
|
/// In Debug builds only, Symbol has a name() method that lets
|
||||||
/// you look up its name in a global intern table. This table is
|
/// you look up its name in a global intern table. This table is
|
||||||
|
@ -86,8 +87,12 @@ impl Symbol {
|
||||||
self.module_id().is_builtin()
|
self.module_id().is_builtin()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_builtin_ability(self) -> bool {
|
pub fn is_derivable_ability(self) -> bool {
|
||||||
BUILTIN_ABILITIES.contains(&self)
|
self.derivable_ability().is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn derivable_ability(self) -> Option<&'static (Symbol, &'static [Symbol])> {
|
||||||
|
DERIVABLE_ABILITIES.iter().find(|(name, _)| *name == self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn module_string<'a>(&self, interns: &'a Interns) -> &'a ModuleName {
|
pub fn module_string<'a>(&self, interns: &'a Interns) -> &'a ModuleName {
|
||||||
|
|
|
@ -169,6 +169,11 @@ pub enum Problem {
|
||||||
region: Region,
|
region: Region,
|
||||||
},
|
},
|
||||||
NoIdentifiersIntroduced(Region),
|
NoIdentifiersIntroduced(Region),
|
||||||
|
OverloadedSpecialization {
|
||||||
|
overload: Region,
|
||||||
|
original_opaque: Symbol,
|
||||||
|
ability_member: Symbol,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
|
|
@ -86,7 +86,7 @@ impl PendingDerivesTable {
|
||||||
} in derives
|
} in derives
|
||||||
{
|
{
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
ability.is_builtin_ability(),
|
ability.is_derivable_ability(),
|
||||||
"Not a builtin - should have been caught during can"
|
"Not a builtin - should have been caught during can"
|
||||||
);
|
);
|
||||||
let derive_key = RequestedDeriveKey { opaque, ability };
|
let derive_key = RequestedDeriveKey { opaque, ability };
|
||||||
|
@ -394,7 +394,7 @@ impl ObligationCache<'_> {
|
||||||
for &member in members_of_ability {
|
for &member in members_of_ability {
|
||||||
if self
|
if self
|
||||||
.abilities_store
|
.abilities_store
|
||||||
.get_specialization(member, opaque)
|
.get_implementation(member, opaque)
|
||||||
.is_none()
|
.is_none()
|
||||||
{
|
{
|
||||||
let root_data = self.abilities_store.member_def(member).unwrap();
|
let root_data = self.abilities_store.member_def(member).unwrap();
|
||||||
|
@ -671,9 +671,15 @@ pub fn resolve_ability_specialization(
|
||||||
|
|
||||||
let resolved = match obligated {
|
let resolved = match obligated {
|
||||||
Obligated::Opaque(symbol) => {
|
Obligated::Opaque(symbol) => {
|
||||||
let specialization = abilities_store.get_specialization(ability_member, symbol)?;
|
match abilities_store.get_implementation(ability_member, symbol)? {
|
||||||
|
roc_types::types::MemberImpl::Impl(spec_symbol) => {
|
||||||
Resolved::Specialization(specialization.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(_) => {
|
Obligated::Adhoc(_) => {
|
||||||
// TODO: more rules need to be validated here, like is this a builtin ability?
|
// TODO: more rules need to be validated here, like is this a builtin ability?
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
use crate::solve::{self, Aliases};
|
use crate::solve::{self, Aliases};
|
||||||
use roc_can::abilities::{AbilitiesStore, ResolvedSpecializations};
|
use roc_can::abilities::{AbilitiesStore, ImplKey, ResolvedImpl};
|
||||||
use roc_can::constraint::{Constraint as ConstraintSoa, Constraints};
|
use roc_can::constraint::{Constraint as ConstraintSoa, Constraints};
|
||||||
use roc_can::expr::PendingDerives;
|
use roc_can::expr::PendingDerives;
|
||||||
use roc_can::module::{ExposedByModule, RigidVariables};
|
use roc_can::module::{ExposedByModule, ResolvedImplementations, RigidVariables};
|
||||||
use roc_collections::all::MutMap;
|
use roc_collections::all::MutMap;
|
||||||
use roc_collections::VecMap;
|
use roc_collections::VecMap;
|
||||||
use roc_derive::SharedDerivedModule;
|
use roc_derive::SharedDerivedModule;
|
||||||
use roc_error_macros::internal_error;
|
use roc_error_macros::internal_error;
|
||||||
use roc_module::symbol::{ModuleId, Symbol};
|
use roc_module::symbol::{ModuleId, Symbol};
|
||||||
use roc_types::subs::{Content, ExposedTypesStorageSubs, FlatType, StorageSubs, Subs, Variable};
|
use roc_types::subs::{Content, ExposedTypesStorageSubs, FlatType, StorageSubs, Subs, Variable};
|
||||||
use roc_types::types::Alias;
|
use roc_types::types::{Alias, MemberImpl};
|
||||||
|
|
||||||
/// A marker that a given Subs has been solved.
|
/// A marker that a given Subs has been solved.
|
||||||
/// The only way to obtain a Solved<Subs> is by running the solver on it.
|
/// The only way to obtain a Solved<Subs> is by running the solver on it.
|
||||||
|
@ -48,7 +48,7 @@ pub struct SolvedModule {
|
||||||
pub exposed_vars_by_symbol: Vec<(Symbol, Variable)>,
|
pub exposed_vars_by_symbol: Vec<(Symbol, Variable)>,
|
||||||
|
|
||||||
/// Used when importing this module into another module
|
/// Used when importing this module into another module
|
||||||
pub solved_specializations: ResolvedSpecializations,
|
pub solved_implementations: ResolvedImplementations,
|
||||||
pub exposed_types: ExposedTypesStorageSubs,
|
pub exposed_types: ExposedTypesStorageSubs,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ pub fn exposed_types_storage_subs(
|
||||||
home: ModuleId,
|
home: ModuleId,
|
||||||
solved_subs: &mut Solved<Subs>,
|
solved_subs: &mut Solved<Subs>,
|
||||||
exposed_vars_by_symbol: &[(Symbol, Variable)],
|
exposed_vars_by_symbol: &[(Symbol, Variable)],
|
||||||
solved_specializations: &ResolvedSpecializations,
|
solved_implementations: &ResolvedImplementations,
|
||||||
abilities_store: &AbilitiesStore,
|
abilities_store: &AbilitiesStore,
|
||||||
) -> ExposedTypesStorageSubs {
|
) -> ExposedTypesStorageSubs {
|
||||||
let subs = solved_subs.inner_mut();
|
let subs = solved_subs.inner_mut();
|
||||||
|
@ -121,31 +121,42 @@ pub fn exposed_types_storage_subs(
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut stored_specialization_lambda_set_vars =
|
let mut stored_specialization_lambda_set_vars =
|
||||||
VecMap::with_capacity(solved_specializations.len());
|
VecMap::with_capacity(solved_implementations.len());
|
||||||
|
|
||||||
for (_, member_specialization) in solved_specializations.iter() {
|
for (_, member_impl) in solved_implementations.iter() {
|
||||||
for (_, &lset_var) in member_specialization.specialization_lambda_sets.iter() {
|
match member_impl {
|
||||||
let specialization_lset_ambient_function_var =
|
ResolvedImpl::Impl(member_specialization) => {
|
||||||
subs.get_lambda_set(lset_var).ambient_function;
|
// Export all the lambda sets and their ambient functions.
|
||||||
|
for (_, &lset_var) in member_specialization.specialization_lambda_sets.iter() {
|
||||||
|
let specialization_lset_ambient_function_var =
|
||||||
|
subs.get_lambda_set(lset_var).ambient_function;
|
||||||
|
|
||||||
// Import the ambient function of this specialization lambda set; that will import the
|
// Import the ambient function of this specialization lambda set; that will import the
|
||||||
// lambda set as well. The ambient function is needed for the lambda set compaction
|
// lambda set as well. The ambient function is needed for the lambda set compaction
|
||||||
// algorithm.
|
// algorithm.
|
||||||
let imported_lset_ambient_function_var = storage_subs
|
let imported_lset_ambient_function_var = storage_subs
|
||||||
.import_variable_from(subs, specialization_lset_ambient_function_var)
|
.import_variable_from(subs, specialization_lset_ambient_function_var)
|
||||||
.variable;
|
.variable;
|
||||||
|
|
||||||
let imported_lset_var = match storage_subs
|
let imported_lset_var = match storage_subs
|
||||||
.as_inner()
|
.as_inner()
|
||||||
.get_content_without_compacting(imported_lset_ambient_function_var)
|
.get_content_without_compacting(imported_lset_ambient_function_var)
|
||||||
{
|
{
|
||||||
Content::Structure(FlatType::Func(_, lambda_set_var, _)) => *lambda_set_var,
|
Content::Structure(FlatType::Func(_, lambda_set_var, _)) => *lambda_set_var,
|
||||||
content => internal_error!(
|
content => internal_error!(
|
||||||
"ambient lambda set function import is not a function, found: {:?}",
|
"ambient lambda set function import is not a function, found: {:?}",
|
||||||
roc_types::subs::SubsFmtContent(content, storage_subs.as_inner())
|
roc_types::subs::SubsFmtContent(content, storage_subs.as_inner())
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
stored_specialization_lambda_set_vars.insert(lset_var, imported_lset_var);
|
stored_specialization_lambda_set_vars.insert(lset_var, imported_lset_var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ResolvedImpl::Derived => {
|
||||||
|
// nothing to do
|
||||||
|
}
|
||||||
|
ResolvedImpl::Error => {
|
||||||
|
// nothing to do
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,3 +182,37 @@ pub fn exposed_types_storage_subs(
|
||||||
stored_ability_member_vars,
|
stored_ability_member_vars,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extracts the ability member implementations owned by a solved module.
|
||||||
|
pub fn extract_module_owned_implementations(
|
||||||
|
module_id: ModuleId,
|
||||||
|
abilities_store: &AbilitiesStore,
|
||||||
|
) -> ResolvedImplementations {
|
||||||
|
abilities_store
|
||||||
|
.iter_declared_implementations()
|
||||||
|
.filter_map(|((member, typ), member_impl)| {
|
||||||
|
// This module solved this specialization if either the member or the type comes from the
|
||||||
|
// module.
|
||||||
|
if member.module_id() != module_id && typ.module_id() != module_id {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let impl_key = ImplKey {
|
||||||
|
opaque: typ,
|
||||||
|
ability_member: member,
|
||||||
|
};
|
||||||
|
|
||||||
|
let resolved_impl = match member_impl {
|
||||||
|
MemberImpl::Impl(impl_symbol) => {
|
||||||
|
let specialization = abilities_store.specialization_info(*impl_symbol).expect(
|
||||||
|
"declared implementations should be resolved conclusively after solving",
|
||||||
|
);
|
||||||
|
ResolvedImpl::Impl(specialization.clone())
|
||||||
|
}
|
||||||
|
MemberImpl::Derived => ResolvedImpl::Derived,
|
||||||
|
MemberImpl::Error => ResolvedImpl::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
Some((impl_key, resolved_impl))
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::ability::{
|
||||||
};
|
};
|
||||||
use crate::module::Solved;
|
use crate::module::Solved;
|
||||||
use bumpalo::Bump;
|
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::Constraint::{self, *};
|
||||||
use roc_can::constraint::{Constraints, Cycle, LetConstraint, OpportunisticResolve};
|
use roc_can::constraint::{Constraints, Cycle, LetConstraint, OpportunisticResolve};
|
||||||
use roc_can::expected::{Expected, PExpected};
|
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_debug_flags::{ROC_TRACE_COMPACTION, ROC_VERIFY_RIGID_LET_GENERALIZED};
|
||||||
use roc_derive::SharedDerivedModule;
|
use roc_derive::SharedDerivedModule;
|
||||||
use roc_derive_key::{DeriveError, DeriveKey};
|
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::ident::TagName;
|
||||||
use roc_module::symbol::{ModuleId, Symbol};
|
use roc_module::symbol::{ModuleId, Symbol};
|
||||||
use roc_problem::can::CycleEntry;
|
use roc_problem::can::CycleEntry;
|
||||||
|
@ -28,8 +28,8 @@ use roc_types::subs::{
|
||||||
};
|
};
|
||||||
use roc_types::types::Type::{self, *};
|
use roc_types::types::Type::{self, *};
|
||||||
use roc_types::types::{
|
use roc_types::types::{
|
||||||
gather_fields_unsorted_iter, AliasCommon, AliasKind, Category, ErrorType, OptAbleType,
|
gather_fields_unsorted_iter, AliasCommon, AliasKind, Category, ErrorType, MemberImpl,
|
||||||
OptAbleVar, PatternCategory, Reason, TypeExtension, Uls,
|
OptAbleType, OptAbleVar, PatternCategory, Reason, TypeExtension, Uls,
|
||||||
};
|
};
|
||||||
use roc_unify::unify::{
|
use roc_unify::unify::{
|
||||||
unify, unify_introduced_ability_specialization, Mode, MustImplementConstraints, Obligated,
|
unify, unify_introduced_ability_specialization, Mode, MustImplementConstraints, Obligated,
|
||||||
|
@ -1705,7 +1705,8 @@ fn check_ability_specialization(
|
||||||
// If the symbol specializes an ability member, we need to make sure that the
|
// If the symbol specializes an ability member, we need to make sure that the
|
||||||
// inferred type for the specialization actually aligns with the expected
|
// inferred type for the specialization actually aligns with the expected
|
||||||
// implementation.
|
// implementation.
|
||||||
if let Some((ability_member, root_data)) = abilities_store.root_name_and_def(symbol) {
|
if let Some((impl_key, root_data)) = abilities_store.impl_key_and_def(symbol) {
|
||||||
|
let ability_member = impl_key.ability_member;
|
||||||
let root_signature_var = root_data.signature_var();
|
let root_signature_var = root_data.signature_var();
|
||||||
let parent_ability = root_data.parent_ability;
|
let parent_ability = root_data.parent_ability;
|
||||||
|
|
||||||
|
@ -1726,7 +1727,7 @@ fn check_ability_specialization(
|
||||||
Mode::EQ,
|
Mode::EQ,
|
||||||
);
|
);
|
||||||
|
|
||||||
match unified {
|
let resolved_mark = match unified {
|
||||||
Success {
|
Success {
|
||||||
vars,
|
vars,
|
||||||
must_implement_ability,
|
must_implement_ability,
|
||||||
|
@ -1755,12 +1756,7 @@ fn check_ability_specialization(
|
||||||
|
|
||||||
let specialization_region = symbol_loc_var.region;
|
let specialization_region = symbol_loc_var.region;
|
||||||
let specialization =
|
let specialization =
|
||||||
MemberSpecialization::new(symbol, specialization_lambda_sets);
|
MemberSpecializationInfo::new(symbol, specialization_lambda_sets);
|
||||||
abilities_store.register_specialization_for_type(
|
|
||||||
ability_member,
|
|
||||||
opaque,
|
|
||||||
specialization,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Make sure we check that the opaque has specialized all members of the
|
// Make sure we check that the opaque has specialized all members of the
|
||||||
// ability, after we finish solving the module.
|
// ability, after we finish solving the module.
|
||||||
|
@ -1774,6 +1770,8 @@ fn check_ability_specialization(
|
||||||
},
|
},
|
||||||
specialization_region,
|
specialization_region,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Ok(specialization)
|
||||||
}
|
}
|
||||||
Some(Obligated::Adhoc(var)) => {
|
Some(Obligated::Adhoc(var)) => {
|
||||||
// This is a specialization of a structural type - never allowed.
|
// This is a specialization of a structural type - never allowed.
|
||||||
|
@ -1791,6 +1789,8 @@ fn check_ability_specialization(
|
||||||
};
|
};
|
||||||
|
|
||||||
problems.push(problem);
|
problems.push(problem);
|
||||||
|
|
||||||
|
Err(())
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// This can happen when every ability constriant on a type variable went
|
// This can happen when every ability constriant on a type variable went
|
||||||
|
@ -1817,6 +1817,8 @@ fn check_ability_specialization(
|
||||||
);
|
);
|
||||||
|
|
||||||
problems.push(problem);
|
problems.push(problem);
|
||||||
|
|
||||||
|
Err(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1839,14 +1841,22 @@ fn check_ability_specialization(
|
||||||
);
|
);
|
||||||
|
|
||||||
problems.push(problem);
|
problems.push(problem);
|
||||||
|
|
||||||
|
Err(())
|
||||||
}
|
}
|
||||||
BadType(vars, problem) => {
|
BadType(vars, problem) => {
|
||||||
subs.commit_snapshot(snapshot);
|
subs.commit_snapshot(snapshot);
|
||||||
introduce(subs, rank, pools, &vars);
|
introduce(subs, rank, pools, &vars);
|
||||||
|
|
||||||
problems.push(TypeError::BadType(problem));
|
problems.push(TypeError::BadType(problem));
|
||||||
|
|
||||||
|
Err(())
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
abilities_store
|
||||||
|
.mark_implementation(impl_key.ability_member, impl_key.opaque, resolved_mark)
|
||||||
|
.expect("marked as a custom implementation, but not recorded as such");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2301,7 +2311,7 @@ fn get_specialization_lambda_set_ambient_function<P: Phase>(
|
||||||
let external_specialized_lset =
|
let external_specialized_lset =
|
||||||
phase.with_module_abilities_store(opaque_home, |abilities_store| {
|
phase.with_module_abilities_store(opaque_home, |abilities_store| {
|
||||||
let opt_specialization =
|
let opt_specialization =
|
||||||
abilities_store.get_specialization(ability_member, opaque);
|
abilities_store.get_implementation(ability_member, opaque);
|
||||||
match (P::IS_LATE, opt_specialization) {
|
match (P::IS_LATE, opt_specialization) {
|
||||||
(false, None) => {
|
(false, None) => {
|
||||||
// doesn't specialize, we'll have reported an error for this
|
// doesn't specialize, we'll have reported an error for this
|
||||||
|
@ -2314,13 +2324,20 @@ fn get_specialization_lambda_set_ambient_function<P: Phase>(
|
||||||
ability_member,
|
ability_member,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
(_, Some(specialization)) => {
|
(_, Some(member_impl)) => match member_impl {
|
||||||
let specialized_lambda_set = *specialization
|
MemberImpl::Impl(spec_symbol) => {
|
||||||
.specialization_lambda_sets
|
let specialization =
|
||||||
.get(&lset_region)
|
abilities_store.specialization_info(*spec_symbol).expect("expected custom implementations to always have complete specialization info by this point");
|
||||||
.expect("lambda set region not resolved");
|
|
||||||
Ok(specialized_lambda_set)
|
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!(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,10 @@ mod solve_expr {
|
||||||
use roc_region::all::{LineColumn, LineColumnRegion, LineInfo, Region};
|
use roc_region::all::{LineColumn, LineColumnRegion, LineInfo, Region};
|
||||||
use roc_reporting::report::{can_problem, type_problem, RocDocAllocator};
|
use roc_reporting::report::{can_problem, type_problem, RocDocAllocator};
|
||||||
use roc_solve::solve::TypeError;
|
use roc_solve::solve::TypeError;
|
||||||
use roc_types::pretty_print::{name_and_print_var, DebugPrint};
|
use roc_types::{
|
||||||
|
pretty_print::{name_and_print_var, DebugPrint},
|
||||||
|
types::MemberImpl,
|
||||||
|
};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
// HELPERS
|
// HELPERS
|
||||||
|
@ -363,11 +366,22 @@ mod solve_expr {
|
||||||
panic!();
|
panic!();
|
||||||
}
|
}
|
||||||
|
|
||||||
let known_specializations = abilities_store.iter_specializations();
|
let known_specializations = abilities_store.iter_declared_implementations().filter_map(
|
||||||
|
|((member, typ), member_impl)| match member_impl {
|
||||||
|
MemberImpl::Impl(impl_symbol) => {
|
||||||
|
let specialization = abilities_store.specialization_info(*impl_symbol).expect(
|
||||||
|
"declared implementations should be resolved conclusively after solving",
|
||||||
|
);
|
||||||
|
Some((member, typ, specialization.clone()))
|
||||||
|
}
|
||||||
|
MemberImpl::Derived | MemberImpl::Error => None,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
let pretty_specializations = known_specializations
|
let pretty_specializations = known_specializations
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|((member, typ), _)| {
|
.map(|(member, typ, _)| {
|
||||||
let member_data = abilities_store.member_def(member).unwrap();
|
let member_data = abilities_store.member_def(member).unwrap();
|
||||||
let member_str = member.as_str(&interns);
|
let member_str = member.as_str(&interns);
|
||||||
let ability_str = member_data.parent_ability.as_str(&interns);
|
let ability_str = member_data.parent_ability.as_str(&interns);
|
||||||
|
@ -6778,6 +6792,8 @@ mod solve_expr {
|
||||||
Diverge has diverge : a -> a | a has Diverge
|
Diverge has diverge : a -> a | a has Diverge
|
||||||
|
|
||||||
A := {} has [Diverge {diverge}]
|
A := {} has [Diverge {diverge}]
|
||||||
|
|
||||||
|
diverge : A -> A
|
||||||
diverge = \@A {} -> diverge (@A {})
|
diverge = \@A {} -> diverge (@A {})
|
||||||
#^^^^^^^{-1} ^^^^^^^
|
#^^^^^^^{-1} ^^^^^^^
|
||||||
|
|
||||||
|
@ -6791,7 +6807,7 @@ mod solve_expr {
|
||||||
),
|
),
|
||||||
@r###"
|
@r###"
|
||||||
A#diverge(4) : A -[[diverge(4)]]-> A
|
A#diverge(4) : A -[[diverge(4)]]-> A
|
||||||
Diverge#diverge(2) : A -[[diverge(4)]]-> A
|
A#diverge(4) : A -[[diverge(4)]]-> A
|
||||||
A#diverge(4) : A -[[diverge(4)]]-> A
|
A#diverge(4) : A -[[diverge(4)]]-> A
|
||||||
"###
|
"###
|
||||||
)
|
)
|
||||||
|
@ -6799,6 +6815,46 @@ mod solve_expr {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn resolve_mutually_recursive_ability_lambda_sets() {
|
fn resolve_mutually_recursive_ability_lambda_sets() {
|
||||||
|
infer_queries!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
|
Bounce has
|
||||||
|
ping : a -> a | a has Bounce
|
||||||
|
pong : a -> a | a has Bounce
|
||||||
|
|
||||||
|
A := {} has [Bounce {ping, pong}]
|
||||||
|
|
||||||
|
ping : A -> A
|
||||||
|
ping = \@A {} -> pong (@A {})
|
||||||
|
#^^^^{-1} ^^^^
|
||||||
|
|
||||||
|
pong : A -> A
|
||||||
|
pong = \@A {} -> ping (@A {})
|
||||||
|
#^^^^{-1} ^^^^
|
||||||
|
|
||||||
|
main =
|
||||||
|
a : A
|
||||||
|
a = ping (@A {})
|
||||||
|
# ^^^^
|
||||||
|
|
||||||
|
a
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
@r###"
|
||||||
|
A#ping(5) : A -[[ping(5)]]-> A
|
||||||
|
A#pong(6) : A -[[pong(6)]]-> A
|
||||||
|
A#pong(6) : A -[[pong(6)]]-> A
|
||||||
|
A#ping(5) : A -[[ping(5)]]-> A
|
||||||
|
A#ping(5) : A -[[ping(5)]]-> A
|
||||||
|
"###
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore = "TODO: this currently runs into trouble with ping and pong first being inferred as overly-general before recursive constraining"]
|
||||||
|
fn resolve_mutually_recursive_ability_lambda_sets_inferred() {
|
||||||
infer_queries!(
|
infer_queries!(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
|
|
|
@ -13,11 +13,14 @@ use ven_pretty::DocAllocator;
|
||||||
|
|
||||||
use crate::pretty_print::{pretty_print_def, Ctx};
|
use crate::pretty_print::{pretty_print_def, Ctx};
|
||||||
use roc_can::{
|
use roc_can::{
|
||||||
abilities::{AbilitiesStore, ResolvedSpecializations, SpecializationLambdaSets},
|
abilities::{AbilitiesStore, SpecializationLambdaSets},
|
||||||
constraint::Constraints,
|
constraint::Constraints,
|
||||||
def::Def,
|
def::Def,
|
||||||
expr::Declarations,
|
expr::Declarations,
|
||||||
module::{ExposedByModule, ExposedForModule, ExposedModuleTypes, RigidVariables},
|
module::{
|
||||||
|
ExposedByModule, ExposedForModule, ExposedModuleTypes, ResolvedImplementations,
|
||||||
|
RigidVariables,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use roc_collections::VecSet;
|
use roc_collections::VecSet;
|
||||||
use roc_constrain::expr::constrain_decls;
|
use roc_constrain::expr::constrain_decls;
|
||||||
|
@ -134,7 +137,7 @@ fn check_derived_typechecks_and_golden(
|
||||||
ModuleId::ENCODE,
|
ModuleId::ENCODE,
|
||||||
ExposedModuleTypes {
|
ExposedModuleTypes {
|
||||||
exposed_types_storage_subs: exposed_encode_types,
|
exposed_types_storage_subs: exposed_encode_types,
|
||||||
resolved_specializations: ResolvedSpecializations::default(),
|
resolved_implementations: ResolvedImplementations::default(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
let exposed_for_module =
|
let exposed_for_module =
|
||||||
|
@ -230,7 +233,7 @@ where
|
||||||
mut interns,
|
mut interns,
|
||||||
exposed_types_storage: exposed_encode_types,
|
exposed_types_storage: exposed_encode_types,
|
||||||
abilities_store,
|
abilities_store,
|
||||||
resolved_specializations,
|
resolved_implementations,
|
||||||
..
|
..
|
||||||
} = roc_load_internal::file::load_and_typecheck_str(
|
} = roc_load_internal::file::load_and_typecheck_str(
|
||||||
&arena,
|
&arena,
|
||||||
|
@ -256,7 +259,7 @@ where
|
||||||
ModuleId::ENCODE,
|
ModuleId::ENCODE,
|
||||||
ExposedModuleTypes {
|
ExposedModuleTypes {
|
||||||
exposed_types_storage_subs: exposed_encode_types.clone(),
|
exposed_types_storage_subs: exposed_encode_types.clone(),
|
||||||
resolved_specializations,
|
resolved_implementations,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
procedure Test.2 (Test.4):
|
procedure Test.2 (Test.4):
|
||||||
let Test.13 : Int1 = 1i64;
|
let Test.13 : U8 = 1i64;
|
||||||
let Test.14 : Int1 = GetTagId Test.4;
|
let Test.14 : U8 = GetTagId Test.4;
|
||||||
let Test.15 : Int1 = lowlevel Eq Test.13 Test.14;
|
let Test.15 : Int1 = lowlevel Eq Test.13 Test.14;
|
||||||
if Test.15 then
|
if Test.15 then
|
||||||
let Test.11 : Int1 = true;
|
let Test.11 : Int1 = true;
|
||||||
|
|
|
@ -3,8 +3,8 @@ procedure Test.0 ():
|
||||||
let Test.14 : [<rnu><null>, C *self] = TagId(0) Test.15;
|
let Test.14 : [<rnu><null>, C *self] = TagId(0) Test.15;
|
||||||
let Test.13 : [<rnu><null>, C *self] = TagId(0) Test.14;
|
let Test.13 : [<rnu><null>, C *self] = TagId(0) Test.14;
|
||||||
let Test.2 : [<rnu><null>, C *self] = TagId(0) Test.13;
|
let Test.2 : [<rnu><null>, C *self] = TagId(0) Test.13;
|
||||||
let Test.10 : Int1 = 1i64;
|
let Test.10 : U8 = 1i64;
|
||||||
let Test.11 : Int1 = GetTagId Test.2;
|
let Test.11 : U8 = GetTagId Test.2;
|
||||||
dec Test.2;
|
dec Test.2;
|
||||||
let Test.12 : Int1 = lowlevel Eq Test.10 Test.11;
|
let Test.12 : Int1 = lowlevel Eq Test.10 Test.11;
|
||||||
if Test.12 then
|
if Test.12 then
|
||||||
|
|
|
@ -3,15 +3,15 @@ procedure Test.0 ():
|
||||||
let Test.20 : [<rnu><null>, C *self] = TagId(0) Test.21;
|
let Test.20 : [<rnu><null>, C *self] = TagId(0) Test.21;
|
||||||
let Test.19 : [<rnu><null>, C *self] = TagId(0) Test.20;
|
let Test.19 : [<rnu><null>, C *self] = TagId(0) Test.20;
|
||||||
let Test.2 : [<rnu><null>, C *self] = TagId(0) Test.19;
|
let Test.2 : [<rnu><null>, C *self] = TagId(0) Test.19;
|
||||||
let Test.16 : Int1 = 0i64;
|
let Test.16 : U8 = 0i64;
|
||||||
let Test.17 : Int1 = GetTagId Test.2;
|
let Test.17 : U8 = GetTagId Test.2;
|
||||||
let Test.18 : Int1 = lowlevel Eq Test.16 Test.17;
|
let Test.18 : Int1 = lowlevel Eq Test.16 Test.17;
|
||||||
if Test.18 then
|
if Test.18 then
|
||||||
let Test.12 : [<rnu><null>, C *self] = UnionAtIndex (Id 0) (Index 0) Test.2;
|
let Test.12 : [<rnu><null>, C *self] = UnionAtIndex (Id 0) (Index 0) Test.2;
|
||||||
inc Test.12;
|
inc Test.12;
|
||||||
dec Test.2;
|
dec Test.2;
|
||||||
let Test.13 : Int1 = 0i64;
|
let Test.13 : U8 = 0i64;
|
||||||
let Test.14 : Int1 = GetTagId Test.12;
|
let Test.14 : U8 = GetTagId Test.12;
|
||||||
dec Test.12;
|
dec Test.12;
|
||||||
let Test.15 : Int1 = lowlevel Eq Test.13 Test.14;
|
let Test.15 : Int1 = lowlevel Eq Test.13 Test.14;
|
||||||
if Test.15 then
|
if Test.15 then
|
||||||
|
|
|
@ -4,7 +4,6 @@ use crate::subs::{
|
||||||
GetSubsSlice, RecordFields, Subs, UnionTags, VarStore, Variable, VariableSubsSlice,
|
GetSubsSlice, RecordFields, Subs, UnionTags, VarStore, Variable, VariableSubsSlice,
|
||||||
};
|
};
|
||||||
use roc_collections::all::{HumanIndex, ImMap, ImSet, MutMap, MutSet, SendMap};
|
use roc_collections::all::{HumanIndex, ImMap, ImSet, MutMap, MutSet, SendMap};
|
||||||
use roc_collections::VecMap;
|
|
||||||
use roc_error_macros::internal_error;
|
use roc_error_macros::internal_error;
|
||||||
use roc_module::called_via::CalledVia;
|
use roc_module::called_via::CalledVia;
|
||||||
use roc_module::ident::{ForeignSymbol, Ident, Lowercase, TagName};
|
use roc_module::ident::{ForeignSymbol, Ident, Lowercase, TagName};
|
||||||
|
@ -2056,13 +2055,15 @@ impl From<&AliasVar> for OptAbleVar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum OpaqueSupports {
|
pub enum MemberImpl {
|
||||||
Derived(Symbol),
|
/// The implementation is claimed to be at the given symbol.
|
||||||
Implemented {
|
/// During solving we validate that the impl is really there.
|
||||||
ability_name: Symbol,
|
Impl(Symbol),
|
||||||
impls: VecMap<Symbol, Symbol>,
|
/// The implementation should be derived.
|
||||||
},
|
Derived,
|
||||||
|
/// The implementation is not present or does not match the expected member type.
|
||||||
|
Error,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use roc_collections::all::MutSet;
|
use roc_collections::all::MutSet;
|
||||||
use roc_module::ident::{Ident, Lowercase, ModuleName};
|
use roc_module::ident::{Ident, Lowercase, ModuleName};
|
||||||
use roc_module::symbol::BUILTIN_ABILITIES;
|
use roc_module::symbol::DERIVABLE_ABILITIES;
|
||||||
use roc_problem::can::PrecedenceProblem::BothNonAssociative;
|
use roc_problem::can::PrecedenceProblem::BothNonAssociative;
|
||||||
use roc_problem::can::{
|
use roc_problem::can::{
|
||||||
BadPattern, ExtensionTypeKind, FloatErrorKind, IntErrorKind, Problem, RuntimeError, ShadowKind,
|
BadPattern, ExtensionTypeKind, FloatErrorKind, IntErrorKind, Problem, RuntimeError, ShadowKind,
|
||||||
|
@ -923,6 +923,26 @@ pub fn can_problem<'b>(
|
||||||
title = "UNNECESSARY DEFINITION".to_string();
|
title = "UNNECESSARY DEFINITION".to_string();
|
||||||
severity = Severity::Warning;
|
severity = Severity::Warning;
|
||||||
}
|
}
|
||||||
|
Problem::OverloadedSpecialization {
|
||||||
|
ability_member,
|
||||||
|
overload,
|
||||||
|
original_opaque,
|
||||||
|
} => {
|
||||||
|
doc = alloc.stack([
|
||||||
|
alloc.reflow("This ability member specialization is already claimed to specialize another opaque type:"),
|
||||||
|
alloc.region(lines.convert_region(overload)),
|
||||||
|
alloc.concat([
|
||||||
|
alloc.reflow("Previously, we found it to specialize "),
|
||||||
|
alloc.symbol_unqualified(ability_member),
|
||||||
|
alloc.reflow(" for "),
|
||||||
|
alloc.symbol_unqualified(original_opaque),
|
||||||
|
alloc.reflow("."),
|
||||||
|
]),
|
||||||
|
alloc.reflow("Ability specializations can only provide implementations for one opauqe type, since all opaque types are different!"),
|
||||||
|
]);
|
||||||
|
title = "OVERLOADED SPECIALIZATION".to_string();
|
||||||
|
severity = Severity::Warning;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Report {
|
Report {
|
||||||
|
@ -934,9 +954,8 @@ pub fn can_problem<'b>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_builtin_abilities<'a>(alloc: &'a RocDocAllocator<'a>) -> RocDocBuilder<'a> {
|
fn list_builtin_abilities<'a>(alloc: &'a RocDocAllocator<'a>) -> RocDocBuilder<'a> {
|
||||||
let doc = alloc.concat([alloc.symbol_qualified(BUILTIN_ABILITIES[0])]);
|
debug_assert!(DERIVABLE_ABILITIES.len() == 1);
|
||||||
debug_assert!(BUILTIN_ABILITIES.len() == 1);
|
alloc.concat([alloc.symbol_qualified(DERIVABLE_ABILITIES[0].0)])
|
||||||
doc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_invalid_optional_value_report<'b>(
|
fn to_invalid_optional_value_report<'b>(
|
||||||
|
|
|
@ -8418,16 +8418,6 @@ All branches in an `if` must have the same type!
|
||||||
The following necessary members are missing implementations:
|
The following necessary members are missing implementations:
|
||||||
|
|
||||||
le
|
le
|
||||||
|
|
||||||
── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─
|
|
||||||
|
|
||||||
The type `Id` does not fully implement the ability `Eq`. The following
|
|
||||||
specializations are missing:
|
|
||||||
|
|
||||||
A specialization for `le`, which is defined here:
|
|
||||||
|
|
||||||
5│ le : a, a -> Bool | a has Eq
|
|
||||||
^^
|
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -8473,6 +8463,19 @@ All branches in an `if` must have the same type!
|
||||||
),
|
),
|
||||||
// TODO: the error message here could be seriously improved!
|
// TODO: the error message here could be seriously improved!
|
||||||
@r###"
|
@r###"
|
||||||
|
── OVERLOADED SPECIALIZATION ───────────────────────────── /code/proj/Main.roc ─
|
||||||
|
|
||||||
|
This ability member specialization is already claimed to specialize
|
||||||
|
another opaque type:
|
||||||
|
|
||||||
|
7│ Two := {} has [Hash {hash}]
|
||||||
|
^^^^
|
||||||
|
|
||||||
|
Previously, we found it to specialize `hash` for `One`.
|
||||||
|
|
||||||
|
Ability specializations can only provide implementations for one
|
||||||
|
opauqe type, since all opaque types are different!
|
||||||
|
|
||||||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||||||
|
|
||||||
This specialization of `hash` is overly general:
|
This specialization of `hash` is overly general:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue