mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-03 11:52:19 +00:00
Represent "able" variables with slices of abilities
This commit is contained in:
parent
0f0678ce73
commit
229548571b
10 changed files with 163 additions and 85 deletions
|
@ -105,7 +105,7 @@ impl OwnedNamedOrAble {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn opt_abilities(&self) -> Option<&[Symbol]> {
|
||||
pub fn opt_abilities(&self) -> Option<&VecSet<Symbol>> {
|
||||
match self {
|
||||
OwnedNamedOrAble::Named(_) => None,
|
||||
OwnedNamedOrAble::Able(av) => Some(&av.abilities),
|
||||
|
@ -127,6 +127,8 @@ pub struct NamedVariable {
|
|||
pub struct AbleVariable {
|
||||
pub variable: Variable,
|
||||
pub name: Lowercase,
|
||||
/// Abilities bound to this type variable.
|
||||
/// INVARIANT: sorted and de-duplicated.
|
||||
pub abilities: VecSet<Symbol>,
|
||||
// NB: there may be multiple occurrences of a variable
|
||||
pub first_seen: Region,
|
||||
|
@ -984,6 +986,11 @@ fn canonicalize_has_clause(
|
|||
|
||||
let var = var_store.fresh();
|
||||
|
||||
let can_abilities = {
|
||||
let mut vec = can_abilities.into_vec();
|
||||
vec.sort();
|
||||
VecSet::from_iter(vec)
|
||||
};
|
||||
introduced_variables.insert_able(var_name, Loc::at(region, var), can_abilities);
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -1044,8 +1044,8 @@ mod test {
|
|||
use roc_region::all::Loc;
|
||||
use roc_types::{
|
||||
subs::{
|
||||
self, Content, Content::*, Descriptor, FlatType, Mark, OptVariable, Rank, Subs,
|
||||
SubsIndex, SubsSlice, Variable,
|
||||
self, Content, Content::*, Descriptor, FlatType, GetSubsSlice, Mark, OptVariable, Rank,
|
||||
Subs, SubsIndex, SubsSlice, Variable,
|
||||
},
|
||||
types::Uls,
|
||||
};
|
||||
|
@ -1107,7 +1107,8 @@ mod test {
|
|||
let mut subs = Subs::new();
|
||||
|
||||
let field_name = SubsIndex::push_new(&mut subs.field_names, "a".into());
|
||||
let var = new_var(&mut subs, FlexAbleVar(Some(field_name), Symbol::UNDERSCORE));
|
||||
let abilities = SubsSlice::extend_new(&mut subs.symbol_names, [Symbol::UNDERSCORE]);
|
||||
let var = new_var(&mut subs, FlexAbleVar(Some(field_name), abilities));
|
||||
|
||||
let mut copied = vec![];
|
||||
|
||||
|
@ -1116,8 +1117,9 @@ mod test {
|
|||
assert_ne!(var, copy);
|
||||
|
||||
match subs.get_content_without_compacting(var) {
|
||||
FlexAbleVar(Some(name), Symbol::UNDERSCORE) => {
|
||||
FlexAbleVar(Some(name), abilities) => {
|
||||
assert_eq!(subs[*name].as_str(), "a");
|
||||
assert_eq!(subs.get_subs_slice(*abilities), [Symbol::UNDERSCORE]);
|
||||
}
|
||||
it => unreachable!("{:?}", it),
|
||||
}
|
||||
|
@ -1128,7 +1130,8 @@ mod test {
|
|||
let mut subs = Subs::new();
|
||||
|
||||
let field_name = SubsIndex::push_new(&mut subs.field_names, "a".into());
|
||||
let var = new_var(&mut subs, RigidAbleVar(field_name, Symbol::UNDERSCORE));
|
||||
let abilities = SubsSlice::extend_new(&mut subs.symbol_names, [Symbol::UNDERSCORE]);
|
||||
let var = new_var(&mut subs, RigidAbleVar(field_name, abilities));
|
||||
|
||||
let mut copied = vec![];
|
||||
|
||||
|
@ -1136,8 +1139,9 @@ mod test {
|
|||
|
||||
assert_ne!(var, copy);
|
||||
match subs.get_content_without_compacting(var) {
|
||||
RigidAbleVar(name, Symbol::UNDERSCORE) => {
|
||||
RigidAbleVar(name, abilities) => {
|
||||
assert_eq!(subs[*name].as_str(), "a");
|
||||
assert_eq!(subs.get_subs_slice(*abilities), [Symbol::UNDERSCORE]);
|
||||
}
|
||||
it => internal_error!("{:?}", it),
|
||||
}
|
||||
|
|
|
@ -19,7 +19,8 @@ use roc_types::{
|
|||
num::int_lit_width_to_variable,
|
||||
subs::{
|
||||
Content, ExhaustiveMark, FlatType, GetSubsSlice, LambdaSet, OptVariable, RecordFields,
|
||||
RedundantMark, SubsIndex, SubsSlice, UnionLambdas, UnionTags, Variable, VariableSubsSlice,
|
||||
RedundantMark, Subs, SubsIndex, SubsSlice, UnionLambdas, UnionTags, Variable,
|
||||
VariableSubsSlice,
|
||||
},
|
||||
types::RecordField,
|
||||
};
|
||||
|
@ -87,7 +88,7 @@ fn hash_record(env: &mut Env<'_>, fn_name: Symbol, fields: Vec<Lowercase>) -> (V
|
|||
let rcd_sym = env.new_symbol("rcd");
|
||||
|
||||
let hasher_sym = env.new_symbol("hasher");
|
||||
let hasher_var = synth_var(env.subs, Content::FlexAbleVar(None, Symbol::HASH_HASHER));
|
||||
let hasher_var = synth_var(env.subs, Content::FlexAbleVar(None, Subs::AB_HASHER));
|
||||
|
||||
let (body_var, body) = record_fields.iter_all().fold(
|
||||
(hasher_var, Expr::Var(hasher_sym, hasher_var)),
|
||||
|
@ -165,7 +166,7 @@ fn hash_tag_union(
|
|||
let union_sym = env.new_symbol("union");
|
||||
|
||||
let hasher_sym = env.new_symbol("hasher");
|
||||
let hasher_var = synth_var(env.subs, Content::FlexAbleVar(None, Symbol::HASH_HASHER));
|
||||
let hasher_var = synth_var(env.subs, Content::FlexAbleVar(None, Subs::AB_HASHER));
|
||||
|
||||
let (discr_width, discr_precision_var, hash_discr_member) = if union_tags.len() > u64::MAX as _
|
||||
{
|
||||
|
@ -320,7 +321,7 @@ fn hash_newtype_tag_union(
|
|||
// hash_union = \hasher, A x1 .. xn ->
|
||||
// Hash.hash (... (Hash.hash discrHasher x1) ...) xn
|
||||
let hasher_sym = env.new_symbol("hasher");
|
||||
let hasher_var = synth_var(env.subs, Content::FlexAbleVar(None, Symbol::HASH_HASHER));
|
||||
let hasher_var = synth_var(env.subs, Content::FlexAbleVar(None, Subs::AB_HASHER));
|
||||
|
||||
// A
|
||||
let tag_name = tag_name;
|
||||
|
|
|
@ -10,7 +10,8 @@ use roc_solve_problem::{
|
|||
};
|
||||
use roc_types::num::NumericRange;
|
||||
use roc_types::subs::{
|
||||
instantiate_rigids, Content, FlatType, GetSubsSlice, Rank, RecordFields, Subs, Variable,
|
||||
instantiate_rigids, Content, FlatType, GetSubsSlice, Rank, RecordFields, Subs, SubsSlice,
|
||||
Variable,
|
||||
};
|
||||
use roc_types::types::{AliasKind, Category, MemberImpl, PatternCategory};
|
||||
use roc_unify::unify::{Env, MustImplementConstraints};
|
||||
|
@ -486,6 +487,7 @@ struct Descend(bool);
|
|||
|
||||
trait DerivableVisitor {
|
||||
const ABILITY: Symbol;
|
||||
const ABILITY_SLICE: SubsSlice<Symbol>;
|
||||
|
||||
#[inline(always)]
|
||||
fn is_derivable_builtin_opaque(_symbol: Symbol) -> bool {
|
||||
|
@ -493,8 +495,9 @@ trait DerivableVisitor {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn visit_flex_able(var: Variable, ability: Symbol) -> Result<(), NotDerivable> {
|
||||
if ability != Self::ABILITY {
|
||||
fn visit_flex_able(var: Variable, abilities: &[Symbol]) -> Result<(), NotDerivable> {
|
||||
// TODO(multi-abilities) flex-able can inherit other abilities
|
||||
if abilities != [Self::ABILITY] {
|
||||
Err(NotDerivable {
|
||||
var,
|
||||
context: NotDerivableContext::NoContext,
|
||||
|
@ -505,8 +508,8 @@ trait DerivableVisitor {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn visit_rigid_able(var: Variable, ability: Symbol) -> Result<(), NotDerivable> {
|
||||
if ability != Self::ABILITY {
|
||||
fn visit_rigid_able(var: Variable, abilities: &[Symbol]) -> Result<(), NotDerivable> {
|
||||
if abilities != [Self::ABILITY] {
|
||||
Err(NotDerivable {
|
||||
var,
|
||||
context: NotDerivableContext::NoContext,
|
||||
|
@ -636,7 +639,7 @@ trait DerivableVisitor {
|
|||
match *content {
|
||||
FlexVar(opt_name) => {
|
||||
// Promote the flex var to be bound to the ability.
|
||||
subs.set_content(var, Content::FlexAbleVar(opt_name, Self::ABILITY));
|
||||
subs.set_content(var, Content::FlexAbleVar(opt_name, Self::ABILITY_SLICE));
|
||||
}
|
||||
RigidVar(_) => {
|
||||
return Err(NotDerivable {
|
||||
|
@ -644,8 +647,12 @@ trait DerivableVisitor {
|
|||
context: NotDerivableContext::NoContext,
|
||||
})
|
||||
}
|
||||
FlexAbleVar(_, ability) => Self::visit_flex_able(var, ability)?,
|
||||
RigidAbleVar(_, ability) => Self::visit_rigid_able(var, ability)?,
|
||||
FlexAbleVar(_, abilities) => {
|
||||
Self::visit_flex_able(var, subs.get_subs_slice(abilities))?
|
||||
}
|
||||
RigidAbleVar(_, abilities) => {
|
||||
Self::visit_rigid_able(var, subs.get_subs_slice(abilities))?
|
||||
}
|
||||
RecursionVar {
|
||||
structure,
|
||||
opt_name: _,
|
||||
|
@ -771,6 +778,7 @@ trait DerivableVisitor {
|
|||
struct DeriveEncoding;
|
||||
impl DerivableVisitor for DeriveEncoding {
|
||||
const ABILITY: Symbol = Symbol::ENCODE_ENCODING;
|
||||
const ABILITY_SLICE: SubsSlice<Symbol> = Subs::AB_ENCODING;
|
||||
|
||||
#[inline(always)]
|
||||
fn is_derivable_builtin_opaque(symbol: Symbol) -> bool {
|
||||
|
@ -849,6 +857,7 @@ impl DerivableVisitor for DeriveEncoding {
|
|||
struct DeriveDecoding;
|
||||
impl DerivableVisitor for DeriveDecoding {
|
||||
const ABILITY: Symbol = Symbol::DECODE_DECODING;
|
||||
const ABILITY_SLICE: SubsSlice<Symbol> = Subs::AB_DECODING;
|
||||
|
||||
#[inline(always)]
|
||||
fn is_derivable_builtin_opaque(symbol: Symbol) -> bool {
|
||||
|
@ -938,6 +947,7 @@ impl DerivableVisitor for DeriveDecoding {
|
|||
struct DeriveHash;
|
||||
impl DerivableVisitor for DeriveHash {
|
||||
const ABILITY: Symbol = Symbol::HASH_HASH_ABILITY;
|
||||
const ABILITY_SLICE: SubsSlice<Symbol> = Subs::AB_HASH;
|
||||
|
||||
#[inline(always)]
|
||||
fn is_derivable_builtin_opaque(symbol: Symbol) -> bool {
|
||||
|
|
|
@ -2601,17 +2601,17 @@ fn type_to_variable<'a>(
|
|||
let copy_var = match opt_abilities {
|
||||
None => helper!(typ),
|
||||
Some(abilities) => {
|
||||
// TODO(abilities)
|
||||
let ability = abilities[0];
|
||||
|
||||
// If this type argument is marked as being bound to an ability, we must
|
||||
// now correctly instantiate it as so.
|
||||
match RegisterVariable::from_type(subs, rank, pools, arena, typ) {
|
||||
RegisterVariable::Direct(var) => {
|
||||
use Content::*;
|
||||
match *subs.get_content_without_compacting(var) {
|
||||
FlexVar(opt_name) => subs
|
||||
.set_content(var, FlexAbleVar(opt_name, ability)),
|
||||
FlexVar(opt_name) => {
|
||||
// TODO(multi-abilities): check run cache
|
||||
let abilities_slice = SubsSlice::extend_new(&mut subs.symbol_names, abilities.iter().copied());
|
||||
subs.set_content(var, FlexAbleVar(opt_name, abilities_slice))
|
||||
},
|
||||
RigidVar(..) => internal_error!("Rigid var in type arg for {:?} - this is a bug in the solver, or our understanding", actual),
|
||||
RigidAbleVar(..) | FlexAbleVar(..) => internal_error!("Able var in type arg for {:?} - this is a bug in the solver, or our understanding", actual),
|
||||
_ => {
|
||||
|
|
|
@ -164,12 +164,14 @@ macro_rules! v {
|
|||
|subs: &mut Subs| { roc_derive::synth_var(subs, Content::FlexVar(None)) }
|
||||
}};
|
||||
($name:ident has $ability:path) => {{
|
||||
use roc_types::subs::{Subs, SubsIndex, Content};
|
||||
use roc_types::subs::{Subs, SubsIndex, SubsSlice, Content};
|
||||
|subs: &mut Subs| {
|
||||
let name_index =
|
||||
SubsIndex::push_new(&mut subs.field_names, stringify!($name).into());
|
||||
|
||||
roc_derive::synth_var(subs, Content::FlexAbleVar(Some(name_index), $ability))
|
||||
let abilities_slice = SubsSlice::extend_new(&mut subs.symbol_names, [$ability]);
|
||||
|
||||
roc_derive::synth_var(subs, Content::FlexAbleVar(Some(name_index), abilities_slice))
|
||||
}
|
||||
}};
|
||||
(^$rec_var:ident) => {{
|
||||
|
|
|
@ -621,16 +621,20 @@ fn write_content<'a>(
|
|||
let name = &subs.field_names[name_index.index as usize];
|
||||
buf.push_str(name.as_str())
|
||||
}
|
||||
FlexAbleVar(opt_name_index, ability) => {
|
||||
FlexAbleVar(opt_name_index, abilities) => {
|
||||
let name = opt_name_index
|
||||
.map(|name_index| subs.field_names[name_index.index as usize].as_str())
|
||||
.unwrap_or(WILDCARD);
|
||||
ctx.able_variables.push((name, *ability));
|
||||
// TODO(multi-abilities)
|
||||
let abilities = subs.get_subs_slice(*abilities);
|
||||
ctx.able_variables.push((name, abilities[0]));
|
||||
buf.push_str(name);
|
||||
}
|
||||
RigidAbleVar(name_index, ability) => {
|
||||
RigidAbleVar(name_index, abilities) => {
|
||||
let name = subs.field_names[name_index.index as usize].as_str();
|
||||
ctx.able_variables.push((name, *ability));
|
||||
// TODO(multi-abilities)
|
||||
let abilities = subs.get_subs_slice(*abilities);
|
||||
ctx.able_variables.push((name, abilities[0]));
|
||||
buf.push_str(name);
|
||||
}
|
||||
RecursionVar {
|
||||
|
|
|
@ -1676,6 +1676,15 @@ impl Subs {
|
|||
pub const TAG_NAME_BAD_UTF_8: SubsIndex<TagName> = SubsIndex::new(3);
|
||||
pub const TAG_NAME_OUT_OF_BOUNDS: SubsIndex<TagName> = SubsIndex::new(4);
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub const AB_ENCODING: SubsSlice<Symbol> = SubsSlice::new(0, 1);
|
||||
#[rustfmt::skip]
|
||||
pub const AB_DECODING: SubsSlice<Symbol> = SubsSlice::new(1, 1);
|
||||
#[rustfmt::skip]
|
||||
pub const AB_HASHER: SubsSlice<Symbol> = SubsSlice::new(2, 1);
|
||||
#[rustfmt::skip]
|
||||
pub const AB_HASH: SubsSlice<Symbol> = SubsSlice::new(3, 1);
|
||||
|
||||
pub fn new() -> Self {
|
||||
Self::with_capacity(0)
|
||||
}
|
||||
|
@ -1692,11 +1701,18 @@ impl Subs {
|
|||
tag_names.push(TagName("BadUtf8".into()));
|
||||
tag_names.push(TagName("OutOfBounds".into()));
|
||||
|
||||
let mut symbol_names = Vec::with_capacity(32);
|
||||
|
||||
symbol_names.push(Symbol::ENCODE_ENCODING);
|
||||
symbol_names.push(Symbol::DECODE_DECODING);
|
||||
symbol_names.push(Symbol::HASH_HASHER);
|
||||
symbol_names.push(Symbol::HASH_HASH_ABILITY);
|
||||
|
||||
let mut subs = Subs {
|
||||
utable: UnificationTable::default(),
|
||||
variables: Vec::new(),
|
||||
tag_names,
|
||||
symbol_names: Vec::new(),
|
||||
symbol_names,
|
||||
field_names: Vec::new(),
|
||||
record_fields: Vec::new(),
|
||||
// store an empty slice at the first position
|
||||
|
@ -1793,7 +1809,9 @@ impl Subs {
|
|||
|
||||
pub fn rigid_able_var(&mut self, var: Variable, name: Lowercase, ability: Symbol) {
|
||||
let name_index = SubsIndex::push_new(&mut self.field_names, name);
|
||||
let content = Content::RigidAbleVar(name_index, ability);
|
||||
// TODO(multi-abilities)
|
||||
let abilities = SubsSlice::extend_new(&mut self.symbol_names, [ability]);
|
||||
let content = Content::RigidAbleVar(name_index, abilities);
|
||||
let desc = Descriptor::from(content);
|
||||
|
||||
self.set(var, desc);
|
||||
|
@ -2261,12 +2279,12 @@ pub enum Content {
|
|||
FlexVar(Option<SubsIndex<Lowercase>>),
|
||||
/// name given in a user-written annotation
|
||||
RigidVar(SubsIndex<Lowercase>),
|
||||
/// Like a [Self::FlexVar], but is also bound to an ability.
|
||||
/// Like a [Self::FlexVar], but is also bound to 1+ abilities.
|
||||
/// This can only happen when unified with a [Self::RigidAbleVar].
|
||||
FlexAbleVar(Option<SubsIndex<Lowercase>>, Symbol),
|
||||
/// Like a [Self::RigidVar], but is also bound to an ability.
|
||||
FlexAbleVar(Option<SubsIndex<Lowercase>>, SubsSlice<Symbol>),
|
||||
/// Like a [Self::RigidVar], but is also bound to 1+ abilities.
|
||||
/// For example, "a has Hash".
|
||||
RigidAbleVar(SubsIndex<Lowercase>, Symbol),
|
||||
RigidAbleVar(SubsIndex<Lowercase>, SubsSlice<Symbol>),
|
||||
/// name given to a recursion variable
|
||||
RecursionVar {
|
||||
structure: Variable,
|
||||
|
@ -3706,7 +3724,7 @@ fn content_to_err_type(
|
|||
ErrorType::RigidVar(name)
|
||||
}
|
||||
|
||||
FlexAbleVar(opt_name, ability) => {
|
||||
FlexAbleVar(opt_name, abilities) => {
|
||||
let name = match opt_name {
|
||||
Some(name_index) => subs.field_names[name_index.index as usize].clone(),
|
||||
None => {
|
||||
|
@ -3720,12 +3738,14 @@ fn content_to_err_type(
|
|||
}
|
||||
};
|
||||
|
||||
ErrorType::FlexAbleVar(name, ability)
|
||||
// TODO(multi-abilities)
|
||||
ErrorType::FlexAbleVar(name, subs.get_subs_slice(abilities)[0])
|
||||
}
|
||||
|
||||
RigidAbleVar(name_index, ability) => {
|
||||
RigidAbleVar(name_index, abilities) => {
|
||||
let name = subs.field_names[name_index.index as usize].clone();
|
||||
ErrorType::RigidAbleVar(name, ability)
|
||||
// TODO(multi-abilities)
|
||||
ErrorType::RigidAbleVar(name, subs.get_subs_slice(abilities)[0])
|
||||
}
|
||||
|
||||
RecursionVar {
|
||||
|
|
|
@ -4,6 +4,7 @@ use crate::subs::{
|
|||
GetSubsSlice, RecordFields, Subs, UnionTags, VarStore, Variable, VariableSubsSlice,
|
||||
};
|
||||
use roc_collections::all::{HumanIndex, ImMap, ImSet, MutMap, MutSet, SendMap};
|
||||
use roc_collections::VecSet;
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_module::called_via::CalledVia;
|
||||
use roc_module::ident::{ForeignSymbol, Ident, Lowercase, TagName};
|
||||
|
@ -247,7 +248,7 @@ pub struct AliasCommon {
|
|||
#[derive(Clone, Debug)]
|
||||
pub struct OptAbleVar {
|
||||
pub var: Variable,
|
||||
pub opt_abilities: Option<Vec<Symbol>>,
|
||||
pub opt_abilities: Option<VecSet<Symbol>>,
|
||||
}
|
||||
|
||||
impl OptAbleVar {
|
||||
|
@ -262,7 +263,7 @@ impl OptAbleVar {
|
|||
#[derive(PartialEq, Eq, Debug)]
|
||||
pub struct OptAbleType {
|
||||
pub typ: Type,
|
||||
pub opt_abilities: Option<Vec<Symbol>>,
|
||||
pub opt_abilities: Option<VecSet<Symbol>>,
|
||||
}
|
||||
|
||||
impl OptAbleType {
|
||||
|
@ -2112,7 +2113,8 @@ pub struct AliasVar {
|
|||
pub name: Lowercase,
|
||||
pub var: Variable,
|
||||
/// `Some` if this variable is bound to abilities; `None` otherwise.
|
||||
pub opt_bound_abilities: Option<Vec<Symbol>>,
|
||||
/// INVARIANT: if abilities are present, they are sorted and de-duplicated.
|
||||
pub opt_bound_abilities: Option<VecSet<Symbol>>,
|
||||
}
|
||||
|
||||
impl AliasVar {
|
||||
|
|
|
@ -68,7 +68,7 @@ macro_rules! mismatch {
|
|||
..Outcome::default()
|
||||
}
|
||||
}};
|
||||
(%not_able, $var:expr, $ability:expr, $msg:expr, $($arg:tt)*) => {{
|
||||
(%not_able, $var:expr, $abilities:expr, $msg:expr, $($arg:tt)*) => {{
|
||||
dbg_do!(ROC_PRINT_MISMATCHES, {
|
||||
eprintln!(
|
||||
"Mismatch in {} Line {} Column {}",
|
||||
|
@ -80,8 +80,14 @@ macro_rules! mismatch {
|
|||
eprintln!("");
|
||||
});
|
||||
|
||||
let mut mismatches = Vec::with_capacity(1 + $abilities.len());
|
||||
mismatches.push(Mismatch::TypeMismatch);
|
||||
for ability in $abilities {
|
||||
mismatches.push(Mismatch::DoesNotImplementAbiity($var, *ability));
|
||||
}
|
||||
|
||||
Outcome {
|
||||
mismatches: vec![Mismatch::TypeMismatch, Mismatch::DoesNotImplementAbiity($var, $ability)],
|
||||
mismatches,
|
||||
..Outcome::default()
|
||||
}
|
||||
}}
|
||||
|
@ -509,8 +515,8 @@ fn unify_context<M: MetaCollector>(env: &mut Env, pool: &mut Pool, ctx: Context)
|
|||
#[allow(clippy::let_and_return)]
|
||||
let result = match &ctx.first_desc.content {
|
||||
FlexVar(opt_name) => unify_flex(env, &ctx, opt_name, &ctx.second_desc.content),
|
||||
FlexAbleVar(opt_name, ability) => {
|
||||
unify_flex_able(env, &ctx, opt_name, *ability, &ctx.second_desc.content)
|
||||
FlexAbleVar(opt_name, abilities) => {
|
||||
unify_flex_able(env, &ctx, opt_name, *abilities, &ctx.second_desc.content)
|
||||
}
|
||||
RecursionVar {
|
||||
opt_name,
|
||||
|
@ -524,8 +530,8 @@ fn unify_context<M: MetaCollector>(env: &mut Env, pool: &mut Pool, ctx: Context)
|
|||
&ctx.second_desc.content,
|
||||
),
|
||||
RigidVar(name) => unify_rigid(env, &ctx, name, &ctx.second_desc.content),
|
||||
RigidAbleVar(name, ability) => {
|
||||
unify_rigid_able(env, &ctx, name, *ability, &ctx.second_desc.content)
|
||||
RigidAbleVar(name, abilities) => {
|
||||
unify_rigid_able(env, &ctx, name, *abilities, &ctx.second_desc.content)
|
||||
}
|
||||
Structure(flat_type) => {
|
||||
unify_structure(env, pool, &ctx, flat_type, &ctx.second_desc.content)
|
||||
|
@ -896,13 +902,13 @@ fn unify_opaque<M: MetaCollector>(
|
|||
// Alias wins
|
||||
merge(env, ctx, Alias(symbol, args, real_var, kind))
|
||||
}
|
||||
FlexAbleVar(_, ability) => {
|
||||
FlexAbleVar(_, abilities) => {
|
||||
// Opaque type wins
|
||||
merge_flex_able_with_concrete(
|
||||
env,
|
||||
ctx,
|
||||
ctx.second,
|
||||
*ability,
|
||||
*abilities,
|
||||
Alias(symbol, args, real_var, kind),
|
||||
opaque_obligation(symbol, ctx.first),
|
||||
)
|
||||
|
@ -956,13 +962,13 @@ fn unify_structure<M: MetaCollector>(
|
|||
// If the other is flex, Structure wins!
|
||||
merge(env, ctx, Structure(*flat_type))
|
||||
}
|
||||
FlexAbleVar(_, ability) => {
|
||||
FlexAbleVar(_, abilities) => {
|
||||
// Structure wins
|
||||
merge_flex_able_with_concrete(
|
||||
env,
|
||||
ctx,
|
||||
ctx.second,
|
||||
*ability,
|
||||
*abilities,
|
||||
Structure(*flat_type),
|
||||
Obligated::Adhoc(ctx.first),
|
||||
)
|
||||
|
@ -976,9 +982,9 @@ fn unify_structure<M: MetaCollector>(
|
|||
_name
|
||||
)
|
||||
}
|
||||
RigidAbleVar(_, _ability) => {
|
||||
RigidAbleVar(_, _abilities) => {
|
||||
mismatch!(
|
||||
%not_able, ctx.first, *_ability,
|
||||
%not_able, ctx.first, env.subs.get_subs_slice(*_abilities),
|
||||
"trying to unify {:?} with RigidAble {:?}",
|
||||
&flat_type,
|
||||
&other
|
||||
|
@ -2826,7 +2832,7 @@ fn unify_rigid<M: MetaCollector>(
|
|||
// Mismatch - Rigid can unify with FlexAble only when the Rigid has an ability
|
||||
// bound as well, otherwise the user failed to correctly annotate the bound.
|
||||
mismatch!(
|
||||
%not_able, ctx.first, *other_ability,
|
||||
%not_able, ctx.first, env.subs.get_subs_slice(*other_ability),
|
||||
"Rigid {:?} with FlexAble {:?}", ctx.first, other
|
||||
)
|
||||
}
|
||||
|
@ -2859,27 +2865,34 @@ fn unify_rigid_able<M: MetaCollector>(
|
|||
env: &mut Env,
|
||||
ctx: &Context,
|
||||
name: &SubsIndex<Lowercase>,
|
||||
ability: Symbol,
|
||||
abilities_slice: SubsSlice<Symbol>,
|
||||
other: &Content,
|
||||
) -> Outcome<M> {
|
||||
match other {
|
||||
FlexVar(_) => {
|
||||
// If the other is flex, rigid wins!
|
||||
merge(env, ctx, RigidAbleVar(*name, ability))
|
||||
// TODO(multi-abilities)
|
||||
merge(env, ctx, RigidAbleVar(*name, abilities_slice))
|
||||
}
|
||||
FlexAbleVar(_, other_ability) => {
|
||||
if ability == *other_ability {
|
||||
FlexAbleVar(_, other_abilities_slice) => {
|
||||
let (abilities, other_abilities) = (
|
||||
env.subs.get_subs_slice(abilities_slice),
|
||||
env.subs.get_subs_slice(*other_abilities_slice),
|
||||
);
|
||||
|
||||
// Invariant: abilities are inserted in sorted order.
|
||||
if abilities == other_abilities {
|
||||
// The ability bounds are the same, so rigid wins!
|
||||
merge(env, ctx, RigidAbleVar(*name, ability))
|
||||
merge(env, ctx, RigidAbleVar(*name, abilities_slice))
|
||||
} else {
|
||||
// Mismatch for now.
|
||||
// TODO check ability hierarchies.
|
||||
mismatch!(
|
||||
%not_able, ctx.second, ability,
|
||||
"RigidAble {:?} with ability {:?} not compatible with ability {:?}",
|
||||
%not_able, ctx.second, abilities,
|
||||
"RigidAble {:?} with abilities {:?} not compatible with abilities {:?}",
|
||||
ctx.first,
|
||||
ability,
|
||||
other_ability
|
||||
abilities,
|
||||
other_abilities,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -2946,40 +2959,52 @@ fn unify_flex_able<M: MetaCollector>(
|
|||
env: &mut Env,
|
||||
ctx: &Context,
|
||||
opt_name: &Option<SubsIndex<Lowercase>>,
|
||||
ability: Symbol,
|
||||
abilities_slice: SubsSlice<Symbol>,
|
||||
other: &Content,
|
||||
) -> Outcome<M> {
|
||||
match other {
|
||||
FlexVar(opt_other_name) => {
|
||||
// Prefer using right's name.
|
||||
let opt_name = (opt_other_name).or(*opt_name);
|
||||
merge(env, ctx, FlexAbleVar(opt_name, ability))
|
||||
merge(env, ctx, FlexAbleVar(opt_name, abilities_slice))
|
||||
}
|
||||
|
||||
FlexAbleVar(opt_other_name, other_ability) => {
|
||||
FlexAbleVar(opt_other_name, other_abilities_slice) => {
|
||||
// Prefer the right's name when possible.
|
||||
let opt_name = (opt_other_name).or(*opt_name);
|
||||
|
||||
if ability == *other_ability {
|
||||
merge(env, ctx, FlexAbleVar(opt_name, ability))
|
||||
let (abilities, other_abilities) = (
|
||||
env.subs.get_subs_slice(abilities_slice),
|
||||
env.subs.get_subs_slice(*other_abilities_slice),
|
||||
);
|
||||
|
||||
// TODO: flex vars can inherit abilities from other flex vars they see
|
||||
if abilities == other_abilities {
|
||||
merge(env, ctx, FlexAbleVar(opt_name, abilities_slice))
|
||||
} else {
|
||||
// Ability names differ; mismatch for now.
|
||||
// TODO check ability hierarchies.
|
||||
mismatch!(
|
||||
%not_able, ctx.second, ability,
|
||||
"FlexAble {:?} with ability {:?} not compatible with ability {:?}",
|
||||
%not_able, ctx.second, abilities,
|
||||
"FlexAble {:?} with abilities {:?} not compatible with abilities {:?}",
|
||||
ctx.first,
|
||||
ability,
|
||||
other_ability
|
||||
abilities,
|
||||
other_abilities
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
RigidAbleVar(_, other_ability) => {
|
||||
if ability == *other_ability {
|
||||
RigidAbleVar(_, other_abilities_slice) => {
|
||||
let (abilities, other_abilities) = (
|
||||
env.subs.get_subs_slice(abilities_slice),
|
||||
env.subs.get_subs_slice(*other_abilities_slice),
|
||||
);
|
||||
|
||||
// Invariant: abilities are inserted in sorted order.
|
||||
if abilities == other_abilities {
|
||||
merge(env, ctx, *other)
|
||||
} else {
|
||||
mismatch!(%not_able, ctx.second, ability, "RigidAble {:?} vs {:?}", ability, other_ability)
|
||||
mismatch!(%not_able, ctx.second, abilities, "RigidAble {:?} vs {:?}", abilities, other_abilities)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2992,7 +3017,7 @@ fn unify_flex_able<M: MetaCollector>(
|
|||
env,
|
||||
ctx,
|
||||
ctx.first,
|
||||
ability,
|
||||
abilities_slice,
|
||||
*other,
|
||||
opaque_obligation(*name, ctx.second),
|
||||
)
|
||||
|
@ -3007,7 +3032,7 @@ fn unify_flex_able<M: MetaCollector>(
|
|||
env,
|
||||
ctx,
|
||||
ctx.first,
|
||||
ability,
|
||||
abilities_slice,
|
||||
*other,
|
||||
Obligated::Adhoc(ctx.second),
|
||||
)
|
||||
|
@ -3022,16 +3047,19 @@ fn merge_flex_able_with_concrete<M: MetaCollector>(
|
|||
env: &mut Env,
|
||||
ctx: &Context,
|
||||
flex_able_var: Variable,
|
||||
ability: Symbol,
|
||||
abilities: SubsSlice<Symbol>,
|
||||
concrete_content: Content,
|
||||
concrete_obligation: Obligated,
|
||||
) -> Outcome<M> {
|
||||
let mut outcome = merge(env, ctx, concrete_content);
|
||||
let must_implement_ability = MustImplementAbility {
|
||||
typ: concrete_obligation,
|
||||
ability,
|
||||
};
|
||||
outcome.must_implement_ability.push(must_implement_ability);
|
||||
|
||||
for &ability in env.subs.get_subs_slice(abilities) {
|
||||
let must_implement_ability = MustImplementAbility {
|
||||
typ: concrete_obligation,
|
||||
ability,
|
||||
};
|
||||
outcome.must_implement_ability.push(must_implement_ability);
|
||||
}
|
||||
|
||||
// Figure which, if any, lambda sets should be specialized thanks to the flex able var
|
||||
// being instantiated. Now as much as I would love to do that here, we don't, because we might
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue