mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 16:21:11 +00:00
Try out UnifyResult
This commit is contained in:
parent
0093298ed0
commit
e949c56f70
1 changed files with 195 additions and 43 deletions
238
src/unify.rs
238
src/unify.rs
|
@ -13,12 +13,18 @@ struct Context {
|
|||
|
||||
struct RecordStructure {
|
||||
fields: ImMap<Lowercase, Variable>,
|
||||
extension: Variable,
|
||||
ext: Variable,
|
||||
}
|
||||
|
||||
type UnifyResult = Result<(), Problem>;
|
||||
|
||||
type Problems = Vec<Problem>;
|
||||
|
||||
#[inline(always)]
|
||||
pub fn unify(subs: &mut Subs, var1: Variable, var2: Variable) {
|
||||
if !subs.equivalent(var1, var2) {
|
||||
pub fn unify(subs: &mut Subs, var1: Variable, var2: Variable) -> UnifyResult {
|
||||
if subs.equivalent(var1, var2) {
|
||||
Ok(())
|
||||
} else {
|
||||
let ctx = Context {
|
||||
first: 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 {
|
||||
FlexVar(opt_name) => unify_flex(subs, &ctx, opt_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),
|
||||
Error(problem) => {
|
||||
// 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,
|
||||
args: &Vec<(Lowercase, Variable)>,
|
||||
real_var: &Variable,
|
||||
) {
|
||||
) -> UnifyResult {
|
||||
let other_content = &ctx.second_desc.content;
|
||||
|
||||
match other_content {
|
||||
|
@ -61,41 +69,71 @@ fn unify_alias(
|
|||
subs,
|
||||
&ctx,
|
||||
Alias(home.clone(), name.clone(), args.clone(), *real_var),
|
||||
)
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
RigidVar(_) => unify(subs, *real_var, ctx.second),
|
||||
Alias(other_home, other_name, other_args, other_real_var) => {
|
||||
if name == other_name && home == other_home {
|
||||
if args.len() == other_args.len() {
|
||||
let mut answer = Ok(());
|
||||
|
||||
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() {
|
||||
merge(subs, &ctx, Error(Problem::ExtraArguments))
|
||||
let problem = Problem::ExtraArguments;
|
||||
|
||||
merge(subs, &ctx, Error(problem.clone()));
|
||||
|
||||
Err(problem)
|
||||
} else {
|
||||
merge(subs, &ctx, Error(Problem::MissingArguments))
|
||||
let problem = Problem::MissingArguments;
|
||||
|
||||
merge(subs, &ctx, Error(problem.clone()));
|
||||
|
||||
Err(problem)
|
||||
}
|
||||
} else {
|
||||
unify(subs, *real_var, *other_real_var)
|
||||
}
|
||||
}
|
||||
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)]
|
||||
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 {
|
||||
FlexVar(_) => {
|
||||
// If the other is flex, Structure wins!
|
||||
merge(subs, ctx, Structure(flat_type.clone()))
|
||||
merge(subs, ctx, Structure(flat_type.clone()));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
RigidVar(_) => {
|
||||
let problem = Problem::GenericMismatch;
|
||||
// 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) => {
|
||||
// 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),
|
||||
Error(problem) => {
|
||||
// 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) {
|
||||
panic!("TODO unify_record");
|
||||
let x = 5;
|
||||
fn unify_record(
|
||||
subs: &mut Subs,
|
||||
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)]
|
||||
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::*;
|
||||
|
||||
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),
|
||||
|
||||
(EmptyRecord, Record(fields, ext)) if fields.is_empty() => unify(subs, ctx.first, *ext),
|
||||
|
||||
(Record(fields1, ext1), Record(fields2, ext2)) => {
|
||||
let structure1 = gather_fields(subs, fields1.clone(), *ext1);
|
||||
let structure2 = gather_fields(subs, fields2.clone(), *ext2);
|
||||
let rec1 = gather_fields(subs, fields1.clone(), *ext1);
|
||||
let rec2 = gather_fields(subs, fields2.clone(), *ext2);
|
||||
|
||||
unify_record(ctx, structure1, structure2)
|
||||
unify_record(subs, ctx, rec1, rec2)
|
||||
}
|
||||
(
|
||||
Apply {
|
||||
|
@ -153,21 +273,35 @@ fn unify_flat_type(subs: &mut Subs, ctx: &Context, left: &FlatType, right: &Flat
|
|||
name: (*r_type_name).clone(),
|
||||
args: (*r_args).clone(),
|
||||
}),
|
||||
)
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
(Func(l_args, l_ret), Func(r_args, r_ret)) => {
|
||||
if l_args.len() == r_args.len() {
|
||||
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() {
|
||||
merge(subs, ctx, Error(Problem::ExtraArguments))
|
||||
merge(subs, ctx, Error(Problem::ExtraArguments));
|
||||
|
||||
Ok(())
|
||||
} 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)]
|
||||
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 {
|
||||
FlexVar(_) => {
|
||||
// If the other is flex, rigid wins!
|
||||
merge(subs, ctx, RigidVar(name.into()))
|
||||
merge(subs, ctx, RigidVar(name.into()));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
RigidVar(_) | Structure(_) => {
|
||||
// Type mismatch! Rigid can only unify with flex, even if the
|
||||
// rigid names are the same.
|
||||
merge(subs, ctx, Error(Problem::GenericMismatch))
|
||||
merge(subs, ctx, Error(Problem::GenericMismatch));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Alias(_, _, _, _) => {
|
||||
panic!("TODO unify_rigid Alias");
|
||||
panic!("TODO");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Error(problem) => {
|
||||
// Error propagates.
|
||||
merge(subs, ctx, Error(problem.clone()))
|
||||
merge(subs, ctx, Error(problem.clone()));
|
||||
|
||||
Err(problem.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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 {
|
||||
FlexVar(None) => {
|
||||
// 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.
|
||||
// (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)
|
||||
}
|
||||
|
||||
_ => RecordStructure {
|
||||
fields,
|
||||
extension: var,
|
||||
},
|
||||
_ => RecordStructure { fields, ext: var },
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue