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)]
mod tests {
fn op1() -> result::result<int, str> { result::ok(666) }

View file

@ -287,6 +287,7 @@ enum type_err {
terr_ref_mutability,
terr_vec_mutability,
terr_tuple_size(uint, uint),
terr_ty_param_size(uint, uint),
terr_record_size(uint, uint),
terr_record_mutability,
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
mod unify {
export fixup_result;
import result::{result, ok, err, chain, map, map2};
export fixup_vars;
export fix_ok;
export fix_err;
export mk_var_bindings;
export resolve_type_structure;
export resolve_type_var;
export result;
export unify;
export ures_ok;
export ures_err;
export var_bindings;
export precise, in_bindings;
enum result { ures_ok(t), ures_err(type_err), }
enum union_result { unres_ok, unres_err(type_err), }
enum fixup_result {
fix_ok(t), // fixup succeeded
fix_err(int), // fixup failed because a type variable was unresolved
}
type ures<T> = result<T,type_err>;
// in case of failure, value is the idx of an unresolved type var
type fres<T> = result<T,int>;
type var_bindings =
{sets: ufind::ufind, types: smallintmap::smallintmap<t>};
@ -1584,8 +1580,10 @@ mod unify {
}
// Unifies two sets.
fn union(cx: @uctxt, set_a: uint, set_b: uint,
variance: variance) -> union_result {
fn union<T:copy>(
cx: @uctxt, set_a: uint, set_b: uint,
variance: variance, nxt: fn() -> ures<T>) -> ures<T> {
let vb = alt cx.st {
in_bindings(vb) { vb }
_ { cx.tcx.sess.bug("someone forgot to document an invariant \
@ -1606,76 +1604,80 @@ mod unify {
alt smallintmap::find(vb.types, root_a) {
none {
alt smallintmap::find(vb.types, root_b) {
none { ufind::union(vb.sets, set_a, set_b); ret unres_ok; }
some(t_b) { replace_type(vb, t_b); ret unres_ok; }
none { ufind::union(vb.sets, set_a, set_b); ret nxt(); }
some(t_b) { replace_type(vb, t_b); ret nxt(); }
}
}
some(t_a) {
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) {
alt unify_step(cx, t_a, t_b, variance) {
ures_ok(t_c) { replace_type(vb, t_c); ret unres_ok; }
ures_err(terr) { ret unres_err(terr); }
}
ret unify_step(cx, t_a, t_b, variance) {|t_c|
replace_type(vb, t_c);
nxt()
};
}
}
}
}
}
fn record_var_binding(cx: @uctxt, key: int, typ: t, variance: variance)
-> result {
fn record_var_binding<T:copy>(
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 } };
ufind::grow(vb.sets, (key as uint) + 1u);
let root = ufind::find(vb.sets, key as uint);
let result_type = typ;
alt smallintmap::find(vb.types, root) {
some(old_type) {
alt unify_step(cx, old_type, typ, variance) {
ures_ok(unified_type) { result_type = unified_type; }
rs { ret rs; }
alt unify_step(cx, old_type, typ, variance, {|v| ok(v)}) {
ok(unified_type) { result_type = unified_type; }
err(e) { ret err(e); }
}
}
none {/* fall through */ }
}
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.
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 cfg = tcx.sess.targ_cfg;
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
// pairwise equal.
fn unify_constrs(base_t: t, expected: [@type_constr],
actual: [@type_constr]) -> result {
let expected_len = vec::len(expected);
let actual_len = vec::len(actual);
fn unify_constrs<T:copy>(
expected: [@type_constr],
actual: [@type_constr],
nxt: fn([@type_constr]) -> ures<T>) -> ures<T> {
if expected_len != actual_len {
ret ures_err(terr_constr_len(expected_len, actual_len));
if check vec::same_length(expected, actual) {
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(base_t: t, expected: @type_constr,
actual_constr: @type_constr) -> result {
let ok_res = ures_ok(base_t);
let err_res = ures_err(terr_constr_mismatch(expected, actual_constr));
fn unify_constr<T:copy>(
expected: @type_constr,
actual_constr: @type_constr,
nxt: fn(@type_constr) -> ures<T>) -> ures<T> {
let err_res = err(terr_constr_mismatch(expected, actual_constr));
if expected.node.id != actual_constr.node.id { ret err_res; }
let expected_arg_len = vec::len(expected.node.args);
let actual_arg_len = vec::len(actual_constr.node.args);
@ -1703,13 +1705,14 @@ mod unify {
}
i += 1u;
}
ret ok_res;
ret nxt(expected);
}
// Unifies two mutability flags.
fn unify_mut(expected: ast::mutability, actual: ast::mutability,
variance: variance) ->
option<(ast::mutability, variance)> {
fn unify_mut<T:copy>(
expected: ast::mutability, actual: ast::mutability,
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
// 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 expected == ast::m_const {
ret some((actual, newvariance));
ret nxt(actual, newvariance);
}
} else if variance == contravariant {
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:
//
// block
@ -1757,90 +1765,91 @@ mod unify {
}
ret alt variance {
invariant if e_proto == a_proto { none }
covariant if sub_proto(a_proto, e_proto) { none }
contravariant if sub_proto(e_proto, a_proto) { none }
_ { some(ures_err(terr_mismatch)) }
invariant if e_proto == a_proto { nxt(e_proto) }
covariant if sub_proto(a_proto, e_proto) { nxt(e_proto) }
contravariant if sub_proto(e_proto, a_proto) { nxt(e_proto) }
_ { ret err(terr_mismatch) }
};
}
fn unify_args(cx: @uctxt, e_args: [arg], a_args: [arg],
variance: variance) -> either<result, [arg]> {
if !vec::same_length(e_args, a_args) {
ret either::left(ures_err(terr_arg_count));
}
// The variance changes (flips basically) when descending
// into arguments of function types
let variance = variance_transform(variance, contravariant);
// Would use vec::map2(), but for the need to return in case of
// error:
let i = 0u, result = [];
for expected_input in e_args {
let actual_input = a_args[i];
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 }
};
fn unify_arg<T:copy>(
cx: @uctxt, e_arg: arg, a_arg: arg,
variance: variance,
nxt: fn(arg) -> ures<T>) -> ures<T> {
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); }
// 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})
}
}
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_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
// into arguments of function types
let variance = variance_transform(variance, contravariant);
map2(e_args, a_args,
{|e,a| unify_arg(cx, e, a, variance, {|v| ok(v)})},
nxt)
} else {
ret err(terr_arg_count);
}
}
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
responsible for checking control flow annotations,
this check is necessary to ensure that the
annotation in an object method matches the
declared object type */
ret ures_err(terr_ret_style_mismatch(e_f.ret_style,
a_f.ret_style));
ret err(terr_ret_style_mismatch(e_ret_style, a_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.
alt unify_step(cx, e_f.output, a_f.output, variance) {
ures_ok(rty) {
ures_ok(mk_fn(cx.tcx, {proto: e_f.proto,
inputs: result_ins,
output: rty
with a_f}))
}
x { x }
fn unify_fn<T:copy>(
cx: @uctxt, e_f: fn_ty, a_f: fn_ty, variance: variance,
nxt: fn(t) -> ures<T>) -> ures<T> {
unify_fn_proto(e_f.proto, a_f.proto, variance) {|proto|
unify_ret_style(e_f.ret_style, a_f.ret_style) {|rs|
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}))
}
}
}
}
}
// If the given type is a variable, returns the structure of that type.
fn resolve_type_structure(vb: @var_bindings, typ: t) ->
fixup_result {
fn resolve_type_structure(vb: @var_bindings, typ: t) -> fres<t> {
alt get(typ).struct {
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);
alt smallintmap::find::<t>(vb.types, root_id) {
none { ret fix_err(vid); }
some(rt) { ret fix_ok(rt); }
none { ret err(vid); }
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],
variance: variance, finish: fn([t]) -> t) -> result {
let result_tps = [], i = 0u;
for exp in expected_tps {
let act = actual_tps[i];
i += 1u;
let result = unify_step(cx, exp, act, variance);
alt result {
ures_ok(rty) { result_tps += [rty]; }
_ { ret result; }
}
}
ures_ok(finish(result_tps))
fn unify_tys<T:copy>(
cx: @uctxt, expected_tps: [t], actual_tps: [t],
variance: variance, nxt: fn([t]) -> ures<T>)
: vec::same_length(expected_tps, actual_tps)
-> ures<T> {
map2(expected_tps, actual_tps,
{|e,a| unify_step(cx, e, a, variance, {|v| ok(v)})},
nxt)
}
fn unify_mt(cx: @uctxt, e_mt: mt, a_mt: mt, variance: variance,
mut_err: type_err, finish: fn(ctxt, mt) -> t) -> result {
alt unify_mut(e_mt.mutbl, a_mt.mutbl, variance) {
none { ures_err(mut_err) }
some((mutt, var)) {
alt unify_step(cx, e_mt.ty, a_mt.ty, var) {
ures_ok(result_sub) {
ures_ok(finish(cx.tcx, {ty: result_sub, mutbl: mutt}))
}
err { err }
}
}
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()))
}
}
fn unify_regions(cx: @uctxt, e_region: region, a_region: region,
variance: variance) -> option<region> {
fn unify_mt<T:copy>(
cx: @uctxt, e_mt: mt, a_mt: mt, variance: variance,
mut_err: type_err,
nxt: fn(mt) -> ures<T>) -> ures<T> {
unify_mut(e_mt.mutbl, a_mt.mutbl, variance, mut_err) {|mutbl,var|
unify_step(cx, e_mt.ty, a_mt.ty, var) {|ty|
nxt({ty: ty, mutbl: mutbl})
}
}
}
fn unify_regions<T:copy>(
cx: @uctxt, e_region: region, a_region: region,
variance: variance,
nxt: fn(region) -> ures<T>) -> ures<T> {
let sub, super;
alt variance {
covariant { super = e_region; sub = a_region; }
contravariant { super = a_region; sub = e_region; }
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 {
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
@ -1934,151 +1962,147 @@ mod unify {
let superscope = region::region_to_scope(cx.tcx.region_map, super);
let subscope = region::region_to_scope(cx.tcx.region_map, sub);
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,
variance: variance) -> result {
fn unify_field<T:copy>(
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.
if expected == actual { ret ures_ok(expected); }
if expected == actual { ret nxt(expected); }
alt (get(expected).struct, get(actual).struct) {
(ty_var(e_id), ty_var(a_id)) {
alt union(cx, e_id as uint, a_id as uint, variance) {
unres_ok { ures_ok(actual) }
unres_err(err) { ures_err(err) }
union(cx, e_id as uint, a_id as uint, variance) {||
nxt(actual)
}
}
(_, ty_var(a_id)) {
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), _) {
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, _) { ures_ok(actual) }
(_, ty_bot) { nxt(expected) }
(ty_bot, _) { nxt(actual) }
(ty_nil, _) | (ty_bool, _) | (ty_int(_), _) | (ty_uint(_), _) |
(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 {
ures_ok(expected)
nxt(expected)
}
(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|
mk_enum(cx.tcx, e_id, tps)
})
unify_tps(cx, e_tps, a_tps, variance) {|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 {
unify_tps(cx, e_tps, a_tps, variance, {|tps|
mk_iface(cx.tcx, e_id, tps)
})
unify_tps(cx, e_tps, a_tps, variance) {|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 {
unify_tps(cx, e_tps, a_tps, variance, {|tps|
mk_class(cx.tcx, e_id, tps)
})
unify_tps(cx, e_tps, a_tps, variance) {|tps|
nxt(mk_class(cx.tcx, e_id, tps))
}
}
(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)) {
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)) {
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)) {
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)) {
alt unify_regions(cx, e_region, a_region, variance) {
none { ures_err(terr_regions_differ) }
some(r_region) {
unify_mt(cx, e_mt, a_mt, variance, terr_ref_mutability,
bind mk_rptr(_, r_region, _))
}
unify_regions(cx, e_region, a_region, variance) {|r|
unify_mt(cx, e_mt, a_mt, variance, terr_ref_mutability,
{|mt| nxt(mk_rptr(cx.tcx, r, mt))})
}
}
(ty_res(e_id, e_inner, e_tps), ty_res(a_id, a_inner, a_tps))
if e_id == a_id {
alt unify_step(cx, e_inner, a_inner, variance) {
ures_ok(res_inner) {
unify_tps(cx, e_tps, a_tps, variance, {|tps|
mk_res(cx.tcx, a_id, res_inner, tps)
})
unify_step(cx, e_inner, a_inner, variance) {|t|
unify_tps(cx, e_tps, a_tps, variance) {|tps|
nxt(mk_res(cx.tcx, a_id, t, tps))
}
}
err { err }
}
}
(ty_rec(e_fields), ty_rec(a_fields)) {
let e_len = e_fields.len(), a_len = a_fields.len();
if e_len != a_len {
ret ures_err(terr_record_size(e_len, a_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))
if check vec::same_length(e_fields, a_fields) {
map2(e_fields, a_fields,
{|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()));
}
}
(ty_tup(e_elems), ty_tup(a_elems)) {
let e_len = e_elems.len(), a_len = a_elems.len();
if e_len != a_len { ret ures_err(terr_tuple_size(e_len, a_len)); }
let result_elems = [], i = 0u;
while i < a_len {
alt unify_step(cx, e_elems[i], a_elems[i], variance) {
ures_ok(rty) { result_elems += [rty]; }
err { ret err; }
if check vec::same_length(e_elems, a_elems) {
unify_tys(cx, e_elems, a_elems, variance) {|elems|
nxt(mk_tup(cx.tcx, elems))
}
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)) {
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)) {
// unify the base types...
alt unify_step(cx, e_t, a_t, variance) {
ures_ok(rty) {
unify_step(cx, e_t, a_t, variance) {|rty|
// FIXME: probably too restrictive --
// requires the constraints to be syntactically equal
unify_constrs(expected, e_constrs, a_constrs)
}
err { err }
unify_constrs(e_constrs, a_constrs) {|constrs|
nxt(mk_constr(cx.tcx, rty, constrs))
}
}
}
(ty_constr(e_t, _), _) {
// If the actual type is *not* a constrained type,
// then we go ahead and just ignore the constraints on
// 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,
tcx: ctxt) -> result {
tcx: ctxt) -> ures<t> {
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) {
let i = 0u;
@ -2104,7 +2128,7 @@ mod unify {
// iff the span is present (so that if we already know we're going
// to error anyway, we don't complain)
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,
unresolved: @mutable option<int>,
vars_seen: std::list::list<int>, vid: int) -> t {
@ -2142,16 +2166,16 @@ mod unify {
tcx, sp, vb, unresolved, std::list::nil, _)), typ);
let ur = *unresolved;
alt ur {
none { ret fix_ok(rty); }
some(var_id) { ret fix_err(var_id); }
none { ret ok(rty); }
some(var_id) { ret err(var_id); }
}
}
fn resolve_type_var(tcx: ctxt, sp: option<span>, vb: @var_bindings,
vid: int) -> fixup_result {
if vid as uint >= ufind::set_count(vb.sets) { ret fix_err(vid); }
vid: int) -> fres<t> {
if vid as uint >= ufind::set_count(vb.sets) { ret err(vid); }
let root_id = ufind::find(vb.sets, vid as uint);
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); }
}
}
@ -2159,8 +2183,8 @@ mod unify {
fn same_type(cx: ctxt, a: t, b: t) -> bool {
alt unify::unify(a, b, unify::precise, cx) {
unify::ures_ok(_) { true }
_ { false }
result::ok(_) { true }
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_ptr_mutability { ret "pointers 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) {
ret "expected a tuple with " + uint::to_str(e_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,
ty_param_bounds_and_ty, lookup_class_items};
import util::ppaux::ty_to_str;
import middle::ty::unify::{ures_ok, ures_err, fix_ok, fix_err};
import std::smallintmap;
import std::map::{hashmap, new_int_hash};
import syntax::print::pprust::*;
@ -200,8 +199,8 @@ fn instantiate_path(fcx: @fn_ctxt, pth: @ast::path,
// Type tests
fn structurally_resolved_type(fcx: @fn_ctxt, sp: span, tp: ty::t) -> ty::t {
alt ty::unify::resolve_type_structure(fcx.var_bindings, tp) {
fix_ok(typ_s) { ret typ_s; }
fix_err(_) {
result::ok(typ_s) { ret typ_s; }
result::err(_) {
fcx.ccx.tcx.sess.span_fatal
(sp, "the type of this value must be known in this context");
}
@ -218,12 +217,11 @@ fn structure_of(fcx: @fn_ctxt, sp: span, typ: ty::t) -> ty::sty {
// is not known yet.
fn structure_of_maybe(fcx: @fn_ctxt, _sp: span, typ: ty::t) ->
option<ty::sty> {
let r =
ty::unify::resolve_type_structure(fcx.var_bindings, typ);
ret alt r {
fix_ok(typ_s) { some(ty::get(typ_s).struct) }
fix_err(_) { none }
}
let r = ty::unify::resolve_type_structure(fcx.var_bindings, typ);
alt r {
result::ok(typ_s) { some(ty::get(typ_s).struct) }
result::err(_) { none }
}
}
fn type_is_integral(fcx: @fn_ctxt, sp: span, typ: ty::t) -> bool {
@ -759,13 +757,13 @@ fn compare_impl_method(tcx: ty::ctxt, sp: span, impl_m: ty::method,
self_full(self_ty, impl_tps));
}
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 +
"` has an incompatible type: " +
ty::type_err_to_str(err));
impl_fty
}
ty::unify::ures_ok(tp) { tp }
result::ok(tp) { tp }
}
}
}
@ -1046,7 +1044,7 @@ mod collect {
// Type unification
mod unify {
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,
ty::unify::in_bindings(fcx.var_bindings),
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 {
alt ty::unify::fixup_vars(fcx.ccx.tcx, none, fcx.var_bindings, typ) {
fix_ok(new_type) { ret new_type; }
fix_err(_) { ret typ; }
result::ok(new_type) { ret new_type; }
result::err(_) { ret typ; }
}
}
@ -1140,8 +1138,8 @@ mod demand {
alt unify::unify(fcx, expected, actual) {
ures_ok(t) { ret mk_result(fcx, t, ty_param_subst_var_ids); }
ures_err(err) {
result::ok(t) { ret mk_result(fcx, t, ty_param_subst_var_ids); }
result::err(err) {
let e_err = resolve_type_vars_if_possible(fcx, expected);
let a_err = resolve_type_vars_if_possible(fcx, actual);
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.
fn are_compatible(fcx: @fn_ctxt, expected: ty::t, actual: ty::t) -> bool {
alt unify::unify(fcx, expected, actual) {
ures_ok(_) { ret true; }
ures_err(_) { ret false; }
result::ok(_) { ret true; }
result::err(_) { ret false; }
}
}
@ -1204,8 +1202,8 @@ mod writeback {
if !ty::type_has_vars(typ) { ret some(typ); }
alt ty::unify::fixup_vars(fcx.ccx.tcx, some(sp), fcx.var_bindings,
typ) {
fix_ok(new_type) { ret some(new_type); }
fix_err(vid) {
result::ok(new_type) { ret some(new_type); }
result::err(vid) {
if !fcx.ccx.tcx.sess.has_errors() {
fcx.ccx.tcx.sess.span_err(sp, "cannot determine a type \
for this expression");
@ -1296,8 +1294,8 @@ mod writeback {
ty::unify::resolve_type_var(wbcx.fcx.ccx.tcx, some(l.span),
wbcx.fcx.var_bindings, var_id);
alt fix_rslt {
fix_ok(lty) { write_ty(wbcx.fcx.ccx.tcx, l.node.id, lty); }
fix_err(_) {
result::ok(lty) { write_ty(wbcx.fcx.ccx.tcx, l.node.id, lty); }
result::err(_) {
wbcx.fcx.ccx.tcx.sess.span_err(l.span,
"cannot determine a type \
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)
} else { {vars: [], ty: self_ty} };
alt unify::unify(fcx, ty, self_ty) {
ures_ok(_) {
result::ok(_) {
if option::is_some(result) {
// FIXME[impl] score specificity to resolve ambiguity?
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} };
let im_bs = ty::lookup_item_type(tcx, im.did).bounds;
alt unify::unify(fcx, ty, self_ty) {
ures_ok(_) {
result::ok(_) {
if option::is_some(found) {
tcx.sess.span_err(
sp, "multiple applicable implementations \
@ -3327,7 +3325,7 @@ mod dict {
subres));
}
}
_ {}
result::err(_) {}
}
}
}
@ -3348,8 +3346,8 @@ mod dict {
fn fixup_ty(fcx: @fn_ctxt, sp: span, ty: ty::t) -> ty::t {
let tcx = fcx.ccx.tcx;
alt ty::unify::fixup_vars(tcx, some(sp), fcx.var_bindings, ty) {
fix_ok(new_type) { new_type }
fix_err(vid) {
result::ok(new_type) { new_type }
result::err(vid) {
tcx.sess.span_fatal(sp, "could not determine a type for a \
bounded type parameter");
}