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
|
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 {
|
||||||
|
@ -373,7 +380,7 @@ impl IAbilitiesStore<Resolved> {
|
||||||
&self,
|
&self,
|
||||||
specializing_symbol: Symbol,
|
specializing_symbol: Symbol,
|
||||||
) -> Option<(ImplKey, &AbilityMemberData<Resolved>)> {
|
) -> 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));
|
debug_assert!(self.ability_members.contains_key(&impl_key.ability_member));
|
||||||
let root_data = self
|
let root_data = self
|
||||||
.ability_members
|
.ability_members
|
||||||
|
|
|
@ -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;
|
||||||
|
@ -671,8 +672,30 @@ fn canonicalize_opaque<'a>(
|
||||||
Err(()) => continue,
|
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 =
|
let opt_old_impl_symbol =
|
||||||
impl_map.insert(member, Loc::at(loc_impl.region, member_impl));
|
impl_map.insert(member, Loc::at(loc_impl.region, member_impl));
|
||||||
|
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -8463,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