mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-03 19:58:18 +00:00
Support shorthand ability implementation syntax
This commit is contained in:
parent
870294b564
commit
c2154ac311
5 changed files with 71 additions and 22 deletions
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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 }
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue