Check whether opaque implements ability using store

This commit is contained in:
Ayaz Hafiz 2022-07-25 13:14:21 -04:00
parent 8659ddc684
commit 6b9c1cb690
No known key found for this signature in database
GPG Key ID: 0E2A37416A25EF58
4 changed files with 58 additions and 84 deletions

View File

@ -282,6 +282,25 @@ impl<Phase: ResolvePhase> IAbilitiesStore<Phase> {
self.specialization_to_root.get(&specializing_symbol)
}
/// Answers the question, "does an opaque type claim to implement a particular ability?"
///
/// Whether the given opaque typ faithfully implements or derives all members of the given ability
/// without errors is not validated.
///
/// When the given ability is not known to the current store, this call will return `false`.
pub fn has_declared_implementation(&self, opaque: Symbol, ability: Symbol) -> bool {
// Idea: choose an ability member and check whether there is a declared implementation for it.
// During canonicalization, we would have added either all members as declared
// implementations, or none if the opaque doesn't implement the ability.
match self.members_of_ability(ability) {
Some(members) => self.declared_implementations.contains_key(&ImplKey {
opaque,
ability_member: members[0],
}),
None => false,
}
}
/// 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 {

View File

@ -33,12 +33,8 @@ pub enum UnderivableReason {
#[derive(PartialEq, Debug, Clone)]
pub enum Unfulfilled {
/// Incomplete custom implementation for an ability by an opaque type.
Incomplete {
typ: Symbol,
ability: Symbol,
missing_members: Vec<Loc<Symbol>>,
},
/// No claimed implementation of an ability for an opaque type.
OpaqueDoesNotImplement { typ: Symbol, ability: Symbol },
/// Cannot derive implementation of an ability for a structural type.
AdhocUnderivable {
typ: ErrorType,
@ -352,28 +348,14 @@ impl ObligationCache<'_> {
}
let ImplKey { opaque, ability } = impl_key;
let has_declared_impl = self
.abilities_store
.has_declared_implementation(opaque, ability);
let members_of_ability = self.abilities_store.members_of_ability(ability).unwrap();
let mut missing_members = Vec::new();
for &member in members_of_ability {
if self
.abilities_store
.get_implementation(roc_can::abilities::ImplKey {
opaque,
ability_member: member,
})
.is_none()
{
let root_data = self.abilities_store.member_def(member).unwrap();
missing_members.push(Loc::at(root_data.region, member));
}
}
let obligation_result = if !missing_members.is_empty() {
Err(Unfulfilled::Incomplete {
let obligation_result = if !has_declared_impl {
Err(Unfulfilled::OpaqueDoesNotImplement {
typ: opaque,
ability,
missing_members,
})
} else {
Ok(())

View File

@ -259,30 +259,15 @@ fn report_unfulfilled_ability<'a>(
unfulfilled: Unfulfilled,
) -> RocDocBuilder<'a> {
match unfulfilled {
Unfulfilled::Incomplete {
typ,
ability,
missing_members,
} => {
debug_assert!(!missing_members.is_empty());
let mut stack = vec![alloc.concat([
Unfulfilled::OpaqueDoesNotImplement { typ, ability } => {
let stack = vec![alloc.concat([
alloc.reflow("The type "),
alloc.symbol_unqualified(typ),
alloc.reflow(" does not fully implement the ability "),
alloc.symbol_unqualified(ability),
alloc.reflow(". The following specializations are missing:"),
alloc.reflow("."),
])];
for member in missing_members.into_iter() {
stack.push(alloc.concat([
alloc.reflow("A specialization for "),
alloc.symbol_unqualified(member.value),
alloc.reflow(", which is defined here:"),
]));
stack.push(alloc.region(lines.convert_region(member.region)));
}
alloc.stack(stack)
}
Unfulfilled::AdhocUnderivable {

View File

@ -8629,35 +8629,29 @@ All branches in an `if` must have the same type!
}
"#
),
@r#"
TYPE MISMATCH /code/proj/Main.roc
@r###"
TYPE MISMATCH /code/proj/Main.roc
This expression has a type that does not implement the abilities it's expected to:
This expression has a type that does not implement the abilities it's expected to:
15 notYet: hash (A 1),
^^^
15 notYet: hash (A 1),
^^^
Roc can't generate an implementation of the `#UserApp.Hash` ability for
Roc can't generate an implementation of the `#UserApp.Hash` ability for
[A (Num a)]b
[A (Num a)]b
Only builtin abilities can have generated implementations!
Only builtin abilities can have generated implementations!
TYPE MISMATCH /code/proj/Main.roc
TYPE MISMATCH /code/proj/Main.roc
This expression has a type that does not implement the abilities it's expected to:
This expression has a type that does not implement the abilities it's expected to:
14 nope: hash (@User {}),
^^^^^^^^
14 nope: hash (@User {}),
^^^^^^^^
The type `User` does not fully implement the ability `Hash`. The following
specializations are missing:
A specialization for `hash`, which is defined here:
4 hash : a -> U64 | a has Hash
^^^^
"#
The type `User` does not fully implement the ability `Hash`.
"###
);
test_report!(
@ -9095,37 +9089,31 @@ All branches in an `if` must have the same type!
// TODO: this error message is quite unfortunate. We should remove the duplication, and
// also support regions that point to things in other modules. See also https://github.com/rtfeldman/roc/issues/3056.
@r###"
TYPE MISMATCH /code/proj/Main.roc
TYPE MISMATCH /code/proj/Main.roc
This expression has a type that does not implement the abilities it's expected to:
This expression has a type that does not implement the abilities it's expected to:
4 main = Encode.toEncoder { x: @A {} }
^^^^^^^^^^^^
4 main = Encode.toEncoder { x: @A {} }
^^^^^^^^^^^^
Roc can't generate an implementation of the `Encode.Encoding` ability
for
Roc can't generate an implementation of the `Encode.Encoding` ability
for
{ x : A }
{ x : A }
In particular, an implementation for
In particular, an implementation for
A
A
cannot be generated.
cannot be generated.
Tip: `A` does not implement `Encoding`. Consider adding a custom
implementation or `has Encode.Encoding` to the definition of `A`.
Tip: `A` does not implement `Encoding`. Consider adding a custom
implementation or `has Encode.Encoding` to the definition of `A`.
INCOMPLETE ABILITY IMPLEMENTATION /code/proj/Main.roc
INCOMPLETE ABILITY IMPLEMENTATION /code/proj/Main.roc
The type `A` does not fully implement the ability `Encoding`. The
following specializations are missing:
A specialization for `toEncoder`, which is defined here:
5
^^^^^^^^^
"###
The type `A` does not fully implement the ability `Encoding`.
"###
);
test_report!(