Skip to content

Commit 3aba50f

Browse files
committed
Implement Robinson's algorithm for type unification. Closes #227.
1 parent 0ddb832 commit 3aba50f

File tree

1 file changed

+70
-75
lines changed

1 file changed

+70
-75
lines changed

src/comp/middle/ty.rs

Lines changed: 70 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -570,24 +570,6 @@ fn count_ty_params(@t ty) -> uint {
570570
ret _vec.len[ast.def_id](*param_ids);
571571
}
572572

573-
fn type_contains_ty_vars(@t ty) -> bool {
574-
state obj checker(@mutable bool has_vars) {
575-
fn fold_simple_ty(@t ty) -> @t {
576-
alt (ty.struct) {
577-
case (ty_var(_)) {
578-
*has_vars = true;
579-
}
580-
case (_) {}
581-
}
582-
ret ty;
583-
}
584-
}
585-
586-
let @mutable bool b = @mutable false;
587-
fold_ty(checker(b), ty);
588-
ret *b;
589-
}
590-
591573
// Type accessors for substructures of types
592574

593575
fn ty_fn_args(@t fty) -> vec[arg] {
@@ -802,7 +784,10 @@ fn is_lval(@ast.expr expr) -> bool {
802784
}
803785
}
804786

805-
// Type unification
787+
// Type unification via Robinson's algorithm (Robinson 1965). Implemented as
788+
// described in Hoder and Voronkov:
789+
//
790+
// http://www.cs.man.ac.uk/~hoderk/ubench/unification_full.pdf
806791

807792
fn unify(@ty.t expected, @ty.t actual, &unify_handler handler)
808793
-> unify_result {
@@ -822,7 +807,7 @@ fn unify(@ty.t expected, @ty.t actual, &unify_handler handler)
822807
ret ures_err(terr_mismatch, expected, actual);
823808
}
824809

825-
fn unify_fn(&hashmap[int,@ty.t] bindings,
810+
fn unify_fn(@hashmap[int,@ty.t] bindings,
826811
@ty.t expected,
827812
@ty.t actual,
828813
&unify_handler handler,
@@ -891,7 +876,7 @@ fn unify(@ty.t expected, @ty.t actual, &unify_handler handler)
891876

892877
}
893878

894-
fn unify_obj(&hashmap[int,@ty.t] bindings,
879+
fn unify_obj(@hashmap[int,@ty.t] bindings,
895880
@ty.t expected,
896881
@ty.t actual,
897882
&unify_handler handler,
@@ -952,27 +937,44 @@ fn unify(@ty.t expected, @ty.t actual, &unify_handler handler)
952937
ret ures_ok(t);
953938
}
954939

955-
fn unify_step(&hashmap[int,@ty.t] bindings, @ty.t expected, @ty.t actual,
956-
&unify_handler handler) -> unify_result {
940+
fn resolve(@hashmap[int,@t] bindings, @t typ) -> @t {
941+
alt (typ.struct) {
942+
case (ty_var(?id)) {
943+
alt (bindings.find(id)) {
944+
case (some[@t](?typ2)) {
945+
ret resolve(bindings, typ2);
946+
}
947+
case (none[@t]) {
948+
// fall through
949+
}
950+
}
951+
}
952+
case (_) {
953+
// fall through
954+
}
955+
}
956+
ret typ;
957+
}
958+
959+
fn unify_step(@hashmap[int,@ty.t] bindings, @ty.t in_expected,
960+
@ty.t in_actual, &unify_handler handler) -> unify_result {
961+
962+
// Resolve any bindings.
963+
auto expected = resolve(bindings, in_expected);
964+
auto actual = resolve(bindings, in_actual);
965+
957966
// TODO: rewrite this using tuple pattern matching when available, to
958967
// avoid all this rightward drift and spikiness.
959968

969+
// TODO: occurs check, to make sure we don't loop forever when
970+
// unifying e.g. 'a and option['a]
971+
960972
alt (actual.struct) {
961973
// If the RHS is a variable type, then just do the appropriate
962974
// binding.
963975
case (ty.ty_var(?actual_id)) {
964-
alt (bindings.find(actual_id)) {
965-
case (some[@ty.t](?actual_ty)) {
966-
// FIXME: change the binding here?
967-
// FIXME: "be"
968-
ret unify_step(bindings, expected, actual_ty,
969-
handler);
970-
}
971-
case (none[@ty.t]) {
972-
bindings.insert(actual_id, expected);
973-
ret ures_ok(expected);
974-
}
975-
}
976+
bindings.insert(actual_id, expected);
977+
ret ures_ok(expected);
976978
}
977979
case (ty.ty_local(?actual_id)) {
978980
auto actual_ty = handler.resolve_local(actual_id);
@@ -1077,8 +1079,6 @@ fn unify(@ty.t expected, @ty.t actual, &unify_handler handler)
10771079
}
10781080
}
10791081

1080-
// TODO: ty_var
1081-
10821082
case (_) {
10831083
ret ures_err(terr_mismatch, expected, actual);
10841084
}
@@ -1102,8 +1102,6 @@ fn unify(@ty.t expected, @ty.t actual, &unify_handler handler)
11021102
}
11031103
}
11041104

1105-
// TODO: ty_var
1106-
11071105
case (_) {
11081106
ret ures_err(terr_mismatch, expected, actual);
11091107
}
@@ -1152,8 +1150,6 @@ fn unify(@ty.t expected, @ty.t actual, &unify_handler handler)
11521150
ret ures_ok(plain_ty(ty.ty_tup(result_elems)));
11531151
}
11541152

1155-
// TODO: ty_var
1156-
11571153
case (_) {
11581154
ret ures_err(terr_mismatch, expected, actual);
11591155
}
@@ -1213,8 +1209,6 @@ fn unify(@ty.t expected, @ty.t actual, &unify_handler handler)
12131209
ret ures_ok(plain_ty(ty.ty_rec(result_fields)));
12141210
}
12151211

1216-
// TODO: ty_var
1217-
12181212
case (_) {
12191213
ret ures_err(terr_mismatch, expected, actual);
12201214
}
@@ -1248,20 +1242,9 @@ fn unify(@ty.t expected, @ty.t actual, &unify_handler handler)
12481242
}
12491243

12501244
case (ty.ty_var(?expected_id)) {
1251-
alt (bindings.find(expected_id)) {
1252-
case (some[@ty.t](?expected_ty)) {
1253-
// FIXME: change the binding here?
1254-
// FIXME: "be"
1255-
ret unify_step(bindings,
1256-
expected_ty,
1257-
actual,
1258-
handler);
1259-
}
1260-
case (none[@ty.t]) {
1261-
bindings.insert(expected_id, actual);
1262-
ret ures_ok(actual);
1263-
}
1264-
}
1245+
// Add a binding.
1246+
bindings.insert(expected_id, actual);
1247+
ret ures_ok(actual);
12651248
}
12661249

12671250
case (ty.ty_local(?expected_id)) {
@@ -1289,31 +1272,43 @@ fn unify(@ty.t expected, @ty.t actual, &unify_handler handler)
12891272
fail;
12901273
}
12911274

1275+
// Performs type binding substitution.
1276+
fn substitute(@hashmap[int,@t] bindings, @t typ) -> @t {
1277+
state obj folder(@hashmap[int,@t] bindings) {
1278+
fn fold_simple_ty(@t typ) -> @t {
1279+
alt (typ.struct) {
1280+
case (ty_var(?id)) {
1281+
alt (bindings.find(id)) {
1282+
case (some[@t](?typ2)) {
1283+
ret substitute(bindings, typ2);
1284+
}
1285+
case (none[@t]) {
1286+
ret typ;
1287+
}
1288+
}
1289+
}
1290+
case (_) {
1291+
ret typ;
1292+
}
1293+
}
1294+
}
1295+
}
1296+
1297+
ret ty.fold_ty(folder(bindings), typ);
1298+
}
1299+
12921300
fn hash_int(&int x) -> uint { ret x as uint; }
12931301
fn eq_int(&int a, &int b) -> bool { ret a == b; }
12941302
auto hasher = hash_int;
12951303
auto eqer = eq_int;
1296-
auto bindings = map.mk_hashmap[int,@ty.t](hasher, eqer);
1297-
1298-
// FIXME: this is a slow way of driving types into residual vars that
1299-
// occur up in the leaves of result type; it can likely be done better
1300-
// when unification is actually ... down in the leaves.
1304+
auto bindings = @map.mk_hashmap[int,@ty.t](hasher, eqer);
13011305

13021306
auto ures = unify_step(bindings, expected, actual, handler);
1303-
while (true) {
1304-
alt (ures) {
1305-
case (ures_ok(?t)) {
1306-
if (!type_contains_ty_vars(t)) {
1307-
ret ures;
1308-
}
1309-
ures = unify_step(bindings, t, actual, handler);
1310-
}
1311-
case (_) {
1312-
ret ures;
1313-
}
1314-
}
1307+
alt (ures) {
1308+
case (ures_ok(?t)) { ret ures_ok(substitute(bindings, t)); }
1309+
case (_) { ret ures; }
13151310
}
1316-
fail;
1311+
fail; // not reached
13171312
}
13181313

13191314
fn type_err_to_str(&ty.type_err err) -> str {

0 commit comments

Comments
 (0)