mirror of
https://github.com/roc-lang/roc.git
synced 2025-07-24 06:55:15 +00:00
Introdue ability member names before other value names
This commit is contained in:
parent
e672ce9ebd
commit
96b32c36cb
3 changed files with 118 additions and 73 deletions
|
@ -16,7 +16,7 @@ use crate::expr::{canonicalize_expr, Output, Recursive};
|
|||
use crate::pattern::{canonicalize_def_header_pattern, BindingsFromPattern, Pattern};
|
||||
use crate::procedure::References;
|
||||
use crate::scope::create_alias;
|
||||
use crate::scope::Scope;
|
||||
use crate::scope::{PendingAbilitiesInScope, Scope};
|
||||
use roc_collections::ReferenceMatrix;
|
||||
use roc_collections::VecMap;
|
||||
use roc_collections::{ImSet, MutMap, SendMap};
|
||||
|
@ -27,7 +27,6 @@ use roc_module::symbol::IdentId;
|
|||
use roc_module::symbol::ModuleId;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_parse::ast;
|
||||
use roc_parse::ast::AbilityMember;
|
||||
use roc_parse::ast::AssignedField;
|
||||
use roc_parse::ast::Defs;
|
||||
use roc_parse::ast::ExtractSpaces;
|
||||
|
@ -143,6 +142,12 @@ impl PendingValueDef<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct PendingAbilityMember<'a> {
|
||||
name: Loc<Symbol>,
|
||||
typ: Loc<ast::TypeAnnotation<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum PendingTypeDef<'a> {
|
||||
/// A structural type alias, e.g. `Ints : List Int`
|
||||
|
@ -162,7 +167,7 @@ enum PendingTypeDef<'a> {
|
|||
|
||||
Ability {
|
||||
name: Loc<Symbol>,
|
||||
members: &'a [ast::AbilityMember<'a>],
|
||||
members: Vec<PendingAbilityMember<'a>>,
|
||||
},
|
||||
|
||||
/// An invalid alias, that is ignored in the rest of the pipeline
|
||||
|
@ -669,28 +674,46 @@ pub(crate) fn canonicalize_defs<'a>(
|
|||
|
||||
let mut pending_type_defs = Vec::with_capacity(loc_defs.type_defs.len());
|
||||
let mut pending_value_defs = Vec::with_capacity(loc_defs.value_defs.len());
|
||||
let mut pending_abilities_in_scope = PendingAbilitiesInScope::default();
|
||||
|
||||
// Convert the type defs into pending defs first, then all the value defs.
|
||||
// Follow this order because we need all value symbols to fully canonicalize type defs (in case
|
||||
// there are opaques that implement an ability using a value symbol). But, value symbols might
|
||||
// shadow symbols defined in a local ability def.
|
||||
|
||||
for (_, either_index) in loc_defs.tags.iter().enumerate() {
|
||||
if let Ok(type_index) = either_index.split() {
|
||||
let type_def = &loc_defs.type_defs[type_index.index()];
|
||||
let pending_type_def = to_pending_type_def(env, type_def, scope, pattern_type);
|
||||
match &pending_type_def {
|
||||
PendingTypeDef::Ability { name, members } => {
|
||||
pending_abilities_in_scope.insert(
|
||||
name.value,
|
||||
members.iter().map(|mem| mem.name.value).collect(),
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
pending_type_defs.push(pending_type_def);
|
||||
}
|
||||
}
|
||||
|
||||
for (index, either_index) in loc_defs.tags.iter().enumerate() {
|
||||
match either_index.split() {
|
||||
Ok(type_index) => {
|
||||
let type_def = &loc_defs.type_defs[type_index.index()];
|
||||
pending_type_defs.push(to_pending_type_def(env, type_def, scope, pattern_type));
|
||||
}
|
||||
Err(value_index) => {
|
||||
let value_def = &loc_defs.value_defs[value_index.index()];
|
||||
let region = loc_defs.regions[index];
|
||||
if let Err(value_index) = either_index.split() {
|
||||
let value_def = &loc_defs.value_defs[value_index.index()];
|
||||
let region = loc_defs.regions[index];
|
||||
|
||||
let pending = to_pending_value_def(
|
||||
env,
|
||||
var_store,
|
||||
&value_def,
|
||||
scope,
|
||||
&mut output,
|
||||
pattern_type,
|
||||
);
|
||||
let pending = to_pending_value_def(
|
||||
env,
|
||||
var_store,
|
||||
&value_def,
|
||||
scope,
|
||||
&pending_abilities_in_scope,
|
||||
&mut output,
|
||||
pattern_type,
|
||||
);
|
||||
|
||||
pending_value_defs.push(Loc::at(region, pending));
|
||||
}
|
||||
pending_value_defs.push(Loc::at(region, pending));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -698,14 +721,8 @@ pub(crate) fn canonicalize_defs<'a>(
|
|||
scope.register_debug_idents();
|
||||
}
|
||||
|
||||
let (aliases, symbols_introduced) = canonicalize_type_defs(
|
||||
env,
|
||||
&mut output,
|
||||
var_store,
|
||||
scope,
|
||||
pending_type_defs,
|
||||
pattern_type,
|
||||
);
|
||||
let (aliases, symbols_introduced) =
|
||||
canonicalize_type_defs(env, &mut output, var_store, scope, pending_type_defs);
|
||||
|
||||
// Now that we have the scope completely assembled, and shadowing resolved,
|
||||
// we're ready to canonicalize any body exprs.
|
||||
|
@ -832,7 +849,6 @@ fn canonicalize_type_defs<'a>(
|
|||
var_store: &mut VarStore,
|
||||
scope: &mut Scope,
|
||||
pending_type_defs: Vec<PendingTypeDef<'a>>,
|
||||
pattern_type: PatternType,
|
||||
) -> (VecMap<Symbol, Alias>, MutMap<Symbol, Region>) {
|
||||
enum TypeDef<'a> {
|
||||
Alias(
|
||||
|
@ -846,7 +862,7 @@ fn canonicalize_type_defs<'a>(
|
|||
&'a Loc<ast::TypeAnnotation<'a>>,
|
||||
Option<&'a Loc<ast::HasAbilities<'a>>>,
|
||||
),
|
||||
Ability(Loc<Symbol>, &'a [AbilityMember<'a>]),
|
||||
Ability(Loc<Symbol>, Vec<PendingAbilityMember<'a>>),
|
||||
}
|
||||
|
||||
let mut type_defs = MutMap::default();
|
||||
|
@ -952,7 +968,7 @@ fn canonicalize_type_defs<'a>(
|
|||
TypeDef::Ability(name, members) => {
|
||||
// For now we enforce that aliases cannot reference abilities, so let's wait to
|
||||
// resolve ability definitions until aliases are resolved and in scope below.
|
||||
abilities.insert(name.value, (name, members));
|
||||
abilities.insert(name.value, members);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -979,7 +995,6 @@ fn canonicalize_type_defs<'a>(
|
|||
scope,
|
||||
abilities,
|
||||
&pending_abilities_in_scope,
|
||||
pattern_type,
|
||||
);
|
||||
|
||||
(aliases, symbols_introduced)
|
||||
|
@ -992,19 +1007,26 @@ fn resolve_abilities<'a>(
|
|||
output: &mut Output,
|
||||
var_store: &mut VarStore,
|
||||
scope: &mut Scope,
|
||||
abilities: MutMap<Symbol, (Loc<Symbol>, &[AbilityMember])>,
|
||||
abilities: MutMap<Symbol, Vec<PendingAbilityMember>>,
|
||||
pending_abilities_in_scope: &[Symbol],
|
||||
pattern_type: PatternType,
|
||||
) {
|
||||
for (loc_ability_name, members) in abilities.into_values() {
|
||||
for (ability, members) in abilities {
|
||||
let mut can_members = Vec::with_capacity(members.len());
|
||||
|
||||
for member in members {
|
||||
for PendingAbilityMember {
|
||||
name:
|
||||
Loc {
|
||||
value: member_sym,
|
||||
region: member_name_region,
|
||||
},
|
||||
typ,
|
||||
} in members
|
||||
{
|
||||
let member_annot = canonicalize_annotation(
|
||||
env,
|
||||
scope,
|
||||
&member.typ.value,
|
||||
member.typ.region,
|
||||
&typ.value,
|
||||
typ.region,
|
||||
var_store,
|
||||
pending_abilities_in_scope,
|
||||
);
|
||||
|
@ -1014,26 +1036,6 @@ fn resolve_abilities<'a>(
|
|||
output.references.insert_type_lookup(symbol);
|
||||
}
|
||||
|
||||
let name_region = member.name.region;
|
||||
let member_name = member.name.extract_spaces().item;
|
||||
|
||||
let member_sym = match scope.introduce(member_name.into(), name_region) {
|
||||
Ok(sym) => sym,
|
||||
Err((original_region, shadow, _new_symbol)) => {
|
||||
env.problem(roc_problem::can::Problem::Shadowing {
|
||||
original_region,
|
||||
shadow,
|
||||
kind: ShadowKind::Variable,
|
||||
});
|
||||
// Pretend the member isn't a part of the ability
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
if pattern_type == PatternType::TopLevelDef {
|
||||
env.top_level_symbols.insert(member_sym);
|
||||
}
|
||||
|
||||
// What variables in the annotation are bound to the parent ability, and what variables
|
||||
// are bound to some other ability?
|
||||
let (variables_bound_to_ability, _variables_bound_to_other_abilities): (
|
||||
|
@ -1043,7 +1045,7 @@ fn resolve_abilities<'a>(
|
|||
.introduced_variables
|
||||
.able
|
||||
.iter()
|
||||
.partition(|av| av.ability == loc_ability_name.value);
|
||||
.partition(|av| av.ability == ability);
|
||||
|
||||
let var_bound_to_ability = match variables_bound_to_ability.as_slice() {
|
||||
[one] => one.variable,
|
||||
|
@ -1052,8 +1054,8 @@ fn resolve_abilities<'a>(
|
|||
// need to be a part of the ability.
|
||||
env.problem(Problem::AbilityMemberMissingHasClause {
|
||||
member: member_sym,
|
||||
ability: loc_ability_name.value,
|
||||
region: name_region,
|
||||
ability,
|
||||
region: member_name_region,
|
||||
});
|
||||
// Pretend the member isn't a part of the ability
|
||||
continue;
|
||||
|
@ -1072,7 +1074,7 @@ fn resolve_abilities<'a>(
|
|||
.collect();
|
||||
env.problem(Problem::AbilityMemberMultipleBoundVars {
|
||||
member: member_sym,
|
||||
ability: loc_ability_name.value,
|
||||
ability,
|
||||
span_has_clauses,
|
||||
bound_var_names,
|
||||
});
|
||||
|
@ -1104,8 +1106,8 @@ fn resolve_abilities<'a>(
|
|||
can_members.push((
|
||||
member_sym,
|
||||
AbilityMemberData {
|
||||
parent_ability: loc_ability_name.value,
|
||||
region: name_region,
|
||||
parent_ability: ability,
|
||||
region: member_name_region,
|
||||
typ: PendingMemberType::Local {
|
||||
variables,
|
||||
signature,
|
||||
|
@ -1116,9 +1118,7 @@ fn resolve_abilities<'a>(
|
|||
}
|
||||
|
||||
// Store what symbols a type must define implementations for to have this ability.
|
||||
scope
|
||||
.abilities_store
|
||||
.register_ability(loc_ability_name.value, can_members);
|
||||
scope.abilities_store.register_ability(ability, can_members);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2175,10 +2175,38 @@ fn to_pending_type_def<'a>(
|
|||
};
|
||||
}
|
||||
|
||||
let mut named_members = Vec::with_capacity(members.len());
|
||||
|
||||
for member in *members {
|
||||
let name_region = member.name.region;
|
||||
let member_name = member.name.extract_spaces().item;
|
||||
|
||||
let member_sym = match scope.introduce(member_name.into(), name_region) {
|
||||
Ok(sym) => sym,
|
||||
Err((original_region, shadow, _new_symbol)) => {
|
||||
env.problem(roc_problem::can::Problem::Shadowing {
|
||||
original_region,
|
||||
shadow,
|
||||
kind: ShadowKind::Variable,
|
||||
});
|
||||
// Pretend the member isn't a part of the ability
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
named_members.push(PendingAbilityMember {
|
||||
name: Loc::at(name_region, member_sym),
|
||||
typ: member.typ,
|
||||
});
|
||||
|
||||
if pattern_type == PatternType::TopLevelDef {
|
||||
env.top_level_symbols.insert(member_sym);
|
||||
}
|
||||
}
|
||||
|
||||
PendingTypeDef::Ability {
|
||||
name,
|
||||
// We'll handle adding the member symbols later on when we do all value defs.
|
||||
members,
|
||||
members: named_members,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2200,6 +2228,7 @@ fn to_pending_value_def<'a>(
|
|||
var_store: &mut VarStore,
|
||||
def: &'a ast::ValueDef<'a>,
|
||||
scope: &mut Scope,
|
||||
pending_abilities_in_scope: &PendingAbilitiesInScope,
|
||||
output: &mut Output,
|
||||
pattern_type: PatternType,
|
||||
) -> PendingValue<'a> {
|
||||
|
@ -2212,6 +2241,7 @@ fn to_pending_value_def<'a>(
|
|||
env,
|
||||
var_store,
|
||||
scope,
|
||||
pending_abilities_in_scope,
|
||||
output,
|
||||
pattern_type,
|
||||
&loc_pattern.value,
|
||||
|
@ -2230,6 +2260,7 @@ fn to_pending_value_def<'a>(
|
|||
env,
|
||||
var_store,
|
||||
scope,
|
||||
pending_abilities_in_scope,
|
||||
output,
|
||||
pattern_type,
|
||||
&loc_pattern.value,
|
||||
|
@ -2262,6 +2293,7 @@ fn to_pending_value_def<'a>(
|
|||
env,
|
||||
var_store,
|
||||
scope,
|
||||
pending_abilities_in_scope,
|
||||
output,
|
||||
pattern_type,
|
||||
&body_pattern.value,
|
||||
|
|
|
@ -5,7 +5,7 @@ use crate::num::{
|
|||
finish_parsing_base, finish_parsing_float, finish_parsing_num, FloatBound, IntBound, NumBound,
|
||||
ParsedNumResult,
|
||||
};
|
||||
use crate::scope::Scope;
|
||||
use crate::scope::{PendingAbilitiesInScope, Scope};
|
||||
use roc_module::ident::{Ident, Lowercase, TagName};
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_parse::ast::{self, StrLiteral, StrSegment};
|
||||
|
@ -179,6 +179,7 @@ pub fn canonicalize_def_header_pattern<'a>(
|
|||
env: &mut Env<'a>,
|
||||
var_store: &mut VarStore,
|
||||
scope: &mut Scope,
|
||||
pending_abilities_in_scope: &PendingAbilitiesInScope,
|
||||
output: &mut Output,
|
||||
pattern_type: PatternType,
|
||||
pattern: &ast::Pattern<'a>,
|
||||
|
@ -189,7 +190,11 @@ pub fn canonicalize_def_header_pattern<'a>(
|
|||
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(), region) {
|
||||
match scope.introduce_or_shadow_ability_member(
|
||||
pending_abilities_in_scope,
|
||||
(*name).into(),
|
||||
region,
|
||||
) {
|
||||
Ok((symbol, shadowing_ability_member)) => {
|
||||
let can_pattern = match shadowing_ability_member {
|
||||
// A fresh identifier.
|
||||
|
|
|
@ -9,6 +9,9 @@ use crate::abilities::PendingAbilitiesStore;
|
|||
|
||||
use bitvec::vec::BitVec;
|
||||
|
||||
// ability -> member names
|
||||
pub(crate) type PendingAbilitiesInScope = VecMap<Symbol, Vec<Symbol>>;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Scope {
|
||||
/// The type aliases currently in scope
|
||||
|
@ -288,6 +291,7 @@ impl Scope {
|
|||
#[allow(clippy::type_complexity)]
|
||||
pub fn introduce_or_shadow_ability_member(
|
||||
&mut self,
|
||||
pending_abilities_in_scope: &PendingAbilitiesInScope,
|
||||
ident: Ident,
|
||||
region: Region,
|
||||
) -> Result<(Symbol, Option<Symbol>), (Region, Loc<Ident>, Symbol)> {
|
||||
|
@ -297,7 +301,11 @@ impl Scope {
|
|||
Err((original_symbol, original_region)) => {
|
||||
let shadow_symbol = self.scopeless_symbol(ident, region);
|
||||
|
||||
if self.abilities_store.is_ability_member_name(original_symbol) {
|
||||
if self.abilities_store.is_ability_member_name(original_symbol)
|
||||
|| pending_abilities_in_scope
|
||||
.iter()
|
||||
.any(|(_, members)| members.iter().any(|m| *m == original_symbol))
|
||||
{
|
||||
// TODO: remove register_specializing_symbol
|
||||
self.abilities_store
|
||||
.register_specializing_symbol(shadow_symbol, original_symbol);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue