Try out UnifyResult

This commit is contained in:
Richard Feldman 2019-12-07 19:34:06 -05:00
parent 0093298ed0
commit e949c56f70

View file

@ -13,12 +13,18 @@ struct Context {
struct RecordStructure { struct RecordStructure {
fields: ImMap<Lowercase, Variable>, fields: ImMap<Lowercase, Variable>,
extension: Variable, ext: Variable,
} }
type UnifyResult = Result<(), Problem>;
type Problems = Vec<Problem>;
#[inline(always)] #[inline(always)]
pub fn unify(subs: &mut Subs, var1: Variable, var2: Variable) { pub fn unify(subs: &mut Subs, var1: Variable, var2: Variable) -> UnifyResult {
if !subs.equivalent(var1, var2) { if subs.equivalent(var1, var2) {
Ok(())
} else {
let ctx = Context { let ctx = Context {
first: var1, first: var1,
first_desc: subs.get(var1), first_desc: subs.get(var1),
@ -30,7 +36,7 @@ pub fn unify(subs: &mut Subs, var1: Variable, var2: Variable) {
} }
} }
fn unify_context(subs: &mut Subs, ctx: Context) { fn unify_context(subs: &mut Subs, ctx: Context) -> UnifyResult {
match &ctx.first_desc.content { match &ctx.first_desc.content {
FlexVar(opt_name) => unify_flex(subs, &ctx, opt_name, &ctx.second_desc.content), FlexVar(opt_name) => unify_flex(subs, &ctx, opt_name, &ctx.second_desc.content),
RigidVar(name) => unify_rigid(subs, &ctx, name, &ctx.second_desc.content), RigidVar(name) => unify_rigid(subs, &ctx, name, &ctx.second_desc.content),
@ -38,7 +44,9 @@ fn unify_context(subs: &mut Subs, ctx: Context) {
Alias(home, name, args, real_var) => unify_alias(subs, &ctx, home, name, args, real_var), Alias(home, name, args, real_var) => unify_alias(subs, &ctx, home, name, args, real_var),
Error(problem) => { Error(problem) => {
// Error propagates. Whatever we're comparing it to doesn't matter! // Error propagates. Whatever we're comparing it to doesn't matter!
merge(subs, &ctx, Error(problem.clone())) merge(subs, &ctx, Error(problem.clone()));
Err(problem.clone())
} }
} }
} }
@ -51,7 +59,7 @@ fn unify_alias(
name: &Uppercase, name: &Uppercase,
args: &Vec<(Lowercase, Variable)>, args: &Vec<(Lowercase, Variable)>,
real_var: &Variable, real_var: &Variable,
) { ) -> UnifyResult {
let other_content = &ctx.second_desc.content; let other_content = &ctx.second_desc.content;
match other_content { match other_content {
@ -61,41 +69,71 @@ fn unify_alias(
subs, subs,
&ctx, &ctx,
Alias(home.clone(), name.clone(), args.clone(), *real_var), Alias(home.clone(), name.clone(), args.clone(), *real_var),
) );
Ok(())
} }
RigidVar(_) => unify(subs, *real_var, ctx.second), RigidVar(_) => unify(subs, *real_var, ctx.second),
Alias(other_home, other_name, other_args, other_real_var) => { Alias(other_home, other_name, other_args, other_real_var) => {
if name == other_name && home == other_home { if name == other_name && home == other_home {
if args.len() == other_args.len() { if args.len() == other_args.len() {
let mut answer = Ok(());
for ((_, l_var), (_, r_var)) in args.iter().zip(other_args.iter()) { for ((_, l_var), (_, r_var)) in args.iter().zip(other_args.iter()) {
unify(subs, *l_var, *r_var); let result = unify(subs, *l_var, *r_var);
answer = answer.and_then(|()| result);
} }
merge(subs, &ctx, other_content.clone()) merge(subs, &ctx, other_content.clone());
answer
} else if args.len() > other_args.len() { } else if args.len() > other_args.len() {
merge(subs, &ctx, Error(Problem::ExtraArguments)) let problem = Problem::ExtraArguments;
merge(subs, &ctx, Error(problem.clone()));
Err(problem)
} else { } else {
merge(subs, &ctx, Error(Problem::MissingArguments)) let problem = Problem::MissingArguments;
merge(subs, &ctx, Error(problem.clone()));
Err(problem)
} }
} else { } else {
unify(subs, *real_var, *other_real_var) unify(subs, *real_var, *other_real_var)
} }
} }
Structure(_) => unify(subs, *real_var, ctx.second), Structure(_) => unify(subs, *real_var, ctx.second),
Error(problem) => merge(subs, ctx, Error(problem.clone())), Error(problem) => {
merge(subs, ctx, Error(problem.clone()));
Err(problem.clone())
}
} }
} }
#[inline(always)] #[inline(always)]
fn unify_structure(subs: &mut Subs, ctx: &Context, flat_type: &FlatType, other: &Content) { fn unify_structure(
subs: &mut Subs,
ctx: &Context,
flat_type: &FlatType,
other: &Content,
) -> UnifyResult {
match other { match other {
FlexVar(_) => { FlexVar(_) => {
// If the other is flex, Structure wins! // If the other is flex, Structure wins!
merge(subs, ctx, Structure(flat_type.clone())) merge(subs, ctx, Structure(flat_type.clone()));
Ok(())
} }
RigidVar(_) => { RigidVar(_) => {
let problem = Problem::GenericMismatch;
// Type mismatch! Rigid can only unify with flex. // Type mismatch! Rigid can only unify with flex.
merge(subs, ctx, Error(Problem::GenericMismatch)) merge(subs, ctx, Error(problem.clone()));
Err(problem)
} }
Structure(ref other_flat_type) => { Structure(ref other_flat_type) => {
// Unify the two flat types // Unify the two flat types
@ -104,32 +142,114 @@ fn unify_structure(subs: &mut Subs, ctx: &Context, flat_type: &FlatType, other:
Alias(_, _, _, real_var) => unify(subs, ctx.first, *real_var), Alias(_, _, _, real_var) => unify(subs, ctx.first, *real_var),
Error(problem) => { Error(problem) => {
// Error propagates. // Error propagates.
merge(subs, ctx, Error(problem.clone())) merge(subs, ctx, Error(problem.clone()));
Err(problem.clone())
} }
} }
} }
fn unify_record(ctx: &Context, structure1: RecordStructure, structure2: RecordStructure) { fn unify_record(
panic!("TODO unify_record"); subs: &mut Subs,
let x = 5; ctx: &Context,
rec1: RecordStructure,
rec2: RecordStructure,
) -> UnifyResult {
let fields1 = rec1.fields;
let fields2 = rec2.fields;
let shared_fields = fields1
.clone()
.intersection_with(fields2.clone(), |one, two| (one, two));
let unique_fields1 = fields1.clone().difference(fields2.clone());
let unique_fields2 = fields2.difference(fields1);
if unique_fields1.is_empty() {
if unique_fields2.is_empty() {
unify(subs, rec1.ext, rec2.ext);
unify_shared_fields(subs, ctx, shared_fields, ImMap::default(), rec1.ext)
} else {
// subRecord <- fresh context (Structure (Record1 uniqueFields2 ext2))
// subUnify ext1 subRecord
// unifySharedFields context sharedFields Map.empty subRecord
panic!("TODO 1");
}
} else if unique_fields2.is_empty() {
// subRecord <- fresh context (Structure (Record1 uniqueFields1 ext1))
// subUnify subRecord ext2
// unifySharedFields context sharedFields Map.empty subRecord
panic!("TODO 2");
} else {
// let otherFields = Map.union uniqueFields1 uniqueFields2
// ext <- fresh context Type.unnamedFlexVar
// sub1 <- fresh context (Structure (Record1 uniqueFields1 ext))
// sub2 <- fresh context (Structure (Record1 uniqueFields2 ext))
// subUnify ext1 sub2
// subUnify sub1 ext2
// unifySharedFields context sharedFields otherFields ext
//
panic!("TODO 3");
}
}
fn unify_shared_fields(
subs: &mut Subs,
ctx: &Context,
shared_fields: ImMap<Lowercase, (Variable, Variable)>,
other_fields: ImMap<Lowercase, Variable>,
ext: Variable,
) -> UnifyResult {
let mut matching_fields = ImMap::default();
let num_shared_fields = shared_fields.len();
for (name, (actual, expected)) in shared_fields {
// TODO another way to do this might be to pass around a problems vec
// and check to see if its length increased after doing this unification.
if unify(subs, actual, expected).is_ok() {
matching_fields.insert(name, actual);
}
}
if num_shared_fields == matching_fields.len() {
let flat_type = FlatType::Record(matching_fields.union(other_fields), ext);
merge(subs, ctx, Structure(flat_type));
Ok(())
} else {
let problem = Problem::GenericMismatch;
// Type mismatch! Rigid can only unify with flex.
merge(subs, ctx, Error(problem.clone()));
Err(problem)
}
} }
#[inline(always)] #[inline(always)]
fn unify_flat_type(subs: &mut Subs, ctx: &Context, left: &FlatType, right: &FlatType) { fn unify_flat_type(
subs: &mut Subs,
ctx: &Context,
left: &FlatType,
right: &FlatType,
) -> UnifyResult {
use crate::subs::FlatType::*; use crate::subs::FlatType::*;
match (left, right) { match (left, right) {
(EmptyRecord, EmptyRecord) => merge(subs, ctx, Structure(left.clone())), (EmptyRecord, EmptyRecord) => {
merge(subs, ctx, Structure(left.clone()));
Ok(())
}
(Record(fields, ext), EmptyRecord) if fields.is_empty() => unify(subs, *ext, ctx.second), (Record(fields, ext), EmptyRecord) if fields.is_empty() => unify(subs, *ext, ctx.second),
(EmptyRecord, Record(fields, ext)) if fields.is_empty() => unify(subs, ctx.first, *ext), (EmptyRecord, Record(fields, ext)) if fields.is_empty() => unify(subs, ctx.first, *ext),
(Record(fields1, ext1), Record(fields2, ext2)) => { (Record(fields1, ext1), Record(fields2, ext2)) => {
let structure1 = gather_fields(subs, fields1.clone(), *ext1); let rec1 = gather_fields(subs, fields1.clone(), *ext1);
let structure2 = gather_fields(subs, fields2.clone(), *ext2); let rec2 = gather_fields(subs, fields2.clone(), *ext2);
unify_record(ctx, structure1, structure2) unify_record(subs, ctx, rec1, rec2)
} }
( (
Apply { Apply {
@ -153,21 +273,35 @@ fn unify_flat_type(subs: &mut Subs, ctx: &Context, left: &FlatType, right: &Flat
name: (*r_type_name).clone(), name: (*r_type_name).clone(),
args: (*r_args).clone(), args: (*r_args).clone(),
}), }),
) );
Ok(())
} }
(Func(l_args, l_ret), Func(r_args, r_ret)) => { (Func(l_args, l_ret), Func(r_args, r_ret)) => {
if l_args.len() == r_args.len() { if l_args.len() == r_args.len() {
unify_zip(subs, l_args.iter(), r_args.iter()); unify_zip(subs, l_args.iter(), r_args.iter());
unify(subs, *l_ret, *r_ret); let answer = unify(subs, *l_ret, *r_ret);
merge(subs, ctx, Structure(Func((*r_args).clone(), *r_ret))) merge(subs, ctx, Structure(Func((*r_args).clone(), *r_ret)));
answer
} else if l_args.len() > r_args.len() { } else if l_args.len() > r_args.len() {
merge(subs, ctx, Error(Problem::ExtraArguments)) merge(subs, ctx, Error(Problem::ExtraArguments));
Ok(())
} else { } else {
merge(subs, ctx, Error(Problem::MissingArguments)) merge(subs, ctx, Error(Problem::MissingArguments));
Ok(())
} }
} }
_ => merge(subs, ctx, Error(Problem::GenericMismatch)), _ => {
let problem = Problem::GenericMismatch;
merge(subs, ctx, Error(problem.clone()));
Err(problem)
}
} }
} }
@ -181,39 +315,60 @@ where
} }
#[inline(always)] #[inline(always)]
fn unify_rigid(subs: &mut Subs, ctx: &Context, name: &str, other: &Content) { fn unify_rigid(subs: &mut Subs, ctx: &Context, name: &str, other: &Content) -> UnifyResult {
match other { match other {
FlexVar(_) => { FlexVar(_) => {
// If the other is flex, rigid wins! // If the other is flex, rigid wins!
merge(subs, ctx, RigidVar(name.into())) merge(subs, ctx, RigidVar(name.into()));
Ok(())
} }
RigidVar(_) | Structure(_) => { RigidVar(_) | Structure(_) => {
// Type mismatch! Rigid can only unify with flex, even if the // Type mismatch! Rigid can only unify with flex, even if the
// rigid names are the same. // rigid names are the same.
merge(subs, ctx, Error(Problem::GenericMismatch)) merge(subs, ctx, Error(Problem::GenericMismatch));
Ok(())
} }
Alias(_, _, _, _) => { Alias(_, _, _, _) => {
panic!("TODO unify_rigid Alias"); panic!("TODO unify_rigid Alias");
panic!("TODO");
Ok(())
} }
Error(problem) => { Error(problem) => {
// Error propagates. // Error propagates.
merge(subs, ctx, Error(problem.clone())) merge(subs, ctx, Error(problem.clone()));
Err(problem.clone())
} }
} }
} }
#[inline(always)] #[inline(always)]
fn unify_flex(subs: &mut Subs, ctx: &Context, opt_name: &Option<Box<str>>, other: &Content) { fn unify_flex(
subs: &mut Subs,
ctx: &Context,
opt_name: &Option<Box<str>>,
other: &Content,
) -> UnifyResult {
match other { match other {
FlexVar(None) => { FlexVar(None) => {
// If both are flex, and only left has a name, keep the name around. // If both are flex, and only left has a name, keep the name around.
merge(subs, ctx, FlexVar(opt_name.clone())) merge(subs, ctx, FlexVar(opt_name.clone()));
Ok(())
} }
FlexVar(Some(_)) | RigidVar(_) | Structure(_) | Alias(_, _, _, _) | Error(_) => { FlexVar(Some(_)) | RigidVar(_) | Structure(_) | Alias(_, _, _, _) => {
// In all other cases, if left is flex, defer to right. // In all other cases, if left is flex, defer to right.
// (This includes using right's name if both are flex and named.) // (This includes using right's name if both are flex and named.)
merge(subs, ctx, other.clone()) merge(subs, ctx, other.clone());
Ok(())
}
Error(problem) => {
merge(subs, ctx, Error(problem.clone()));
Err(problem.clone())
} }
} }
} }
@ -235,10 +390,7 @@ fn gather_fields(
gather_fields(subs, fields, var) gather_fields(subs, fields, var)
} }
_ => RecordStructure { _ => RecordStructure { fields, ext: var },
fields,
extension: var,
},
} }
} }