Support shorthand ability implementation syntax

This commit is contained in:
Ayaz Hafiz 2022-07-18 13:56:42 -04:00
parent 870294b564
commit c2154ac311
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
5 changed files with 71 additions and 22 deletions

View file

@ -436,7 +436,7 @@ fn canonicalize_claimed_ability_impl<'a>(
let label_str = label.value; let label_str = label.value;
let region = label.region; let region = label.region;
let _member_symbol = let member_symbol =
match env.qualified_lookup_with_module_id(scope, ability_home, label_str, region) { match env.qualified_lookup_with_module_id(scope, ability_home, label_str, region) {
Ok(symbol) => symbol, Ok(symbol) => symbol,
Err(_) => { Err(_) => {
@ -449,21 +449,18 @@ fn canonicalize_claimed_ability_impl<'a>(
} }
}; };
todo!(); match scope.lookup_ability_member_shadow(member_symbol) {
// match scope.lookup(&label_str.into(), label.region) { Some(symbol) => {
// Ok(symbol) => { return Ok((member_symbol, symbol));
// return Ok((member_symbol, symbol)); }
// } None => {
// Err(_) => { env.problem(Problem::ImplementationNotFound {
// // [Eq { eq }] member: member_symbol,
region: label.region,
// env.problem(Problem::ImplementationNotFound { });
// ability, Err(())
// member: scope.lookup(), }
// region: label.region, }
// });
// }
// }
} }
AssignedField::RequiredValue(label, _spaces, value) => { AssignedField::RequiredValue(label, _spaces, value) => {
let impl_ident = match value.value { let impl_ident = match value.value {

View file

@ -30,6 +30,12 @@ pub struct Scope {
/// Identifiers that are imported (and introduced in the header) /// Identifiers that are imported (and introduced in the header)
imports: Vec<(Ident, Symbol, Region)>, imports: Vec<(Ident, Symbol, Region)>,
/// Shadows of an ability member, for example a local specialization of `eq` for the ability
/// member `Eq has eq : a, a -> Bool` gets a shadow symbol it can use for its implementation.
///
/// Only one shadow of an ability member is permitted per scope.
shadows: VecMap<Symbol, Loc<Symbol>>,
/// Identifiers that are in scope, and defined in the current module /// Identifiers that are in scope, and defined in the current module
pub locals: ScopedIdentIds, pub locals: ScopedIdentIds,
} }
@ -51,6 +57,7 @@ impl Scope {
locals: ScopedIdentIds::from_ident_ids(home, initial_ident_ids), locals: ScopedIdentIds::from_ident_ids(home, initial_ident_ids),
aliases: VecMap::default(), aliases: VecMap::default(),
abilities_store: starting_abilities_store, abilities_store: starting_abilities_store,
shadows: VecMap::default(),
imports, imports,
} }
} }
@ -59,6 +66,10 @@ impl Scope {
self.lookup_str(ident.as_str(), region) self.lookup_str(ident.as_str(), region)
} }
pub fn lookup_ability_member_shadow(&self, member: Symbol) -> Option<Symbol> {
self.shadows.get(&member).map(|loc_shadow| loc_shadow.value)
}
pub fn add_docs_imports(&mut self) { pub fn add_docs_imports(&mut self) {
self.imports self.imports
.push(("Dict".into(), Symbol::DICT_DICT, Region::zero())); .push(("Dict".into(), Symbol::DICT_DICT, Region::zero()));
@ -306,11 +317,26 @@ impl Scope {
.iter() .iter()
.any(|(_, members)| members.iter().any(|m| *m == original_symbol)) .any(|(_, members)| members.iter().any(|m| *m == original_symbol))
{ {
// TODO: remove register_specializing_symbol match self.shadows.get(&original_symbol) {
self.abilities_store Some(loc_original_shadow) => {
.register_specializing_symbol(shadow_symbol, original_symbol); // Duplicate shadow of an ability members; that's illegal.
let shadow = Loc {
value: ident.clone(),
region,
};
Err((loc_original_shadow.region, shadow, shadow_symbol))
}
None => {
// TODO: remove register_specializing_symbol
self.abilities_store
.register_specializing_symbol(shadow_symbol, original_symbol);
Ok((shadow_symbol, Some(original_symbol))) self.shadows
.insert(original_symbol, Loc::at(region, shadow_symbol));
Ok((shadow_symbol, Some(original_symbol)))
}
}
} else { } else {
// This is an illegal shadow. // This is an illegal shadow.
let shadow = Loc { let shadow = Loc {

View file

@ -131,6 +131,10 @@ pub enum Problem {
AbilityUsedAsType(Lowercase, Symbol, Region), AbilityUsedAsType(Lowercase, Symbol, Region),
NestedSpecialization(Symbol, Region), NestedSpecialization(Symbol, Region),
IllegalClaimedAbility(Region), IllegalClaimedAbility(Region),
ImplementationNotFound {
member: Symbol,
region: Region,
},
NotAnAbilityMember { NotAnAbilityMember {
ability: Symbol, ability: Symbol,
name: String, name: String,

View file

@ -48,6 +48,7 @@ const ABILITY_NOT_ON_TOPLEVEL: &str = "ABILITY NOT ON TOP-LEVEL";
const SPECIALIZATION_NOT_ON_TOPLEVEL: &str = "SPECIALIZATION NOT ON TOP-LEVEL"; const SPECIALIZATION_NOT_ON_TOPLEVEL: &str = "SPECIALIZATION NOT ON TOP-LEVEL";
const ABILITY_USED_AS_TYPE: &str = "ABILITY USED AS TYPE"; const ABILITY_USED_AS_TYPE: &str = "ABILITY USED AS TYPE";
const ILLEGAL_DERIVE: &str = "ILLEGAL DERIVE"; const ILLEGAL_DERIVE: &str = "ILLEGAL DERIVE";
const IMPLEMENTATION_NOT_FOUND: &str = "IMPLEMENTATION NOT FOUND";
const NOT_AN_ABILITY_MEMBER: &str = "NOT AN ABILITY MEMBER"; const NOT_AN_ABILITY_MEMBER: &str = "NOT AN ABILITY MEMBER";
const OPTIONAL_ABILITY_IMPLEMENTATION: &str = "OPTIONAL ABILITY IMPLEMENTATION"; const OPTIONAL_ABILITY_IMPLEMENTATION: &str = "OPTIONAL ABILITY IMPLEMENTATION";
const QUALIFIED_ABILITY_IMPLEMENTATION: &str = "QUALIFIED ABILITY IMPLEMENTATION"; const QUALIFIED_ABILITY_IMPLEMENTATION: &str = "QUALIFIED ABILITY IMPLEMENTATION";
@ -769,6 +770,18 @@ pub fn can_problem<'b>(
title = NOT_AN_ABILITY_MEMBER.to_string(); title = NOT_AN_ABILITY_MEMBER.to_string();
severity = Severity::RuntimeError; severity = Severity::RuntimeError;
} }
Problem::ImplementationNotFound { member, region } => {
let member_str = member.as_str(alloc.interns);
doc = alloc.stack([
alloc.concat([
alloc.reflow("An implementation of "), alloc.symbol_unqualified(member), alloc.reflow(" could not be found in this scope:"),
]),
alloc.region(lines.convert_region(region)),
alloc.tip().append(alloc.concat([alloc.reflow("consider adding a value of name "), alloc.symbol_unqualified(member), alloc.reflow(" in this scope, or using another variable that implements this ability member, like "), alloc.type_str(&format!("{{ {}: my{} }}", member_str, member_str))]))
]);
title = IMPLEMENTATION_NOT_FOUND.to_string();
severity = Severity::RuntimeError;
}
Problem::OptionalAbilityImpl { ability, region } => { Problem::OptionalAbilityImpl { ability, region } => {
let hint = if ability.is_builtin() { let hint = if ability.is_builtin() {
alloc.hint("").append( alloc.hint("").append(

View file

@ -9134,11 +9134,10 @@ All branches in an `if` must have the same type!
); );
test_report!( test_report!(
#[ignore="TODO"]
opaque_ability_impl_not_found_shorthand_syntax, opaque_ability_impl_not_found_shorthand_syntax,
indoc!( indoc!(
r#" r#"
app "test" provides [] to "./platform" app "test" provides [A] to "./platform"
Eq has eq : a, a -> U64 | a has Eq Eq has eq : a, a -> U64 | a has Eq
@ -9146,6 +9145,16 @@ All branches in an `if` must have the same type!
"# "#
), ),
@r###" @r###"
IMPLEMENTATION NOT FOUND /code/proj/Main.roc
An implementation of `eq` could not be found in this scope:
5 A := U8 has [Eq {eq}]
^^
Tip: consider adding a value of name `eq` in this scope, or using
another variable that implements this ability member, like
{ eq: myeq }
"### "###
); );