Implement new inference algorithm.
This commit is contained in:
@@ -169,14 +169,14 @@ fn test_enumerate() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_map_and_to_list() {
|
fn test_map_and_to_list() {
|
||||||
let a = bind vec::iter([0, 1, 2], _);
|
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);
|
let c = to_list(b);
|
||||||
assert c == [0, 2, 4];
|
assert c == [0, 2, 4];
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_map_directly_on_vec() {
|
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);
|
let c = to_list(b);
|
||||||
assert c == [0, 2, 4];
|
assert c == [0, 2, 4];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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:
|
// Note:
|
||||||
//
|
//
|
||||||
@@ -171,6 +189,22 @@ fn map2<S,T,U:copy,V:copy,W>(ss: [S], ts: [T],
|
|||||||
ret nxt(vs);
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
fn op1() -> result::result<int, str> { result::ok(666) }
|
fn op1() -> result::result<int, str> { result::ok(666) }
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ export rsplit;
|
|||||||
export rsplitn;
|
export rsplitn;
|
||||||
export shift;
|
export shift;
|
||||||
export pop;
|
export pop;
|
||||||
|
export clear;
|
||||||
export push;
|
export push;
|
||||||
export grow;
|
export grow;
|
||||||
export grow_fn;
|
export grow_fn;
|
||||||
@@ -164,6 +165,13 @@ fn from_mut<T>(+v: [mutable T]) -> [T] unsafe {
|
|||||||
r
|
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
|
// Accessors
|
||||||
|
|
||||||
#[doc = "Returns the first element of a vector"]
|
#[doc = "Returns the first element of a vector"]
|
||||||
@@ -336,6 +344,14 @@ fn pop<T>(&v: [const T]) -> T unsafe {
|
|||||||
val
|
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"]
|
#[doc = "Append an element to a vector"]
|
||||||
fn push<T>(&v: [const T], +initval: T) {
|
fn push<T>(&v: [const T], +initval: T) {
|
||||||
v += [initval];
|
v += [initval];
|
||||||
@@ -466,8 +482,8 @@ Concatenate a vector of vectors.
|
|||||||
Flattens a vector of vectors of T into a single vector of T.
|
Flattens a vector of vectors of T into a single vector of T.
|
||||||
"]
|
"]
|
||||||
fn concat<T: copy>(v: [const [const T]]) -> [T] {
|
fn concat<T: copy>(v: [const [const T]]) -> [T] {
|
||||||
let mut r: [T] = [];
|
let mut r = [];
|
||||||
for inner: [T] in v { r += inner; }
|
for inner in v { r += from_const(inner); }
|
||||||
ret r;
|
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] {
|
fn connect<T: copy>(v: [const [const T]], sep: T) -> [T] {
|
||||||
let mut r: [T] = [];
|
let mut r: [T] = [];
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
for inner: [T] in v {
|
for inner in v {
|
||||||
if first { first = false; } else { push(r, sep); }
|
if first { first = false; } else { push(r, sep); }
|
||||||
r += inner;
|
r += from_const(inner);
|
||||||
}
|
}
|
||||||
ret r;
|
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"]
|
#[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"]
|
#[doc = "Return the length of the vector"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn len() -> uint { len(self) }
|
fn len() -> uint { len(self) }
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ export opt_str;
|
|||||||
export opt_strs;
|
export opt_strs;
|
||||||
export opt_maybe_str;
|
export opt_maybe_str;
|
||||||
export opt_default;
|
export opt_default;
|
||||||
|
export result; //NDM
|
||||||
|
|
||||||
enum name { long(str), short(char), }
|
enum name { long(str), short(char), }
|
||||||
|
|
||||||
|
|||||||
@@ -634,6 +634,7 @@ impl helpers for @e::encode_ctxt {
|
|||||||
fn ty_str_ctxt() -> @tyencode::ctxt {
|
fn ty_str_ctxt() -> @tyencode::ctxt {
|
||||||
@{ds: e::def_to_str,
|
@{ds: e::def_to_str,
|
||||||
tcx: self.ccx.tcx,
|
tcx: self.ccx.tcx,
|
||||||
|
reachable: self.ccx.reachable,
|
||||||
abbrevs: tyencode::ac_use_abbrevs(self.type_abbrevs)}
|
abbrevs: tyencode::ac_use_abbrevs(self.type_abbrevs)}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -221,6 +221,7 @@ fn encode_type_param_bounds(ebml_w: ebml::writer, ecx: @encode_ctxt,
|
|||||||
params: [ty_param]) {
|
params: [ty_param]) {
|
||||||
let ty_str_ctxt = @{ds: def_to_str,
|
let ty_str_ctxt = @{ds: def_to_str,
|
||||||
tcx: ecx.ccx.tcx,
|
tcx: ecx.ccx.tcx,
|
||||||
|
reachable: ecx.ccx.reachable,
|
||||||
abbrevs: tyencode::ac_use_abbrevs(ecx.type_abbrevs)};
|
abbrevs: tyencode::ac_use_abbrevs(ecx.type_abbrevs)};
|
||||||
for param in params {
|
for param in params {
|
||||||
ebml_w.start_tag(tag_items_data_item_ty_param_bounds);
|
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 =
|
let ty_str_ctxt =
|
||||||
@{ds: def_to_str,
|
@{ds: def_to_str,
|
||||||
tcx: ecx.ccx.tcx,
|
tcx: ecx.ccx.tcx,
|
||||||
|
reachable: ecx.ccx.reachable,
|
||||||
abbrevs: tyencode::ac_use_abbrevs(ecx.type_abbrevs)};
|
abbrevs: tyencode::ac_use_abbrevs(ecx.type_abbrevs)};
|
||||||
tyencode::enc_ty(ebml_w.writer, ty_str_ctxt, typ);
|
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
|
// Get the encoded string for a type
|
||||||
fn encoded_ty(tcx: ty::ctxt, t: ty::t) -> str {
|
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();
|
let buf = io::mem_buffer();
|
||||||
tyencode::enc_ty(io::mem_buffer_writer(buf), cx, t);
|
tyencode::enc_ty(io::mem_buffer_writer(buf), cx, t);
|
||||||
ret io::mem_buffer_str(buf);
|
ret io::mem_buffer_str(buf);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import syntax::ast::*;
|
|||||||
import driver::session::session;
|
import driver::session::session;
|
||||||
import middle::ty;
|
import middle::ty;
|
||||||
import syntax::print::pprust::*;
|
import syntax::print::pprust::*;
|
||||||
|
import middle::trans::reachable;
|
||||||
|
|
||||||
export ctxt;
|
export ctxt;
|
||||||
export ty_abbrev;
|
export ty_abbrev;
|
||||||
@@ -18,7 +19,8 @@ export enc_mode;
|
|||||||
type ctxt =
|
type ctxt =
|
||||||
// Def -> str Callback:
|
// Def -> str Callback:
|
||||||
// The type context.
|
// 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.
|
// 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.
|
// Extra parameters are for converting to/from def_ids in the string rep.
|
||||||
@@ -55,10 +57,15 @@ fn enc_ty(w: io::writer, cx: @ctxt, t: ty::t) {
|
|||||||
let pos = w.tell();
|
let pos = w.tell();
|
||||||
alt ty::type_def_id(t) {
|
alt ty::type_def_id(t) {
|
||||||
some(def_id) {
|
some(def_id) {
|
||||||
|
// 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_char('"');
|
||||||
w.write_str(cx.ds(def_id));
|
w.write_str(cx.ds(def_id));
|
||||||
w.write_char('|');
|
w.write_char('|');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
_ {}
|
_ {}
|
||||||
}
|
}
|
||||||
enc_sty(w, cx, ty::get(t).struct);
|
enc_sty(w, cx, ty::get(t).struct);
|
||||||
|
|||||||
668
src/rustc/middle/infer.rs
Normal file
668
src/rustc/middle/infer.rs
Normal 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); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,7 +37,7 @@ export field;
|
|||||||
export field_idx;
|
export field_idx;
|
||||||
export get_field;
|
export get_field;
|
||||||
export get_fields;
|
export get_fields;
|
||||||
export fm_general, fm_rptr;
|
export fm_var, fm_general, fm_rptr;
|
||||||
export get_element_type;
|
export get_element_type;
|
||||||
export is_binopable;
|
export is_binopable;
|
||||||
export is_pred_ty;
|
export is_pred_ty;
|
||||||
@@ -140,6 +140,7 @@ export item_path;
|
|||||||
export item_path_str;
|
export item_path_str;
|
||||||
export ast_ty_to_ty_cache_entry;
|
export ast_ty_to_ty_cache_entry;
|
||||||
export atttce_unresolved, atttce_resolved;
|
export atttce_unresolved, atttce_resolved;
|
||||||
|
export mach_sty;
|
||||||
|
|
||||||
// Data types
|
// Data types
|
||||||
|
|
||||||
@@ -304,6 +305,8 @@ type constr = constr_general<uint>;
|
|||||||
enum type_err {
|
enum type_err {
|
||||||
terr_mismatch,
|
terr_mismatch,
|
||||||
terr_ret_style_mismatch(ast::ret_style, ast::ret_style),
|
terr_ret_style_mismatch(ast::ret_style, ast::ret_style),
|
||||||
|
terr_mutability,
|
||||||
|
terr_proto_mismatch(ast::proto, ast::proto),
|
||||||
terr_box_mutability,
|
terr_box_mutability,
|
||||||
terr_ptr_mutability,
|
terr_ptr_mutability,
|
||||||
terr_ref_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) +
|
ret to_str(actual) + " function found where " + to_str(expect) +
|
||||||
" function was expected";
|
" 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_box_mutability { ret "boxed values differ in mutability"; }
|
||||||
terr_vec_mutability { ret "vectors differ in mutability"; }
|
terr_vec_mutability { ret "vectors differ in mutability"; }
|
||||||
terr_ptr_mutability { ret "pointers differ in mutability"; }
|
terr_ptr_mutability { ret "pointers differ in mutability"; }
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ type fn_ctxt =
|
|||||||
{ret_ty: ty::t,
|
{ret_ty: ty::t,
|
||||||
purity: ast::purity,
|
purity: ast::purity,
|
||||||
proto: ast::proto,
|
proto: ast::proto,
|
||||||
var_bindings: @ty::unify::var_bindings,
|
infcx: infer::infer_ctxt,
|
||||||
locals: hashmap<ast::node_id, int>,
|
locals: hashmap<ast::node_id, int>,
|
||||||
next_var_id: @mutable int,
|
next_var_id: @mutable int,
|
||||||
ccx: @crate_ctxt};
|
ccx: @crate_ctxt};
|
||||||
@@ -206,7 +206,7 @@ fn instantiate_path(fcx: @fn_ctxt, pth: @ast::path,
|
|||||||
|
|
||||||
// Type tests
|
// Type tests
|
||||||
fn structurally_resolved_type(fcx: @fn_ctxt, sp: span, tp: ty::t) -> ty::t {
|
fn structurally_resolved_type(fcx: @fn_ctxt, sp: span, tp: ty::t) -> ty::t {
|
||||||
alt ty::unify::resolve_type_structure(fcx.var_bindings, tp) {
|
alt infer::resolve_type_structure(fcx.infcx, tp) {
|
||||||
result::ok(typ_s) { ret typ_s; }
|
result::ok(typ_s) { ret typ_s; }
|
||||||
result::err(_) {
|
result::err(_) {
|
||||||
fcx.ccx.tcx.sess.span_fatal
|
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.
|
// is not known yet.
|
||||||
fn structure_of_maybe(fcx: @fn_ctxt, _sp: span, typ: ty::t) ->
|
fn structure_of_maybe(fcx: @fn_ctxt, _sp: span, typ: ty::t) ->
|
||||||
option<ty::sty> {
|
option<ty::sty> {
|
||||||
let r = ty::unify::resolve_type_structure(fcx.var_bindings, typ);
|
let r = infer::resolve_type_structure(fcx.infcx, typ);
|
||||||
alt r {
|
alt r {
|
||||||
result::ok(typ_s) { some(ty::get(typ_s).struct) }
|
result::ok(typ_s) { some(ty::get(typ_s).struct) }
|
||||||
result::err(_) { none }
|
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,
|
if_fty = fixup_self_in_method_ty(tcx, if_fty, substs,
|
||||||
self_full(self_ty, impl_tps));
|
self_full(self_ty, impl_tps));
|
||||||
}
|
}
|
||||||
alt ty::unify::unify(impl_fty, if_fty, ty::unify::precise, tcx) {
|
alt infer::compare_tys(tcx, impl_fty, if_fty) {
|
||||||
result::err(err) {
|
result::err(err) {
|
||||||
tcx.sess.span_err(sp, "method `" + if_m.ident +
|
tcx.sess.span_err(sp, "method `" + if_m.ident +
|
||||||
"` has an incompatible type: " +
|
"` has an incompatible type: " +
|
||||||
ty::type_err_to_str(tcx, err));
|
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,
|
rb: @ty::unify::region_bindings,
|
||||||
expected: ty::t,
|
expected: ty::t,
|
||||||
actual: ty::t)
|
actual: ty::t)
|
||||||
-> result<ty::t, ty::type_err> {
|
-> result<(), ty::type_err> {
|
||||||
let irb = ty::unify::in_region_bindings(fcx.var_bindings, rb);
|
//let irb = ty::unify::in_region_bindings(fcx.var_bindings, rb);
|
||||||
ret ty::unify::unify(expected, actual, irb, fcx.ccx.tcx);
|
ret infer::mk_subty(fcx.infcx, actual, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unify(fcx: @fn_ctxt, expected: ty::t, actual: ty::t) ->
|
fn unify(fcx: @fn_ctxt, expected: ty::t, actual: ty::t) ->
|
||||||
result<ty::t, ty::type_err> {
|
result<(), ty::type_err> {
|
||||||
ret ty::unify::unify(expected, actual,
|
ret infer::mk_subty(fcx.infcx, actual, expected);
|
||||||
ty::unify::in_bindings(fcx.var_bindings),
|
|
||||||
fcx.ccx.tcx);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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 {
|
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::ok(new_type) { ret new_type; }
|
||||||
result::err(_) { ret typ; }
|
result::err(_) { ret typ; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Demands - procedures that require that two types unify and emit an error
|
// Demands - procedures that require that two types unify and emit an error
|
||||||
// message if they don't.
|
// message if they don't.
|
||||||
type ty_param_substs_and_ty = {substs: [ty::t], ty: ty::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
|
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,
|
fn with_region_bindings(fcx: @fn_ctxt,
|
||||||
sp: span,
|
sp: span,
|
||||||
rb: @ty::unify::region_bindings,
|
rb: @ty::unify::region_bindings,
|
||||||
@@ -1217,7 +1219,7 @@ mod demand {
|
|||||||
fn full(fcx: @fn_ctxt,
|
fn full(fcx: @fn_ctxt,
|
||||||
sp: span,
|
sp: span,
|
||||||
unifier: fn@(@fn_ctxt, ty::t, ty::t)
|
unifier: fn@(@fn_ctxt, ty::t, ty::t)
|
||||||
-> result<ty::t, ty::type_err>,
|
-> result<(), ty::type_err>,
|
||||||
expected: ty::t,
|
expected: ty::t,
|
||||||
actual: ty::t,
|
actual: ty::t,
|
||||||
ty_param_substs_0: [ty::t]) ->
|
ty_param_substs_0: [ty::t]) ->
|
||||||
@@ -1247,7 +1249,9 @@ mod demand {
|
|||||||
|
|
||||||
|
|
||||||
alt unifier(fcx, expected, actual) {
|
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) {
|
result::err(err) {
|
||||||
let e_err = resolve_type_vars_if_possible(fcx, expected);
|
let e_err = resolve_type_vars_if_possible(fcx, expected);
|
||||||
let a_err = resolve_type_vars_if_possible(fcx, actual);
|
let a_err = resolve_type_vars_if_possible(fcx, actual);
|
||||||
@@ -1311,9 +1315,14 @@ mod writeback {
|
|||||||
fn resolve_type_vars_in_type(fcx: @fn_ctxt, sp: span, typ: ty::t) ->
|
fn resolve_type_vars_in_type(fcx: @fn_ctxt, sp: span, typ: ty::t) ->
|
||||||
option<ty::t> {
|
option<ty::t> {
|
||||||
if !ty::type_has_vars(typ) { ret some(typ); }
|
if !ty::type_has_vars(typ) { ret some(typ); }
|
||||||
alt ty::unify::fixup_vars(fcx.ccx.tcx, some(sp), fcx.var_bindings,
|
alt infer::fixup_vars(fcx.infcx, typ) {
|
||||||
typ) {
|
|
||||||
result::ok(new_type) { ret some(new_type); }
|
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) {
|
result::err(vid) {
|
||||||
if !fcx.ccx.tcx.sess.has_errors() {
|
if !fcx.ccx.tcx.sess.has_errors() {
|
||||||
fcx.ccx.tcx.sess.span_err(sp, "cannot determine a type \
|
fcx.ccx.tcx.sess.span_err(sp, "cannot determine a type \
|
||||||
@@ -1396,16 +1405,29 @@ mod writeback {
|
|||||||
fn visit_pat(p: @ast::pat, wbcx: wb_ctxt, v: wb_vt) {
|
fn visit_pat(p: @ast::pat, wbcx: wb_ctxt, v: wb_vt) {
|
||||||
if !wbcx.success { ret; }
|
if !wbcx.success { ret; }
|
||||||
resolve_type_vars_for_node(wbcx, p.span, p.id);
|
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);
|
visit::visit_pat(p, wbcx, v);
|
||||||
}
|
}
|
||||||
fn visit_local(l: @ast::local, wbcx: wb_ctxt, v: wb_vt) {
|
fn visit_local(l: @ast::local, wbcx: wb_ctxt, v: wb_vt) {
|
||||||
if !wbcx.success { ret; }
|
if !wbcx.success { ret; }
|
||||||
let var_id = lookup_local(wbcx.fcx, l.span, l.node.id);
|
let var_id = lookup_local(wbcx.fcx, l.span, l.node.id);
|
||||||
let fix_rslt =
|
alt infer::resolve_var(wbcx.fcx.infcx, var_id) {
|
||||||
ty::unify::resolve_type_var(wbcx.fcx.ccx.tcx, some(l.span),
|
result::ok(lty) {
|
||||||
wbcx.fcx.var_bindings, var_id);
|
#debug["Type for local %s (id %d) resolved to %s",
|
||||||
alt fix_rslt {
|
pat_to_str(l.node.pat), l.node.id,
|
||||||
result::ok(lty) { write_ty(wbcx.fcx.ccx.tcx, l.node.id, lty); }
|
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(_) {
|
result::err(_) {
|
||||||
wbcx.fcx.ccx.tcx.sess.span_err(l.span,
|
wbcx.fcx.ccx.tcx.sess.span_err(l.span,
|
||||||
"cannot determine a type \
|
"cannot determine a type \
|
||||||
@@ -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
|
// Local variable gathering. We gather up all locals and create variable IDs
|
||||||
// for them before typechecking the function.
|
// for them before typechecking the function.
|
||||||
type gather_result =
|
type gather_result =
|
||||||
{var_bindings: @ty::unify::var_bindings,
|
{infcx: infer::infer_ctxt,
|
||||||
locals: hashmap<ast::node_id, int>,
|
locals: hashmap<ast::node_id, int>,
|
||||||
next_var_id: @mutable int};
|
next_var_id: @mutable int};
|
||||||
|
|
||||||
@@ -1500,14 +1522,14 @@ fn gather_locals(ccx: @crate_ctxt,
|
|||||||
body: ast::blk,
|
body: ast::blk,
|
||||||
id: ast::node_id,
|
id: ast::node_id,
|
||||||
old_fcx: option<@fn_ctxt>) -> gather_result {
|
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 {
|
none {
|
||||||
{vb: ty::unify::mk_var_bindings(),
|
{infcx: infer::new_infer_ctxt(ccx.tcx),
|
||||||
locals: int_hash::<int>(),
|
locals: int_hash::<int>(),
|
||||||
nvi: @mutable 0}
|
nvi: @mutable 0}
|
||||||
}
|
}
|
||||||
some(fcx) {
|
some(fcx) {
|
||||||
{vb: fcx.var_bindings,
|
{infcx: fcx.infcx,
|
||||||
locals: fcx.locals,
|
locals: fcx.locals,
|
||||||
nvi: fcx.next_var_id}
|
nvi: fcx.next_var_id}
|
||||||
}
|
}
|
||||||
@@ -1522,8 +1544,7 @@ fn gather_locals(ccx: @crate_ctxt,
|
|||||||
alt ty_opt {
|
alt ty_opt {
|
||||||
none {/* nothing to do */ }
|
none {/* nothing to do */ }
|
||||||
some(typ) {
|
some(typ) {
|
||||||
ty::unify::unify(ty::mk_var(tcx, var_id), typ,
|
infer::mk_eqty(infcx, ty::mk_var(tcx, var_id), typ);
|
||||||
ty::unify::in_bindings(vb), tcx);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1533,6 +1554,8 @@ fn gather_locals(ccx: @crate_ctxt,
|
|||||||
let mut i = 0u;
|
let mut i = 0u;
|
||||||
for arg: ty::arg in args {
|
for arg: ty::arg in args {
|
||||||
assign(decl.inputs[i].id, some(arg.ty));
|
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;
|
i += 1u;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1548,15 +1571,19 @@ fn gather_locals(ccx: @crate_ctxt,
|
|||||||
}
|
}
|
||||||
|
|
||||||
assign(local.node.id, local_ty_opt);
|
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);
|
visit::visit_local(local, e, v);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add pattern bindings.
|
// Add pattern bindings.
|
||||||
let visit_pat = fn@(p: @ast::pat, &&e: (), v: visit::vt<()>) {
|
let visit_pat = fn@(p: @ast::pat, &&e: (), v: visit::vt<()>) {
|
||||||
alt p.node {
|
alt p.node {
|
||||||
ast::pat_ident(_, _)
|
ast::pat_ident(path, _)
|
||||||
if !pat_util::pat_is_variant(ccx.tcx.def_map, p) {
|
if !pat_util::pat_is_variant(ccx.tcx.def_map, p) {
|
||||||
assign(p.id, none);
|
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()};
|
with *visit::default_visitor()};
|
||||||
|
|
||||||
visit::visit_block(body, (), visit::mk_vt(visit));
|
visit::visit_block(body, (), visit::mk_vt(visit));
|
||||||
ret {var_bindings: vb,
|
ret {infcx: infcx,
|
||||||
locals: locals,
|
locals: locals,
|
||||||
next_var_id: nvi};
|
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,
|
element_ty: ty::t, body: ast::blk,
|
||||||
node_id: ast::node_id) -> bool {
|
node_id: ast::node_id) -> bool {
|
||||||
let locid = lookup_local(fcx, local.span, local.node.id);
|
let locid = lookup_local(fcx, local.span, local.node.id);
|
||||||
let element_ty = demand::simple(fcx, local.span, element_ty,
|
demand::simple(fcx, local.span,
|
||||||
ty::mk_var(fcx.ccx.tcx, locid));
|
ty::mk_var(fcx.ccx.tcx, locid),
|
||||||
|
element_ty);
|
||||||
let bot = check_decl_local(fcx, local);
|
let bot = check_decl_local(fcx, local);
|
||||||
check_block_no_value(fcx, body);
|
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);
|
write_nil(fcx.ccx.tcx, node_id);
|
||||||
ret bot;
|
ret bot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// A generic function for checking the then and else in an if
|
// A generic function for checking the then and else in an if
|
||||||
// or if-check
|
// or if-check
|
||||||
fn check_then_else(fcx: @fn_ctxt, thn: ast::blk,
|
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,
|
fn lookup_op_method(fcx: @fn_ctxt, op_ex: @ast::expr, self_t: ty::t,
|
||||||
opname: str, args: [option<@ast::expr>])
|
opname: str, args: [option<@ast::expr>])
|
||||||
-> option<ty::t> {
|
-> option<(ty::t, bool)> {
|
||||||
let callee_id = ast_util::op_expr_callee_id(op_ex);
|
let callee_id = ast_util::op_expr_callee_id(op_ex);
|
||||||
alt lookup_method(fcx, op_ex, callee_id, opname, self_t, []) {
|
alt lookup_method(fcx, op_ex, callee_id, opname, self_t, []) {
|
||||||
some(origin) {
|
some(origin) {
|
||||||
let method_ty = ty::node_id_to_type(fcx.ccx.tcx, callee_id);
|
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);
|
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 }
|
_ { none }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn check_binop(fcx: @fn_ctxt, ex: @ast::expr, ty: ty::t,
|
// could be either a expr_binop or an expr_assign_binop
|
||||||
op: ast::binop, rhs: @ast::expr) -> ty::t {
|
fn check_binop(fcx: @fn_ctxt, expr: @ast::expr,
|
||||||
let resolved_t = structurally_resolved_type(fcx, ex.span, ty);
|
op: ast::binop,
|
||||||
|
lhs: @ast::expr,
|
||||||
|
rhs: @ast::expr) -> bool {
|
||||||
let tcx = fcx.ccx.tcx;
|
let tcx = fcx.ccx.tcx;
|
||||||
if ty::is_binopable(tcx, resolved_t, op) {
|
let lhs_bot = check_expr(fcx, lhs);
|
||||||
ret alt op {
|
let lhs_t = expr_ty(tcx, lhs);
|
||||||
ast::eq | ast::lt | ast::le | ast::ne | ast::ge |
|
let lhs_t = structurally_resolved_type(fcx, lhs.span, lhs_t);
|
||||||
ast::gt { ty::mk_bool(tcx) }
|
ret alt (op, ty::get(lhs_t).struct) {
|
||||||
_ { resolved_t }
|
(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) {
|
alt binop_method(op) {
|
||||||
some(name) {
|
some(name) {
|
||||||
alt lookup_op_method(fcx, ex, resolved_t, name, [some(rhs)]) {
|
alt lookup_op_method(fcx, ex, lhs_resolved_t, name, [some(rhs)]) {
|
||||||
some(ret_ty) { ret ret_ty; }
|
some(pair) { ret pair; }
|
||||||
_ {}
|
_ {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ {}
|
_ {}
|
||||||
}
|
}
|
||||||
|
check_expr(fcx, rhs);
|
||||||
tcx.sess.span_err(
|
tcx.sess.span_err(
|
||||||
ex.span, "binary operation " + ast_util::binop_to_str(op) +
|
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,
|
fn check_user_unop(fcx: @fn_ctxt, op_str: str, mname: str,
|
||||||
ex: @ast::expr, rhs_t: ty::t) -> ty::t {
|
ex: @ast::expr, rhs_t: ty::t) -> ty::t {
|
||||||
alt lookup_op_method(fcx, ex, rhs_t, mname, []) {
|
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(
|
fcx.ccx.tcx.sess.span_err(
|
||||||
ex.span, #fmt["cannot apply unary operator `%s` to type `%s`",
|
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);
|
let typ = check_lit(fcx.ccx, lit);
|
||||||
write_ty(tcx, id, typ);
|
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) {
|
// Something of a hack: special rules for comparison operators that
|
||||||
check_expr_with(fcx, rhs, lhs_t)
|
// simply unify LHS and RHS. This helps with inference as LHS and RHS
|
||||||
} else {
|
// do not need to be "resolvable". Some tests, particularly those with
|
||||||
let rhs_bot = check_expr(fcx, rhs);
|
// complicated iface requirements, fail without this---I think this code
|
||||||
let rhs_t = expr_ty(tcx, rhs);
|
// can be removed if we improve iface resolution to be more eager when
|
||||||
require_integral(fcx, rhs.span, rhs_t);
|
// possible.
|
||||||
rhs_bot
|
ast::expr_binary(ast::eq, lhs, rhs) |
|
||||||
};
|
ast::expr_binary(ast::ne, lhs, rhs) |
|
||||||
|
ast::expr_binary(ast::lt, lhs, rhs) |
|
||||||
if !ast_util::lazy_binop(binop) { bot |= rhs_bot; }
|
ast::expr_binary(ast::le, lhs, rhs) |
|
||||||
|
ast::expr_binary(ast::gt, lhs, rhs) |
|
||||||
let result = check_binop(fcx, expr, lhs_t, binop, rhs);
|
ast::expr_binary(ast::ge, lhs, rhs) {
|
||||||
write_ty(tcx, id, result);
|
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) {
|
ast::expr_assign_op(op, lhs, rhs) {
|
||||||
require_impure(tcx.sess, fcx.purity, expr.span);
|
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 lhs_t = ty::expr_ty(tcx, lhs);
|
||||||
let result = check_binop(fcx, expr, lhs_t, op, rhs);
|
let result_t = ty::expr_ty(tcx, expr);
|
||||||
demand::simple(fcx, expr.span, result, lhs_t);
|
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) {
|
ast::expr_unary(unop, oper) {
|
||||||
bot = check_expr(fcx, 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);
|
raw_base_t);
|
||||||
alt lookup_op_method(fcx, expr, resolved, "[]",
|
alt lookup_op_method(fcx, expr, resolved, "[]",
|
||||||
[some(idx)]) {
|
[some(idx)]) {
|
||||||
some(ret_ty) { write_ty(tcx, id, ret_ty); }
|
some((ret_ty, _)) { write_ty(tcx, id, ret_ty); }
|
||||||
_ {
|
_ {
|
||||||
tcx.sess.span_fatal(
|
tcx.sess.span_fatal(
|
||||||
expr.span, "cannot index a value of type `" +
|
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,
|
@{ret_ty: rty,
|
||||||
purity: ast::pure_fn,
|
purity: ast::pure_fn,
|
||||||
proto: ast::proto_box,
|
proto: ast::proto_box,
|
||||||
var_bindings: ty::unify::mk_var_bindings(),
|
infcx: infer::new_infer_ctxt(ccx.tcx),
|
||||||
locals: int_hash::<int>(),
|
locals: int_hash::<int>(),
|
||||||
next_var_id: @mutable 0,
|
next_var_id: @mutable 0,
|
||||||
ccx: ccx};
|
ccx: ccx};
|
||||||
@@ -3260,7 +3351,7 @@ fn check_enum_variants(ccx: @crate_ctxt, sp: span, vs: [ast::variant],
|
|||||||
@{ret_ty: rty,
|
@{ret_ty: rty,
|
||||||
purity: ast::pure_fn,
|
purity: ast::pure_fn,
|
||||||
proto: ast::proto_box,
|
proto: ast::proto_box,
|
||||||
var_bindings: ty::unify::mk_var_bindings(),
|
infcx: infer::new_infer_ctxt(ccx.tcx),
|
||||||
locals: int_hash::<int>(),
|
locals: int_hash::<int>(),
|
||||||
next_var_id: @mutable 0,
|
next_var_id: @mutable 0,
|
||||||
ccx: ccx};
|
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)),
|
@{ret_ty: ty::ty_fn_ret(ty::node_id_to_type(ccx.tcx, id)),
|
||||||
purity: purity,
|
purity: purity,
|
||||||
proto: proto,
|
proto: proto,
|
||||||
var_bindings: gather_result.var_bindings,
|
infcx: gather_result.infcx,
|
||||||
locals: gather_result.locals,
|
locals: gather_result.locals,
|
||||||
next_var_id: gather_result.next_var_id,
|
next_var_id: gather_result.next_var_id,
|
||||||
ccx: ccx};
|
ccx: ccx};
|
||||||
@@ -3723,8 +3814,12 @@ mod vtable {
|
|||||||
|
|
||||||
fn fixup_ty(fcx: @fn_ctxt, sp: span, ty: ty::t) -> ty::t {
|
fn fixup_ty(fcx: @fn_ctxt, sp: span, ty: ty::t) -> ty::t {
|
||||||
let tcx = fcx.ccx.tcx;
|
let tcx = fcx.ccx.tcx;
|
||||||
alt ty::unify::fixup_vars(tcx, some(sp), fcx.var_bindings, ty) {
|
alt infer::fixup_vars(fcx.infcx, ty) {
|
||||||
result::ok(new_type) { new_type }
|
result::ok(new_type) { new_type }
|
||||||
|
result::err(-1) {
|
||||||
|
tcx.sess.span_fatal(sp, "bounded type parameter with \
|
||||||
|
cyclic type");
|
||||||
|
}
|
||||||
result::err(vid) {
|
result::err(vid) {
|
||||||
tcx.sess.span_fatal(sp, "could not determine a type for a \
|
tcx.sess.span_fatal(sp, "could not determine a type for a \
|
||||||
bounded type parameter");
|
bounded type parameter");
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ mod middle {
|
|||||||
mod reachable;
|
mod reachable;
|
||||||
}
|
}
|
||||||
mod ty;
|
mod ty;
|
||||||
|
mod infer;
|
||||||
mod ast_map;
|
mod ast_map;
|
||||||
mod resolve;
|
mod resolve;
|
||||||
mod typeck;
|
mod typeck;
|
||||||
|
|||||||
@@ -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 ty_to_str(cx: ctxt, typ: t) -> str {
|
||||||
fn fn_input_to_str(cx: ctxt, input: {mode: ast::mode, ty: t}) ->
|
fn fn_input_to_str(cx: ctxt, input: {mode: ast::mode, ty: t}) ->
|
||||||
str {
|
str {
|
||||||
@@ -72,14 +81,6 @@ fn ty_to_str(cx: ctxt, typ: t) -> str {
|
|||||||
fn field_to_str(cx: ctxt, f: field) -> str {
|
fn field_to_str(cx: ctxt, f: field) -> str {
|
||||||
ret f.ident + ": " + mt_to_str(cx, f.mt);
|
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 {
|
fn parameterized(cx: ctxt, base: str, tps: [ty::t]) -> str {
|
||||||
if vec::len(tps) > 0u {
|
if vec::len(tps) > 0u {
|
||||||
let strs = vec::map(tps, {|t| ty_to_str(cx, t)});
|
let strs = vec::map(tps, {|t| ty_to_str(cx, t)});
|
||||||
|
|||||||
@@ -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 assoc_list = astsrv::exec(srv) {|ctxt|
|
||||||
|
|
||||||
let def_map = from_def_assoc_list(def_assoc_list);
|
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|
|
ctxt.exp_map.items {|exp_id, defs|
|
||||||
let path = alt check ctxt.ast_map.get(exp_id) {
|
let path = alt check ctxt.ast_map.get(exp_id) {
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ fn sectionalize(desc: option<str>) -> (option<str>, [doc::section]) {
|
|||||||
|
|
||||||
let lines = str::lines(option::get(desc));
|
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 current_section = none;
|
||||||
let mut sections = [];
|
let mut sections = [];
|
||||||
|
|
||||||
|
|||||||
3
src/test/auxiliary/noexporttypelib.rs
Normal file
3
src/test/auxiliary/noexporttypelib.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export foo;
|
||||||
|
type oint = option<int>;
|
||||||
|
fn foo() -> oint { some(3) }
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
// error-pattern:mismatched types
|
|
||||||
// issue #500
|
// 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`
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,5 +7,5 @@ fn apply_int(f: fn(int) -> int, a: int) -> int { f(a) }
|
|||||||
fn main() {
|
fn main() {
|
||||||
let f = {|i| i};
|
let f = {|i| i};
|
||||||
assert apply_int(f, 2) == 2;
|
assert apply_int(f, 2) == 2;
|
||||||
assert apply(f, 2) == 2; //! ERROR expected argument mode ++
|
assert apply(f, 2) == 2; //! ERROR expected argument mode &&
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
// error-pattern: mismatched types
|
|
||||||
|
|
||||||
fn main() {
|
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]]) {
|
fn f(&&v: [mutable [const int]]) {
|
||||||
v[0] = [mutable 3]
|
v[0] = [mutable 3]
|
||||||
}
|
}
|
||||||
|
|
||||||
f(v);
|
f(v); //! ERROR (values differ in mutability)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
// error-pattern: mismatched types
|
|
||||||
|
|
||||||
fn main() {
|
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]]) {
|
fn f(&&v: [mutable [const int]]) {
|
||||||
v[0] = [3]
|
v[0] = [3]
|
||||||
}
|
}
|
||||||
|
|
||||||
f(v);
|
f(v); //! ERROR (values differ in mutability)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
// error-pattern: mismatched types
|
|
||||||
|
|
||||||
fn main() {
|
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]]]) {
|
fn f(&&v: [mutable [mutable [const int]]]) {
|
||||||
v[0][1] = [mutable 3]
|
v[0][1] = [mutable 3]
|
||||||
}
|
}
|
||||||
|
|
||||||
f(v);
|
f(v); //! ERROR (values differ in mutability)
|
||||||
}
|
}
|
||||||
|
|||||||
13
src/test/compile-fail/noexporttypeexe.rs
Normal file
13
src/test/compile-fail/noexporttypeexe.rs
Normal 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>`
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,2 +1,4 @@
|
|||||||
// error-pattern: can not instantiate infinite type
|
fn main() {
|
||||||
fn main() { let f; f = @f; }
|
let f; //! ERROR this local variable has a type of infinite size
|
||||||
|
f = @f;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
// error-pattern:expected `bool` but found `int`
|
|
||||||
// issue #516
|
// 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`
|
||||||
|
}
|
||||||
|
|||||||
13
src/test/compile-fail/vec-concat-bug.rs
Normal file
13
src/test/compile-fail/vec-concat-bug.rs
Normal 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() {}
|
||||||
Reference in New Issue
Block a user