Introdue ability member names before other value names

This commit is contained in:
Ayaz Hafiz 2022-07-18 13:32:35 -04:00
parent e672ce9ebd
commit 96b32c36cb
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
3 changed files with 118 additions and 73 deletions

View file

@ -16,7 +16,7 @@ use crate::expr::{canonicalize_expr, Output, Recursive};
use crate::pattern::{canonicalize_def_header_pattern, BindingsFromPattern, Pattern}; use crate::pattern::{canonicalize_def_header_pattern, BindingsFromPattern, Pattern};
use crate::procedure::References; use crate::procedure::References;
use crate::scope::create_alias; use crate::scope::create_alias;
use crate::scope::Scope; use crate::scope::{PendingAbilitiesInScope, Scope};
use roc_collections::ReferenceMatrix; use roc_collections::ReferenceMatrix;
use roc_collections::VecMap; use roc_collections::VecMap;
use roc_collections::{ImSet, MutMap, SendMap}; use roc_collections::{ImSet, MutMap, SendMap};
@ -27,7 +27,6 @@ use roc_module::symbol::IdentId;
use roc_module::symbol::ModuleId; use roc_module::symbol::ModuleId;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_parse::ast; use roc_parse::ast;
use roc_parse::ast::AbilityMember;
use roc_parse::ast::AssignedField; use roc_parse::ast::AssignedField;
use roc_parse::ast::Defs; use roc_parse::ast::Defs;
use roc_parse::ast::ExtractSpaces; 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)] #[derive(Debug, Clone)]
enum PendingTypeDef<'a> { enum PendingTypeDef<'a> {
/// A structural type alias, e.g. `Ints : List Int` /// A structural type alias, e.g. `Ints : List Int`
@ -162,7 +167,7 @@ enum PendingTypeDef<'a> {
Ability { Ability {
name: Loc<Symbol>, name: Loc<Symbol>,
members: &'a [ast::AbilityMember<'a>], members: Vec<PendingAbilityMember<'a>>,
}, },
/// An invalid alias, that is ignored in the rest of the pipeline /// 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_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_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() { for (index, either_index) in loc_defs.tags.iter().enumerate() {
match either_index.split() { if let Err(value_index) = either_index.split() {
Ok(type_index) => { let value_def = &loc_defs.value_defs[value_index.index()];
let type_def = &loc_defs.type_defs[type_index.index()]; let region = loc_defs.regions[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];
let pending = to_pending_value_def( let pending = to_pending_value_def(
env, env,
var_store, var_store,
&value_def, &value_def,
scope, scope,
&mut output, &pending_abilities_in_scope,
pattern_type, &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(); scope.register_debug_idents();
} }
let (aliases, symbols_introduced) = canonicalize_type_defs( let (aliases, symbols_introduced) =
env, canonicalize_type_defs(env, &mut output, var_store, scope, pending_type_defs);
&mut output,
var_store,
scope,
pending_type_defs,
pattern_type,
);
// Now that we have the scope completely assembled, and shadowing resolved, // Now that we have the scope completely assembled, and shadowing resolved,
// we're ready to canonicalize any body exprs. // we're ready to canonicalize any body exprs.
@ -832,7 +849,6 @@ fn canonicalize_type_defs<'a>(
var_store: &mut VarStore, var_store: &mut VarStore,
scope: &mut Scope, scope: &mut Scope,
pending_type_defs: Vec<PendingTypeDef<'a>>, pending_type_defs: Vec<PendingTypeDef<'a>>,
pattern_type: PatternType,
) -> (VecMap<Symbol, Alias>, MutMap<Symbol, Region>) { ) -> (VecMap<Symbol, Alias>, MutMap<Symbol, Region>) {
enum TypeDef<'a> { enum TypeDef<'a> {
Alias( Alias(
@ -846,7 +862,7 @@ fn canonicalize_type_defs<'a>(
&'a Loc<ast::TypeAnnotation<'a>>, &'a Loc<ast::TypeAnnotation<'a>>,
Option<&'a Loc<ast::HasAbilities<'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(); let mut type_defs = MutMap::default();
@ -952,7 +968,7 @@ fn canonicalize_type_defs<'a>(
TypeDef::Ability(name, members) => { TypeDef::Ability(name, members) => {
// For now we enforce that aliases cannot reference abilities, so let's wait to // 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. // 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, scope,
abilities, abilities,
&pending_abilities_in_scope, &pending_abilities_in_scope,
pattern_type,
); );
(aliases, symbols_introduced) (aliases, symbols_introduced)
@ -992,19 +1007,26 @@ fn resolve_abilities<'a>(
output: &mut Output, output: &mut Output,
var_store: &mut VarStore, var_store: &mut VarStore,
scope: &mut Scope, scope: &mut Scope,
abilities: MutMap<Symbol, (Loc<Symbol>, &[AbilityMember])>, abilities: MutMap<Symbol, Vec<PendingAbilityMember>>,
pending_abilities_in_scope: &[Symbol], 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()); 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( let member_annot = canonicalize_annotation(
env, env,
scope, scope,
&member.typ.value, &typ.value,
member.typ.region, typ.region,
var_store, var_store,
pending_abilities_in_scope, pending_abilities_in_scope,
); );
@ -1014,26 +1036,6 @@ fn resolve_abilities<'a>(
output.references.insert_type_lookup(symbol); 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 // What variables in the annotation are bound to the parent ability, and what variables
// are bound to some other ability? // are bound to some other ability?
let (variables_bound_to_ability, _variables_bound_to_other_abilities): ( let (variables_bound_to_ability, _variables_bound_to_other_abilities): (
@ -1043,7 +1045,7 @@ fn resolve_abilities<'a>(
.introduced_variables .introduced_variables
.able .able
.iter() .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() { let var_bound_to_ability = match variables_bound_to_ability.as_slice() {
[one] => one.variable, [one] => one.variable,
@ -1052,8 +1054,8 @@ fn resolve_abilities<'a>(
// need to be a part of the ability. // need to be a part of the ability.
env.problem(Problem::AbilityMemberMissingHasClause { env.problem(Problem::AbilityMemberMissingHasClause {
member: member_sym, member: member_sym,
ability: loc_ability_name.value, ability,
region: name_region, region: member_name_region,
}); });
// Pretend the member isn't a part of the ability // Pretend the member isn't a part of the ability
continue; continue;
@ -1072,7 +1074,7 @@ fn resolve_abilities<'a>(
.collect(); .collect();
env.problem(Problem::AbilityMemberMultipleBoundVars { env.problem(Problem::AbilityMemberMultipleBoundVars {
member: member_sym, member: member_sym,
ability: loc_ability_name.value, ability,
span_has_clauses, span_has_clauses,
bound_var_names, bound_var_names,
}); });
@ -1104,8 +1106,8 @@ fn resolve_abilities<'a>(
can_members.push(( can_members.push((
member_sym, member_sym,
AbilityMemberData { AbilityMemberData {
parent_ability: loc_ability_name.value, parent_ability: ability,
region: name_region, region: member_name_region,
typ: PendingMemberType::Local { typ: PendingMemberType::Local {
variables, variables,
signature, signature,
@ -1116,9 +1118,7 @@ fn resolve_abilities<'a>(
} }
// Store what symbols a type must define implementations for to have this ability. // Store what symbols a type must define implementations for to have this ability.
scope scope.abilities_store.register_ability(ability, can_members);
.abilities_store
.register_ability(loc_ability_name.value, 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 { PendingTypeDef::Ability {
name, name,
// We'll handle adding the member symbols later on when we do all value defs. members: named_members,
members,
} }
} }
} }
@ -2200,6 +2228,7 @@ fn to_pending_value_def<'a>(
var_store: &mut VarStore, var_store: &mut VarStore,
def: &'a ast::ValueDef<'a>, def: &'a ast::ValueDef<'a>,
scope: &mut Scope, scope: &mut Scope,
pending_abilities_in_scope: &PendingAbilitiesInScope,
output: &mut Output, output: &mut Output,
pattern_type: PatternType, pattern_type: PatternType,
) -> PendingValue<'a> { ) -> PendingValue<'a> {
@ -2212,6 +2241,7 @@ fn to_pending_value_def<'a>(
env, env,
var_store, var_store,
scope, scope,
pending_abilities_in_scope,
output, output,
pattern_type, pattern_type,
&loc_pattern.value, &loc_pattern.value,
@ -2230,6 +2260,7 @@ fn to_pending_value_def<'a>(
env, env,
var_store, var_store,
scope, scope,
pending_abilities_in_scope,
output, output,
pattern_type, pattern_type,
&loc_pattern.value, &loc_pattern.value,
@ -2262,6 +2293,7 @@ fn to_pending_value_def<'a>(
env, env,
var_store, var_store,
scope, scope,
pending_abilities_in_scope,
output, output,
pattern_type, pattern_type,
&body_pattern.value, &body_pattern.value,

View file

@ -5,7 +5,7 @@ use crate::num::{
finish_parsing_base, finish_parsing_float, finish_parsing_num, FloatBound, IntBound, NumBound, finish_parsing_base, finish_parsing_float, finish_parsing_num, FloatBound, IntBound, NumBound,
ParsedNumResult, ParsedNumResult,
}; };
use crate::scope::Scope; use crate::scope::{PendingAbilitiesInScope, Scope};
use roc_module::ident::{Ident, Lowercase, TagName}; use roc_module::ident::{Ident, Lowercase, TagName};
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_parse::ast::{self, StrLiteral, StrSegment}; use roc_parse::ast::{self, StrLiteral, StrSegment};
@ -179,6 +179,7 @@ pub fn canonicalize_def_header_pattern<'a>(
env: &mut Env<'a>, env: &mut Env<'a>,
var_store: &mut VarStore, var_store: &mut VarStore,
scope: &mut Scope, scope: &mut Scope,
pending_abilities_in_scope: &PendingAbilitiesInScope,
output: &mut Output, output: &mut Output,
pattern_type: PatternType, pattern_type: PatternType,
pattern: &ast::Pattern<'a>, pattern: &ast::Pattern<'a>,
@ -189,7 +190,11 @@ pub fn canonicalize_def_header_pattern<'a>(
match pattern { match pattern {
// Identifiers that shadow ability members may appear (and may only appear) at the header of a def. // Identifiers that shadow ability members may appear (and may only appear) at the header of a def.
Identifier(name) => { 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)) => { Ok((symbol, shadowing_ability_member)) => {
let can_pattern = match shadowing_ability_member { let can_pattern = match shadowing_ability_member {
// A fresh identifier. // A fresh identifier.

View file

@ -9,6 +9,9 @@ use crate::abilities::PendingAbilitiesStore;
use bitvec::vec::BitVec; use bitvec::vec::BitVec;
// ability -> member names
pub(crate) type PendingAbilitiesInScope = VecMap<Symbol, Vec<Symbol>>;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Scope { pub struct Scope {
/// The type aliases currently in scope /// The type aliases currently in scope
@ -288,6 +291,7 @@ impl Scope {
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
pub fn introduce_or_shadow_ability_member( pub fn introduce_or_shadow_ability_member(
&mut self, &mut self,
pending_abilities_in_scope: &PendingAbilitiesInScope,
ident: Ident, ident: Ident,
region: Region, region: Region,
) -> Result<(Symbol, Option<Symbol>), (Region, Loc<Ident>, Symbol)> { ) -> Result<(Symbol, Option<Symbol>), (Region, Loc<Ident>, Symbol)> {
@ -297,7 +301,11 @@ impl Scope {
Err((original_symbol, original_region)) => { Err((original_symbol, original_region)) => {
let shadow_symbol = self.scopeless_symbol(ident, 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 // TODO: remove register_specializing_symbol
self.abilities_store self.abilities_store
.register_specializing_symbol(shadow_symbol, original_symbol); .register_specializing_symbol(shadow_symbol, original_symbol);