mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 08:11:12 +00:00
Handle symbols that shadow ability member definitions
Just add the shadowing symbol for now. We'll handle checking that a specialization's type matches the member's type definition in a later pass, during typechecking.
This commit is contained in:
parent
884d07344e
commit
73bfff699f
7 changed files with 193 additions and 40 deletions
|
@ -34,6 +34,9 @@ pub struct AbilitiesStore {
|
|||
/// member `member`.
|
||||
#[allow(unused)]
|
||||
declared_implementations: MutSet<(Symbol, Symbol)>,
|
||||
|
||||
/// Cache of all ability member names in scope.
|
||||
ability_member_symbols: MutSet<Symbol>,
|
||||
}
|
||||
|
||||
impl AbilitiesStore {
|
||||
|
@ -45,7 +48,7 @@ impl AbilitiesStore {
|
|||
let mut members_vec = Vec::with_capacity(members.len());
|
||||
for (member, signature, bound_has_clauses) in members.into_iter() {
|
||||
members_vec.push(member);
|
||||
self.ability_members.insert(
|
||||
let old_member = self.ability_members.insert(
|
||||
member,
|
||||
AbilityMemberData {
|
||||
parent_ability: ability,
|
||||
|
@ -53,12 +56,26 @@ impl AbilitiesStore {
|
|||
bound_has_clauses,
|
||||
},
|
||||
);
|
||||
debug_assert!(old_member.is_none(), "Replacing existing member definition");
|
||||
|
||||
let old_member = self.ability_member_symbols.insert(member);
|
||||
debug_assert!(!old_member, "Replacing existing member entry");
|
||||
}
|
||||
self.members_of_ability.insert(ability, members_vec);
|
||||
let old_ability = self.members_of_ability.insert(ability, members_vec);
|
||||
debug_assert!(
|
||||
old_ability.is_none(),
|
||||
"Replacing existing ability definition"
|
||||
);
|
||||
}
|
||||
|
||||
pub fn register_implementation(&mut self, implementing_type: Symbol, ability_member: Symbol) {
|
||||
self.declared_implementations
|
||||
let old_impl = self
|
||||
.declared_implementations
|
||||
.insert((implementing_type, ability_member));
|
||||
debug_assert!(!old_impl, "Replacing existing implementation");
|
||||
}
|
||||
|
||||
pub fn is_ability_member_name(&self, name: Symbol) -> bool {
|
||||
self.ability_member_symbols.contains(&name)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::env::Env;
|
|||
use crate::expr::ClosureData;
|
||||
use crate::expr::Expr::{self, *};
|
||||
use crate::expr::{canonicalize_expr, local_successors_with_duplicates, Output, Recursive};
|
||||
use crate::pattern::{bindings_from_patterns, canonicalize_pattern, Pattern};
|
||||
use crate::pattern::{bindings_from_patterns, canonicalize_def_header_pattern, Pattern};
|
||||
use crate::procedure::References;
|
||||
use crate::scope::create_alias;
|
||||
use crate::scope::Scope;
|
||||
|
@ -524,7 +524,14 @@ pub fn canonicalize_defs<'a>(
|
|||
// once we've finished assembling the entire scope.
|
||||
let mut pending_value_defs = Vec::with_capacity(value_defs.len());
|
||||
for loc_def in value_defs.into_iter() {
|
||||
match to_pending_value_def(env, var_store, loc_def.value, &mut scope, pattern_type) {
|
||||
match to_pending_value_def(
|
||||
env,
|
||||
var_store,
|
||||
loc_def.value,
|
||||
&mut scope,
|
||||
&abilities_store,
|
||||
pattern_type,
|
||||
) {
|
||||
None => { /* skip */ }
|
||||
Some((new_output, pending_def)) => {
|
||||
// store the top-level defs, used to ensure that closures won't capture them
|
||||
|
@ -1011,6 +1018,13 @@ fn pattern_to_vars_by_symbol(
|
|||
vars_by_symbol.insert(*symbol, expr_var);
|
||||
}
|
||||
|
||||
AbilityMemberSpecialization {
|
||||
ident,
|
||||
specializes: _,
|
||||
} => {
|
||||
vars_by_symbol.insert(*ident, expr_var);
|
||||
}
|
||||
|
||||
AppliedTag { arguments, .. } => {
|
||||
for (var, nested) in arguments {
|
||||
pattern_to_vars_by_symbol(vars_by_symbol, &nested.value, *var);
|
||||
|
@ -1748,36 +1762,12 @@ fn to_pending_type_def<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
fn pending_typed_body<'a>(
|
||||
env: &mut Env<'a>,
|
||||
loc_pattern: &'a Loc<ast::Pattern<'a>>,
|
||||
loc_ann: &'a Loc<ast::TypeAnnotation<'a>>,
|
||||
loc_expr: &'a Loc<ast::Expr<'a>>,
|
||||
var_store: &mut VarStore,
|
||||
scope: &mut Scope,
|
||||
pattern_type: PatternType,
|
||||
) -> (Output, PendingValueDef<'a>) {
|
||||
// This takes care of checking for shadowing and adding idents to scope.
|
||||
let (output, loc_can_pattern) = canonicalize_pattern(
|
||||
env,
|
||||
var_store,
|
||||
scope,
|
||||
pattern_type,
|
||||
&loc_pattern.value,
|
||||
loc_pattern.region,
|
||||
);
|
||||
|
||||
(
|
||||
output,
|
||||
PendingValueDef::TypedBody(loc_pattern, loc_can_pattern, loc_ann, loc_expr),
|
||||
)
|
||||
}
|
||||
|
||||
fn to_pending_value_def<'a>(
|
||||
env: &mut Env<'a>,
|
||||
var_store: &mut VarStore,
|
||||
def: &'a ast::ValueDef<'a>,
|
||||
scope: &mut Scope,
|
||||
abilities_store: &AbilitiesStore,
|
||||
pattern_type: PatternType,
|
||||
) -> Option<(Output, PendingValueDef<'a>)> {
|
||||
use ast::ValueDef::*;
|
||||
|
@ -1785,10 +1775,11 @@ fn to_pending_value_def<'a>(
|
|||
match def {
|
||||
Annotation(loc_pattern, loc_ann) => {
|
||||
// This takes care of checking for shadowing and adding idents to scope.
|
||||
let (output, loc_can_pattern) = canonicalize_pattern(
|
||||
let (output, loc_can_pattern) = canonicalize_def_header_pattern(
|
||||
env,
|
||||
var_store,
|
||||
scope,
|
||||
abilities_store,
|
||||
pattern_type,
|
||||
&loc_pattern.value,
|
||||
loc_pattern.region,
|
||||
|
@ -1801,10 +1792,11 @@ fn to_pending_value_def<'a>(
|
|||
}
|
||||
Body(loc_pattern, loc_expr) => {
|
||||
// This takes care of checking for shadowing and adding idents to scope.
|
||||
let (output, loc_can_pattern) = canonicalize_pattern(
|
||||
let (output, loc_can_pattern) = canonicalize_def_header_pattern(
|
||||
env,
|
||||
var_store,
|
||||
scope,
|
||||
abilities_store,
|
||||
pattern_type,
|
||||
&loc_pattern.value,
|
||||
loc_pattern.region,
|
||||
|
@ -1829,14 +1821,21 @@ fn to_pending_value_def<'a>(
|
|||
//
|
||||
// { x, y } : { x : Int, y ? Bool }*
|
||||
// { x, y ? False } = rec
|
||||
Some(pending_typed_body(
|
||||
//
|
||||
// This takes care of checking for shadowing and adding idents to scope.
|
||||
let (output, loc_can_pattern) = canonicalize_def_header_pattern(
|
||||
env,
|
||||
body_pattern,
|
||||
ann_type,
|
||||
body_expr,
|
||||
var_store,
|
||||
scope,
|
||||
abilities_store,
|
||||
pattern_type,
|
||||
&body_pattern.value,
|
||||
body_pattern.region,
|
||||
);
|
||||
|
||||
Some((
|
||||
output,
|
||||
PendingValueDef::TypedBody(body_pattern, loc_can_pattern, ann_type, body_expr),
|
||||
))
|
||||
} else {
|
||||
// the pattern of the annotation does not match the pattern of the body direc
|
||||
|
|
|
@ -589,7 +589,8 @@ fn fix_values_captured_in_closure_pattern(
|
|||
| Shadowed(..)
|
||||
| MalformedPattern(_, _)
|
||||
| UnsupportedPattern(_)
|
||||
| OpaqueNotInScope(..) => (),
|
||||
| OpaqueNotInScope(..)
|
||||
| AbilityMemberSpecialization { .. } => (),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use crate::abilities::AbilitiesStore;
|
||||
use crate::annotation::freshen_opaque_def;
|
||||
use crate::env::Env;
|
||||
use crate::expr::{canonicalize_expr, unescape_char, Expr, IntValue, Output};
|
||||
|
@ -62,6 +63,17 @@ pub enum Pattern {
|
|||
SingleQuote(char),
|
||||
Underscore,
|
||||
|
||||
/// An identifier that marks a specialization of an ability member.
|
||||
/// For example, given an ability member definition `hash : a -> U64 | a has Hash`,
|
||||
/// there may be the specialization `hash : Bool -> U64`. In this case we generate a
|
||||
/// new symbol for the specailized "hash" identifier.
|
||||
AbilityMemberSpecialization {
|
||||
/// The symbol for this specialization.
|
||||
ident: Symbol,
|
||||
/// The ability name being specialized.
|
||||
specializes: Symbol,
|
||||
},
|
||||
|
||||
// Runtime Exceptions
|
||||
Shadowed(Region, Loc<Ident>, Symbol),
|
||||
OpaqueNotInScope(Loc<Ident>),
|
||||
|
@ -101,6 +113,11 @@ pub fn symbols_from_pattern_help(pattern: &Pattern, symbols: &mut Vec<Symbol>) {
|
|||
symbols.push(*symbol);
|
||||
}
|
||||
|
||||
AbilityMemberSpecialization { ident, specializes } => {
|
||||
symbols.push(*ident);
|
||||
symbols.push(*specializes);
|
||||
}
|
||||
|
||||
AppliedTag { arguments, .. } => {
|
||||
for (_, nested) in arguments {
|
||||
symbols_from_pattern_help(&nested.value, symbols);
|
||||
|
@ -136,6 +153,55 @@ pub fn symbols_from_pattern_help(pattern: &Pattern, symbols: &mut Vec<Symbol>) {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn canonicalize_def_header_pattern<'a>(
|
||||
env: &mut Env<'a>,
|
||||
var_store: &mut VarStore,
|
||||
scope: &mut Scope,
|
||||
abilities_store: &AbilitiesStore,
|
||||
pattern_type: PatternType,
|
||||
pattern: &ast::Pattern<'a>,
|
||||
region: Region,
|
||||
) -> (Output, Loc<Pattern>) {
|
||||
use roc_parse::ast::Pattern::*;
|
||||
|
||||
let mut output = Output::default();
|
||||
match pattern {
|
||||
// Identifiers that shadow ability members may appear (and may only appear) at the header of a def.
|
||||
Identifier(name) => match scope.introduce_or_shadow_ability_member(
|
||||
(*name).into(),
|
||||
&env.exposed_ident_ids,
|
||||
&mut env.ident_ids,
|
||||
region,
|
||||
abilities_store,
|
||||
) {
|
||||
Ok((symbol, shadowing_ability_member)) => {
|
||||
output.references.bound_symbols.insert(symbol);
|
||||
let can_pattern = match shadowing_ability_member {
|
||||
// A fresh identifier.
|
||||
None => Pattern::Identifier(symbol),
|
||||
// Likely a specialization of an ability.
|
||||
Some(ability_member_name) => Pattern::AbilityMemberSpecialization {
|
||||
ident: symbol,
|
||||
specializes: ability_member_name,
|
||||
},
|
||||
};
|
||||
(output, Loc::at(region, can_pattern))
|
||||
}
|
||||
Err((original_region, shadow, new_symbol)) => {
|
||||
env.problem(Problem::RuntimeError(RuntimeError::Shadowing {
|
||||
original_region,
|
||||
shadow: shadow.clone(),
|
||||
}));
|
||||
output.references.bound_symbols.insert(new_symbol);
|
||||
|
||||
let can_pattern = Pattern::Shadowed(original_region, shadow, new_symbol);
|
||||
(output, Loc::at(region, can_pattern))
|
||||
}
|
||||
},
|
||||
_ => canonicalize_pattern(env, var_store, scope, pattern_type, pattern, region),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn canonicalize_pattern<'a>(
|
||||
env: &mut Env<'a>,
|
||||
var_store: &mut VarStore,
|
||||
|
@ -594,7 +660,12 @@ fn add_bindings_from_patterns(
|
|||
use Pattern::*;
|
||||
|
||||
match pattern {
|
||||
Identifier(symbol) | Shadowed(_, _, symbol) => {
|
||||
Identifier(symbol)
|
||||
| Shadowed(_, _, symbol)
|
||||
| AbilityMemberSpecialization {
|
||||
ident: symbol,
|
||||
specializes: _,
|
||||
} => {
|
||||
answer.push((*symbol, *region));
|
||||
}
|
||||
AppliedTag {
|
||||
|
|
|
@ -6,6 +6,8 @@ use roc_region::all::{Loc, Region};
|
|||
use roc_types::subs::{VarStore, Variable};
|
||||
use roc_types::types::{Alias, AliasKind, Type};
|
||||
|
||||
use crate::abilities::AbilitiesStore;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Scope {
|
||||
/// All the identifiers in scope, mapped to were they were defined and
|
||||
|
@ -232,6 +234,50 @@ impl Scope {
|
|||
}
|
||||
}
|
||||
|
||||
/// Like [Self::introduce], but handles the case of when an ident matches an ability member
|
||||
/// name. In such cases a new symbol is created for the ident (since it's expected to be a
|
||||
/// specialization of the ability member), but the ident is not added to the ident->symbol map.
|
||||
///
|
||||
/// If the ident does not match an ability name, the behavior of this function is exactly that
|
||||
/// of `introduce`.
|
||||
pub fn introduce_or_shadow_ability_member(
|
||||
&mut self,
|
||||
ident: Ident,
|
||||
exposed_ident_ids: &IdentIds,
|
||||
all_ident_ids: &mut IdentIds,
|
||||
region: Region,
|
||||
abilities_store: &AbilitiesStore,
|
||||
) -> Result<(Symbol, Option<Symbol>), (Region, Loc<Ident>, Symbol)> {
|
||||
match self.idents.get(&ident) {
|
||||
Some(&(original_symbol, original_region)) => {
|
||||
let shadow_ident_id = all_ident_ids.add(ident.clone());
|
||||
let shadow_symbol = Symbol::new(self.home, shadow_ident_id);
|
||||
|
||||
self.symbols.insert(shadow_symbol, region);
|
||||
|
||||
if abilities_store.is_ability_member_name(original_symbol) {
|
||||
// Add a symbol for the shadow, but don't re-associate the member name.
|
||||
Ok((shadow_symbol, Some(original_symbol)))
|
||||
} else {
|
||||
// This is an illegal shadow.
|
||||
let shadow = Loc {
|
||||
value: ident.clone(),
|
||||
region,
|
||||
};
|
||||
|
||||
self.idents.insert(ident, (shadow_symbol, region));
|
||||
|
||||
Err((original_region, shadow, shadow_symbol))
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let new_symbol =
|
||||
self.commit_introduction(ident, exposed_ident_ids, all_ident_ids, region);
|
||||
Ok((new_symbol, None))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn commit_introduction(
|
||||
&mut self,
|
||||
ident: Ident,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue