Implement new inference algorithm.

This commit is contained in:
Niko Matsakis 2012-03-22 20:06:01 -07:00
parent 40443768b1
commit 042c532a08
24 changed files with 1002 additions and 123 deletions

View File

@ -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];
}

View File

@ -91,6 +91,24 @@ fn chain<T, U: copy, V: copy>(res: result<T, V>, op: fn(T) -> result<U, V>)
}
}
#[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<T: copy, U: copy, V: copy>(
res: result<T, V>,
op: fn(V) -> result<T, U>)
-> result<T, U> {
alt res {
ok(t) { ok(t) }
err(v) { op(v) }
}
}
// ______________________________________________________________________
// Note:
//
@ -171,6 +189,22 @@ fn map2<S,T,U:copy,V:copy,W>(ss: [S], ts: [T],
ret nxt(vs);
}
fn iter2<S,T,U:copy>(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<int, str> { result::ok(666) }

View File

@ -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<T>(+v: [mutable T]) -> [T] unsafe {
r
}
// This function only exists to work around bugs in the type checker.
fn from_const<T>(+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<T>(&v: [const T]) -> T unsafe {
val
}
#[doc = "
Removes all elements from a vector without affecting
how much space is reserved.
"]
fn clear<T>(&v: [const T]) unsafe {
unsafe::set_len(v, 0u);
}
#[doc = "Append an element to a vector"]
fn push<T>(&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<T: copy>(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<T: copy>(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<E,T>(v: [mutable E], f: fn(*mutable E) -> T) -> T unsafe {
}
#[doc = "An extension implementation providing a `len` method"]
impl vec_len<T> for [T] {
impl vec_len<T> for [const T] {
#[doc = "Return the length of the vector"]
#[inline(always)]
fn len() -> uint { len(self) }

View File

@ -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), }

View File

@ -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)}
}
}

View File

@ -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);

View File

@ -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('|');
}
}
_ {}
}

668
src/rustc/middle/infer.rs Normal file
View File

@ -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<ty::t>;
type bounds = {lb: bound, ub: bound};
enum var_value {
redirect(uint),
bounded(bounds)
}
enum infer_ctxt = @{
tcx: ty::ctxt,
vals: smallintmap<var_value>,
mut bindings: [(uint, var_value)]
};
type ures = result::result<(), ty::type_err>;
type fres<T> = result::result<T,int>;
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<ty::t> {
cx.resolve_ty(a)
}
fn resolve_var(cx: infer_ctxt, vid: int) -> fres<ty::t> {
cx.fixup_vars(ty::mk_var(cx.tcx, vid))
}
fn fixup_vars(cx: infer_ctxt, a: ty::t) -> fres<ty::t> {
cx.fixup_vars(a)
}
impl methods for ures {
fn then<T:copy>(f: fn() -> result<T,ty::type_err>)
-> result<T,ty::type_err> {
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 <T%u> 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<T:copy,E:copy>(f: fn() -> result<T,E>) -> result<T,E> {
assert self.bindings.len() == 0u;
let r = self.try(f);
vec::clear(self.bindings);
ret r;
}
fn try<T:copy,E:copy>(f: fn() -> result<T,E>) -> result<T,E> {
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<bounds, ty::type_err> {
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(<T%u>,%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(<T%u> <: <T%u>)",
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(<T%u> <: %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 <: <T%u>)",
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<ty::t> {
#debug["Resolve OK: %s", self.ty_to_str(t)];
result::ok(t)
}
fn rerr(v: int) -> fres<ty::t> {
#debug["Resolve error: %?", v];
result::err(v)
}
fn resolve_var(vid: int) -> fres<ty::t> {
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<ty::t> {
alt ty::get(typ).struct {
ty::ty_var(vid) { self.resolve_var(vid) }
_ { self.rok(typ) }
}
}
fn subst_vars(unresolved: @mutable option<int>,
vars_seen: std::list::list<int>,
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<ty::t> {
let unresolved = @mutable none::<int>;
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); }
}
}
}

View File

@ -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<uint>;
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"; }

View File

@ -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<ast::node_id, int>,
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<ty::sty> {
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<ty::t, ty::type_err> {
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<ty::t, ty::type_err> {
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<ty::t, ty::type_err>,
-> 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<ty::t> {
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<ast::node_id, int>,
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::<int>(),
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 <T%d>",
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 <T%d>",
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 <T%d>",
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<ty::t> {
-> 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::<int>(),
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::<int>(),
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");

View File

@ -31,6 +31,7 @@ mod middle {
mod reachable;
}
mod ty;
mod infer;
mod ast_map;
mod resolve;
mod typeck;

View File

@ -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)});

View File

@ -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) {

View File

@ -90,7 +90,7 @@ fn sectionalize(desc: option<str>) -> (option<str>, [doc::section]) {
let lines = str::lines(option::get(desc));
let mut new_desc = none;
let mut new_desc = none::<str>;
let mut current_section = none;
let mut sections = [];

View File

@ -0,0 +1,3 @@
export foo;
type oint = option<int>;
fn foo() -> oint { some(3) }

View File

@ -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`
}

View File

@ -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 &&
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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<int>`
}

View File

@ -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;
}

View File

@ -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`
}

View File

@ -0,0 +1,13 @@
fn concat<T: copy>(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() {}