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 {
|
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,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue