Update solve and unify

This commit is contained in:
Richard Feldman 2019-12-03 01:27:20 -05:00
parent c41167ec6e
commit 5eb843326c
4 changed files with 188 additions and 115 deletions

51
src/can/ident.rs Normal file
View file

@ -0,0 +1,51 @@
use crate::ident::UnqualifiedIdent;
use std::fmt;
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct ModuleName(Box<str>);
/// An uncapitalized identifier, such as a field name or local variable
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Lowercase(Box<str>);
/// A capitalized identifier, such as a tag name or module name
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Uppercase(Box<str>);
impl Lowercase {
pub fn from_unqualified_ident(ident: &UnqualifiedIdent<'_>) -> Self {
Self(ident.as_str().into())
}
}
impl Into<Box<str>> for Lowercase {
fn into(self) -> Box<str> {
self.0
}
}
/// Rather than displaying as this:
///
/// Lowercase("foo")
///
/// ...instead display as this:
///
/// 'foo'
impl fmt::Debug for Lowercase {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "'{}'", self.0)
}
}
/// Rather than displaying as this:
///
/// Uppercase("Foo")
///
/// ...instead display as this:
///
/// 'Foo'
impl fmt::Debug for Uppercase {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "'{}'", self.0)
}
}

View file

@ -3,6 +3,7 @@ use crate::collections::ImMap;
use crate::subs::{Content, Descriptor, FlatType, Subs, Variable};
use crate::types::Constraint::{self, *};
use crate::types::Type::{self, *};
use crate::unify::unify;
type Env = ImMap<Symbol, Variable>;
@ -11,10 +12,10 @@ pub fn solve(env: &Env, subs: &mut Subs, constraint: &Constraint) {
True => (),
Eq(typ, expected_type, _region) => {
// TODO use region?
let actual = type_to_variable(subs, typ.clone());
let expected = type_to_variable(subs, expected_type.clone().get_type());
let actual = type_to_var(subs, typ.clone());
let expected = type_to_var(subs, expected_type.clone().get_type());
subs.union(actual, expected);
unify(subs, actual, expected);
}
Lookup(symbol, expected_type, _region) => {
// TODO use region?
@ -22,9 +23,9 @@ pub fn solve(env: &Env, subs: &mut Subs, constraint: &Constraint) {
subs.copy_var(*env.get(&symbol).unwrap_or_else(|| {
panic!("Could not find symbol {:?} in env {:?}", symbol, env)
}));
let expected = type_to_variable(subs, expected_type.clone().get_type());
let expected = type_to_var(subs, expected_type.clone().get_type());
subs.union(actual, expected);
unify(subs, actual, expected);
}
And(sub_constraints) => {
for sub_constraint in sub_constraints.iter() {
@ -33,10 +34,10 @@ pub fn solve(env: &Env, subs: &mut Subs, constraint: &Constraint) {
}
Pattern(_region, _category, typ, expected) => {
// TODO use region?
let actual = type_to_variable(subs, typ.clone());
let expected = type_to_variable(subs, expected.clone().get_type());
let actual = type_to_var(subs, typ.clone());
let expected = type_to_var(subs, expected.clone().get_type());
subs.union(actual, expected);
unify(subs, actual, expected);
}
Let(let_con) => {
match &let_con.ret_constraint {
@ -58,7 +59,7 @@ pub fn solve(env: &Env, subs: &mut Subs, constraint: &Constraint) {
// inserted earlier in solving. (If we allowed
// shadowing, we'd need to do something fancier here.)
if !new_env.contains_key(&symbol) {
let var = type_to_variable(subs, loc_type.value.clone());
let var = type_to_var(subs, loc_type.value.clone());
new_env.insert(symbol.clone(), var);
}
@ -75,7 +76,11 @@ pub fn solve(env: &Env, subs: &mut Subs, constraint: &Constraint) {
}
}
fn type_to_variable(subs: &mut Subs, typ: Type) -> Variable {
fn type_to_var(subs: &mut Subs, typ: Type) -> Variable {
type_to_variable(subs, &ImMap::default(), typ)
}
fn type_to_variable(subs: &mut Subs, aliases: &ImMap<Box<str>, Variable>, typ: Type) -> Variable {
match typ {
Variable(var) => var,
Apply {
@ -86,7 +91,7 @@ fn type_to_variable(subs: &mut Subs, typ: Type) -> Variable {
let mut arg_vars = Vec::with_capacity(args.len());
for arg in args {
arg_vars.push(type_to_variable(subs, arg.clone()))
arg_vars.push(type_to_variable(subs, aliases, arg.clone()))
}
let flat_type = FlatType::Apply {
@ -107,11 +112,11 @@ fn type_to_variable(subs: &mut Subs, typ: Type) -> Variable {
let mut arg_vars = Vec::with_capacity(args.len());
for arg in args {
arg_vars.push(type_to_variable(subs, arg.clone()))
arg_vars.push(type_to_variable(subs, aliases, arg.clone()))
}
let ret_var = type_to_variable(subs, *ret_type);
let content: Content = Content::Structure(FlatType::Func(arg_vars, ret_var));
let ret_var = type_to_variable(subs, aliases, *ret_type);
let content = Content::Structure(FlatType::Func(arg_vars, ret_var));
subs.fresh(Descriptor::from(content))
}

View file

@ -1,9 +1,47 @@
use crate::ena::unify::{InPlace, UnificationTable, UnifyKey};
use crate::types::Problem;
use crate::unify;
use std::fmt;
use std::sync::atomic::{AtomicUsize, Ordering};
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
pub struct Mark(u8);
impl Mark {
#[inline(always)]
pub fn none() -> Mark {
Mark(0)
}
#[inline(always)]
pub fn occurs() -> Mark {
Mark(1)
}
#[inline(always)]
pub fn get_var_names() -> Mark {
Mark(2)
}
#[inline(always)]
pub fn next(self) -> Mark {
Mark(self.0 - 1)
}
}
impl fmt::Debug for Mark {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self == &Mark::none() {
write!(f, "Mark::none")
} else if self == &Mark::occurs() {
write!(f, "Mark::occurs")
} else if self == &Mark::get_var_names() {
write!(f, "Mark::get_var_names")
} else {
write!(f, "Mark({})", self.0)
}
}
}
#[derive(Debug, Default)]
pub struct Subs {
utable: UnificationTable<InPlace<Variable>>,
@ -91,15 +129,11 @@ impl Subs {
}
/// Unions two keys without the possibility of failure.
pub fn union(&mut self, left: Variable, right: Variable) {
pub fn union(&mut self, left: Variable, right: Variable, desc: Descriptor) {
let l_root = self.utable.get_root_key(left);
let r_root = self.utable.get_root_key(right);
if l_root != r_root {
let combined = unify::unify_vars(self, l_root, r_root);
self.utable.unify_roots(l_root, r_root, combined)
}
self.utable.unify_roots(l_root, r_root, desc)
}
pub fn get(&mut self, key: Variable) -> Descriptor {
@ -112,9 +146,8 @@ impl Subs {
pub fn set(&mut self, key: Variable, r_value: Descriptor) {
let l_key = self.utable.get_root_key(key);
let unified = unify::unify_var_val(self, l_key, &r_value);
self.utable.update_value(l_key, |node| node.value = unified);
self.utable.update_value(l_key, |node| node.value = r_value);
}
pub fn copy_var(&mut self, var: Variable) -> Variable {
@ -123,20 +156,9 @@ impl Subs {
var
}
// pub fn set_rank(&mut self, key: Variable, rank: usize) {
// let mut descriptor = self.utable.probe_value(key);
// descriptor.rank = rank;
// let result = self.utable.unify_var_value(key, descriptor);
// // Updating the rank should never fail!
// debug_assert_eq!(result, Ok(()));
// }
// pub fn equivalent(&mut self, left: Variable, right: Variable) -> bool {
// self.utable.unioned(left, right)
// }
pub fn equivalent(&mut self, left: Variable, right: Variable) -> bool {
self.utable.unioned(left, right)
}
}
#[inline(always)]
@ -152,17 +174,23 @@ fn unnamed_flex_var() -> Content {
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Descriptor {
pub content: Content,
pub rank: usize,
pub mark: u32,
pub rank: u8,
pub mark: Mark,
pub copy: Option<Variable>,
}
impl Default for Descriptor {
fn default() -> Self {
unnamed_flex_var().into()
}
}
impl From<Content> for Descriptor {
fn from(content: Content) -> Descriptor {
Descriptor {
content,
rank: 0,
mark: 2, // no mark
mark: Mark::none(),
copy: None,
}
}

View file

@ -1,61 +1,68 @@
use crate::subs::Content::{self, *};
use crate::subs::{Descriptor, FlatType, Subs, Variable};
use crate::subs::{Descriptor, FlatType, Mark, Subs, Variable};
use crate::types::Problem;
#[inline(always)]
pub fn unify_vars(subs: &mut Subs, left_key: Variable, right_key: Variable) -> Descriptor {
let right = subs.get(right_key);
unify_var_val(subs, left_key, &right)
struct Context {
first: Variable,
first_desc: Descriptor,
second: Variable,
second_desc: Descriptor,
}
#[inline(always)]
pub fn unify_var_val(subs: &mut Subs, left_key: Variable, right: &Descriptor) -> Descriptor {
let left = subs.get(left_key);
pub fn unify(subs: &mut Subs, var1: Variable, var2: Variable) {
if !subs.equivalent(var1, var2) {
let ctx = Context {
first: var1,
first_desc: subs.get(var1),
second: var2,
second_desc: subs.get(var2),
};
unify(subs, &left, right)
unify_context(subs, &ctx)
}
}
fn unify(subs: &mut Subs, left: &Descriptor, right: &Descriptor) -> Descriptor {
match left.content {
FlexVar(ref opt_name) => unify_flex(opt_name, &right.content),
RigidVar(ref name) => unify_rigid(name, &right.content),
Structure(ref flat_type) => unify_structure(subs, flat_type, &right.content),
fn unify_context(subs: &mut Subs, ctx: &Context) {
match ctx.first_desc.content {
FlexVar(ref opt_name) => unify_flex(subs, ctx, opt_name, &ctx.second_desc.content),
RigidVar(ref name) => unify_rigid(subs, ctx, name, &ctx.second_desc.content),
Structure(ref flat_type) => unify_structure(subs, ctx, flat_type, &ctx.second_desc.content),
Error(ref problem) => {
// Error propagates. Whatever we're comparing it to doesn't matter!
from_content(Error(problem.clone()))
merge(subs, ctx, Error(problem.clone()))
}
}
}
#[inline(always)]
fn unify_structure(subs: &mut Subs, flat_type: &FlatType, other: &Content) -> Descriptor {
fn unify_structure(subs: &mut Subs, ctx: &Context, flat_type: &FlatType, other: &Content) {
match other {
FlexVar(_) => {
// If the other is flex, Structure wins!
from_content(Structure(flat_type.clone()))
merge(subs, ctx, Structure(flat_type.clone()))
}
RigidVar(_) => {
// Type mismatch! Rigid can only unify with flex.
from_content(Error(Problem::GenericMismatch))
merge(subs, ctx, Error(Problem::GenericMismatch))
}
Structure(ref other_flat_type) => {
// Unify the two flat types
unify_flat_type(subs, flat_type, other_flat_type)
unify_flat_type(subs, ctx, flat_type, other_flat_type)
}
Error(problem) => {
// Error propagates.
from_content(Error(problem.clone()))
merge(subs, ctx, Error(problem.clone()))
}
}
}
#[inline(always)]
fn unify_flat_type(subs: &mut Subs, left: &FlatType, right: &FlatType) -> Descriptor {
fn unify_flat_type(subs: &mut Subs, ctx: &Context, left: &FlatType, right: &FlatType) {
use crate::subs::FlatType::*;
match (left, right) {
(EmptyRecord, EmptyRecord) => from_content(Structure(left.clone())),
(EmptyRecord, EmptyRecord) => merge(subs, ctx, Structure(left.clone())),
(
Apply {
module_name: l_module_name,
@ -68,103 +75,85 @@ fn unify_flat_type(subs: &mut Subs, left: &FlatType, right: &FlatType) -> Descri
args: r_args,
},
) if l_module_name == r_module_name && l_type_name == r_type_name => {
let args = unify_args(subs, l_args.iter(), r_args.iter());
let flat_type = Apply {
module_name: l_module_name.clone(),
name: l_type_name.clone(),
args,
};
unify_zip(subs, l_args.iter(), r_args.iter());
from_content(Structure(flat_type))
merge(
subs,
ctx,
Structure(Apply {
module_name: (*r_module_name).clone(),
name: (*r_type_name).clone(),
args: (*r_args).clone(),
}),
)
}
(Func(l_args, l_ret), Func(r_args, r_ret)) => {
if l_args.len() == r_args.len() {
let args = unify_args(subs, l_args.iter(), r_args.iter());
let ret = union_vars(subs, *l_ret, *r_ret);
let flat_type = Func(args, ret);
unify_zip(subs, l_args.iter(), r_args.iter());
unify(subs, *l_ret, *r_ret);
from_content(Structure(flat_type))
merge(subs, ctx, Structure(Func((*r_args).clone(), *r_ret)))
} else if l_args.len() > r_args.len() {
from_content(Error(Problem::ExtraArguments))
merge(subs, ctx, Error(Problem::ExtraArguments))
} else {
from_content(Error(Problem::MissingArguments))
merge(subs, ctx, Error(Problem::MissingArguments))
}
}
_ => from_content(Error(Problem::GenericMismatch)),
_ => merge(subs, ctx, Error(Problem::GenericMismatch)),
}
}
fn unify_args<'a, I>(subs: &mut Subs, left_iter: I, right_iter: I) -> Vec<Variable>
fn unify_zip<'a, I>(subs: &mut Subs, left_iter: I, right_iter: I)
where
I: Iterator<Item = &'a Variable>,
{
left_iter
.zip(right_iter)
.map(|(&l_var, &r_var)| {
// Look up the descriptors we have for these variables, and unify them.
let descriptor = unify_vars(subs, l_var, r_var);
// set r_var to be the unioned value, then union l_var to r_var
subs.set(r_var, descriptor);
subs.union(l_var, r_var);
r_var
})
.collect()
}
fn union_vars(subs: &mut Subs, l_var: Variable, r_var: Variable) -> Variable {
// Look up the descriptors we have for these variables, and unify them.
let descriptor = unify_vars(subs, l_var, r_var);
// set r_var to be the unioned value, then union l_var to r_var
subs.set(r_var, descriptor);
subs.union(l_var, r_var);
r_var
for (&l_var, &r_var) in left_iter.zip(right_iter) {
unify(subs, l_var, r_var);
}
}
#[inline(always)]
fn unify_rigid(name: &str, other: &Content) -> Descriptor {
fn unify_rigid(subs: &mut Subs, ctx: &Context, name: &str, other: &Content) {
match other {
FlexVar(_) => {
// If the other is flex, rigid wins!
from_content(RigidVar(name.into()))
merge(subs, ctx, RigidVar(name.into()))
}
RigidVar(_) | Structure(_) => {
// Type mismatch! Rigid can only unify with flex, even if the
// rigid names are the same.
from_content(Error(Problem::GenericMismatch))
merge(subs, ctx, Error(Problem::GenericMismatch))
}
Error(problem) => {
// Error propagates.
from_content(Error(problem.clone()))
merge(subs, ctx, Error(problem.clone()))
}
}
}
#[inline(always)]
fn unify_flex(opt_name: &Option<Box<str>>, other: &Content) -> Descriptor {
fn unify_flex(subs: &mut Subs, ctx: &Context, opt_name: &Option<Box<str>>, other: &Content) {
match other {
FlexVar(None) => {
// If both are flex, and only left has a name, keep the name around.
from_content(FlexVar(opt_name.clone()))
merge(subs, ctx, FlexVar(opt_name.clone()))
}
FlexVar(Some(_)) | RigidVar(_) | Structure(_) | Error(_) => {
// In all other cases, if left is flex, defer to right.
// (This includes using right's name if both are flex and named.)
from_content(other.clone())
merge(subs, ctx, other.clone())
}
}
}
/// TODO this was f/k/a merge() - got rid of the rank stuff...good idea? Bad?
/// TODO it used to be { rank: std::cmp::min(left_rank, right_rank), ... }
fn from_content(content: Content) -> Descriptor {
Descriptor {
fn merge(subs: &mut Subs, ctx: &Context, content: Content) {
let rank = ctx.first_desc.rank.min(ctx.second_desc.rank);
let desc = Descriptor {
content,
rank: 0,
mark: 2, // no mark
rank,
mark: Mark::none(),
copy: None,
}
};
subs.union(ctx.first, ctx.second, desc);
}