mirror of
https://github.com/roc-lang/roc.git
synced 2025-07-24 06:55:15 +00:00
Detect ability specializations that overload different opaque types
This commit is contained in:
parent
51bce825fb
commit
10db3f8574
5 changed files with 70 additions and 2 deletions
|
@ -279,6 +279,13 @@ impl<Phase: ResolvePhase> IAbilitiesStore<Phase> {
|
|||
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
|
||||
/// imported `symbols`, and their specializations (if any).
|
||||
pub fn closure_from_imported(&self, symbols: &VecSet<Symbol>) -> PendingAbilitiesStore {
|
||||
|
@ -373,7 +380,7 @@ impl IAbilitiesStore<Resolved> {
|
|||
&self,
|
||||
specializing_symbol: Symbol,
|
||||
) -> Option<(ImplKey, &AbilityMemberData<Resolved>)> {
|
||||
let impl_key = self.specialization_to_root.get(&specializing_symbol)?;
|
||||
let impl_key = self.impl_key(specializing_symbol)?;
|
||||
debug_assert!(self.ability_members.contains_key(&impl_key.ability_member));
|
||||
let root_data = self
|
||||
.ability_members
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::abilities::AbilityMemberData;
|
||||
use crate::abilities::ImplKey;
|
||||
use crate::abilities::MemberVariables;
|
||||
use crate::abilities::PendingMemberType;
|
||||
use crate::annotation::canonicalize_annotation;
|
||||
|
@ -671,8 +672,30 @@ fn canonicalize_opaque<'a>(
|
|||
Err(()) => continue,
|
||||
};
|
||||
|
||||
let member_impl = MemberImpl::Impl(impl_symbol);
|
||||
// Did the user claim this implementation for a specialization of a different
|
||||
// type? e.g.
|
||||
//
|
||||
// A has [Hash {hash: myHash}]
|
||||
// B has [Hash {hash: myHash}]
|
||||
//
|
||||
// If so, that's an error and we drop the impl for this opaque type.
|
||||
let member_impl = match scope.abilities_store.impl_key(impl_symbol) {
|
||||
Some(ImplKey {
|
||||
opaque,
|
||||
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));
|
||||
|
||||
|
|
|
@ -169,6 +169,11 @@ pub enum Problem {
|
|||
region: Region,
|
||||
},
|
||||
NoIdentifiersIntroduced(Region),
|
||||
OverloadedSpecialization {
|
||||
overload: Region,
|
||||
original_opaque: Symbol,
|
||||
ability_member: Symbol,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
|
|
|
@ -923,6 +923,26 @@ pub fn can_problem<'b>(
|
|||
title = "UNNECESSARY DEFINITION".to_string();
|
||||
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 {
|
||||
|
|
|
@ -8463,6 +8463,19 @@ All branches in an `if` must have the same type!
|
|||
),
|
||||
// TODO: the error message here could be seriously improved!
|
||||
@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 ─
|
||||
|
||||
This specialization of `hash` is overly general:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue