rewrite unify in a modal style, extend result module

This commit is contained in:
Niko Matsakis 2012-03-13 20:46:16 -04:00
parent 9086c6f5a2
commit 7c70d35a10
3 changed files with 370 additions and 263 deletions

View file

@ -89,6 +89,86 @@ fn chain<T, U: copy, V: copy>(res: result<T, V>, op: fn(T) -> result<U, V>)
} }
} }
// ______________________________________________________________________
// Note:
//
// These helper functions are written in a "pre-chained" (a.k.a,
// deforested) style because I have found that, in practice, this is
// the most concise way to do things. That means that they do not not
// terminate with a call to `ok(v)` but rather `nxt(v)`. If you would
// like to just get the result, just pass in `ok` as `nxt`.
#[doc = "
Maps each element in the vector `ts` using the operation `op`. Should an
error occur, no further mappings are performed and the error is returned.
Should no error occur, a vector containing the result of each map is
passed to the `nxt` function.
Here is an example which increments every integer in a vector,
checking for overflow:
fn inc_conditionally(x: uint) -> result<uint,str> {
if x == uint::max_value { ret err(\"overflow\"); }
else { ret ok(x+1u); }
}
map([1u, 2u, 3u], inc_conditionally) {|incd|
assert incd == [2u, 3u, 4u];
}
Note: if you have to combine a deforested style transform with map,
you should use `ok` for the `nxt` operation, as shown here (this is an
alternate version of the previous example where the
`inc_conditionally()` routine is deforested):
fn inc_conditionally<T>(x: uint,
nxt: fn(uint) -> result<T,str>) -> result<T,str> {
if x == uint::max_value { ret err(\"overflow\"); }
else { ret nxt(x+1u); }
}
map([1u, 2u, 3u], inc_conditionally(_, ok)) {|incd|
assert incd == [2u, 3u, 4u];
}
"]
fn map<T,U:copy,V:copy,W>(ts: [T],
op: fn(T) -> result<V,U>,
nxt: fn([V]) -> result<W,U>) -> result<W,U> {
let mut vs: [V] = [];
vec::reserve(vs, vec::len(ts));
for t in ts {
alt op(t) {
ok(v) { vs += [v]; }
err(u) { ret err(u); }
}
}
ret nxt(vs);
}
#[doc = "Same as map, but it operates over two parallel vectors.
A precondition is used here to ensure that the vectors are the same
length. While we do not often use preconditions in the standard
library, a precondition is used here because result::t is generally
used in 'careful' code contexts where it is both appropriate and easy
to accommodate an error like the vectors being of different lengths."]
fn map2<S,T,U:copy,V:copy,W>(ss: [S], ts: [T],
op: fn(S,T) -> result<V,U>,
nxt: fn([V]) -> result<W,U>)
: vec::same_length(ss, ts)
-> result<W,U> {
let n = vec::len(ts);
let mut vs = [];
vec::reserve(vs, n);
let mut i = 0u;
while i < n {
alt op(ss[i],ts[i]) {
ok(v) { vs += [v]; }
err(u) { ret err(u); }
}
i += 1u;
}
ret nxt(vs);
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
fn op1() -> result::result<int, str> { result::ok(666) } fn op1() -> result::result<int, str> { result::ok(666) }

View file

@ -287,6 +287,7 @@ enum type_err {
terr_ref_mutability, terr_ref_mutability,
terr_vec_mutability, terr_vec_mutability,
terr_tuple_size(uint, uint), terr_tuple_size(uint, uint),
terr_ty_param_size(uint, uint),
terr_record_size(uint, uint), terr_record_size(uint, uint),
terr_record_mutability, terr_record_mutability,
terr_record_fields(ast::ident, ast::ident), terr_record_fields(ast::ident, ast::ident),
@ -1550,26 +1551,21 @@ fn set_default_mode(cx: ctxt, m: ast::mode, m_def: ast::rmode) {
// //
// http://www.cs.man.ac.uk/~hoderk/ubench/unification_full.pdf // http://www.cs.man.ac.uk/~hoderk/ubench/unification_full.pdf
mod unify { mod unify {
export fixup_result; import result::{result, ok, err, chain, map, map2};
export fixup_vars; export fixup_vars;
export fix_ok;
export fix_err;
export mk_var_bindings; export mk_var_bindings;
export resolve_type_structure; export resolve_type_structure;
export resolve_type_var; export resolve_type_var;
export result;
export unify; export unify;
export ures_ok;
export ures_err;
export var_bindings; export var_bindings;
export precise, in_bindings; export precise, in_bindings;
enum result { ures_ok(t), ures_err(type_err), } type ures<T> = result<T,type_err>;
enum union_result { unres_ok, unres_err(type_err), }
enum fixup_result { // in case of failure, value is the idx of an unresolved type var
fix_ok(t), // fixup succeeded type fres<T> = result<T,int>;
fix_err(int), // fixup failed because a type variable was unresolved
}
type var_bindings = type var_bindings =
{sets: ufind::ufind, types: smallintmap::smallintmap<t>}; {sets: ufind::ufind, types: smallintmap::smallintmap<t>};
@ -1584,8 +1580,10 @@ mod unify {
} }
// Unifies two sets. // Unifies two sets.
fn union(cx: @uctxt, set_a: uint, set_b: uint, fn union<T:copy>(
variance: variance) -> union_result { cx: @uctxt, set_a: uint, set_b: uint,
variance: variance, nxt: fn() -> ures<T>) -> ures<T> {
let vb = alt cx.st { let vb = alt cx.st {
in_bindings(vb) { vb } in_bindings(vb) { vb }
_ { cx.tcx.sess.bug("someone forgot to document an invariant \ _ { cx.tcx.sess.bug("someone forgot to document an invariant \
@ -1606,76 +1604,80 @@ mod unify {
alt smallintmap::find(vb.types, root_a) { alt smallintmap::find(vb.types, root_a) {
none { none {
alt smallintmap::find(vb.types, root_b) { alt smallintmap::find(vb.types, root_b) {
none { ufind::union(vb.sets, set_a, set_b); ret unres_ok; } none { ufind::union(vb.sets, set_a, set_b); ret nxt(); }
some(t_b) { replace_type(vb, t_b); ret unres_ok; } some(t_b) { replace_type(vb, t_b); ret nxt(); }
} }
} }
some(t_a) { some(t_a) {
alt smallintmap::find(vb.types, root_b) { alt smallintmap::find(vb.types, root_b) {
none { replace_type(vb, t_a); ret unres_ok; } none { replace_type(vb, t_a); ret nxt(); }
some(t_b) { some(t_b) {
alt unify_step(cx, t_a, t_b, variance) { ret unify_step(cx, t_a, t_b, variance) {|t_c|
ures_ok(t_c) { replace_type(vb, t_c); ret unres_ok; } replace_type(vb, t_c);
ures_err(terr) { ret unres_err(terr); } nxt()
} };
} }
} }
} }
} }
} }
fn record_var_binding(cx: @uctxt, key: int, typ: t, variance: variance) fn record_var_binding<T:copy>(
-> result { cx: @uctxt, key: int,
typ: t, variance: variance,
nxt: fn(t) -> ures<T>) -> ures<T> {
let vb = alt check cx.st { in_bindings(vb) { vb } }; let vb = alt check cx.st { in_bindings(vb) { vb } };
ufind::grow(vb.sets, (key as uint) + 1u); ufind::grow(vb.sets, (key as uint) + 1u);
let root = ufind::find(vb.sets, key as uint); let root = ufind::find(vb.sets, key as uint);
let result_type = typ; let result_type = typ;
alt smallintmap::find(vb.types, root) { alt smallintmap::find(vb.types, root) {
some(old_type) { some(old_type) {
alt unify_step(cx, old_type, typ, variance) { alt unify_step(cx, old_type, typ, variance, {|v| ok(v)}) {
ures_ok(unified_type) { result_type = unified_type; } ok(unified_type) { result_type = unified_type; }
rs { ret rs; } err(e) { ret err(e); }
} }
} }
none {/* fall through */ } none {/* fall through */ }
} }
smallintmap::insert(vb.types, root, result_type); smallintmap::insert(vb.types, root, result_type);
ret ures_ok(mk_var(cx.tcx, key)); ret nxt(mk_var(cx.tcx, key));
} }
// Simple structural type comparison. // Simple structural type comparison.
fn struct_cmp(cx: @uctxt, expected: t, actual: t) -> result { fn struct_cmp<T:copy>(
cx: @uctxt, expected: t, actual: t,
nxt: fn(t) -> ures<T>) -> ures<T> {
let tcx = cx.tcx; let tcx = cx.tcx;
let cfg = tcx.sess.targ_cfg; let cfg = tcx.sess.targ_cfg;
if mach_sty(cfg, expected) == mach_sty(cfg, actual) { if mach_sty(cfg, expected) == mach_sty(cfg, actual) {
ret ures_ok(expected); ret nxt(expected);
} }
ret ures_err(terr_mismatch); ret err(terr_mismatch);
} }
// Right now this just checks that the lists of constraints are // Right now this just checks that the lists of constraints are
// pairwise equal. // pairwise equal.
fn unify_constrs(base_t: t, expected: [@type_constr], fn unify_constrs<T:copy>(
actual: [@type_constr]) -> result { expected: [@type_constr],
let expected_len = vec::len(expected); actual: [@type_constr],
let actual_len = vec::len(actual); nxt: fn([@type_constr]) -> ures<T>) -> ures<T> {
if expected_len != actual_len { if check vec::same_length(expected, actual) {
ret ures_err(terr_constr_len(expected_len, actual_len)); map2(expected, actual,
{|e,a| unify_constr(e, a, {|v| ok(v)})}, nxt)
} else {
ret err(terr_constr_len(expected.len(), actual.len()));
} }
let i = 0u;
let rslt;
for c: @type_constr in expected {
rslt = unify_constr(base_t, c, actual[i]);
alt rslt { ures_ok(_) { } ures_err(_) { ret rslt; } }
i += 1u;
} }
ret ures_ok(base_t);
} fn unify_constr<T:copy>(
fn unify_constr(base_t: t, expected: @type_constr, expected: @type_constr,
actual_constr: @type_constr) -> result { actual_constr: @type_constr,
let ok_res = ures_ok(base_t); nxt: fn(@type_constr) -> ures<T>) -> ures<T> {
let err_res = ures_err(terr_constr_mismatch(expected, actual_constr));
let err_res = err(terr_constr_mismatch(expected, actual_constr));
if expected.node.id != actual_constr.node.id { ret err_res; } if expected.node.id != actual_constr.node.id { ret err_res; }
let expected_arg_len = vec::len(expected.node.args); let expected_arg_len = vec::len(expected.node.args);
let actual_arg_len = vec::len(actual_constr.node.args); let actual_arg_len = vec::len(actual_constr.node.args);
@ -1703,13 +1705,14 @@ mod unify {
} }
i += 1u; i += 1u;
} }
ret ok_res; ret nxt(expected);
} }
// Unifies two mutability flags. // Unifies two mutability flags.
fn unify_mut(expected: ast::mutability, actual: ast::mutability, fn unify_mut<T:copy>(
variance: variance) -> expected: ast::mutability, actual: ast::mutability,
option<(ast::mutability, variance)> { variance: variance, mut_err: type_err,
nxt: fn(ast::mutability, variance) -> ures<T>) -> ures<T> {
// If you're unifying on something mutable then we have to // If you're unifying on something mutable then we have to
// be invariant on the inner type // be invariant on the inner type
@ -1722,20 +1725,25 @@ mod unify {
} }
}; };
if expected == actual { ret some((expected, newvariance)); } if expected == actual {
ret nxt(expected, newvariance);
}
if variance == covariant { if variance == covariant {
if expected == ast::m_const { if expected == ast::m_const {
ret some((actual, newvariance)); ret nxt(actual, newvariance);
} }
} else if variance == contravariant { } else if variance == contravariant {
if actual == ast::m_const { if actual == ast::m_const {
ret some((expected, newvariance)); ret nxt(expected, newvariance);
} }
} }
ret none; ret err(mut_err);
} }
fn unify_fn_proto(e_proto: ast::proto, a_proto: ast::proto,
variance: variance) -> option<result> { fn unify_fn_proto<T:copy>(
e_proto: ast::proto, a_proto: ast::proto, variance: variance,
nxt: fn(ast::proto) -> ures<T>) -> ures<T> {
// Prototypes form a diamond-shaped partial order: // Prototypes form a diamond-shaped partial order:
// //
// block // block
@ -1757,90 +1765,91 @@ mod unify {
} }
ret alt variance { ret alt variance {
invariant if e_proto == a_proto { none } invariant if e_proto == a_proto { nxt(e_proto) }
covariant if sub_proto(a_proto, e_proto) { none } covariant if sub_proto(a_proto, e_proto) { nxt(e_proto) }
contravariant if sub_proto(e_proto, a_proto) { none } contravariant if sub_proto(e_proto, a_proto) { nxt(e_proto) }
_ { some(ures_err(terr_mismatch)) } _ { ret err(terr_mismatch) }
}; };
} }
fn unify_args(cx: @uctxt, e_args: [arg], a_args: [arg],
variance: variance) -> either<result, [arg]> { fn unify_arg<T:copy>(
if !vec::same_length(e_args, a_args) { cx: @uctxt, e_arg: arg, a_arg: arg,
ret either::left(ures_err(terr_arg_count)); variance: variance,
nxt: fn(arg) -> ures<T>) -> ures<T> {
// Unify the result modes.
chain(unify_mode(cx.tcx, e_arg.mode, a_arg.mode)) {|mode|
unify_step(cx, e_arg.ty, a_arg.ty, variance) {|ty|
nxt({mode: mode, ty: ty})
} }
}
}
fn unify_args<T:copy>(
cx: @uctxt, e_args: [arg], a_args: [arg],
variance: variance, nxt: fn([arg]) -> ures<T>) -> ures<T> {
if check vec::same_length(e_args, a_args) {
// The variance changes (flips basically) when descending // The variance changes (flips basically) when descending
// into arguments of function types // into arguments of function types
let variance = variance_transform(variance, contravariant); let variance = variance_transform(variance, contravariant);
// Would use vec::map2(), but for the need to return in case of map2(e_args, a_args,
// error: {|e,a| unify_arg(cx, e, a, variance, {|v| ok(v)})},
let i = 0u, result = []; nxt)
for expected_input in e_args { } else {
let actual_input = a_args[i]; ret err(terr_arg_count);
i += 1u;
// Unify the result modes.
let result_mode =
alt unify_mode(cx.tcx, expected_input.mode,
actual_input.mode) {
result::err(err) { ret either::left(ures_err(err)); }
result::ok(m) { m }
};
alt unify_step(cx, expected_input.ty, actual_input.ty,
variance) {
ures_ok(rty) { result += [{mode: result_mode, ty: rty}]; }
err { ret either::left(err); }
} }
} }
either::right(result)
}
fn unify_fn(cx: @uctxt, e_f: fn_ty, a_f: fn_ty, variance: variance)
-> result {
alt unify_fn_proto(e_f.proto, a_f.proto, variance) {
some(err) { ret err; }
none { /* fall through */ }
}
if a_f.ret_style != ast::noreturn && a_f.ret_style != e_f.ret_style { fn unify_ret_style<T:copy>(
e_ret_style: ret_style,
a_ret_style: ret_style,
nxt: fn(ret_style) -> ures<T>) -> ures<T> {
if a_ret_style != ast::noreturn && a_ret_style != e_ret_style {
/* even though typestate checking is mostly /* even though typestate checking is mostly
responsible for checking control flow annotations, responsible for checking control flow annotations,
this check is necessary to ensure that the this check is necessary to ensure that the
annotation in an object method matches the annotation in an object method matches the
declared object type */ declared object type */
ret ures_err(terr_ret_style_mismatch(e_f.ret_style, ret err(terr_ret_style_mismatch(e_ret_style, a_ret_style));
a_f.ret_style)); } else {
nxt(a_ret_style)
}
} }
let result_ins = alt unify_args(cx, e_f.inputs, a_f.inputs,
variance) {
either::left(err) { ret err; }
either::right(ts) { ts }
};
// Check the output. fn unify_fn<T:copy>(
alt unify_step(cx, e_f.output, a_f.output, variance) { cx: @uctxt, e_f: fn_ty, a_f: fn_ty, variance: variance,
ures_ok(rty) { nxt: fn(t) -> ures<T>) -> ures<T> {
ures_ok(mk_fn(cx.tcx, {proto: e_f.proto,
inputs: result_ins, unify_fn_proto(e_f.proto, a_f.proto, variance) {|proto|
output: rty unify_ret_style(e_f.ret_style, a_f.ret_style) {|rs|
with a_f})) unify_args(cx, e_f.inputs, a_f.inputs, variance) {|args|
unify_step(cx, e_f.output, a_f.output, variance) {|rty|
let cs = e_f.constraints; // FIXME: Unify?
nxt(mk_fn(cx.tcx, {proto: proto,
inputs: args,
output: rty,
ret_style: rs,
constraints: cs}))
}
}
} }
x { x }
} }
} }
// If the given type is a variable, returns the structure of that type. // If the given type is a variable, returns the structure of that type.
fn resolve_type_structure(vb: @var_bindings, typ: t) -> fn resolve_type_structure(vb: @var_bindings, typ: t) -> fres<t> {
fixup_result {
alt get(typ).struct { alt get(typ).struct {
ty_var(vid) { ty_var(vid) {
if vid as uint >= ufind::set_count(vb.sets) { ret fix_err(vid); } if vid as uint >= ufind::set_count(vb.sets) { ret err(vid); }
let root_id = ufind::find(vb.sets, vid as uint); let root_id = ufind::find(vb.sets, vid as uint);
alt smallintmap::find::<t>(vb.types, root_id) { alt smallintmap::find::<t>(vb.types, root_id) {
none { ret fix_err(vid); } none { ret err(vid); }
some(rt) { ret fix_ok(rt); } some(rt) { ret ok(rt); }
} }
} }
_ { ret fix_ok(typ); } _ { ret ok(typ); }
} }
} }
@ -1886,47 +1895,66 @@ mod unify {
} }
} }
fn unify_tps(cx: @uctxt, expected_tps: [t], actual_tps: [t], fn unify_tys<T:copy>(
variance: variance, finish: fn([t]) -> t) -> result { cx: @uctxt, expected_tps: [t], actual_tps: [t],
let result_tps = [], i = 0u; variance: variance, nxt: fn([t]) -> ures<T>)
for exp in expected_tps { : vec::same_length(expected_tps, actual_tps)
let act = actual_tps[i]; -> ures<T> {
i += 1u;
let result = unify_step(cx, exp, act, variance); map2(expected_tps, actual_tps,
alt result { {|e,a| unify_step(cx, e, a, variance, {|v| ok(v)})},
ures_ok(rty) { result_tps += [rty]; } nxt)
_ { ret result; } }
fn unify_tps<T:copy>(
cx: @uctxt, expected_tps: [t], actual_tps: [t],
variance: variance, nxt: fn([t]) -> ures<T>)
-> ures<T> {
if check vec::same_length(expected_tps, actual_tps) {
map2(expected_tps, actual_tps,
{|e,a| unify_step(cx, e, a, variance, {|v| ok(v)})},
nxt)
} else {
err(terr_ty_param_size(expected_tps.len(),
actual_tps.len()))
} }
} }
ures_ok(finish(result_tps))
} fn unify_mt<T:copy>(
fn unify_mt(cx: @uctxt, e_mt: mt, a_mt: mt, variance: variance, cx: @uctxt, e_mt: mt, a_mt: mt, variance: variance,
mut_err: type_err, finish: fn(ctxt, mt) -> t) -> result { mut_err: type_err,
alt unify_mut(e_mt.mutbl, a_mt.mutbl, variance) { nxt: fn(mt) -> ures<T>) -> ures<T> {
none { ures_err(mut_err) } unify_mut(e_mt.mutbl, a_mt.mutbl, variance, mut_err) {|mutbl,var|
some((mutt, var)) { unify_step(cx, e_mt.ty, a_mt.ty, var) {|ty|
alt unify_step(cx, e_mt.ty, a_mt.ty, var) { nxt({ty: ty, mutbl: mutbl})
ures_ok(result_sub) {
ures_ok(finish(cx.tcx, {ty: result_sub, mutbl: mutt}))
}
err { err }
} }
} }
} }
}
fn unify_regions(cx: @uctxt, e_region: region, a_region: region, fn unify_regions<T:copy>(
variance: variance) -> option<region> { cx: @uctxt, e_region: region, a_region: region,
variance: variance,
nxt: fn(region) -> ures<T>) -> ures<T> {
let sub, super; let sub, super;
alt variance { alt variance {
covariant { super = e_region; sub = a_region; } covariant { super = e_region; sub = a_region; }
contravariant { super = a_region; sub = e_region; } contravariant { super = a_region; sub = e_region; }
invariant { invariant {
ret if e_region == a_region { some(e_region) } else { none }; ret if e_region == a_region {
nxt(e_region)
} else {
err(terr_regions_differ)
};
} }
} }
if sub == ty::re_inferred || super == ty::re_inferred { if sub == ty::re_inferred || super == ty::re_inferred {
ret if sub == super { some(super) } else { none }; ret if sub == super {
nxt(super)
} else {
err(terr_regions_differ)
};
} }
// Outer regions are subtypes of inner regions. (This is somewhat // Outer regions are subtypes of inner regions. (This is somewhat
@ -1934,151 +1962,147 @@ mod unify {
let superscope = region::region_to_scope(cx.tcx.region_map, super); let superscope = region::region_to_scope(cx.tcx.region_map, super);
let subscope = region::region_to_scope(cx.tcx.region_map, sub); let subscope = region::region_to_scope(cx.tcx.region_map, sub);
if region::scope_contains(cx.tcx.region_map, subscope, superscope) { if region::scope_contains(cx.tcx.region_map, subscope, superscope) {
ret some(super); ret nxt(super);
} }
ret none; ret err(terr_regions_differ);
} }
fn unify_step(cx: @uctxt, expected: t, actual: t, fn unify_field<T:copy>(
variance: variance) -> result { cx: @uctxt, e_field: field, a_field: field,
variance: variance,
nxt: fn(field) -> ures<T>) -> ures<T> {
if e_field.ident != a_field.ident {
ret err(terr_record_fields(e_field.ident,
a_field.ident));
}
unify_mt(cx, e_field.mt, a_field.mt, variance,
terr_record_mutability) {|mt|
nxt({ident: e_field.ident, mt: mt})
}
}
fn unify_step<T:copy>(
cx: @uctxt, expected: t, actual: t,
variance: variance, nxt: fn(t) -> ures<T>) -> ures<T> {
// Fast path. // Fast path.
if expected == actual { ret ures_ok(expected); } if expected == actual { ret nxt(expected); }
alt (get(expected).struct, get(actual).struct) { alt (get(expected).struct, get(actual).struct) {
(ty_var(e_id), ty_var(a_id)) { (ty_var(e_id), ty_var(a_id)) {
alt union(cx, e_id as uint, a_id as uint, variance) { union(cx, e_id as uint, a_id as uint, variance) {||
unres_ok { ures_ok(actual) } nxt(actual)
unres_err(err) { ures_err(err) }
} }
} }
(_, ty_var(a_id)) { (_, ty_var(a_id)) {
let v = variance_transform(variance, contravariant); let v = variance_transform(variance, contravariant);
record_var_binding(cx, a_id, expected, v) record_var_binding(cx, a_id, expected, v, nxt)
} }
(ty_var(e_id), _) { (ty_var(e_id), _) {
let v = variance_transform(variance, covariant); let v = variance_transform(variance, covariant);
record_var_binding(cx, e_id, actual, v) record_var_binding(cx, e_id, actual, v, nxt)
} }
(_, ty_bot) { ures_ok(expected) } (_, ty_bot) { nxt(expected) }
(ty_bot, _) { ures_ok(actual) } (ty_bot, _) { nxt(actual) }
(ty_nil, _) | (ty_bool, _) | (ty_int(_), _) | (ty_uint(_), _) | (ty_nil, _) | (ty_bool, _) | (ty_int(_), _) | (ty_uint(_), _) |
(ty_float(_), _) | (ty_str, _) | (ty_send_type, _) { (ty_float(_), _) | (ty_str, _) | (ty_send_type, _) {
struct_cmp(cx, expected, actual) struct_cmp(cx, expected, actual, nxt)
} }
(ty_param(e_n, _), ty_param(a_n, _)) if e_n == a_n { (ty_param(e_n, _), ty_param(a_n, _)) if e_n == a_n {
ures_ok(expected) nxt(expected)
} }
(ty_enum(e_id, e_tps), ty_enum(a_id, a_tps)) if e_id == a_id { (ty_enum(e_id, e_tps), ty_enum(a_id, a_tps)) if e_id == a_id {
unify_tps(cx, e_tps, a_tps, variance, {|tps| unify_tps(cx, e_tps, a_tps, variance) {|tps|
mk_enum(cx.tcx, e_id, tps) nxt(mk_enum(cx.tcx, e_id, tps))
}) }
} }
(ty_iface(e_id, e_tps), ty_iface(a_id, a_tps)) if e_id == a_id { (ty_iface(e_id, e_tps), ty_iface(a_id, a_tps)) if e_id == a_id {
unify_tps(cx, e_tps, a_tps, variance, {|tps| unify_tps(cx, e_tps, a_tps, variance) {|tps|
mk_iface(cx.tcx, e_id, tps) nxt(mk_iface(cx.tcx, e_id, tps))
}) }
} }
(ty_class(e_id, e_tps), ty_class(a_id, a_tps)) if e_id == a_id { (ty_class(e_id, e_tps), ty_class(a_id, a_tps)) if e_id == a_id {
unify_tps(cx, e_tps, a_tps, variance, {|tps| unify_tps(cx, e_tps, a_tps, variance) {|tps|
mk_class(cx.tcx, e_id, tps) nxt(mk_class(cx.tcx, e_id, tps))
}) }
} }
(ty_box(e_mt), ty_box(a_mt)) { (ty_box(e_mt), ty_box(a_mt)) {
unify_mt(cx, e_mt, a_mt, variance, terr_box_mutability, mk_box) unify_mt(cx, e_mt, a_mt, variance, terr_box_mutability,
{|mt| nxt(mk_box(cx.tcx, mt))})
} }
(ty_uniq(e_mt), ty_uniq(a_mt)) { (ty_uniq(e_mt), ty_uniq(a_mt)) {
unify_mt(cx, e_mt, a_mt, variance, terr_box_mutability, mk_uniq) unify_mt(cx, e_mt, a_mt, variance, terr_box_mutability,
{|mt| nxt(mk_uniq(cx.tcx, mt))})
} }
(ty_vec(e_mt), ty_vec(a_mt)) { (ty_vec(e_mt), ty_vec(a_mt)) {
unify_mt(cx, e_mt, a_mt, variance, terr_vec_mutability, mk_vec) unify_mt(cx, e_mt, a_mt, variance, terr_vec_mutability,
{|mt| nxt(mk_vec(cx.tcx, mt))})
} }
(ty_ptr(e_mt), ty_ptr(a_mt)) { (ty_ptr(e_mt), ty_ptr(a_mt)) {
unify_mt(cx, e_mt, a_mt, variance, terr_ptr_mutability, mk_ptr) unify_mt(cx, e_mt, a_mt, variance, terr_ptr_mutability,
{|mt| nxt(mk_ptr(cx.tcx, mt))})
} }
(ty_rptr(e_region, e_mt), ty_rptr(a_region, a_mt)) { (ty_rptr(e_region, e_mt), ty_rptr(a_region, a_mt)) {
alt unify_regions(cx, e_region, a_region, variance) { unify_regions(cx, e_region, a_region, variance) {|r|
none { ures_err(terr_regions_differ) }
some(r_region) {
unify_mt(cx, e_mt, a_mt, variance, terr_ref_mutability, unify_mt(cx, e_mt, a_mt, variance, terr_ref_mutability,
bind mk_rptr(_, r_region, _)) {|mt| nxt(mk_rptr(cx.tcx, r, mt))})
}
} }
} }
(ty_res(e_id, e_inner, e_tps), ty_res(a_id, a_inner, a_tps)) (ty_res(e_id, e_inner, e_tps), ty_res(a_id, a_inner, a_tps))
if e_id == a_id { if e_id == a_id {
alt unify_step(cx, e_inner, a_inner, variance) { unify_step(cx, e_inner, a_inner, variance) {|t|
ures_ok(res_inner) { unify_tps(cx, e_tps, a_tps, variance) {|tps|
unify_tps(cx, e_tps, a_tps, variance, {|tps| nxt(mk_res(cx.tcx, a_id, t, tps))
mk_res(cx.tcx, a_id, res_inner, tps)
})
} }
err { err }
} }
} }
(ty_rec(e_fields), ty_rec(a_fields)) { (ty_rec(e_fields), ty_rec(a_fields)) {
let e_len = e_fields.len(), a_len = a_fields.len(); if check vec::same_length(e_fields, a_fields) {
if e_len != a_len { map2(e_fields, a_fields,
ret ures_err(terr_record_size(e_len, a_len)); {|e,a| unify_field(cx, e, a, variance, {|v| ok(v)})},
{|fields| nxt(mk_rec(cx.tcx, fields))})
} else {
ret err(terr_record_size(e_fields.len(),
a_fields.len()));
} }
let result_fields = [], i = 0u;
while i < a_len {
let e_field = e_fields[i], a_field = a_fields[i];
if e_field.ident != a_field.ident {
ret ures_err(terr_record_fields(e_field.ident,
a_field.ident));
}
alt unify_mt(cx, e_field.mt, a_field.mt, variance,
terr_record_mutability, {|cx, mt|
result_fields += [{mt: mt with e_field}];
mk_nil(cx)
}) {
ures_ok(_) {}
err { ret err; }
}
i += 1u;
}
ures_ok(mk_rec(cx.tcx, result_fields))
} }
(ty_tup(e_elems), ty_tup(a_elems)) { (ty_tup(e_elems), ty_tup(a_elems)) {
let e_len = e_elems.len(), a_len = a_elems.len(); if check vec::same_length(e_elems, a_elems) {
if e_len != a_len { ret ures_err(terr_tuple_size(e_len, a_len)); } unify_tys(cx, e_elems, a_elems, variance) {|elems|
let result_elems = [], i = 0u; nxt(mk_tup(cx.tcx, elems))
while i < a_len {
alt unify_step(cx, e_elems[i], a_elems[i], variance) {
ures_ok(rty) { result_elems += [rty]; }
err { ret err; }
} }
i += 1u; } else {
err(terr_tuple_size(e_elems.len(), a_elems.len()))
} }
ures_ok(mk_tup(cx.tcx, result_elems))
} }
(ty_fn(e_fty), ty_fn(a_fty)) { (ty_fn(e_fty), ty_fn(a_fty)) {
unify_fn(cx, e_fty, a_fty, variance) unify_fn(cx, e_fty, a_fty, variance, nxt)
} }
(ty_constr(e_t, e_constrs), ty_constr(a_t, a_constrs)) { (ty_constr(e_t, e_constrs), ty_constr(a_t, a_constrs)) {
// unify the base types... // unify the base types...
alt unify_step(cx, e_t, a_t, variance) { unify_step(cx, e_t, a_t, variance) {|rty|
ures_ok(rty) {
// FIXME: probably too restrictive -- // FIXME: probably too restrictive --
// requires the constraints to be syntactically equal // requires the constraints to be syntactically equal
unify_constrs(expected, e_constrs, a_constrs) unify_constrs(e_constrs, a_constrs) {|constrs|
nxt(mk_constr(cx.tcx, rty, constrs))
} }
err { err }
} }
} }
(ty_constr(e_t, _), _) { (ty_constr(e_t, _), _) {
// If the actual type is *not* a constrained type, // If the actual type is *not* a constrained type,
// then we go ahead and just ignore the constraints on // then we go ahead and just ignore the constraints on
// the expected type. typestate handles the rest. // the expected type. typestate handles the rest.
unify_step(cx, e_t, actual, variance) unify_step(cx, e_t, actual, variance, nxt)
} }
_ { ures_err(terr_mismatch) } _ { err(terr_mismatch) }
} }
} }
fn unify(expected: t, actual: t, st: unify_style, fn unify(expected: t, actual: t, st: unify_style,
tcx: ctxt) -> result { tcx: ctxt) -> ures<t> {
let cx = @{st: st, tcx: tcx}; let cx = @{st: st, tcx: tcx};
ret unify_step(cx, expected, actual, covariant); ret unify_step(cx, expected, actual, covariant, {|v| ok(v)});
} }
fn dump_var_bindings(tcx: ctxt, vb: @var_bindings) { fn dump_var_bindings(tcx: ctxt, vb: @var_bindings) {
let i = 0u; let i = 0u;
@ -2104,7 +2128,7 @@ mod unify {
// iff the span is present (so that if we already know we're going // iff the span is present (so that if we already know we're going
// to error anyway, we don't complain) // to error anyway, we don't complain)
fn fixup_vars(tcx: ctxt, sp: option<span>, vb: @var_bindings, fn fixup_vars(tcx: ctxt, sp: option<span>, vb: @var_bindings,
typ: t) -> fixup_result { typ: t) -> fres<t> {
fn subst_vars(tcx: ctxt, sp: option<span>, vb: @var_bindings, fn subst_vars(tcx: ctxt, sp: option<span>, vb: @var_bindings,
unresolved: @mutable option<int>, unresolved: @mutable option<int>,
vars_seen: std::list::list<int>, vid: int) -> t { vars_seen: std::list::list<int>, vid: int) -> t {
@ -2142,16 +2166,16 @@ mod unify {
tcx, sp, vb, unresolved, std::list::nil, _)), typ); tcx, sp, vb, unresolved, std::list::nil, _)), typ);
let ur = *unresolved; let ur = *unresolved;
alt ur { alt ur {
none { ret fix_ok(rty); } none { ret ok(rty); }
some(var_id) { ret fix_err(var_id); } some(var_id) { ret err(var_id); }
} }
} }
fn resolve_type_var(tcx: ctxt, sp: option<span>, vb: @var_bindings, fn resolve_type_var(tcx: ctxt, sp: option<span>, vb: @var_bindings,
vid: int) -> fixup_result { vid: int) -> fres<t> {
if vid as uint >= ufind::set_count(vb.sets) { ret fix_err(vid); } if vid as uint >= ufind::set_count(vb.sets) { ret err(vid); }
let root_id = ufind::find(vb.sets, vid as uint); let root_id = ufind::find(vb.sets, vid as uint);
alt smallintmap::find::<t>(vb.types, root_id) { alt smallintmap::find::<t>(vb.types, root_id) {
none { ret fix_err(vid); } none { ret err(vid); }
some(rt) { ret fixup_vars(tcx, sp, vb, rt); } some(rt) { ret fixup_vars(tcx, sp, vb, rt); }
} }
} }
@ -2159,8 +2183,8 @@ mod unify {
fn same_type(cx: ctxt, a: t, b: t) -> bool { fn same_type(cx: ctxt, a: t, b: t) -> bool {
alt unify::unify(a, b, unify::precise, cx) { alt unify::unify(a, b, unify::precise, cx) {
unify::ures_ok(_) { true } result::ok(_) { true }
_ { false } result::err(_) { false }
} }
} }
@ -2181,6 +2205,11 @@ fn type_err_to_str(err: type_err) -> str {
terr_vec_mutability { ret "vectors differ in mutability"; } terr_vec_mutability { ret "vectors differ in mutability"; }
terr_ptr_mutability { ret "pointers differ in mutability"; } terr_ptr_mutability { ret "pointers differ in mutability"; }
terr_ref_mutability { ret "references differ in mutability"; } terr_ref_mutability { ret "references differ in mutability"; }
terr_ty_param_size(e_sz, a_sz) {
ret "expected a type with " + uint::to_str(e_sz, 10u) +
" type params but found one with " + uint::to_str(a_sz, 10u) +
" type params";
}
terr_tuple_size(e_sz, a_sz) { terr_tuple_size(e_sz, a_sz) {
ret "expected a tuple with " + uint::to_str(e_sz, 10u) + ret "expected a tuple with " + uint::to_str(e_sz, 10u) +
" elements but found one with " + uint::to_str(a_sz, 10u) + " elements but found one with " + uint::to_str(a_sz, 10u) +

View file

@ -12,7 +12,6 @@ import middle::ty::{node_id_to_type, arg, block_ty,
expr_ty, field, node_type_table, mk_nil, expr_ty, field, node_type_table, mk_nil,
ty_param_bounds_and_ty, lookup_class_items}; ty_param_bounds_and_ty, lookup_class_items};
import util::ppaux::ty_to_str; import util::ppaux::ty_to_str;
import middle::ty::unify::{ures_ok, ures_err, fix_ok, fix_err};
import std::smallintmap; import std::smallintmap;
import std::map::{hashmap, new_int_hash}; import std::map::{hashmap, new_int_hash};
import syntax::print::pprust::*; import syntax::print::pprust::*;
@ -200,8 +199,8 @@ fn instantiate_path(fcx: @fn_ctxt, pth: @ast::path,
// Type tests // Type tests
fn structurally_resolved_type(fcx: @fn_ctxt, sp: span, tp: ty::t) -> ty::t { fn structurally_resolved_type(fcx: @fn_ctxt, sp: span, tp: ty::t) -> ty::t {
alt ty::unify::resolve_type_structure(fcx.var_bindings, tp) { alt ty::unify::resolve_type_structure(fcx.var_bindings, tp) {
fix_ok(typ_s) { ret typ_s; } result::ok(typ_s) { ret typ_s; }
fix_err(_) { result::err(_) {
fcx.ccx.tcx.sess.span_fatal fcx.ccx.tcx.sess.span_fatal
(sp, "the type of this value must be known in this context"); (sp, "the type of this value must be known in this context");
} }
@ -218,11 +217,10 @@ fn structure_of(fcx: @fn_ctxt, sp: span, typ: ty::t) -> ty::sty {
// is not known yet. // is not known yet.
fn structure_of_maybe(fcx: @fn_ctxt, _sp: span, typ: ty::t) -> fn structure_of_maybe(fcx: @fn_ctxt, _sp: span, typ: ty::t) ->
option<ty::sty> { option<ty::sty> {
let r = let r = ty::unify::resolve_type_structure(fcx.var_bindings, typ);
ty::unify::resolve_type_structure(fcx.var_bindings, typ); alt r {
ret alt r { result::ok(typ_s) { some(ty::get(typ_s).struct) }
fix_ok(typ_s) { some(ty::get(typ_s).struct) } result::err(_) { none }
fix_err(_) { none }
} }
} }
@ -759,13 +757,13 @@ fn compare_impl_method(tcx: ty::ctxt, sp: span, impl_m: ty::method,
self_full(self_ty, impl_tps)); self_full(self_ty, impl_tps));
} }
alt ty::unify::unify(impl_fty, if_fty, ty::unify::precise, tcx) { alt ty::unify::unify(impl_fty, if_fty, ty::unify::precise, tcx) {
ty::unify::ures_err(err) { result::err(err) {
tcx.sess.span_err(sp, "method `" + if_m.ident + tcx.sess.span_err(sp, "method `" + if_m.ident +
"` has an incompatible type: " + "` has an incompatible type: " +
ty::type_err_to_str(err)); ty::type_err_to_str(err));
impl_fty impl_fty
} }
ty::unify::ures_ok(tp) { tp } result::ok(tp) { tp }
} }
} }
} }
@ -1046,7 +1044,7 @@ mod collect {
// Type unification // Type unification
mod unify { mod unify {
fn unify(fcx: @fn_ctxt, expected: ty::t, actual: ty::t) -> fn unify(fcx: @fn_ctxt, expected: ty::t, actual: ty::t) ->
ty::unify::result { result<ty::t, ty::type_err> {
ret ty::unify::unify(expected, actual, ret ty::unify::unify(expected, actual,
ty::unify::in_bindings(fcx.var_bindings), ty::unify::in_bindings(fcx.var_bindings),
fcx.ccx.tcx); fcx.ccx.tcx);
@ -1089,8 +1087,8 @@ fn do_autoderef(fcx: @fn_ctxt, sp: span, t: ty::t) -> ty::t {
fn resolve_type_vars_if_possible(fcx: @fn_ctxt, typ: ty::t) -> ty::t { fn resolve_type_vars_if_possible(fcx: @fn_ctxt, typ: ty::t) -> ty::t {
alt ty::unify::fixup_vars(fcx.ccx.tcx, none, fcx.var_bindings, typ) { alt ty::unify::fixup_vars(fcx.ccx.tcx, none, fcx.var_bindings, typ) {
fix_ok(new_type) { ret new_type; } result::ok(new_type) { ret new_type; }
fix_err(_) { ret typ; } result::err(_) { ret typ; }
} }
} }
@ -1140,8 +1138,8 @@ mod demand {
alt unify::unify(fcx, expected, actual) { alt unify::unify(fcx, expected, actual) {
ures_ok(t) { ret mk_result(fcx, t, ty_param_subst_var_ids); } result::ok(t) { ret mk_result(fcx, t, ty_param_subst_var_ids); }
ures_err(err) { result::err(err) {
let e_err = resolve_type_vars_if_possible(fcx, expected); let e_err = resolve_type_vars_if_possible(fcx, expected);
let a_err = resolve_type_vars_if_possible(fcx, actual); let a_err = resolve_type_vars_if_possible(fcx, actual);
fcx.ccx.tcx.sess.span_err(sp, fcx.ccx.tcx.sess.span_err(sp,
@ -1161,8 +1159,8 @@ mod demand {
// Returns true if the two types unify and false if they don't. // Returns true if the two types unify and false if they don't.
fn are_compatible(fcx: @fn_ctxt, expected: ty::t, actual: ty::t) -> bool { fn are_compatible(fcx: @fn_ctxt, expected: ty::t, actual: ty::t) -> bool {
alt unify::unify(fcx, expected, actual) { alt unify::unify(fcx, expected, actual) {
ures_ok(_) { ret true; } result::ok(_) { ret true; }
ures_err(_) { ret false; } result::err(_) { ret false; }
} }
} }
@ -1204,8 +1202,8 @@ mod writeback {
if !ty::type_has_vars(typ) { ret some(typ); } if !ty::type_has_vars(typ) { ret some(typ); }
alt ty::unify::fixup_vars(fcx.ccx.tcx, some(sp), fcx.var_bindings, alt ty::unify::fixup_vars(fcx.ccx.tcx, some(sp), fcx.var_bindings,
typ) { typ) {
fix_ok(new_type) { ret some(new_type); } result::ok(new_type) { ret some(new_type); }
fix_err(vid) { result::err(vid) {
if !fcx.ccx.tcx.sess.has_errors() { if !fcx.ccx.tcx.sess.has_errors() {
fcx.ccx.tcx.sess.span_err(sp, "cannot determine a type \ fcx.ccx.tcx.sess.span_err(sp, "cannot determine a type \
for this expression"); for this expression");
@ -1296,8 +1294,8 @@ mod writeback {
ty::unify::resolve_type_var(wbcx.fcx.ccx.tcx, some(l.span), ty::unify::resolve_type_var(wbcx.fcx.ccx.tcx, some(l.span),
wbcx.fcx.var_bindings, var_id); wbcx.fcx.var_bindings, var_id);
alt fix_rslt { alt fix_rslt {
fix_ok(lty) { write_ty(wbcx.fcx.ccx.tcx, l.node.id, lty); } result::ok(lty) { write_ty(wbcx.fcx.ccx.tcx, l.node.id, lty); }
fix_err(_) { result::err(_) {
wbcx.fcx.ccx.tcx.sess.span_err(l.span, wbcx.fcx.ccx.tcx.sess.span_err(l.span,
"cannot determine a type \ "cannot determine a type \
for this local variable"); for this local variable");
@ -1849,7 +1847,7 @@ fn lookup_method_inner(fcx: @fn_ctxt, expr: @ast::expr,
bind_params(fcx, self_ty, n_tps) bind_params(fcx, self_ty, n_tps)
} else { {vars: [], ty: self_ty} }; } else { {vars: [], ty: self_ty} };
alt unify::unify(fcx, ty, self_ty) { alt unify::unify(fcx, ty, self_ty) {
ures_ok(_) { result::ok(_) {
if option::is_some(result) { if option::is_some(result) {
// FIXME[impl] score specificity to resolve ambiguity? // FIXME[impl] score specificity to resolve ambiguity?
if !complained { if !complained {
@ -1867,7 +1865,7 @@ fn lookup_method_inner(fcx: @fn_ctxt, expr: @ast::expr,
}); });
} }
} }
_ {} result::err(_) {}
} }
} }
_ {} _ {}
@ -3311,7 +3309,7 @@ mod dict {
} else { {vars: [], ty: self_ty} }; } else { {vars: [], ty: self_ty} };
let im_bs = ty::lookup_item_type(tcx, im.did).bounds; let im_bs = ty::lookup_item_type(tcx, im.did).bounds;
alt unify::unify(fcx, ty, self_ty) { alt unify::unify(fcx, ty, self_ty) {
ures_ok(_) { result::ok(_) {
if option::is_some(found) { if option::is_some(found) {
tcx.sess.span_err( tcx.sess.span_err(
sp, "multiple applicable implementations \ sp, "multiple applicable implementations \
@ -3327,7 +3325,7 @@ mod dict {
subres)); subres));
} }
} }
_ {} result::err(_) {}
} }
} }
} }
@ -3348,8 +3346,8 @@ mod dict {
fn fixup_ty(fcx: @fn_ctxt, sp: span, ty: ty::t) -> ty::t { fn fixup_ty(fcx: @fn_ctxt, sp: span, ty: ty::t) -> ty::t {
let tcx = fcx.ccx.tcx; let tcx = fcx.ccx.tcx;
alt ty::unify::fixup_vars(tcx, some(sp), fcx.var_bindings, ty) { alt ty::unify::fixup_vars(tcx, some(sp), fcx.var_bindings, ty) {
fix_ok(new_type) { new_type } result::ok(new_type) { new_type }
fix_err(vid) { result::err(vid) {
tcx.sess.span_fatal(sp, "could not determine a type for a \ tcx.sess.span_fatal(sp, "could not determine a type for a \
bounded type parameter"); bounded type parameter");
} }