From 042c532a084fa3871a3a2d8955ff82c246db8015 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 22 Mar 2012 20:06:01 -0700 Subject: [PATCH] Implement new inference algorithm. --- src/libcore/iter.rs | 4 +- src/libcore/result.rs | 34 + src/libcore/vec.rs | 26 +- src/libstd/getopts.rs | 1 + src/rustc/metadata/astencode.rs | 1 + src/rustc/metadata/encoder.rs | 7 +- src/rustc/metadata/tyencode.rs | 15 +- src/rustc/middle/infer.rs | 668 ++++++++++++++++++ src/rustc/middle/ty.rs | 10 +- src/rustc/middle/typeck.rs | 257 ++++--- src/rustc/rustc.rc | 1 + src/rustc/util/ppaux.rs | 17 +- src/rustdoc/reexport_pass.rs | 2 +- src/rustdoc/sectionalize_pass.rs | 2 +- src/test/auxiliary/noexporttypelib.rs | 3 + src/test/compile-fail/binop-typeck.rs | 8 +- src/test/compile-fail/mode-inference-fail.rs | 2 +- .../compile-fail/mutable-huh-variance-vec1.rs | 9 +- .../compile-fail/mutable-huh-variance-vec2.rs | 9 +- .../compile-fail/mutable-huh-variance-vec3.rs | 9 +- src/test/compile-fail/noexporttypeexe.rs | 13 + src/test/compile-fail/occurs-check.rs | 6 +- src/test/compile-fail/type-mismatch.rs | 8 +- src/test/compile-fail/vec-concat-bug.rs | 13 + 24 files changed, 1002 insertions(+), 123 deletions(-) create mode 100644 src/rustc/middle/infer.rs create mode 100644 src/test/auxiliary/noexporttypelib.rs create mode 100644 src/test/compile-fail/noexporttypeexe.rs create mode 100644 src/test/compile-fail/vec-concat-bug.rs diff --git a/src/libcore/iter.rs b/src/libcore/iter.rs index 141f7db2cb76..3d4ad1cf762a 100644 --- a/src/libcore/iter.rs +++ b/src/libcore/iter.rs @@ -169,14 +169,14 @@ fn test_enumerate() { #[test] fn test_map_and_to_list() { let a = bind vec::iter([0, 1, 2], _); - let b = bind map(a, {|i| i*2}, _); + let b = bind map(a, {|i| 2*i}, _); let c = to_list(b); assert c == [0, 2, 4]; } #[test] fn test_map_directly_on_vec() { - let b = bind map([0, 1, 2], {|i| i*2}, _); + let b = bind map([0, 1, 2], {|i| 2*i}, _); let c = to_list(b); assert c == [0, 2, 4]; } diff --git a/src/libcore/result.rs b/src/libcore/result.rs index 3e040c14a0a8..37a55e97aa1a 100644 --- a/src/libcore/result.rs +++ b/src/libcore/result.rs @@ -91,6 +91,24 @@ fn chain(res: result, op: fn(T) -> result) } } +#[doc = " +Call a function based on a previous result + +If `res` is `err` then the value is extracted and passed to `op` +whereupon `op`s result is returned. if `res` is `ok` then it is +immediately returned. This function can be used to pass through a +successful result while handling an error. +"] +fn chain_err( + res: result, + op: fn(V) -> result) + -> result { + alt res { + ok(t) { ok(t) } + err(v) { op(v) } + } +} + // ______________________________________________________________________ // Note: // @@ -171,6 +189,22 @@ fn map2(ss: [S], ts: [T], ret nxt(vs); } +fn iter2(ss: [S], ts: [T], + op: fn(S,T) -> result<(),U>) + : vec::same_length(ss, ts) + -> result<(),U> { + let n = vec::len(ts); + let mut i = 0u; + while i < n { + alt op(ss[i],ts[i]) { + ok(()) { } + err(u) { ret err(u); } + } + i += 1u; + } + ret ok(()); +} + #[cfg(test)] mod tests { fn op1() -> result::result { result::ok(666) } diff --git a/src/libcore/vec.rs b/src/libcore/vec.rs index 71c4bbfe6ea6..c760813bf7ff 100644 --- a/src/libcore/vec.rs +++ b/src/libcore/vec.rs @@ -27,6 +27,7 @@ export rsplit; export rsplitn; export shift; export pop; +export clear; export push; export grow; export grow_fn; @@ -164,6 +165,13 @@ fn from_mut(+v: [mutable T]) -> [T] unsafe { r } +// This function only exists to work around bugs in the type checker. +fn from_const(+v: [const T]) -> [T] unsafe { + let r = ::unsafe::reinterpret_cast(v); + ::unsafe::forget(v); + r +} + // Accessors #[doc = "Returns the first element of a vector"] @@ -336,6 +344,14 @@ fn pop(&v: [const T]) -> T unsafe { val } +#[doc = " +Removes all elements from a vector without affecting +how much space is reserved. +"] +fn clear(&v: [const T]) unsafe { + unsafe::set_len(v, 0u); +} + #[doc = "Append an element to a vector"] fn push(&v: [const T], +initval: T) { v += [initval]; @@ -466,8 +482,8 @@ Concatenate a vector of vectors. Flattens a vector of vectors of T into a single vector of T. "] fn concat(v: [const [const T]]) -> [T] { - let mut r: [T] = []; - for inner: [T] in v { r += inner; } + let mut r = []; + for inner in v { r += from_const(inner); } ret r; } @@ -477,9 +493,9 @@ Concatenate a vector of vectors, placing a given separator between each fn connect(v: [const [const T]], sep: T) -> [T] { let mut r: [T] = []; let mut first = true; - for inner: [T] in v { + for inner in v { if first { first = false; } else { push(r, sep); } - r += inner; + r += from_const(inner); } ret r; } @@ -885,7 +901,7 @@ fn as_mut_buf(v: [mutable E], f: fn(*mutable E) -> T) -> T unsafe { } #[doc = "An extension implementation providing a `len` method"] -impl vec_len for [T] { +impl vec_len for [const T] { #[doc = "Return the length of the vector"] #[inline(always)] fn len() -> uint { len(self) } diff --git a/src/libstd/getopts.rs b/src/libstd/getopts.rs index 75abfd6257b1..91f9c499ce15 100644 --- a/src/libstd/getopts.rs +++ b/src/libstd/getopts.rs @@ -63,6 +63,7 @@ export opt_str; export opt_strs; export opt_maybe_str; export opt_default; +export result; //NDM enum name { long(str), short(char), } diff --git a/src/rustc/metadata/astencode.rs b/src/rustc/metadata/astencode.rs index c2351b3a3052..b5d2b18a39c7 100644 --- a/src/rustc/metadata/astencode.rs +++ b/src/rustc/metadata/astencode.rs @@ -634,6 +634,7 @@ impl helpers for @e::encode_ctxt { fn ty_str_ctxt() -> @tyencode::ctxt { @{ds: e::def_to_str, tcx: self.ccx.tcx, + reachable: self.ccx.reachable, abbrevs: tyencode::ac_use_abbrevs(self.type_abbrevs)} } } diff --git a/src/rustc/metadata/encoder.rs b/src/rustc/metadata/encoder.rs index 3e9e5d842abe..d648cc168bd6 100644 --- a/src/rustc/metadata/encoder.rs +++ b/src/rustc/metadata/encoder.rs @@ -221,6 +221,7 @@ fn encode_type_param_bounds(ebml_w: ebml::writer, ecx: @encode_ctxt, params: [ty_param]) { let ty_str_ctxt = @{ds: def_to_str, tcx: ecx.ccx.tcx, + reachable: ecx.ccx.reachable, abbrevs: tyencode::ac_use_abbrevs(ecx.type_abbrevs)}; for param in params { ebml_w.start_tag(tag_items_data_item_ty_param_bounds); @@ -240,6 +241,7 @@ fn write_type(ecx: @encode_ctxt, ebml_w: ebml::writer, typ: ty::t) { let ty_str_ctxt = @{ds: def_to_str, tcx: ecx.ccx.tcx, + reachable: ecx.ccx.reachable, abbrevs: tyencode::ac_use_abbrevs(ecx.type_abbrevs)}; tyencode::enc_ty(ebml_w.writer, ty_str_ctxt, typ); } @@ -966,7 +968,10 @@ fn encode_metadata(cx: @crate_ctxt, crate: @crate) -> [u8] { // Get the encoded string for a type fn encoded_ty(tcx: ty::ctxt, t: ty::t) -> str { - let cx = @{ds: def_to_str, tcx: tcx, abbrevs: tyencode::ac_no_abbrevs}; + let cx = @{ds: def_to_str, + tcx: tcx, + reachable: std::map::int_hash(), + abbrevs: tyencode::ac_no_abbrevs}; let buf = io::mem_buffer(); tyencode::enc_ty(io::mem_buffer_writer(buf), cx, t); ret io::mem_buffer_str(buf); diff --git a/src/rustc/metadata/tyencode.rs b/src/rustc/metadata/tyencode.rs index 80f830ce88da..6e1e8b15f564 100644 --- a/src/rustc/metadata/tyencode.rs +++ b/src/rustc/metadata/tyencode.rs @@ -6,6 +6,7 @@ import syntax::ast::*; import driver::session::session; import middle::ty; import syntax::print::pprust::*; +import middle::trans::reachable; export ctxt; export ty_abbrev; @@ -18,7 +19,8 @@ export enc_mode; type ctxt = // Def -> str Callback: // The type context. - {ds: fn@(def_id) -> str, tcx: ty::ctxt, abbrevs: abbrev_ctxt}; + {ds: fn@(def_id) -> str, tcx: ty::ctxt, + reachable: reachable::map, abbrevs: abbrev_ctxt}; // Compact string representation for ty.t values. API ty_str & parse_from_str. // Extra parameters are for converting to/from def_ids in the string rep. @@ -55,9 +57,14 @@ fn enc_ty(w: io::writer, cx: @ctxt, t: ty::t) { let pos = w.tell(); alt ty::type_def_id(t) { some(def_id) { - w.write_char('"'); - w.write_str(cx.ds(def_id)); - w.write_char('|'); + // Do not emit node ids that map to unexported names. Those + // are not helpful. + if def_id.crate != local_crate || + cx.reachable.contains_key(def_id.node) { + w.write_char('"'); + w.write_str(cx.ds(def_id)); + w.write_char('|'); + } } _ {} } diff --git a/src/rustc/middle/infer.rs b/src/rustc/middle/infer.rs new file mode 100644 index 000000000000..694f07d41da3 --- /dev/null +++ b/src/rustc/middle/infer.rs @@ -0,0 +1,668 @@ +import std::smallintmap; +import std::smallintmap::smallintmap; +import std::smallintmap::map; +import middle::ty; +import syntax::ast; +import util::ppaux::{ty_to_str, mt_to_str}; +import result::{result, chain, chain_err, ok, iter2}; +import ty::type_is_bot; + +export infer_ctxt; +export new_infer_ctxt; +export mk_subty; +export mk_eqty; +export resolve_type_structure; +export fixup_vars; +export resolve_var; +export compare_tys; + +type bound = option; + +type bounds = {lb: bound, ub: bound}; + +enum var_value { + redirect(uint), + bounded(bounds) +} + +enum infer_ctxt = @{ + tcx: ty::ctxt, + vals: smallintmap, + mut bindings: [(uint, var_value)] +}; + +type ures = result::result<(), ty::type_err>; +type fres = result::result; + +fn new_infer_ctxt(tcx: ty::ctxt) -> infer_ctxt { + infer_ctxt(@{tcx: tcx, + vals: smallintmap::mk(), + mut bindings: []}) +} + +fn mk_subty(cx: infer_ctxt, a: ty::t, b: ty::t) -> ures { + #debug[">> mk_subty(%s <: %s)", cx.ty_to_str(a), cx.ty_to_str(b)]; + cx.commit {|| + cx.tys(a, b) + } +} + +fn mk_eqty(cx: infer_ctxt, a: ty::t, b: ty::t) -> ures { + #debug["> mk_eqty(%s <: %s)", cx.ty_to_str(a), cx.ty_to_str(b)]; + cx.commit {|| + mk_subty(cx, a, b).then {|| + mk_subty(cx, b, a) + } + } +} + +fn compare_tys(tcx: ty::ctxt, a: ty::t, b: ty::t) -> ures { + let infcx = new_infer_ctxt(tcx); + #debug["> compare_tys(%s == %s)", infcx.ty_to_str(a), infcx.ty_to_str(b)]; + infcx.commit {|| + mk_subty(infcx, a, b).then {|| + mk_subty(infcx, b, a) + } + } +} + +fn resolve_type_structure(cx: infer_ctxt, a: ty::t) -> fres { + cx.resolve_ty(a) +} + +fn resolve_var(cx: infer_ctxt, vid: int) -> fres { + cx.fixup_vars(ty::mk_var(cx.tcx, vid)) +} + +fn fixup_vars(cx: infer_ctxt, a: ty::t) -> fres { + cx.fixup_vars(a) +} + +impl methods for ures { + fn then(f: fn() -> result) + -> result { + chain(self) {|_i| f() } + } +} + +impl unify_methods for infer_ctxt { + fn uok() -> ures { + #debug["Unification OK"]; + result::ok(()) + } + + fn uerr(e: ty::type_err) -> ures { + #debug["Unification error: %?", e]; + result::err(e) + } + + fn ty_to_str(t: ty::t) -> str { + ty_to_str(self.tcx, t) + } + + fn bound_to_str(b: bound) -> str { + alt b { + none { "none" } + some(t) { self.ty_to_str(t) } + } + } + + fn bounds_to_str(v: bounds) -> str { + #fmt["{%s <: X <: %s}", + self.bound_to_str(v.lb), + self.bound_to_str(v.ub)] + } + + fn var_value_to_str(v: var_value) -> str { + alt v { + redirect(v) { #fmt["redirect(%u)", v] } + bounded(b) { self.bounds_to_str(b) } + } + } + + fn set(vid: uint, +new_v: var_value) { + let old_v = self.vals.get(vid); + vec::push(self.bindings, (vid, old_v)); + + #debug["Updating variable from %s to %s", + vid, + self.var_value_to_str(old_v), + self.var_value_to_str(new_v)]; + + self.vals.insert(vid, new_v); + } + + fn rollback_to(len: uint) { + while self.bindings.len() != len { + let (vid, old_v) = vec::pop(self.bindings); + self.vals.insert(vid, old_v); + } + } + + fn commit(f: fn() -> result) -> result { + assert self.bindings.len() == 0u; + let r = self.try(f); + vec::clear(self.bindings); + ret r; + } + + fn try(f: fn() -> result) -> result { + let l = self.bindings.len(); + #debug["try(l=%u)", l]; + let r = f(); + alt r { + result::ok(_) { #debug["try--ok"]; } + result::err(_) { #debug["try--rollback"]; } + } + ret r; + } + + fn get(vid: uint) -> {root: uint, bounds:bounds} { + alt self.vals.find(vid) { + none { + let bnds = {lb: none, ub: none}; + self.vals.insert(vid, bounded(bnds)); + {root: vid, bounds: bnds} + } + some(redirect(vid)) { + let {root, bounds} = self.get(vid); + if root != vid { + self.vals.insert(vid, redirect(root)); + } + {root: root, bounds: bounds} + } + some(bounded(bounds)) { + {root: vid, bounds: bounds} + } + } + } + + // Take bound a if it is set, else take bound b. + fn aelseb(a: bound, b: bound) -> bound { + alt (a, b) { + (none, none) { none } + (some(_), none) { a } + (none, some(_)) { b } + (some(_), some(_)) { a } + } + } + + // Combines the two bounds. Returns a bounds r where (r.lb <: + // a,b) and (a,b <: r.ub). + fn merge_bnds(a: bound, b: bound) -> result { + alt (a, b) { + (none, none) { + ok({lb: none, ub: none}) + } + (some(_), none) { + ok({lb: a, ub: a}) + } + (none, some(_)) { + ok({lb: b, ub: b}) + } + (some(t_a), some(t_b)) { + let r1 = self.try {|| + self.tys(t_a, t_b).then {|| + ok({lb: a, ub: b}) + } + }; + chain_err(r1) {|_e| + self.tys(t_b, t_a).then {|| + ok({lb: b, ub: a}) + } + } + } + } + } + + // Given a variable with bounds `a`, returns a new set of bounds + // such that `a` <: `b`. The new bounds will always be a subset + // of the old bounds. If this cannot be achieved, the result is + // failure. + fn merge(v_id: uint, a: bounds, b: bounds) -> ures { + // Think of the two diamonds, we want to find the + // intersection. There are basically four possibilities (you + // can swap A/B in these pictures): + // + // A A + // / \ / \ + // / B \ / B \ + // / / \ \ / / \ \ + // * * * * * / * * + // \ \ / / \ / / + // \ B / / \ / / + // \ / * \ / + // A \ / A + // B + + #debug["merge(,%s,%s)", + v_id, + self.bounds_to_str(a), + self.bounds_to_str(b)]; + + chain(self.merge_bnds(a.ub, b.ub)) {|ub| + chain(self.merge_bnds(a.lb, b.lb)) {|lb| + let bnds = {lb: lb.ub, ub: ub.lb}; + + // the new bounds must themselves + // be relatable: + self.bnds(lb.ub, ub.lb).then {|| + self.set(v_id, bounded(bnds)); + self.uok() + } + } + } + } + + fn vars(a_id: uint, b_id: uint) -> ures { + #debug["vars( <: )", + a_id, b_id]; + + // Need to make sub_id a subtype of sup_id. + let {root: a_id, bounds: a_bounds} = self.get(a_id); + let {root: b_id, bounds: b_bounds} = self.get(b_id); + + if a_id == b_id { ret self.uok(); } + self.merge(a_id, a_bounds, b_bounds).then {|| + // For max perf, we should consider the rank here. + self.set(b_id, redirect(a_id)); + self.uok() + } + } + + fn varty(a_id: uint, b: ty::t) -> ures { + #debug["varty( <: %s)", + a_id, self.ty_to_str(b)]; + let {root: a_id, bounds: a_bounds} = self.get(a_id); + let b_bounds = {lb: none, ub: some(b)}; + self.merge(a_id, a_bounds, b_bounds) + } + + fn tyvar(a: ty::t, b_id: uint) -> ures { + #debug["tyvar(%s <: )", + self.ty_to_str(a), b_id]; + let a_bounds = {lb: some(a), ub: none}; + let {root: b_id, bounds: b_bounds} = self.get(b_id); + self.merge(b_id, a_bounds, b_bounds) + } + + fn tyvecs(as: [ty::t], bs: [ty::t]) + : vec::same_length(as, bs) -> ures { + iter2(as, bs) {|a,b| self.tys(a,b) } + } + + fn regions(a: ty::region, b: ty::region) -> ures { + // FIXME: This is wrong. We should be keeping a set of region + // bindings around. + alt (a, b) { + (ty::re_param(_), _) | (_, ty::re_param(_)) { + ret if a == b { + self.uok() + } else { + self.uerr(ty::terr_regions_differ(true, b, a)) + }; + } + _ { /* fall through */ } + } + + let bscope = region::region_to_scope(self.tcx.region_map, b); + let ascope = region::region_to_scope(self.tcx.region_map, a); + if region::scope_contains(self.tcx.region_map, ascope, bscope) { + self.uok() + } else { + self.uerr(ty::terr_regions_differ(false, a, b)) + } + } + + fn mts(a: ty::mt, b: ty::mt) -> ures { + #debug("mts(%s <: %s)", + mt_to_str(self.tcx, a), + mt_to_str(self.tcx, b)); + + if a.mutbl != b.mutbl && b.mutbl != ast::m_const { + ret self.uerr(ty::terr_mutability); + } + + alt b.mutbl { + ast::m_mutbl { + // If supertype is mutable, subtype must mtach exactly + // (i.e., invariant if mutable): + self.tys(a.ty, b.ty).then {|| + self.tys(b.ty, a.ty) + } + } + ast::m_imm | ast::m_const { + // Otherwise we can be covariant: + self.tys(a.ty, b.ty) + } + } + } + + fn flds(a: ty::field, b: ty::field) -> ures { + if a.ident != b.ident { + ret self.uerr(ty::terr_record_fields(a.ident, b.ident)); + } + self.mts(a.mt, b.mt) + } + + fn tps(as: [ty::t], bs: [ty::t]) -> ures { + if check vec::same_length(as, bs) { + self.tyvecs(as, bs) + } else { + self.uerr(ty::terr_ty_param_size(as.len(), bs.len())) + } + } + + fn protos(a: ast::proto, b: ast::proto) -> ures { + alt (a, b) { + (_, ast::proto_any) { self.uok() } + (ast::proto_bare, _) { self.uok() } + (_, _) if a == b { self.uok() } + _ { self.uerr(ty::terr_proto_mismatch(a, b)) } + } + } + + fn ret_styles( + a_ret_style: ast::ret_style, + b_ret_style: ast::ret_style) -> ures { + + if b_ret_style != ast::noreturn && b_ret_style != a_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 */ + self.uerr(ty::terr_ret_style_mismatch(a_ret_style, b_ret_style)) + } else { + self.uok() + } + } + + fn modes(a: ast::mode, b: ast::mode) -> ures { + alt ty::unify_mode(self.tcx, a, b) { + result::ok(_) { self.uok() } + result::err(e) { self.uerr(e) } + } + } + + fn args(a: ty::arg, b: ty::arg) -> ures { + self.modes(a.mode, b.mode).then {|| + self.tys(b.ty, a.ty) // Note: contravariant + } + } + + fn argvecs( + a_args: [ty::arg], + b_args: [ty::arg]) -> ures { + + if check vec::same_length(a_args, b_args) { + iter2(a_args, b_args) {|a, b| self.args(a, b) } + } else { + ret self.uerr(ty::terr_arg_count); + } + } + + fn fns(a_f: ty::fn_ty, b_f: ty::fn_ty) -> ures { + self.protos(a_f.proto, b_f.proto).then {|| + self.ret_styles(a_f.ret_style, b_f.ret_style).then {|| + self.argvecs(a_f.inputs, b_f.inputs).then {|| + self.tys(a_f.output, b_f.output).then {|| + // FIXME---constraints + self.uok() + } + } + } + } + } + + fn constrs( + expected: @ty::type_constr, + actual_constr: @ty::type_constr) -> ures { + + let err_res = + self.uerr(ty::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); + if expected_arg_len != actual_arg_len { ret err_res; } + let mut i = 0u; + for a in expected.node.args { + let actual = actual_constr.node.args[i]; + alt a.node { + ast::carg_base { + alt actual.node { + ast::carg_base { } + _ { ret err_res; } + } + } + ast::carg_lit(l) { + alt actual.node { + ast::carg_lit(m) { + if l != m { ret err_res; } + } + _ { ret err_res; } + } + } + ast::carg_ident(p) { + alt actual.node { + ast::carg_ident(q) { + if p.node != q.node { ret err_res; } + } + _ { ret err_res; } + } + } + } + i += 1u; + } + ret self.uok(); + } + + fn bnds(a: bound, b: bound) -> ures { + #debug("bnds(%s <: %s)", + self.bound_to_str(a), + self.bound_to_str(b)); + + alt (a, b) { + (none, none) | + (some(_), none) | + (none, some(_)) { self.uok() } + (some(t_a), some(t_b)) { self.tys(t_a, t_b) } + } + } + + fn tys(a: ty::t, b: ty::t) -> ures { + #debug("tys(%s <: %s)", + ty_to_str(self.tcx, a), + ty_to_str(self.tcx, b)); + + // Fast path. + if a == b { ret self.uok(); } + + alt (ty::get(a).struct, ty::get(b).struct) { + (ty::ty_var(a_id), ty::ty_var(b_id)) { + self.vars(a_id as uint, b_id as uint) + } + (ty::ty_var(a_id), _) { + self.varty(a_id as uint, b) + } + (_, ty::ty_var(b_id)) { + self.tyvar(a, b_id as uint) + } + + (_, ty::ty_bot) { self.uok() } + (ty::ty_bot, _) { self.uok() } + + (ty::ty_nil, _) | + (ty::ty_bool, _) | + (ty::ty_int(_), _) | + (ty::ty_uint(_), _) | + (ty::ty_float(_), _) | + (ty::ty_str, _) { + let cfg = self.tcx.sess.targ_cfg; + if ty::mach_sty(cfg, a) == ty::mach_sty(cfg, b) { + self.uok() + } else { + self.uerr(ty::terr_mismatch) + } + } + + (ty::ty_param(a_n, _), ty::ty_param(b_n, _)) + if a_n == b_n { + self.uok() + } + + (ty::ty_enum(a_id, a_tps), ty::ty_enum(b_id, b_tps)) | + (ty::ty_iface(a_id, a_tps), ty::ty_iface(b_id, b_tps)) | + (ty::ty_class(a_id, a_tps), ty::ty_class(b_id, b_tps)) + if a_id == b_id { + self.tps(a_tps, b_tps) + } + + (ty::ty_box(a_mt), ty::ty_box(b_mt)) | + (ty::ty_uniq(a_mt), ty::ty_uniq(b_mt)) | + (ty::ty_vec(a_mt), ty::ty_vec(b_mt)) | + (ty::ty_ptr(a_mt), ty::ty_ptr(b_mt)) { + self.mts(a_mt, b_mt) + } + + (ty::ty_rptr(a_r, a_mt), ty::ty_rptr(b_r, b_mt)) { + self.mts(a_mt, b_mt).then {|| + self.regions(a_r, b_r) + } + } + + (ty::ty_res(a_id, a_t, a_tps), ty::ty_res(b_id, b_t, b_tps)) + if a_id == b_id { + self.tys(a_t, b_t).then {|| + self.tps(a_tps, b_tps) + } + } + + (ty::ty_rec(a_fields), ty::ty_rec(b_fields)) { + if check vec::same_length(a_fields, b_fields) { + iter2(a_fields, b_fields) {|a,b| + self.flds(a, b) + } + } else { + ret self.uerr(ty::terr_record_size(a_fields.len(), + b_fields.len())); + } + } + + (ty::ty_tup(a_tys), ty::ty_tup(b_tys)) { + if check vec::same_length(a_tys, b_tys) { + self.tyvecs(a_tys, b_tys) + } else { + self.uerr(ty::terr_tuple_size(a_tys.len(), b_tys.len())) + } + } + + (ty::ty_fn(a_fty), ty::ty_fn(b_fty)) { + self.fns(a_fty, b_fty) + } + + (ty::ty_constr(a_t, a_constrs), ty::ty_constr(b_t, b_constrs)) { + self.tys(a_t, b_t).then {|| + if check vec::same_length(a_constrs, b_constrs) { + iter2(a_constrs, b_constrs) {|a,b| + self.constrs(a, b) + } + } else { + ret self.uerr(ty::terr_constr_len(a_constrs.len(), + b_constrs.len())); + } + } + } + + _ { self.uerr(ty::terr_mismatch) } + } + } +} + +impl resolve_methods for infer_ctxt { + fn rok(t: ty::t) -> fres { + #debug["Resolve OK: %s", self.ty_to_str(t)]; + result::ok(t) + } + + fn rerr(v: int) -> fres { + #debug["Resolve error: %?", v]; + result::err(v) + } + + fn resolve_var(vid: int) -> fres { + let {root:_, bounds} = self.get(vid as uint); + + // Nonobvious: prefer the most specific type + // (i.e., the lower bound) to the more general + // one. More general types in Rust (e.g., fn()) + // tend to carry more restrictions or higher + // perf. penalties, so it pays to know more. + + alt bounds { + { ub:_, lb:some(t) } if !type_is_bot(t) { self.rok(t) } + { ub:some(t), lb:_ } { self.rok(t) } + { ub:_, lb:some(t) } { self.rok(t) } + { ub:none, lb:none } { self.rerr(vid) } + } + } + + fn resolve_ty(typ: ty::t) -> fres { + alt ty::get(typ).struct { + ty::ty_var(vid) { self.resolve_var(vid) } + _ { self.rok(typ) } + } + } + + fn subst_vars(unresolved: @mutable option, + vars_seen: std::list::list, + vid: int) -> ty::t { + // Should really return a fixup_result instead of a t, but fold_ty + // doesn't allow returning anything but a t. + alt self.resolve_var(vid) { + result::err(vid) { + *unresolved = some(vid); + ret ty::mk_var(self.tcx, vid); + } + result::ok(rt) { + let mut give_up = false; + std::list::iter(vars_seen) {|v| + if v == vid { + *unresolved = some(-1); // hack: communicate inf ty + give_up = true; + } + } + + // Return the type unchanged, so we can error out + // downstream + if give_up { ret rt; } + ret ty::fold_ty(self.tcx, + ty::fm_var( + self.subst_vars( + unresolved, + std::list::cons(vid, @vars_seen), + _)), + rt); + } + } + } + + fn fixup_vars(typ: ty::t) -> fres { + let unresolved = @mutable none::; + let rty = + ty::fold_ty(self.tcx, + ty::fm_var( + self.subst_vars( + unresolved, + std::list::nil, + _)), + typ); + + let ur = *unresolved; + alt ur { + none { ret self.rok(rty); } + some(var_id) { ret self.rerr(var_id); } + } + } +} \ No newline at end of file diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs index 233f5e38e9d7..81cdb7cc4735 100644 --- a/src/rustc/middle/ty.rs +++ b/src/rustc/middle/ty.rs @@ -37,7 +37,7 @@ export field; export field_idx; export get_field; export get_fields; -export fm_general, fm_rptr; +export fm_var, fm_general, fm_rptr; export get_element_type; export is_binopable; export is_pred_ty; @@ -140,6 +140,7 @@ export item_path; export item_path_str; export ast_ty_to_ty_cache_entry; export atttce_unresolved, atttce_resolved; +export mach_sty; // Data types @@ -304,6 +305,8 @@ type constr = constr_general; enum type_err { terr_mismatch, terr_ret_style_mismatch(ast::ret_style, ast::ret_style), + terr_mutability, + terr_proto_mismatch(ast::proto, ast::proto), terr_box_mutability, terr_ptr_mutability, terr_ref_mutability, @@ -2360,6 +2363,11 @@ fn type_err_to_str(cx: ctxt, err: type_err) -> str { ret to_str(actual) + " function found where " + to_str(expect) + " function was expected"; } + terr_proto_mismatch(e, a) { + ret #fmt["closure protocol mismatch (%s vs %s)", + proto_to_str(e), proto_to_str(a)]; + } + terr_mutability { ret "values differ in mutability"; } terr_box_mutability { ret "boxed values differ in mutability"; } terr_vec_mutability { ret "vectors differ in mutability"; } terr_ptr_mutability { ret "pointers differ in mutability"; } diff --git a/src/rustc/middle/typeck.rs b/src/rustc/middle/typeck.rs index 19c3e2739491..8d56e3101b5b 100644 --- a/src/rustc/middle/typeck.rs +++ b/src/rustc/middle/typeck.rs @@ -73,7 +73,7 @@ type fn_ctxt = {ret_ty: ty::t, purity: ast::purity, proto: ast::proto, - var_bindings: @ty::unify::var_bindings, + infcx: infer::infer_ctxt, locals: hashmap, next_var_id: @mutable int, ccx: @crate_ctxt}; @@ -206,7 +206,7 @@ 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) { + alt infer::resolve_type_structure(fcx.infcx, tp) { result::ok(typ_s) { ret typ_s; } result::err(_) { fcx.ccx.tcx.sess.span_fatal @@ -225,7 +225,7 @@ 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 { - let r = ty::unify::resolve_type_structure(fcx.var_bindings, typ); + let r = infer::resolve_type_structure(fcx.infcx, typ); alt r { result::ok(typ_s) { some(ty::get(typ_s).struct) } result::err(_) { none } @@ -798,15 +798,15 @@ fn compare_impl_method(tcx: ty::ctxt, sp: span, impl_m: ty::method, if_fty = fixup_self_in_method_ty(tcx, if_fty, substs, self_full(self_ty, impl_tps)); } - alt ty::unify::unify(impl_fty, if_fty, ty::unify::precise, tcx) { + alt infer::compare_tys(tcx, impl_fty, if_fty) { result::err(err) { tcx.sess.span_err(sp, "method `" + if_m.ident + "` has an incompatible type: " + ty::type_err_to_str(tcx, err)); - impl_fty } - result::ok(tp) { tp } + result::ok(()) { } } + ret impl_fty; } } @@ -1132,16 +1132,14 @@ mod unify { rb: @ty::unify::region_bindings, expected: ty::t, actual: ty::t) - -> result { - let irb = ty::unify::in_region_bindings(fcx.var_bindings, rb); - ret ty::unify::unify(expected, actual, irb, fcx.ccx.tcx); + -> result<(), ty::type_err> { + //let irb = ty::unify::in_region_bindings(fcx.var_bindings, rb); + ret infer::mk_subty(fcx.infcx, actual, expected); } fn unify(fcx: @fn_ctxt, expected: ty::t, actual: ty::t) -> - result { - ret ty::unify::unify(expected, actual, - ty::unify::in_bindings(fcx.var_bindings), - fcx.ccx.tcx); + result<(), ty::type_err> { + ret infer::mk_subty(fcx.infcx, actual, expected); } } @@ -1180,13 +1178,12 @@ 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) { + alt infer::fixup_vars(fcx.infcx, typ) { result::ok(new_type) { ret new_type; } result::err(_) { ret typ; } } } - // Demands - procedures that require that two types unify and emit an error // message if they don't. type ty_param_substs_and_ty = {substs: [ty::t], ty: ty::t}; @@ -1197,6 +1194,11 @@ mod demand { full(fcx, sp, unify::unify, expected, actual, []).ty } + // n.b.: order of arguments is reversed. + fn subty(fcx: @fn_ctxt, sp: span, actual: ty::t, expected: ty::t) { + full(fcx, sp, unify::unify, expected, actual, []); + } + fn with_region_bindings(fcx: @fn_ctxt, sp: span, rb: @ty::unify::region_bindings, @@ -1217,7 +1219,7 @@ mod demand { fn full(fcx: @fn_ctxt, sp: span, unifier: fn@(@fn_ctxt, ty::t, ty::t) - -> result, + -> result<(), ty::type_err>, expected: ty::t, actual: ty::t, ty_param_substs_0: [ty::t]) -> @@ -1247,7 +1249,9 @@ mod demand { alt unifier(fcx, expected, actual) { - result::ok(t) { ret mk_result(fcx, t, ty_param_subst_var_ids); } + result::ok(()) { + ret mk_result(fcx, expected, 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); @@ -1311,9 +1315,14 @@ mod writeback { fn resolve_type_vars_in_type(fcx: @fn_ctxt, sp: span, typ: ty::t) -> option { if !ty::type_has_vars(typ) { ret some(typ); } - alt ty::unify::fixup_vars(fcx.ccx.tcx, some(sp), fcx.var_bindings, - typ) { + alt infer::fixup_vars(fcx.infcx, typ) { result::ok(new_type) { ret some(new_type); } + result::err(-1) { + fcx.ccx.tcx.sess.span_err( + sp, + "can not instantiate infinite type"); + ret none; + } result::err(vid) { if !fcx.ccx.tcx.sess.has_errors() { fcx.ccx.tcx.sess.span_err(sp, "cannot determine a type \ @@ -1396,16 +1405,29 @@ mod writeback { fn visit_pat(p: @ast::pat, wbcx: wb_ctxt, v: wb_vt) { if !wbcx.success { ret; } resolve_type_vars_for_node(wbcx, p.span, p.id); + #debug["Type for pattern binding %s (id %d) resolved to %s", + pat_to_str(p), p.id, + ty_to_str(wbcx.fcx.ccx.tcx, + ty::node_id_to_type(wbcx.fcx.ccx.tcx, + p.id))]; visit::visit_pat(p, wbcx, v); } fn visit_local(l: @ast::local, wbcx: wb_ctxt, v: wb_vt) { if !wbcx.success { ret; } let var_id = lookup_local(wbcx.fcx, l.span, l.node.id); - let fix_rslt = - ty::unify::resolve_type_var(wbcx.fcx.ccx.tcx, some(l.span), - wbcx.fcx.var_bindings, var_id); - alt fix_rslt { - result::ok(lty) { write_ty(wbcx.fcx.ccx.tcx, l.node.id, lty); } + alt infer::resolve_var(wbcx.fcx.infcx, var_id) { + result::ok(lty) { + #debug["Type for local %s (id %d) resolved to %s", + pat_to_str(l.node.pat), l.node.id, + ty_to_str(wbcx.fcx.ccx.tcx, lty)]; + write_ty(wbcx.fcx.ccx.tcx, l.node.id, lty); + } + result::err(-1) { + wbcx.fcx.ccx.tcx.sess.span_err( + l.span, + "this local variable has a type of infinite size"); + wbcx.success = false; + } result::err(_) { wbcx.fcx.ccx.tcx.sess.span_err(l.span, "cannot determine a type \ @@ -1490,7 +1512,7 @@ fn check_intrinsic_type(tcx: ty::ctxt, it: @ast::native_item) { // Local variable gathering. We gather up all locals and create variable IDs // for them before typechecking the function. type gather_result = - {var_bindings: @ty::unify::var_bindings, + {infcx: infer::infer_ctxt, locals: hashmap, next_var_id: @mutable int}; @@ -1500,14 +1522,14 @@ fn gather_locals(ccx: @crate_ctxt, body: ast::blk, id: ast::node_id, old_fcx: option<@fn_ctxt>) -> gather_result { - let {vb: vb, locals: locals, nvi: nvi} = alt old_fcx { + let {infcx, locals, nvi} = alt old_fcx { none { - {vb: ty::unify::mk_var_bindings(), + {infcx: infer::new_infer_ctxt(ccx.tcx), locals: int_hash::(), nvi: @mutable 0} } some(fcx) { - {vb: fcx.var_bindings, + {infcx: fcx.infcx, locals: fcx.locals, nvi: fcx.next_var_id} } @@ -1522,8 +1544,7 @@ fn gather_locals(ccx: @crate_ctxt, alt ty_opt { none {/* nothing to do */ } some(typ) { - ty::unify::unify(ty::mk_var(tcx, var_id), typ, - ty::unify::in_bindings(vb), tcx); + infer::mk_eqty(infcx, ty::mk_var(tcx, var_id), typ); } } }; @@ -1533,6 +1554,8 @@ fn gather_locals(ccx: @crate_ctxt, let mut i = 0u; for arg: ty::arg in args { assign(decl.inputs[i].id, some(arg.ty)); + #debug["Argument %s is assigned to ", + decl.inputs[i].ident, locals.get(decl.inputs[i].id)]; i += 1u; } @@ -1548,15 +1571,19 @@ fn gather_locals(ccx: @crate_ctxt, } assign(local.node.id, local_ty_opt); + #debug["Local variable %s is assigned to ", + pat_to_str(local.node.pat), locals.get(local.node.id)]; visit::visit_local(local, e, v); }; // Add pattern bindings. let visit_pat = fn@(p: @ast::pat, &&e: (), v: visit::vt<()>) { alt p.node { - ast::pat_ident(_, _) + ast::pat_ident(path, _) if !pat_util::pat_is_variant(ccx.tcx.def_map, p) { assign(p.id, none); + #debug["Pattern binding %s is assigned to ", + path.node.idents[0], locals.get(p.id)]; } _ {} } @@ -1577,7 +1604,7 @@ fn gather_locals(ccx: @crate_ctxt, with *visit::default_visitor()}; visit::visit_block(body, (), visit::mk_vt(visit)); - ret {var_bindings: vb, + ret {infcx: infcx, locals: locals, next_var_id: nvi}; } @@ -2419,19 +2446,15 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier, element_ty: ty::t, body: ast::blk, node_id: ast::node_id) -> bool { let locid = lookup_local(fcx, local.span, local.node.id); - let element_ty = demand::simple(fcx, local.span, element_ty, - ty::mk_var(fcx.ccx.tcx, locid)); + demand::simple(fcx, local.span, + ty::mk_var(fcx.ccx.tcx, locid), + element_ty); let bot = check_decl_local(fcx, local); check_block_no_value(fcx, body); - // Unify type of decl with element type of the seq - demand::simple(fcx, local.span, - ty::node_id_to_type(fcx.ccx.tcx, local.node.id), - element_ty); write_nil(fcx.ccx.tcx, node_id); ret bot; } - // A generic function for checking the then and else in an if // or if-check fn check_then_else(fcx: @fn_ctxt, thn: ast::blk, @@ -2470,49 +2493,108 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier, } fn lookup_op_method(fcx: @fn_ctxt, op_ex: @ast::expr, self_t: ty::t, opname: str, args: [option<@ast::expr>]) - -> option { + -> option<(ty::t, bool)> { let callee_id = ast_util::op_expr_callee_id(op_ex); alt lookup_method(fcx, op_ex, callee_id, opname, self_t, []) { some(origin) { let method_ty = ty::node_id_to_type(fcx.ccx.tcx, callee_id); - check_call_or_bind(fcx, op_ex.span, method_ty, args); + let r = check_call_or_bind(fcx, op_ex.span, method_ty, args); fcx.ccx.method_map.insert(op_ex.id, origin); - some(ty::ty_fn_ret(method_ty)) + some((ty::ty_fn_ret(method_ty), r.bot)) } _ { none } } } - fn check_binop(fcx: @fn_ctxt, ex: @ast::expr, ty: ty::t, - op: ast::binop, rhs: @ast::expr) -> ty::t { - let resolved_t = structurally_resolved_type(fcx, ex.span, ty); + // could be either a expr_binop or an expr_assign_binop + fn check_binop(fcx: @fn_ctxt, expr: @ast::expr, + op: ast::binop, + lhs: @ast::expr, + rhs: @ast::expr) -> bool { let tcx = fcx.ccx.tcx; - if ty::is_binopable(tcx, resolved_t, op) { - ret alt op { - ast::eq | ast::lt | ast::le | ast::ne | ast::ge | - ast::gt { ty::mk_bool(tcx) } - _ { resolved_t } - }; - } + let lhs_bot = check_expr(fcx, lhs); + let lhs_t = expr_ty(tcx, lhs); + let lhs_t = structurally_resolved_type(fcx, lhs.span, lhs_t); + ret alt (op, ty::get(lhs_t).struct) { + (ast::add, ty::ty_vec(lhs_mt)) { + // For adding vectors with type L=[M TL] and R=[M TR], the result + // is somewhat subtle. Let L_c=[const TL] and R_c=[const TR] be + // const versions of the vectors in L and R. Next, let T be a + // fresh type variable where TL <: T and TR <: T. Then the result + // type is a fresh type variable T1 where T1 <: [const T]. This + // allows the result to be either a mutable or immutable vector, + // depending on external demands. + let const_vec_t = + ty::mk_vec(tcx, {ty: next_ty_var(fcx), + mutbl: ast::m_const}); + demand::simple(fcx, lhs.span, const_vec_t, lhs_t); + let rhs_bot = check_expr_with(fcx, rhs, const_vec_t); + let result_var = next_ty_var(fcx); + demand::simple(fcx, lhs.span, const_vec_t, result_var); + write_ty(tcx, expr.id, result_var); + lhs_bot | rhs_bot + } + (_, _) if ty::type_is_integral(lhs_t) && + ast_util::is_shift_binop(op) { + // Shift is a special case: rhs can be any integral type + let rhs_bot = check_expr(fcx, rhs); + let rhs_t = expr_ty(tcx, rhs); + require_integral(fcx, rhs.span, rhs_t); + write_ty(tcx, expr.id, lhs_t); + lhs_bot | rhs_bot + } + + (_, _) if ty::is_binopable(tcx, lhs_t, op) { + let rhs_bot = check_expr_with(fcx, rhs, lhs_t); + let rhs_t = alt op { + ast::eq | ast::lt | ast::le | ast::ne | ast::ge | + ast::gt { + // these comparison operators are handled in a + // separate case below. + tcx.sess.span_bug( + expr.span, + #fmt["Comparison operator in expr_binop: %s", + ast_util::binop_to_str(op)]); + } + _ { lhs_t } + }; + write_ty(tcx, expr.id, rhs_t); + if !ast_util::lazy_binop(op) { lhs_bot | rhs_bot } + else { lhs_bot } + } + + (_, _) { + let (result, rhs_bot) = + check_user_binop(fcx, expr, lhs_t, op, rhs); + write_ty(tcx, expr.id, result); + lhs_bot | rhs_bot + } + }; + } + fn check_user_binop(fcx: @fn_ctxt, ex: @ast::expr, lhs_resolved_t: ty::t, + op: ast::binop, rhs: @ast::expr) -> (ty::t, bool) { + let tcx = fcx.ccx.tcx; alt binop_method(op) { some(name) { - alt lookup_op_method(fcx, ex, resolved_t, name, [some(rhs)]) { - some(ret_ty) { ret ret_ty; } + alt lookup_op_method(fcx, ex, lhs_resolved_t, name, [some(rhs)]) { + some(pair) { ret pair; } _ {} } } _ {} } + check_expr(fcx, rhs); tcx.sess.span_err( ex.span, "binary operation " + ast_util::binop_to_str(op) + - " cannot be applied to type `" + ty_to_str(tcx, resolved_t) + + " cannot be applied to type `" + + ty_to_str(tcx, lhs_resolved_t) + "`"); - resolved_t + (lhs_resolved_t, false) } fn check_user_unop(fcx: @fn_ctxt, op_str: str, mname: str, ex: @ast::expr, rhs_t: ty::t) -> ty::t { alt lookup_op_method(fcx, ex, rhs_t, mname, []) { - some(ret_ty) { ret_ty } + some((ret_ty, _)) { ret_ty } _ { fcx.ccx.tcx.sess.span_err( ex.span, #fmt["cannot apply unary operator `%s` to type `%s`", @@ -2530,30 +2612,39 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier, let typ = check_lit(fcx.ccx, lit); write_ty(tcx, id, typ); } - ast::expr_binary(binop, lhs, rhs) { - let lhs_t = next_ty_var(fcx); - bot = check_expr_with(fcx, lhs, lhs_t); - let rhs_bot = if !ast_util::is_shift_binop(binop) { - check_expr_with(fcx, rhs, lhs_t) - } else { - let rhs_bot = check_expr(fcx, rhs); - let rhs_t = expr_ty(tcx, rhs); - require_integral(fcx, rhs.span, rhs_t); - rhs_bot - }; - - if !ast_util::lazy_binop(binop) { bot |= rhs_bot; } - - let result = check_binop(fcx, expr, lhs_t, binop, rhs); - write_ty(tcx, id, result); + // Something of a hack: special rules for comparison operators that + // simply unify LHS and RHS. This helps with inference as LHS and RHS + // do not need to be "resolvable". Some tests, particularly those with + // complicated iface requirements, fail without this---I think this code + // can be removed if we improve iface resolution to be more eager when + // possible. + ast::expr_binary(ast::eq, lhs, rhs) | + ast::expr_binary(ast::ne, lhs, rhs) | + ast::expr_binary(ast::lt, lhs, rhs) | + ast::expr_binary(ast::le, lhs, rhs) | + ast::expr_binary(ast::gt, lhs, rhs) | + ast::expr_binary(ast::ge, lhs, rhs) { + let tcx = fcx.ccx.tcx; + bot |= check_expr(fcx, lhs); + let lhs_t = expr_ty(tcx, lhs); + bot |= check_expr_with(fcx, rhs, lhs_t); + write_ty(tcx, id, ty::mk_bool(tcx)); + } + ast::expr_binary(op, lhs, rhs) { + bot |= check_binop(fcx, expr, op, lhs, rhs); } ast::expr_assign_op(op, lhs, rhs) { require_impure(tcx.sess, fcx.purity, expr.span); - bot = check_assignment(fcx, expr.span, lhs, rhs, id); + bot |= check_binop(fcx, expr, op, lhs, rhs); let lhs_t = ty::expr_ty(tcx, lhs); - let result = check_binop(fcx, expr, lhs_t, op, rhs); - demand::simple(fcx, expr.span, result, lhs_t); + let result_t = ty::expr_ty(tcx, expr); + demand::simple(fcx, expr.span, result_t, lhs_t); + + // Overwrite result of check_binop...this preserves existing behavior + // but seems quite dubious with regard to user-defined methods + // and so forth. - Niko + write_nil(tcx, expr.id); } ast::expr_unary(unop, oper) { bot = check_expr(fcx, oper); @@ -3036,7 +3127,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier, raw_base_t); alt lookup_op_method(fcx, expr, resolved, "[]", [some(idx)]) { - some(ret_ty) { write_ty(tcx, id, ret_ty); } + some((ret_ty, _)) { write_ty(tcx, id, ret_ty); } _ { tcx.sess.span_fatal( expr.span, "cannot index a value of type `" + @@ -3241,7 +3332,7 @@ fn check_const(ccx: @crate_ctxt, _sp: span, e: @ast::expr, id: ast::node_id) { @{ret_ty: rty, purity: ast::pure_fn, proto: ast::proto_box, - var_bindings: ty::unify::mk_var_bindings(), + infcx: infer::new_infer_ctxt(ccx.tcx), locals: int_hash::(), next_var_id: @mutable 0, ccx: ccx}; @@ -3260,7 +3351,7 @@ fn check_enum_variants(ccx: @crate_ctxt, sp: span, vs: [ast::variant], @{ret_ty: rty, purity: ast::pure_fn, proto: ast::proto_box, - var_bindings: ty::unify::mk_var_bindings(), + infcx: infer::new_infer_ctxt(ccx.tcx), locals: int_hash::(), next_var_id: @mutable 0, ccx: ccx}; @@ -3438,7 +3529,7 @@ fn check_fn(ccx: @crate_ctxt, @{ret_ty: ty::ty_fn_ret(ty::node_id_to_type(ccx.tcx, id)), purity: purity, proto: proto, - var_bindings: gather_result.var_bindings, + infcx: gather_result.infcx, locals: gather_result.locals, next_var_id: gather_result.next_var_id, ccx: ccx}; @@ -3723,8 +3814,12 @@ mod vtable { 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) { + alt infer::fixup_vars(fcx.infcx, ty) { result::ok(new_type) { new_type } + result::err(-1) { + tcx.sess.span_fatal(sp, "bounded type parameter with \ + cyclic type"); + } result::err(vid) { tcx.sess.span_fatal(sp, "could not determine a type for a \ bounded type parameter"); diff --git a/src/rustc/rustc.rc b/src/rustc/rustc.rc index 25b4c37cc9bf..5eb6ca7853f9 100644 --- a/src/rustc/rustc.rc +++ b/src/rustc/rustc.rc @@ -31,6 +31,7 @@ mod middle { mod reachable; } mod ty; + mod infer; mod ast_map; mod resolve; mod typeck; diff --git a/src/rustc/util/ppaux.rs b/src/rustc/util/ppaux.rs index 032f3620631d..112308548de6 100644 --- a/src/rustc/util/ppaux.rs +++ b/src/rustc/util/ppaux.rs @@ -28,6 +28,15 @@ fn region_to_str(cx: ctxt, region: region) -> str { } } +fn mt_to_str(cx: ctxt, m: mt) -> str { + let mstr = alt m.mutbl { + ast::m_mutbl { "mut " } + ast::m_imm { "" } + ast::m_const { "const " } + }; + ret mstr + ty_to_str(cx, m.ty); +} + fn ty_to_str(cx: ctxt, typ: t) -> str { fn fn_input_to_str(cx: ctxt, input: {mode: ast::mode, ty: t}) -> str { @@ -72,14 +81,6 @@ fn ty_to_str(cx: ctxt, typ: t) -> str { fn field_to_str(cx: ctxt, f: field) -> str { ret f.ident + ": " + mt_to_str(cx, f.mt); } - fn mt_to_str(cx: ctxt, m: mt) -> str { - let mstr = alt m.mutbl { - ast::m_mutbl { "mut " } - ast::m_imm { "" } - ast::m_const { "const " } - }; - ret mstr + ty_to_str(cx, m.ty); - } fn parameterized(cx: ctxt, base: str, tps: [ty::t]) -> str { if vec::len(tps) > 0u { let strs = vec::map(tps, {|t| ty_to_str(cx, t)}); diff --git a/src/rustdoc/reexport_pass.rs b/src/rustdoc/reexport_pass.rs index e07959042911..958c1fece7b2 100644 --- a/src/rustdoc/reexport_pass.rs +++ b/src/rustdoc/reexport_pass.rs @@ -170,7 +170,7 @@ fn build_reexport_path_map(srv: astsrv::srv, -def_map: def_map) -> path_map { let assoc_list = astsrv::exec(srv) {|ctxt| let def_map = from_def_assoc_list(def_assoc_list); - let path_map = map::str_hash(); + let path_map = map::str_hash::<[(str,doc::itemtag)]>(); ctxt.exp_map.items {|exp_id, defs| let path = alt check ctxt.ast_map.get(exp_id) { diff --git a/src/rustdoc/sectionalize_pass.rs b/src/rustdoc/sectionalize_pass.rs index 7ce71d88a967..4e055be8a1a2 100644 --- a/src/rustdoc/sectionalize_pass.rs +++ b/src/rustdoc/sectionalize_pass.rs @@ -90,7 +90,7 @@ fn sectionalize(desc: option) -> (option, [doc::section]) { let lines = str::lines(option::get(desc)); - let mut new_desc = none; + let mut new_desc = none::; let mut current_section = none; let mut sections = []; diff --git a/src/test/auxiliary/noexporttypelib.rs b/src/test/auxiliary/noexporttypelib.rs new file mode 100644 index 000000000000..865b283e01d2 --- /dev/null +++ b/src/test/auxiliary/noexporttypelib.rs @@ -0,0 +1,3 @@ +export foo; +type oint = option; +fn foo() -> oint { some(3) } diff --git a/src/test/compile-fail/binop-typeck.rs b/src/test/compile-fail/binop-typeck.rs index 9cb6dea53602..e2669be751b2 100644 --- a/src/test/compile-fail/binop-typeck.rs +++ b/src/test/compile-fail/binop-typeck.rs @@ -1,4 +1,8 @@ -// error-pattern:mismatched types // issue #500 -fn main() { let x = true; let y = 1; let z = x + y; } +fn main() { + let x = true; + let y = 1; + let z = x + y; + //!^ ERROR binary operation + cannot be applied to type `bool` +} diff --git a/src/test/compile-fail/mode-inference-fail.rs b/src/test/compile-fail/mode-inference-fail.rs index 732c9a4311f2..c5a7cba4ea07 100644 --- a/src/test/compile-fail/mode-inference-fail.rs +++ b/src/test/compile-fail/mode-inference-fail.rs @@ -7,5 +7,5 @@ fn apply_int(f: fn(int) -> int, a: int) -> int { f(a) } fn main() { let f = {|i| i}; assert apply_int(f, 2) == 2; - assert apply(f, 2) == 2; //! ERROR expected argument mode ++ + assert apply(f, 2) == 2; //! ERROR expected argument mode && } diff --git a/src/test/compile-fail/mutable-huh-variance-vec1.rs b/src/test/compile-fail/mutable-huh-variance-vec1.rs index e688fe2c26ed..895322309d0f 100644 --- a/src/test/compile-fail/mutable-huh-variance-vec1.rs +++ b/src/test/compile-fail/mutable-huh-variance-vec1.rs @@ -1,11 +1,12 @@ -// error-pattern: mismatched types - fn main() { - let v = [mutable [0]]; + // Note: explicit type annot is required here + // because otherwise the inference gets smart + // and assigns a type of [mut [const int]]. + let v: [mut [int]] = [mutable [0]]; fn f(&&v: [mutable [const int]]) { v[0] = [mutable 3] } - f(v); + f(v); //! ERROR (values differ in mutability) } diff --git a/src/test/compile-fail/mutable-huh-variance-vec2.rs b/src/test/compile-fail/mutable-huh-variance-vec2.rs index 81422f047f48..3a0725f6643d 100644 --- a/src/test/compile-fail/mutable-huh-variance-vec2.rs +++ b/src/test/compile-fail/mutable-huh-variance-vec2.rs @@ -1,11 +1,12 @@ -// error-pattern: mismatched types - fn main() { - let v = [mutable [mutable 0]]; + // Note: explicit type annot is required here + // because otherwise the inference gets smart + // and assigns a type of [mut [const int]]. + let v: [mut [mut int]] = [mutable [mutable 0]]; fn f(&&v: [mutable [const int]]) { v[0] = [3] } - f(v); + f(v); //! ERROR (values differ in mutability) } diff --git a/src/test/compile-fail/mutable-huh-variance-vec3.rs b/src/test/compile-fail/mutable-huh-variance-vec3.rs index ef5da13a3af8..786535dd4bcb 100644 --- a/src/test/compile-fail/mutable-huh-variance-vec3.rs +++ b/src/test/compile-fail/mutable-huh-variance-vec3.rs @@ -1,11 +1,12 @@ -// error-pattern: mismatched types - fn main() { - let v = [mutable [mutable [0]]]; + // Note: explicit type annot is required here + // because otherwise the inference gets smart + // and assigns a type of [mut [const int]]. + let v: [mut[mut[int]]] = [mutable [mutable [0]]]; fn f(&&v: [mutable [mutable [const int]]]) { v[0][1] = [mutable 3] } - f(v); + f(v); //! ERROR (values differ in mutability) } diff --git a/src/test/compile-fail/noexporttypeexe.rs b/src/test/compile-fail/noexporttypeexe.rs new file mode 100644 index 000000000000..b88f3b34c61d --- /dev/null +++ b/src/test/compile-fail/noexporttypeexe.rs @@ -0,0 +1,13 @@ +// aux-build:noexporttypelib.rs + +use noexporttypelib; + +fn main() { + // Here, the type returned by foo() is not exported. + // This used to cause internal errors when serializing + // because the def_id associated with the type was + // not convertible to a path. + let x: int = noexporttypelib::foo(); + //!^ ERROR expected `int` but found `core::option::option` +} + diff --git a/src/test/compile-fail/occurs-check.rs b/src/test/compile-fail/occurs-check.rs index 069bd0a5bbd7..4c4cacb36e7e 100644 --- a/src/test/compile-fail/occurs-check.rs +++ b/src/test/compile-fail/occurs-check.rs @@ -1,2 +1,4 @@ -// error-pattern: can not instantiate infinite type -fn main() { let f; f = @f; } +fn main() { + let f; //! ERROR this local variable has a type of infinite size + f = @f; +} diff --git a/src/test/compile-fail/type-mismatch.rs b/src/test/compile-fail/type-mismatch.rs index ad9e4f0505c0..08deb8a2d7c3 100644 --- a/src/test/compile-fail/type-mismatch.rs +++ b/src/test/compile-fail/type-mismatch.rs @@ -1,4 +1,8 @@ -// error-pattern:expected `bool` but found `int` // issue #516 -fn main() { let x = true; let y = 1; let z = x + y; } +fn main() { + let x = true; + let y = 1; + let z = x + y; + //!^ ERROR binary operation + cannot be applied to type `bool` +} diff --git a/src/test/compile-fail/vec-concat-bug.rs b/src/test/compile-fail/vec-concat-bug.rs new file mode 100644 index 000000000000..02e37aac6b88 --- /dev/null +++ b/src/test/compile-fail/vec-concat-bug.rs @@ -0,0 +1,13 @@ +fn concat(v: [const [const T]]) -> [T] { + let mut r = []; + + // Earlier versions of our type checker accepted this: + for inner: [T] in v { + //!^ ERROR found `[const 'a]` (values differ in mutability) + r += inner; + } + + ret r; +} + +fn main() {} \ No newline at end of file