new region inference, seperate infer into modules, improve error msgs

Fixes #2806
Fixes #3197
Fixes #3138
This commit is contained in:
Niko Matsakis
2012-08-13 15:06:13 -07:00
parent 3b09c3deaa
commit 8ee79c79aa
50 changed files with 3866 additions and 2563 deletions

View File

@@ -90,7 +90,7 @@ priv impl<A> DVec<A> {
} }
#[inline(always)] #[inline(always)]
fn borrow<B>(f: fn(-~[mut A]) -> B) -> B { fn check_out<B>(f: fn(-~[mut A]) -> B) -> B {
unsafe { unsafe {
let mut data = unsafe::reinterpret_cast(null::<()>()); let mut data = unsafe::reinterpret_cast(null::<()>());
data <-> self.data; data <-> self.data;
@@ -124,13 +124,13 @@ impl<A> DVec<A> {
*/ */
#[inline(always)] #[inline(always)]
fn swap(f: fn(-~[mut A]) -> ~[mut A]) { fn swap(f: fn(-~[mut A]) -> ~[mut A]) {
self.borrow(|v| self.give_back(f(v))) self.check_out(|v| self.give_back(f(v)))
} }
/// Returns the number of elements currently in the dvec /// Returns the number of elements currently in the dvec
pure fn len() -> uint { pure fn len() -> uint {
unchecked { unchecked {
do self.borrow |v| { do self.check_out |v| {
let l = v.len(); let l = v.len();
self.give_back(v); self.give_back(v);
l l
@@ -146,7 +146,7 @@ impl<A> DVec<A> {
/// Remove and return the last element /// Remove and return the last element
fn pop() -> A { fn pop() -> A {
do self.borrow |v| { do self.check_out |v| {
let mut v <- v; let mut v <- v;
let result = vec::pop(v); let result = vec::pop(v);
self.give_back(v); self.give_back(v);
@@ -176,7 +176,7 @@ impl<A> DVec<A> {
/// Remove and return the first element /// Remove and return the first element
fn shift() -> A { fn shift() -> A {
do self.borrow |v| { do self.check_out |v| {
let mut v = vec::from_mut(v); let mut v = vec::from_mut(v);
let result = vec::shift(v); let result = vec::shift(v);
self.give_back(vec::to_mut(v)); self.give_back(vec::to_mut(v));
@@ -184,10 +184,29 @@ impl<A> DVec<A> {
} }
} }
// Reverse the elements in the list, in place /// Reverse the elements in the list, in place
fn reverse() { fn reverse() {
do self.borrow |v| { do self.check_out |v| {
vec::reverse(v); vec::reverse(v);
self.give_back(v);
}
}
/// Gives access to the vector as a slice with immutable contents
fn borrow<R>(op: fn(x: &[A]) -> R) -> R {
do self.check_out |v| {
let result = op(v);
self.give_back(v);
result
}
}
/// Gives access to the vector as a slice with mutable contents
fn borrow_mut<R>(op: fn(x: &[mut A]) -> R) -> R {
do self.check_out |v| {
let result = op(v);
self.give_back(v);
result
} }
} }
} }
@@ -249,7 +268,7 @@ impl<A: copy> DVec<A> {
*/ */
pure fn get() -> ~[A] { pure fn get() -> ~[A] {
unchecked { unchecked {
do self.borrow |v| { do self.check_out |v| {
let w = vec::from_mut(copy v); let w = vec::from_mut(copy v);
self.give_back(v); self.give_back(v);
w w

View File

@@ -1187,7 +1187,9 @@ fn spawn_raw(+opts: TaskOpts, +f: fn~()) {
}; };
if result { if result {
// Unwinding function in case any ancestral enlisting fails // Unwinding function in case any ancestral enlisting fails
let bail = |tg| { leave_taskgroup(tg, child, false) }; let bail = |tg: TaskGroupInner| {
leave_taskgroup(tg, child, false)
};
// Attempt to join every ancestor group. // Attempt to join every ancestor group.
result = result =
for each_ancestor(ancestors, some(bail)) |ancestor_tg| { for each_ancestor(ancestors, some(bail)) |ancestor_tg| {

View File

@@ -41,10 +41,11 @@ impl<T> Cell<T> {
} }
// Calls a closure with a reference to the value. // Calls a closure with a reference to the value.
fn with_ref(f: fn(v: &T)) { fn with_ref<R>(op: fn(v: &T) -> R) -> R {
let val = move self.take(); let v = self.take();
f(&val); let r = op(&v);
self.put_back(move val); self.put_back(v);
return move r;
} }
} }

View File

@@ -16,7 +16,7 @@ import middle::lint;
import middle::lint::{get_lint_level, allow}; import middle::lint::{get_lint_level, allow};
import syntax::ast::*; import syntax::ast::*;
import syntax::print::pprust::*; import syntax::print::pprust::*;
import util::ppaux::{ty_to_str, tys_to_str}; import util::ppaux::{ty_to_str, proto_ty_to_str, tys_to_str};
export tv_vid, tvi_vid, region_vid, vid; export tv_vid, tvi_vid, region_vid, vid;
export br_hashmap; export br_hashmap;
@@ -119,6 +119,7 @@ export proto_kind, kind_lteq, type_kind;
export operators; export operators;
export type_err, terr_vstore_kind; export type_err, terr_vstore_kind;
export type_err_to_str; export type_err_to_str;
export expected_found;
export type_needs_drop; export type_needs_drop;
export type_is_empty; export type_is_empty;
export type_is_integral; export type_is_integral;
@@ -179,6 +180,7 @@ export fn_proto, proto_bare, proto_vstore;
export ast_proto_to_proto; export ast_proto_to_proto;
export is_blockish; export is_blockish;
export method_call_bounds; export method_call_bounds;
export hash_region;
// Data types // Data types
@@ -368,7 +370,7 @@ enum region {
re_static, re_static,
/// A region variable. Should not exist after typeck. /// A region variable. Should not exist after typeck.
re_var(region_vid), re_var(region_vid)
} }
enum bound_region { enum bound_region {
@@ -455,30 +457,35 @@ enum terr_vstore_kind {
terr_vec, terr_str, terr_fn, terr_trait terr_vec, terr_str, terr_fn, terr_trait
} }
struct expected_found<T> {
expected: T;
found: T;
}
// Data structures used in type unification // Data structures used in type unification
enum type_err { enum type_err {
terr_mismatch, terr_mismatch,
terr_ret_style_mismatch(ast::ret_style, ast::ret_style), terr_ret_style_mismatch(expected_found<ast::ret_style>),
terr_purity_mismatch(purity, purity), terr_purity_mismatch(expected_found<purity>),
terr_mutability, terr_mutability,
terr_proto_mismatch(ty::fn_proto, ty::fn_proto), terr_proto_mismatch(expected_found<ty::fn_proto>),
terr_box_mutability, terr_box_mutability,
terr_ptr_mutability, terr_ptr_mutability,
terr_ref_mutability, terr_ref_mutability,
terr_vec_mutability, terr_vec_mutability,
terr_tuple_size(uint, uint), terr_tuple_size(expected_found<uint>),
terr_ty_param_size(uint, uint), terr_ty_param_size(expected_found<uint>),
terr_record_size(uint, uint), terr_record_size(expected_found<uint>),
terr_record_mutability, terr_record_mutability,
terr_record_fields(ast::ident, ast::ident), terr_record_fields(expected_found<ident>),
terr_arg_count, terr_arg_count,
terr_mode_mismatch(mode, mode), terr_mode_mismatch(expected_found<mode>),
terr_regions_does_not_outlive(region, region), terr_regions_does_not_outlive(region, region),
terr_regions_not_same(region, region), terr_regions_not_same(region, region),
terr_regions_no_overlap(region, region), terr_regions_no_overlap(region, region),
terr_vstores_differ(terr_vstore_kind, vstore, vstore), terr_vstores_differ(terr_vstore_kind, expected_found<vstore>),
terr_in_field(@type_err, ast::ident), terr_in_field(@type_err, ast::ident),
terr_sorts(t, t), terr_sorts(expected_found<t>),
terr_self_substs, terr_self_substs,
terr_no_integral_type, terr_no_integral_type,
} }
@@ -512,7 +519,7 @@ impl tvi_vid: vid {
impl region_vid: vid { impl region_vid: vid {
pure fn to_uint() -> uint { *self } pure fn to_uint() -> uint { *self }
pure fn to_str() -> ~str { fmt!{"<R%u>", self.to_uint()} } pure fn to_str() -> ~str { fmt!{"%?", self} }
} }
trait purity_to_str { trait purity_to_str {
@@ -2195,6 +2202,17 @@ fn br_hashmap<V:copy>() -> hashmap<bound_region, V> {
map::hashmap(hash_bound_region, sys::shape_eq) map::hashmap(hash_bound_region, sys::shape_eq)
} }
pure fn hash_region(r: &region) -> uint {
match *r { // no idea if this is any good
re_bound(br) => (hash_bound_region(&br)) << 2u | 0u,
re_free(id, br) => ((id as uint) << 4u) |
(hash_bound_region(&br)) << 2u | 1u,
re_scope(id) => ((id as uint) << 2u) | 2u,
re_var(id) => (id.to_uint() << 2u) | 3u,
re_bot => 4u
}
}
// Type hashing. // Type hashing.
pure fn hash_type_structure(st: &sty) -> uint { pure fn hash_type_structure(st: &sty) -> uint {
pure fn hash_uint(id: uint, n: uint) -> uint { (id << 2u) + n } pure fn hash_uint(id: uint, n: uint) -> uint { (id << 2u) + n }
@@ -2210,16 +2228,6 @@ pure fn hash_type_structure(st: &sty) -> uint {
for vec::each(subtys) |s| { h = (h << 2u) + type_id(s) } for vec::each(subtys) |s| { h = (h << 2u) + type_id(s) }
h h
} }
pure fn hash_region(r: &region) -> uint {
match *r { // no idea if this is any good
re_bound(br) => (hash_bound_region(&br)) << 2u | 0u,
re_free(id, br) => ((id as uint) << 4u) |
(hash_bound_region(&br)) << 2u | 1u,
re_scope(id) => ((id as uint) << 2u) | 2u,
re_var(id) => (id.to_uint() << 2u) | 3u,
re_bot => 4u
}
}
pure fn hash_substs(h: uint, substs: &substs) -> uint { pure fn hash_substs(h: uint, substs: &substs) -> uint {
let h = hash_subtys(h, substs.tps); let h = hash_subtys(h, substs.tps);
h + substs.self_r.map_default(0u, |r| hash_region(&r)) h + substs.self_r.map_default(0u, |r| hash_region(&r))
@@ -2569,8 +2577,11 @@ fn resolved_mode(cx: ctxt, m: ast::mode) -> ast::rmode {
fn arg_mode(cx: ctxt, a: arg) -> ast::rmode { resolved_mode(cx, a.mode) } fn arg_mode(cx: ctxt, a: arg) -> ast::rmode { resolved_mode(cx, a.mode) }
// Unifies `m1` and `m2`. Returns unified value or failure code. // Unifies `m1` and `m2`. Returns unified value or failure code.
fn unify_mode(cx: ctxt, m1: ast::mode, m2: ast::mode) fn unify_mode(cx: ctxt, modes: expected_found<ast::mode>)
-> result<ast::mode, type_err> { -> result<ast::mode, type_err> {
let m1 = modes.expected;
let m2 = modes.found;
match (canon_mode(cx, m1), canon_mode(cx, m2)) { match (canon_mode(cx, m1), canon_mode(cx, m2)) {
(m1, m2) if (m1 == m2) => { (m1, m2) if (m1 == m2) => {
result::ok(m1) result::ok(m1)
@@ -2584,7 +2595,7 @@ fn unify_mode(cx: ctxt, m1: ast::mode, m2: ast::mode)
result::ok(m1) result::ok(m1)
} }
(m1, m2) => { (m1, m2) => {
result::err(terr_mode_mismatch(m1, m2)) result::err(terr_mode_mismatch(modes))
} }
} }
} }
@@ -2638,91 +2649,96 @@ fn type_err_to_str(cx: ctxt, err: &type_err) -> ~str {
} }
match *err { match *err {
terr_mismatch => return ~"types differ", terr_mismatch => ~"types differ",
terr_ret_style_mismatch(expect, actual) => { terr_ret_style_mismatch(values) => {
fn to_str(s: ast::ret_style) -> ~str { fn to_str(s: ast::ret_style) -> ~str {
match s { match s {
ast::noreturn => ~"non-returning", ast::noreturn => ~"non-returning",
ast::return_val => ~"return-by-value" ast::return_val => ~"return-by-value"
} }
} }
return to_str(actual) + ~" function found where " + to_str(expect) + fmt!("expected %s function, found %s function",
~" function was expected"; to_str(values.expected),
to_str(values.expected))
} }
terr_purity_mismatch(f1, f2) => { terr_purity_mismatch(values) => {
return fmt!{"expected %s fn but found %s fn", fmt!{"expected %s fn but found %s fn",
purity_to_str(f1), purity_to_str(f2)}; purity_to_str(values.expected),
purity_to_str(values.found)}
} }
terr_proto_mismatch(e, a) => { terr_proto_mismatch(values) => {
return fmt!{"closure protocol mismatch (%s vs %s)", fmt!{"expected %s closure, found %s closure",
util::ppaux::proto_ty_to_str(cx, e), proto_ty_to_str(cx, values.expected),
util::ppaux::proto_ty_to_str(cx, a)}; proto_ty_to_str(cx, values.found)}
} }
terr_mutability => return ~"values differ in mutability", terr_mutability => ~"values differ in mutability",
terr_box_mutability => return ~"boxed values differ in mutability", terr_box_mutability => ~"boxed values differ in mutability",
terr_vec_mutability => return ~"vectors differ in mutability", terr_vec_mutability => ~"vectors differ in mutability",
terr_ptr_mutability => return ~"pointers differ in mutability", terr_ptr_mutability => ~"pointers differ in mutability",
terr_ref_mutability => return ~"references differ in mutability", terr_ref_mutability => ~"references differ in mutability",
terr_ty_param_size(e_sz, a_sz) => { terr_ty_param_size(values) => {
return ~"expected a type with " + uint::to_str(e_sz, 10u) + fmt!("expected a type with %? type params \
~" type params but found one with " + uint::to_str(a_sz, 10u) + but found one with %? type params",
~" type params"; values.expected, values.found)
} }
terr_tuple_size(e_sz, a_sz) => { terr_tuple_size(values) => {
return ~"expected a tuple with " + uint::to_str(e_sz, 10u) + fmt!("expected a tuple with %? elements \
~" elements but found one with " + uint::to_str(a_sz, 10u) + but found one with %? elements",
~" elements"; values.expected, values.found)
} }
terr_record_size(e_sz, a_sz) => { terr_record_size(values) => {
return ~"expected a record with " + uint::to_str(e_sz, 10u) + fmt!("expected a record with %? fields \
~" fields but found one with " + uint::to_str(a_sz, 10u) + but found one with %? fields",
~" fields"; values.expected, values.found)
} }
terr_record_mutability => { terr_record_mutability => {
return ~"record elements differ in mutability"; ~"record elements differ in mutability"
} }
terr_record_fields(e_fld, a_fld) => { terr_record_fields(values) => {
return ~"expected a record with field `" + *e_fld + fmt!("expected a record with field `%s` \
~"` but found one with field `" + *a_fld + ~"`"; but found one with field `%s`",
*values.expected, *values.found)
} }
terr_arg_count => return ~"incorrect number of function parameters", terr_arg_count => ~"incorrect number of function parameters",
terr_mode_mismatch(e_mode, a_mode) => { terr_mode_mismatch(values) => {
return ~"expected argument mode " + mode_to_str(e_mode) + fmt!("expected argument mode %s, but found %s",
~" but found " + mode_to_str(a_mode); mode_to_str(values.expected), mode_to_str(values.found))
} }
terr_regions_does_not_outlive(subregion, superregion) => { terr_regions_does_not_outlive(subregion, superregion) => {
return fmt!{"%s does not necessarily outlive %s", fmt!{"%s does not necessarily outlive %s",
explain_region(cx, subregion), explain_region(cx, superregion),
explain_region(cx, superregion)}; explain_region(cx, subregion)}
} }
terr_regions_not_same(region1, region2) => { terr_regions_not_same(region1, region2) => {
return fmt!{"%s is not the same as %s", fmt!{"%s is not the same as %s",
explain_region(cx, region1), explain_region(cx, region1),
explain_region(cx, region2)}; explain_region(cx, region2)}
} }
terr_regions_no_overlap(region1, region2) => { terr_regions_no_overlap(region1, region2) => {
return fmt!{"%s does not intersect %s", fmt!("%s does not intersect %s",
explain_region(cx, region1), explain_region(cx, region1),
explain_region(cx, region2)}; explain_region(cx, region2))
} }
terr_vstores_differ(k, e_vs, a_vs) => { terr_vstores_differ(k, values) => {
return fmt!{"%s storage differs: expected %s but found %s", fmt!("%s storage differs: expected %s but found %s",
terr_vstore_kind_to_str(k), terr_vstore_kind_to_str(k),
vstore_to_str(cx, e_vs), vstore_to_str(cx, values.expected),
vstore_to_str(cx, a_vs)}; vstore_to_str(cx, values.found))
} }
terr_in_field(err, fname) => { terr_in_field(err, fname) => {
return fmt!{"in field `%s`, %s", *fname, type_err_to_str(cx, err)}; fmt!{"in field `%s`, %s", *fname, type_err_to_str(cx, err)}
} }
terr_sorts(exp, act) => { terr_sorts(values) => {
return fmt!{"%s vs %s", ty_sort_str(cx, exp), ty_sort_str(cx, act)}; fmt!{"expected %s but found %s",
ty_sort_str(cx, values.expected),
ty_sort_str(cx, values.found)}
} }
terr_self_substs => { terr_self_substs => {
return ~"inconsistent self substitution"; // XXX this is more of a bug ~"inconsistent self substitution" // XXX this is more of a bug
} }
terr_no_integral_type => { terr_no_integral_type => {
return ~"couldn't determine an appropriate integral type for integer \ ~"couldn't determine an appropriate integral type for integer \
literal"; literal"
} }
} }
} }

View File

@@ -206,6 +206,7 @@ fn no_params(t: ty::t) -> ty::ty_param_bounds_and_ty {
fn require_same_types( fn require_same_types(
tcx: ty::ctxt, tcx: ty::ctxt,
maybe_infcx: option<infer::infer_ctxt>, maybe_infcx: option<infer::infer_ctxt>,
t1_is_expected: bool,
span: span, span: span,
t1: ty::t, t1: ty::t,
t2: ty::t, t2: ty::t,
@@ -223,7 +224,7 @@ fn require_same_types(
} }
} }
match infer::mk_eqty(l_infcx, t1, t2) { match infer::mk_eqty(l_infcx, t1_is_expected, span, t1, t2) {
result::ok(()) => true, result::ok(()) => true,
result::err(ref terr) => { result::err(ref terr) => {
l_tcx.sess.span_err(span, msg() + ~": " + l_tcx.sess.span_err(span, msg() + ~": " +

View File

@@ -72,8 +72,8 @@ fn ast_region_to_region<AC: ast_conv, RS: region_scope copy owned>(
self: AC, rscope: RS, span: span, a_r: @ast::region) -> ty::region { self: AC, rscope: RS, span: span, a_r: @ast::region) -> ty::region {
let res = match a_r.node { let res = match a_r.node {
ast::re_anon => rscope.anon_region(), ast::re_anon => rscope.anon_region(span),
ast::re_named(id) => rscope.named_region(id) ast::re_named(id) => rscope.named_region(span, id)
}; };
get_region_reporting_err(self.tcx(), span, res) get_region_reporting_err(self.tcx(), span, res)
@@ -106,7 +106,7 @@ fn ast_path_to_substs_and_ty<AC: ast_conv, RS: region_scope copy owned>(
none none
} }
(true, none) => { (true, none) => {
let res = rscope.anon_region(); let res = rscope.anon_region(path.span);
let r = get_region_reporting_err(self.tcx(), path.span, res); let r = get_region_reporting_err(self.tcx(), path.span, res);
some(r) some(r)
} }
@@ -409,8 +409,10 @@ fn ty_of_arg<AC: ast_conv, RS: region_scope copy owned>(
let mode = { let mode = {
match a.mode { match a.mode {
ast::infer(_) if expected_ty.is_some() => { ast::infer(_) if expected_ty.is_some() => {
result::get(ty::unify_mode(self.tcx(), a.mode, result::get(ty::unify_mode(
expected_ty.get().mode)) self.tcx(),
ty::expected_found {expected: expected_ty.get().mode,
found: a.mode}))
} }
ast::infer(_) => { ast::infer(_) => {
match ty::get(ty).struct { match ty::get(ty).struct {
@@ -425,7 +427,10 @@ fn ty_of_arg<AC: ast_conv, RS: region_scope copy owned>(
// will have been unified with m yet: // will have been unified with m yet:
_ => { _ => {
let m1 = ast::expl(ty::default_arg_mode_for_ty(ty)); let m1 = ast::expl(ty::default_arg_mode_for_ty(ty));
result::get(ty::unify_mode(self.tcx(), a.mode, m1)) result::get(ty::unify_mode(
self.tcx(),
ty::expected_found {expected: m1,
found: a.mode}))
} }
} }
} }
@@ -446,7 +451,7 @@ fn ast_proto_to_proto<AC: ast_conv, RS: region_scope copy owned>(
ast::proto_box => ast::proto_box =>
ty::proto_vstore(ty::vstore_box), ty::proto_vstore(ty::vstore_box),
ast::proto_block => { ast::proto_block => {
let result = rscope.anon_region(); let result = rscope.anon_region(span);
let region = get_region_reporting_err(self.tcx(), span, result); let region = get_region_reporting_err(self.tcx(), span, result);
ty::proto_vstore(ty::vstore_slice(region)) ty::proto_vstore(ty::vstore_slice(region))
} }

View File

@@ -102,12 +102,12 @@ type fn_ctxt_ =
// the end of (almost) any enclosing block or expression. We // the end of (almost) any enclosing block or expression. We
// want to pick the narrowest block that encompasses all uses. // want to pick the narrowest block that encompasses all uses.
// //
// What we do in such cases is to generate a region variable and // What we do in such cases is to generate a region variable with
// assign it the following two fields as bounds. The lower bound // `region_lb` as a lower bound. The regionck pass then adds
// is always the innermost enclosing expression. The upper bound // other constriants based on how the variable is used and region
// is the outermost enclosing expression that we could legally // inference selects the ultimate value. Finally, borrowck is
// use. In practice, this is the innermost loop or function // charged with guaranteeing that the value whose address was taken
// body. // can actually be made to live as long as it needs to live.
mut region_lb: ast::node_id, mut region_lb: ast::node_id,
in_scope_regions: isr_alist, in_scope_regions: isr_alist,
@@ -292,20 +292,22 @@ fn check_fn(ccx: @crate_ctxt,
arg_tys: ~[ty::t]) { arg_tys: ~[ty::t]) {
let tcx = fcx.ccx.tcx; let tcx = fcx.ccx.tcx;
let assign = fn@(nid: ast::node_id, ty_opt: option<ty::t>) { let assign = fn@(span: span, nid: ast::node_id,
ty_opt: option<ty::t>) {
let var_id = fcx.infcx.next_ty_var_id(); let var_id = fcx.infcx.next_ty_var_id();
fcx.locals.insert(nid, var_id); fcx.locals.insert(nid, var_id);
match ty_opt { match ty_opt {
none => {/* nothing to do */ } none => {/* nothing to do */ }
some(typ) => { some(typ) => {
infer::mk_eqty(fcx.infcx, ty::mk_var(tcx, var_id), typ); infer::mk_eqty(fcx.infcx, false, span,
ty::mk_var(tcx, var_id), typ);
} }
} }
}; };
// Add formal parameters. // Add formal parameters.
do vec::iter2(arg_tys, decl.inputs) |arg_ty, input| { do vec::iter2(arg_tys, decl.inputs) |arg_ty, input| {
assign(input.id, some(arg_ty)); assign(input.ty.span, input.id, some(arg_ty));
debug!{"Argument %s is assigned to %s", debug!{"Argument %s is assigned to %s",
*input.ident, fcx.locals.get(input.id).to_str()}; *input.ident, fcx.locals.get(input.id).to_str()};
} }
@@ -317,7 +319,7 @@ fn check_fn(ccx: @crate_ctxt,
ast::ty_infer => none, ast::ty_infer => none,
_ => some(fcx.to_ty(local.node.ty)) _ => some(fcx.to_ty(local.node.ty))
}; };
assign(local.node.id, o_ty); assign(local.span, local.node.id, o_ty);
debug!{"Local variable %s is assigned to %s", debug!{"Local variable %s is assigned to %s",
pat_to_str(local.node.pat), pat_to_str(local.node.pat),
fcx.locals.get(local.node.id).to_str()}; fcx.locals.get(local.node.id).to_str()};
@@ -329,7 +331,7 @@ fn check_fn(ccx: @crate_ctxt,
match p.node { match p.node {
ast::pat_ident(_, path, _) ast::pat_ident(_, path, _)
if !pat_util::pat_is_variant(fcx.ccx.tcx.def_map, p) => { if !pat_util::pat_is_variant(fcx.ccx.tcx.def_map, p) => {
assign(p.id, none); assign(p.span, p.id, none);
debug!{"Pattern binding %s is assigned to %s", debug!{"Pattern binding %s is assigned to %s",
*path.idents[0], *path.idents[0],
fcx.locals.get(p.id).to_str()}; fcx.locals.get(p.id).to_str()};
@@ -525,14 +527,14 @@ impl @fn_ctxt: ast_conv {
} }
impl @fn_ctxt: region_scope { impl @fn_ctxt: region_scope {
fn anon_region() -> result<ty::region, ~str> { fn anon_region(span: span) -> result<ty::region, ~str> {
result::ok(self.infcx.next_region_var_nb()) result::ok(self.infcx.next_region_var_nb(span))
} }
fn named_region(id: ast::ident) -> result<ty::region, ~str> { fn named_region(span: span, id: ast::ident) -> result<ty::region, ~str> {
do empty_rscope.named_region(id).chain_err |_e| { do empty_rscope.named_region(span, id).chain_err |_e| {
match self.in_scope_regions.find(ty::br_named(id)) { match self.in_scope_regions.find(ty::br_named(id)) {
some(r) => result::ok(r), some(r) => result::ok(r),
none if *id == ~"blk" => self.block_region(), none if *id == ~"blk" => result::ok(self.block_region()),
none => { none => {
result::err(fmt!{"named region `%s` not in scope here", *id}) result::err(fmt!{"named region `%s` not in scope here", *id})
} }
@@ -543,8 +545,8 @@ impl @fn_ctxt: region_scope {
impl @fn_ctxt { impl @fn_ctxt {
fn tag() -> ~str { fmt!{"%x", ptr::addr_of(*self) as uint} } fn tag() -> ~str { fmt!{"%x", ptr::addr_of(*self) as uint} }
fn block_region() -> result<ty::region, ~str> { fn block_region() -> ty::region {
result::ok(ty::re_scope(self.region_lb)) ty::re_scope(self.region_lb)
} }
#[inline(always)] #[inline(always)]
fn write_ty(node_id: ast::node_id, ty: ty::t) { fn write_ty(node_id: ast::node_id, ty: ty::t) {
@@ -619,8 +621,9 @@ impl @fn_ctxt {
ty::type_err_to_str(self.ccx.tcx, err)}); ty::type_err_to_str(self.ccx.tcx, err)});
} }
fn mk_subty(sub: ty::t, sup: ty::t) -> result<(), ty::type_err> { fn mk_subty(a_is_expected: bool, span: span,
infer::mk_subty(self.infcx, sub, sup) sub: ty::t, sup: ty::t) -> result<(), ty::type_err> {
infer::mk_subty(self.infcx, a_is_expected, span, sub, sup)
} }
fn can_mk_subty(sub: ty::t, sup: ty::t) -> result<(), ty::type_err> { fn can_mk_subty(sub: ty::t, sup: ty::t) -> result<(), ty::type_err> {
@@ -641,12 +644,14 @@ impl @fn_ctxt {
infer::can_mk_assignty(self.infcx, anmnt, sub, sup) infer::can_mk_assignty(self.infcx, anmnt, sub, sup)
} }
fn mk_eqty(sub: ty::t, sup: ty::t) -> result<(), ty::type_err> { fn mk_eqty(a_is_expected: bool, span: span,
infer::mk_eqty(self.infcx, sub, sup) sub: ty::t, sup: ty::t) -> result<(), ty::type_err> {
infer::mk_eqty(self.infcx, a_is_expected, span, sub, sup)
} }
fn mk_subr(sub: ty::region, sup: ty::region) -> result<(), ty::type_err> { fn mk_subr(a_is_expected: bool, span: span,
infer::mk_subr(self.infcx, sub, sup) sub: ty::region, sup: ty::region) -> result<(), ty::type_err> {
infer::mk_subr(self.infcx, a_is_expected, span, sub, sup)
} }
fn require_unsafe(sp: span, op: ~str) { fn require_unsafe(sp: span, op: ~str) {
@@ -748,8 +753,10 @@ fn check_expr(fcx: @fn_ctxt, expr: @ast::expr,
// declared on the impl declaration e.g., `impl<A,B> for ~[(A,B)]` // declared on the impl declaration e.g., `impl<A,B> for ~[(A,B)]`
// would return ($0, $1) where $0 and $1 are freshly instantiated type // would return ($0, $1) where $0 and $1 are freshly instantiated type
// variables. // variables.
fn impl_self_ty(fcx: @fn_ctxt, did: ast::def_id, require_rp: bool) fn impl_self_ty(fcx: @fn_ctxt,
-> ty_param_substs_and_ty { expr: @ast::expr, // (potential) receiver for this impl
did: ast::def_id,
require_rp: bool) -> ty_param_substs_and_ty {
let tcx = fcx.ccx.tcx; let tcx = fcx.ccx.tcx;
let {n_tps, rp, raw_ty} = if did.crate == ast::local_crate { let {n_tps, rp, raw_ty} = if did.crate == ast::local_crate {
@@ -786,7 +793,8 @@ fn impl_self_ty(fcx: @fn_ctxt, did: ast::def_id, require_rp: bool)
}; };
let rp = rp || require_rp; let rp = rp || require_rp;
let self_r = if rp {some(fcx.infcx.next_region_var_nb())} else {none}; let self_r = if rp {some(fcx.infcx.next_region_var(expr.span, expr.id))}
else {none};
let tps = fcx.infcx.next_ty_vars(n_tps); let tps = fcx.infcx.next_ty_vars(n_tps);
let substs = {self_r: self_r, self_ty: none, tps: tps}; let substs = {self_r: self_r, self_ty: none, tps: tps};
@@ -840,7 +848,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
sty @ ty::ty_fn(ref fn_ty) => { sty @ ty::ty_fn(ref fn_ty) => {
replace_bound_regions_in_fn_ty( replace_bound_regions_in_fn_ty(
fcx.ccx.tcx, @nil, none, fn_ty, fcx.ccx.tcx, @nil, none, fn_ty,
|_br| fcx.infcx.next_region_var_nb()).fn_ty |_br| fcx.infcx.next_region_var(sp,
call_expr_id)).fn_ty
} }
sty => { sty => {
// I would like to make this span_err, but it's // I would like to make this span_err, but it's
@@ -1442,8 +1451,23 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
bot = check_expr(fcx, oprnd, unpack_expected(fcx, expected, |ty| bot = check_expr(fcx, oprnd, unpack_expected(fcx, expected, |ty|
match ty { ty::ty_rptr(_, mt) => some(mt.ty), _ => none } match ty { ty::ty_rptr(_, mt) => some(mt.ty), _ => none }
)); ));
//let region = region_of(fcx, oprnd);
let region = fcx.infcx.next_region_var_with_scope_lb(expr.id); // Note: at this point, we cannot say what the best lifetime
// is to use for resulting pointer. We want to use the
// shortest lifetime possible so as to avoid spurious borrowck
// errors. Moreover, the longest lifetime will depend on the
// precise details of the value whose address is being taken
// (and how long it is valid), which we don't know yet until type
// inference is complete.
//
// Therefore, here we simply generate a region variable with
// the current expression as a lower bound. The region
// inferencer will then select the ultimate value. Finally,
// borrowck is charged with guaranteeing that the value whose
// address was taken can actually be made to live as long as
// it needs to live.
let region = fcx.infcx.next_region_var(expr.span, expr.id);
let tm = { ty: fcx.expr_ty(oprnd), mutbl: mutbl }; let tm = { ty: fcx.expr_ty(oprnd), mutbl: mutbl };
let oprnd_t = ty::mk_rptr(tcx, region, tm); let oprnd_t = ty::mk_rptr(tcx, region, tm);
fcx.write_ty(id, oprnd_t); fcx.write_ty(id, oprnd_t);
@@ -1452,7 +1476,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
let defn = lookup_def(fcx, pth.span, id); let defn = lookup_def(fcx, pth.span, id);
let tpt = ty_param_bounds_and_ty_for_def(fcx, expr.span, defn); let tpt = ty_param_bounds_and_ty_for_def(fcx, expr.span, defn);
instantiate_path(fcx, pth, tpt, expr.span, expr.id); let region_lb = ty::re_scope(expr.id);
instantiate_path(fcx, pth, tpt, expr.span, expr.id, region_lb);
} }
ast::expr_mac(_) => tcx.sess.bug(~"unexpanded macro"), ast::expr_mac(_) => tcx.sess.bug(~"unexpanded macro"),
ast::expr_fail(expr_opt) => { ast::expr_fail(expr_opt) => {
@@ -1474,7 +1499,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
some(t) => t, none => fcx.ret_ty some(t) => t, none => fcx.ret_ty
}; };
match expr_opt { match expr_opt {
none => match fcx.mk_eqty(ret_ty, ty::mk_nil(tcx)) { none => match fcx.mk_eqty(false, expr.span,
ret_ty, ty::mk_nil(tcx)) {
result::ok(_) => { /* fall through */ } result::ok(_) => { /* fall through */ }
result::err(_) => { result::err(_) => {
tcx.sess.span_err( tcx.sess.span_err(
@@ -1550,7 +1576,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
let expected_sty = unpack_expected(fcx, expected, |x| some(x)); let expected_sty = unpack_expected(fcx, expected, |x| some(x));
let (inner_ty, proto) = match expected_sty { let (inner_ty, proto) = match expected_sty {
some(ty::ty_fn(fty)) => { some(ty::ty_fn(fty)) => {
match infer::mk_subty(fcx.infcx, fty.output, ty::mk_bool(tcx)) { match fcx.mk_subty(false, expr.span,
fty.output, ty::mk_bool(tcx)) {
result::ok(_) => (), result::ok(_) => (),
result::err(err) => { result::err(err) => {
tcx.sess.span_fatal( tcx.sess.span_fatal(
@@ -1809,7 +1836,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
// Generate the struct type. // Generate the struct type.
let self_region; let self_region;
if region_parameterized { if region_parameterized {
self_region = some(fcx.infcx.next_region_var_nb()); self_region = some(fcx.infcx.next_region_var(expr.span, expr.id));
} else { } else {
self_region = none; self_region = none;
} }
@@ -2307,8 +2334,9 @@ fn ty_param_bounds_and_ty_for_def(fcx: @fn_ctxt, sp: span, defn: ast::def) ->
fn instantiate_path(fcx: @fn_ctxt, fn instantiate_path(fcx: @fn_ctxt,
pth: @ast::path, pth: @ast::path,
tpt: ty_param_bounds_and_ty, tpt: ty_param_bounds_and_ty,
sp: span, span: span,
id: ast::node_id) { node_id: ast::node_id,
region_lb: ty::region) {
let ty_param_count = vec::len(*tpt.bounds); let ty_param_count = vec::len(*tpt.bounds);
let ty_substs_len = vec::len(pth.types); let ty_substs_len = vec::len(pth.types);
@@ -2317,14 +2345,14 @@ fn instantiate_path(fcx: @fn_ctxt,
let self_r = match pth.rp { let self_r = match pth.rp {
some(r) if !tpt.rp => { some(r) if !tpt.rp => {
fcx.ccx.tcx.sess.span_err fcx.ccx.tcx.sess.span_err
(sp, ~"this item is not region-parameterized"); (span, ~"this item is not region-parameterized");
none none
} }
some(r) => { some(r) => {
some(ast_region_to_region(fcx, fcx, sp, r)) some(ast_region_to_region(fcx, fcx, span, r))
} }
none if tpt.rp => { none if tpt.rp => {
some(fcx.infcx.next_region_var_nb()) some(fcx.infcx.next_region_var_with_lb(span, region_lb))
} }
none => { none => {
none none
@@ -2337,22 +2365,22 @@ fn instantiate_path(fcx: @fn_ctxt,
fcx.infcx.next_ty_vars(ty_param_count) fcx.infcx.next_ty_vars(ty_param_count)
} else if ty_param_count == 0u { } else if ty_param_count == 0u {
fcx.ccx.tcx.sess.span_err fcx.ccx.tcx.sess.span_err
(sp, ~"this item does not take type parameters"); (span, ~"this item does not take type parameters");
fcx.infcx.next_ty_vars(ty_param_count) fcx.infcx.next_ty_vars(ty_param_count)
} else if ty_substs_len > ty_param_count { } else if ty_substs_len > ty_param_count {
fcx.ccx.tcx.sess.span_err fcx.ccx.tcx.sess.span_err
(sp, ~"too many type parameters provided for this item"); (span, ~"too many type parameters provided for this item");
fcx.infcx.next_ty_vars(ty_param_count) fcx.infcx.next_ty_vars(ty_param_count)
} else if ty_substs_len < ty_param_count { } else if ty_substs_len < ty_param_count {
fcx.ccx.tcx.sess.span_err fcx.ccx.tcx.sess.span_err
(sp, ~"not enough type parameters provided for this item"); (span, ~"not enough type parameters provided for this item");
fcx.infcx.next_ty_vars(ty_param_count) fcx.infcx.next_ty_vars(ty_param_count)
} else { } else {
pth.types.map(|aty| fcx.to_ty(aty)) pth.types.map(|aty| fcx.to_ty(aty))
}; };
let substs = {self_r: self_r, self_ty: none, tps: tps}; let substs = {self_r: self_r, self_ty: none, tps: tps};
fcx.write_ty_substs(id, tpt.ty, substs); fcx.write_ty_substs(node_id, tpt.ty, substs);
} }
// Resolves `typ` by a single level if `typ` is a type variable. If no // Resolves `typ` by a single level if `typ` is a type variable. If no
@@ -2400,16 +2428,10 @@ fn ast_expr_vstore_to_vstore(fcx: @fn_ctxt, e: @ast::expr, n: uint,
} }
ast::vstore_uniq => ty::vstore_uniq, ast::vstore_uniq => ty::vstore_uniq,
ast::vstore_box => ty::vstore_box, ast::vstore_box => ty::vstore_box,
ast::vstore_slice(a_r) => match fcx.block_region() { ast::vstore_slice(a_r) => {
result::ok(b_r) => { let r = fcx.infcx.next_region_var(e.span, e.id);
let r = fcx.infcx.next_region_var_with_scope_lb(e.id);
ty::vstore_slice(r) ty::vstore_slice(r)
} }
result::err(msg) => {
fcx.ccx.tcx.sess.span_err(e.span, msg);
ty::vstore_slice(ty::re_static)
}
}
} }
} }
@@ -2523,7 +2545,7 @@ fn check_intrinsic_type(ccx: @crate_ctxt, it: @ast::foreign_item) {
expected %u", i_n_tps, n_tps}); expected %u", i_n_tps, n_tps});
} else { } else {
require_same_types( require_same_types(
tcx, none, it.span, i_ty.ty, fty, tcx, none, false, it.span, i_ty.ty, fty,
|| fmt!{"intrinsic has wrong type: \ || fmt!{"intrinsic has wrong type: \
expected `%s`", expected `%s`",
ty_to_str(ccx.tcx, fty)}); ty_to_str(ccx.tcx, fty)});

View File

@@ -63,7 +63,8 @@ fn check_pat_variant(pcx: pat_ctxt, pat: @ast::pat, path: @ast::path,
// Assign the pattern the type of the *enum*, not the variant. // Assign the pattern the type of the *enum*, not the variant.
let enum_tpt = ty::lookup_item_type(tcx, v_def_ids.enm); let enum_tpt = ty::lookup_item_type(tcx, v_def_ids.enm);
instantiate_path(pcx.fcx, path, enum_tpt, pat.span, pat.id); instantiate_path(pcx.fcx, path, enum_tpt, pat.span, pat.id,
pcx.block_region);
// Take the enum type params out of `expected`. // Take the enum type params out of `expected`.
match structure_of(pcx.fcx, pat.span, expected) { match structure_of(pcx.fcx, pat.span, expected) {
@@ -143,7 +144,7 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) {
debug!{"pat_range beginning type: %?", b_ty}; debug!{"pat_range beginning type: %?", b_ty};
debug!{"pat_range ending type: %?", e_ty}; debug!{"pat_range ending type: %?", e_ty};
if !require_same_types( if !require_same_types(
tcx, some(fcx.infcx), pat.span, b_ty, e_ty, tcx, some(fcx.infcx), false, pat.span, b_ty, e_ty,
|| ~"mismatched types in range") { || ~"mismatched types in range") {
// no-op // no-op
} else if !ty::type_is_numeric(b_ty) { } else if !ty::type_is_numeric(b_ty) {
@@ -165,8 +166,8 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) {
// then the type of x is &M T where M is the mutability // then the type of x is &M T where M is the mutability
// and T is the expected type // and T is the expected type
let region_var = let region_var =
fcx.infcx.next_region_var({lb: some(pcx.block_region), fcx.infcx.next_region_var_with_lb(
ub: none}); pat.span, pcx.block_region);
let mt = {ty: expected, mutbl: mutbl}; let mt = {ty: expected, mutbl: mutbl};
let region_ty = ty::mk_rptr(tcx, region_var, mt); let region_ty = ty::mk_rptr(tcx, region_var, mt);
demand::eqtype(fcx, pat.span, region_ty, typ); demand::eqtype(fcx, pat.span, region_ty, typ);

View File

@@ -6,7 +6,8 @@ fn suptype(fcx: @fn_ctxt, sp: span,
expected: ty::t, actual: ty::t) { expected: ty::t, actual: ty::t) {
// n.b.: order of actual, expected is reversed // n.b.: order of actual, expected is reversed
match infer::mk_subty(fcx.infcx, actual, expected) { match infer::mk_subty(fcx.infcx, false, sp,
actual, expected) {
result::ok(()) => { /* ok */ } result::ok(()) => { /* ok */ }
result::err(ref err) => { result::err(ref err) => {
fcx.report_mismatched_types(sp, expected, actual, err); fcx.report_mismatched_types(sp, expected, actual, err);
@@ -17,7 +18,7 @@ fn suptype(fcx: @fn_ctxt, sp: span,
fn eqtype(fcx: @fn_ctxt, sp: span, fn eqtype(fcx: @fn_ctxt, sp: span,
expected: ty::t, actual: ty::t) { expected: ty::t, actual: ty::t) {
match infer::mk_eqty(fcx.infcx, actual, expected) { match infer::mk_eqty(fcx.infcx, false, sp, actual, expected) {
result::ok(()) => { /* ok */ } result::ok(()) => { /* ok */ }
result::err(ref err) => { result::err(ref err) => {
fcx.report_mismatched_types(sp, expected, actual, err); fcx.report_mismatched_types(sp, expected, actual, err);

View File

@@ -439,7 +439,7 @@ struct lookup {
// determine the `self` of the impl with fresh // determine the `self` of the impl with fresh
// variables for each parameter: // variables for each parameter:
let {substs: impl_substs, ty: impl_ty} = let {substs: impl_substs, ty: impl_ty} =
impl_self_ty(self.fcx, im.did, need_rp); impl_self_ty(self.fcx, self.self_expr, im.did, need_rp);
let impl_ty = transform_self_type_for_method( let impl_ty = transform_self_type_for_method(
self.tcx(), impl_substs.self_r, self.tcx(), impl_substs.self_r,
@@ -458,15 +458,17 @@ struct lookup {
self.self_ty, self.self_ty,
impl_ty), impl_ty),
immutable_reference_mode => { immutable_reference_mode => {
let region = self.fcx.infcx.next_region_var_with_scope_lb let region = self.fcx.infcx.next_region_var(
(self.self_expr.id); self.self_expr.span,
self.self_expr.id);
let tm = { ty: self.self_ty, mutbl: ast::m_imm }; let tm = { ty: self.self_ty, mutbl: ast::m_imm };
let ref_ty = ty::mk_rptr(self.tcx(), region, tm); let ref_ty = ty::mk_rptr(self.tcx(), region, tm);
matches = self.fcx.can_mk_subty(ref_ty, impl_ty); matches = self.fcx.can_mk_subty(ref_ty, impl_ty);
} }
mutable_reference_mode => { mutable_reference_mode => {
let region = self.fcx.infcx.next_region_var_with_scope_lb let region = self.fcx.infcx.next_region_var(
(self.self_expr.id); self.self_expr.span,
self.self_expr.id);
let tm = { ty: self.self_ty, mutbl: ast::m_mutbl }; let tm = { ty: self.self_ty, mutbl: ast::m_mutbl };
let ref_ty = ty::mk_rptr(self.tcx(), region, tm); let ref_ty = ty::mk_rptr(self.tcx(), region, tm);
matches = self.fcx.can_mk_subty(ref_ty, impl_ty); matches = self.fcx.can_mk_subty(ref_ty, impl_ty);
@@ -609,8 +611,9 @@ struct lookup {
} }
immutable_reference_mode => { immutable_reference_mode => {
// Borrow as an immutable reference. // Borrow as an immutable reference.
let region_var = self.fcx.infcx.next_region_var_with_scope_lb let region_var = self.fcx.infcx.next_region_var(
(self.self_expr.id); self.self_expr.span,
self.self_expr.id);
self.fcx.infcx.borrowings.push({expr_id: self.self_expr.id, self.fcx.infcx.borrowings.push({expr_id: self.self_expr.id,
span: self.self_expr.span, span: self.self_expr.span,
scope: region_var, scope: region_var,
@@ -618,8 +621,9 @@ struct lookup {
} }
mutable_reference_mode => { mutable_reference_mode => {
// Borrow as a mutable reference. // Borrow as a mutable reference.
let region_var = self.fcx.infcx.next_region_var_with_scope_lb let region_var = self.fcx.infcx.next_region_var(
(self.self_expr.id); self.self_expr.span,
self.self_expr.id);
self.fcx.infcx.borrowings.push({expr_id: self.self_expr.id, self.fcx.infcx.borrowings.push({expr_id: self.self_expr.id,
span: self.self_expr.span, span: self.self_expr.span,
scope: region_var, scope: region_var,

View File

@@ -18,9 +18,9 @@ this point a bit better.
*/ */
import util::ppaux; import util::ppaux;
import ppaux::{note_and_explain_region, ty_to_str};
import syntax::print::pprust; import syntax::print::pprust;
import infer::{resolve_type, resolve_all, force_all, import infer::{resolve_and_force_all_but_regions, fres};
resolve_rvar, force_rvar, fres};
import middle::kind::check_owned; import middle::kind::check_owned;
import middle::pat_util::pat_bindings; import middle::pat_util::pat_bindings;
@@ -52,8 +52,7 @@ impl @rcx {
/// will effectively resolve `<R0>` to be the block B. /// will effectively resolve `<R0>` to be the block B.
fn resolve_type(unresolved_ty: ty::t) -> fres<ty::t> { fn resolve_type(unresolved_ty: ty::t) -> fres<ty::t> {
resolve_type(self.fcx.infcx, unresolved_ty, resolve_type(self.fcx.infcx, unresolved_ty,
(resolve_all | force_all) - resolve_and_force_all_but_regions)
(resolve_rvar | force_rvar))
} }
/// Try to resolve the type for the given node. /// Try to resolve the type for the given node.
@@ -66,6 +65,7 @@ fn regionck_expr(fcx: @fn_ctxt, e: @ast::expr) {
let rcx = rcx_({fcx:fcx, mut errors_reported: 0u}); let rcx = rcx_({fcx:fcx, mut errors_reported: 0u});
let v = regionck_visitor(); let v = regionck_visitor();
v.visit_expr(e, @rcx, v); v.visit_expr(e, @rcx, v);
fcx.infcx.resolve_regions();
} }
fn regionck_fn(fcx: @fn_ctxt, fn regionck_fn(fcx: @fn_ctxt,
@@ -74,6 +74,7 @@ fn regionck_fn(fcx: @fn_ctxt,
let rcx = rcx_({fcx:fcx, mut errors_reported: 0u}); let rcx = rcx_({fcx:fcx, mut errors_reported: 0u});
let v = regionck_visitor(); let v = regionck_visitor();
v.visit_block(blk, @rcx, v); v.visit_block(blk, @rcx, v);
fcx.infcx.resolve_regions();
} }
fn regionck_visitor() -> rvt { fn regionck_visitor() -> rvt {
@@ -209,10 +210,8 @@ fn visit_node(id: ast::node_id, span: span, rcx: @rcx) -> bool {
let tcx = fcx.ccx.tcx; let tcx = fcx.ccx.tcx;
let encl_region = ty::encl_region(tcx, id); let encl_region = ty::encl_region(tcx, id);
debug!{"visit_node(ty=%s, id=%d, encl_region=%s)", debug!{"visit_node(ty=%s, id=%d, encl_region=%?)",
ppaux::ty_to_str(tcx, ty), ty_to_str(tcx, ty), id, encl_region};
id,
ppaux::region_to_str(tcx, encl_region)};
// Otherwise, look at the type and see if it is a region pointer. // Otherwise, look at the type and see if it is a region pointer.
return constrain_regions_in_type(rcx, encl_region, span, ty); return constrain_regions_in_type(rcx, encl_region, span, ty);
@@ -237,9 +236,8 @@ fn constrain_regions_in_type(
region: ty::region) { region: ty::region) {
let tcx = rcx.fcx.ccx.tcx; let tcx = rcx.fcx.ccx.tcx;
debug!{"constrain_region(encl_region=%s, region=%s)", debug!{"constrain_region(encl_region=%?, region=%?)",
ppaux::region_to_str(tcx, encl_region), encl_region, region};
ppaux::region_to_str(tcx, region)};
match region { match region {
ty::re_bound(_) => { ty::re_bound(_) => {
@@ -252,14 +250,15 @@ fn constrain_regions_in_type(
_ => () _ => ()
} }
match rcx.fcx.mk_subr(encl_region, region) { match rcx.fcx.mk_subr(true, span, encl_region, region) {
result::err(_) => { result::err(_) => {
let region1 = rcx.fcx.infcx.resolve_region_if_possible(region);
tcx.sess.span_err( tcx.sess.span_err(
span, span,
fmt!{"reference is not valid outside \ fmt!("reference is not valid outside of its lifetime"));
of its lifetime, %s", note_and_explain_region(
ppaux::region_to_str(tcx, region1)}); tcx,
~"the reference is only valid for",
region);
rcx.errors_reported += 1u; rcx.errors_reported += 1u;
} }
result::ok(()) => { result::ok(()) => {

View File

@@ -1,6 +1,8 @@
import check::{fn_ctxt, impl_self_ty}; import check::{fn_ctxt, impl_self_ty};
import infer::{resolve_type, resolve_all, force_all, fixup_err_to_str}; import infer::{resolve_type, resolve_and_force_all_but_regions,
fixup_err_to_str};
import ast_util::new_def_hash; import ast_util::new_def_hash;
import syntax::print::pprust;
// vtable resolution looks for places where trait bounds are // vtable resolution looks for places where trait bounds are
// subsituted in and figures out which vtable is used. There is some // subsituted in and figures out which vtable is used. There is some
@@ -27,7 +29,7 @@ fn has_trait_bounds(tps: ~[ty::param_bounds]) -> bool {
} }
fn lookup_vtables(fcx: @fn_ctxt, fn lookup_vtables(fcx: @fn_ctxt,
sp: span, expr: @ast::expr,
bounds: @~[ty::param_bounds], bounds: @~[ty::param_bounds],
substs: &ty::substs, substs: &ty::substs,
allow_unsafe: bool, allow_unsafe: bool,
@@ -39,7 +41,7 @@ fn lookup_vtables(fcx: @fn_ctxt,
match bound { match bound {
ty::bound_trait(i_ty) => { ty::bound_trait(i_ty) => {
let i_ty = ty::subst(tcx, substs, i_ty); let i_ty = ty::subst(tcx, substs, i_ty);
vec::push(result, lookup_vtable(fcx, sp, ty, i_ty, vec::push(result, lookup_vtable(fcx, expr, ty, i_ty,
allow_unsafe, is_early)); allow_unsafe, is_early));
} }
_ => () _ => ()
@@ -50,31 +52,36 @@ fn lookup_vtables(fcx: @fn_ctxt,
@result @result
} }
fn fixup_substs(fcx: @fn_ctxt, sp: span, fn fixup_substs(fcx: @fn_ctxt, expr: @ast::expr,
id: ast::def_id, substs: ty::substs, id: ast::def_id, substs: ty::substs,
is_early: bool) -> option<ty::substs> { is_early: bool) -> option<ty::substs> {
let tcx = fcx.ccx.tcx; let tcx = fcx.ccx.tcx;
// use a dummy type just to package up the substs that need fixing up // use a dummy type just to package up the substs that need fixing up
let t = ty::mk_trait(tcx, id, substs, ty::vstore_slice(ty::re_static)); let t = ty::mk_trait(tcx, id, substs, ty::vstore_slice(ty::re_static));
do fixup_ty(fcx, sp, t, is_early).map |t_f| { do fixup_ty(fcx, expr, t, is_early).map |t_f| {
match check ty::get(t_f).struct { match check ty::get(t_f).struct {
ty::ty_trait(_, substs_f, _) => substs_f, ty::ty_trait(_, substs_f, _) => substs_f,
} }
} }
} }
fn relate_trait_tys(fcx: @fn_ctxt, sp: span, fn relate_trait_tys(fcx: @fn_ctxt, expr: @ast::expr,
exp_trait_ty: ty::t, act_trait_ty: ty::t) { exp_trait_ty: ty::t, act_trait_ty: ty::t) {
demand::suptype(fcx, sp, exp_trait_ty, act_trait_ty) demand::suptype(fcx, expr.span, exp_trait_ty, act_trait_ty)
} }
/* /*
Look up the vtable to use when treating an item of type <t> Look up the vtable to use when treating an item of type <t>
as if it has type <trait_ty> as if it has type <trait_ty>
*/ */
fn lookup_vtable(fcx: @fn_ctxt, sp: span, ty: ty::t, trait_ty: ty::t, fn lookup_vtable(fcx: @fn_ctxt,
allow_unsafe: bool, is_early: bool) expr: @ast::expr,
-> vtable_origin { ty: ty::t,
trait_ty: ty::t,
allow_unsafe: bool,
is_early: bool)
-> vtable_origin
{
debug!{"lookup_vtable(ty=%s, trait_ty=%s)", debug!{"lookup_vtable(ty=%s, trait_ty=%s)",
fcx.infcx.ty_to_str(ty), fcx.infcx.ty_to_str(trait_ty)}; fcx.infcx.ty_to_str(ty), fcx.infcx.ty_to_str(trait_ty)};
@@ -84,7 +91,7 @@ fn lookup_vtable(fcx: @fn_ctxt, sp: span, ty: ty::t, trait_ty: ty::t,
let (trait_id, trait_substs) = match check ty::get(trait_ty).struct { let (trait_id, trait_substs) = match check ty::get(trait_ty).struct {
ty::ty_trait(did, substs, _) => (did, substs) ty::ty_trait(did, substs, _) => (did, substs)
}; };
let ty = match fixup_ty(fcx, sp, ty, is_early) { let ty = match fixup_ty(fcx, expr, ty, is_early) {
some(ty) => ty, some(ty) => ty,
none => { none => {
// fixup_ty can only fail if this is early resolution // fixup_ty can only fail if this is early resolution
@@ -111,7 +118,7 @@ fn lookup_vtable(fcx: @fn_ctxt, sp: span, ty: ty::t, trait_ty: ty::t,
if trait_id == idid { if trait_id == idid {
debug!{"(checking vtable) @0 relating ty to trait ty debug!{"(checking vtable) @0 relating ty to trait ty
with did %?", idid}; with did %?", idid};
relate_trait_tys(fcx, sp, trait_ty, ity); relate_trait_tys(fcx, expr, trait_ty, ity);
return vtable_param(n, n_bound); return vtable_param(n, n_bound);
} }
} }
@@ -126,16 +133,18 @@ fn lookup_vtable(fcx: @fn_ctxt, sp: span, ty: ty::t, trait_ty: ty::t,
debug!{"(checking vtable) @1 relating ty to trait ty with did %?", debug!{"(checking vtable) @1 relating ty to trait ty with did %?",
did}; did};
relate_trait_tys(fcx, sp, trait_ty, ty); relate_trait_tys(fcx, expr, trait_ty, ty);
if !allow_unsafe && !is_early { if !allow_unsafe && !is_early {
for vec::each(*ty::trait_methods(tcx, did)) |m| { for vec::each(*ty::trait_methods(tcx, did)) |m| {
if ty::type_has_self(ty::mk_fn(tcx, m.fty)) { if ty::type_has_self(ty::mk_fn(tcx, m.fty)) {
tcx.sess.span_err( tcx.sess.span_err(
sp, ~"a boxed trait with self types may not be \ expr.span,
~"a boxed trait with self types may not be \
passed as a bounded type"); passed as a bounded type");
} else if (*m.tps).len() > 0u { } else if (*m.tps).len() > 0u {
tcx.sess.span_err( tcx.sess.span_err(
sp, ~"a boxed trait with generic methods may not \ expr.span,
~"a boxed trait with generic methods may not \
be passed as a bounded type"); be passed as a bounded type");
} }
@@ -176,9 +185,9 @@ fn lookup_vtable(fcx: @fn_ctxt, sp: span, ty: ty::t, trait_ty: ty::t,
// check whether the type unifies with the type // check whether the type unifies with the type
// that the impl is for, and continue if not // that the impl is for, and continue if not
let {substs: substs, ty: for_ty} = let {substs: substs, ty: for_ty} =
impl_self_ty(fcx, im.did, false); impl_self_ty(fcx, expr, im.did, false);
let im_bs = ty::lookup_item_type(tcx, im.did).bounds; let im_bs = ty::lookup_item_type(tcx, im.did).bounds;
match fcx.mk_subty(ty, for_ty) { match fcx.mk_subty(false, expr.span, ty, for_ty) {
result::err(_) => again, result::err(_) => again,
result::ok(()) => () result::ok(()) => ()
} }
@@ -189,12 +198,12 @@ fn lookup_vtable(fcx: @fn_ctxt, sp: span, ty: ty::t, trait_ty: ty::t,
fcx.infcx.ty_to_str(trait_ty), fcx.infcx.ty_to_str(trait_ty),
fcx.infcx.ty_to_str(of_ty)); fcx.infcx.ty_to_str(of_ty));
let of_ty = ty::subst(tcx, &substs, of_ty); let of_ty = ty::subst(tcx, &substs, of_ty);
relate_trait_tys(fcx, sp, trait_ty, of_ty); relate_trait_tys(fcx, expr, trait_ty, of_ty);
// recursively process the bounds. // recursively process the bounds.
let trait_tps = trait_substs.tps; let trait_tps = trait_substs.tps;
// see comments around the earlier call to fixup_ty // see comments around the earlier call to fixup_ty
let substs_f = match fixup_substs(fcx, sp, trait_id, let substs_f = match fixup_substs(fcx, expr, trait_id,
substs, is_early) { substs, is_early) {
some(substs) => substs, some(substs) => substs,
none => { none => {
@@ -204,9 +213,10 @@ fn lookup_vtable(fcx: @fn_ctxt, sp: span, ty: ty::t, trait_ty: ty::t,
} }
}; };
connect_trait_tps(fcx, sp, substs_f.tps, connect_trait_tps(fcx, expr, substs_f.tps,
trait_tps, im.did); trait_tps, im.did);
let subres = lookup_vtables(fcx, sp, im_bs, &substs_f, let subres = lookup_vtables(
fcx, expr, im_bs, &substs_f,
false, is_early); false, is_early);
vec::push(found, vec::push(found,
vtable_static(im.did, substs_f.tps, vtable_static(im.did, substs_f.tps,
@@ -222,7 +232,8 @@ fn lookup_vtable(fcx: @fn_ctxt, sp: span, ty: ty::t, trait_ty: ty::t,
_ => { _ => {
if !is_early { if !is_early {
fcx.ccx.tcx.sess.span_err( fcx.ccx.tcx.sess.span_err(
sp, ~"multiple applicable methods in scope"); expr.span,
~"multiple applicable methods in scope");
} }
return found[0]; return found[0];
} }
@@ -231,19 +242,22 @@ fn lookup_vtable(fcx: @fn_ctxt, sp: span, ty: ty::t, trait_ty: ty::t,
} }
tcx.sess.span_fatal( tcx.sess.span_fatal(
sp, ~"failed to find an implementation of trait " + expr.span,
ty_to_str(tcx, trait_ty) + ~" for " + fmt!("failed to find an implementation of trait %s for %s",
ty_to_str(tcx, ty)); ty_to_str(tcx, trait_ty), ty_to_str(tcx, ty)));
} }
fn fixup_ty(fcx: @fn_ctxt, sp: span, ty: ty::t, is_early: bool) fn fixup_ty(fcx: @fn_ctxt,
-> option<ty::t> { expr: @ast::expr,
ty: ty::t,
is_early: bool) -> option<ty::t>
{
let tcx = fcx.ccx.tcx; let tcx = fcx.ccx.tcx;
match resolve_type(fcx.infcx, ty, resolve_all | force_all) { match resolve_type(fcx.infcx, ty, resolve_and_force_all_but_regions) {
result::ok(new_type) => some(new_type), result::ok(new_type) => some(new_type),
result::err(e) if !is_early => { result::err(e) if !is_early => {
tcx.sess.span_fatal( tcx.sess.span_fatal(
sp, expr.span,
fmt!{"cannot determine a type \ fmt!{"cannot determine a type \
for this bounded type parameter: %s", for this bounded type parameter: %s",
fixup_err_to_str(e)}) fixup_err_to_str(e)})
@@ -254,7 +268,7 @@ fn fixup_ty(fcx: @fn_ctxt, sp: span, ty: ty::t, is_early: bool)
} }
} }
fn connect_trait_tps(fcx: @fn_ctxt, sp: span, impl_tys: ~[ty::t], fn connect_trait_tps(fcx: @fn_ctxt, expr: @ast::expr, impl_tys: ~[ty::t],
trait_tys: ~[ty::t], impl_did: ast::def_id) { trait_tys: ~[ty::t], impl_did: ast::def_id) {
let tcx = fcx.ccx.tcx; let tcx = fcx.ccx.tcx;
@@ -266,22 +280,23 @@ fn connect_trait_tps(fcx: @fn_ctxt, sp: span, impl_tys: ~[ty::t],
match check ty::get(trait_ty).struct { match check ty::get(trait_ty).struct {
ty::ty_trait(_, substs, _) => { ty::ty_trait(_, substs, _) => {
vec::iter2(substs.tps, trait_tys, vec::iter2(substs.tps, trait_tys,
|a, b| demand::suptype(fcx, sp, a, b)); |a, b| demand::suptype(fcx, expr.span, a, b));
} }
} }
} }
fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) { fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) {
debug!("vtable: early_resolve_expr() ex with id %?: %s",
ex.id, expr_to_str(ex));
let cx = fcx.ccx; let cx = fcx.ccx;
match ex.node { match ex.node {
ast::expr_path(*) => { ast::expr_path(*) => {
debug!("(vtable - resolving expr) resolving path expr");
match fcx.opt_node_ty_substs(ex.id) { match fcx.opt_node_ty_substs(ex.id) {
some(ref substs) => { some(ref substs) => {
let did = ast_util::def_id_of_def(cx.tcx.def_map.get(ex.id)); let did = ast_util::def_id_of_def(cx.tcx.def_map.get(ex.id));
let item_ty = ty::lookup_item_type(cx.tcx, did); let item_ty = ty::lookup_item_type(cx.tcx, did);
if has_trait_bounds(*item_ty.bounds) { if has_trait_bounds(*item_ty.bounds) {
let vtbls = lookup_vtables(fcx, ex.span, item_ty.bounds, let vtbls = lookup_vtables(fcx, ex, item_ty.bounds,
substs, false, is_early); substs, false, is_early);
if !is_early { cx.vtable_map.insert(ex.id, vtbls); } if !is_early { cx.vtable_map.insert(ex.id, vtbls); }
} }
@@ -293,8 +308,6 @@ fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) {
ast::expr_field(*) | ast::expr_binary(*) | ast::expr_field(*) | ast::expr_binary(*) |
ast::expr_unary(*) | ast::expr_assign_op(*) | ast::expr_unary(*) | ast::expr_assign_op(*) |
ast::expr_index(*) => { ast::expr_index(*) => {
debug!("(vtable - resolving expr) resolving field/binary/unary/\
assign/index expr");
match ty::method_call_bounds(cx.tcx, cx.method_map, ex.id) { match ty::method_call_bounds(cx.tcx, cx.method_map, ex.id) {
some(bounds) => { some(bounds) => {
if has_trait_bounds(*bounds) { if has_trait_bounds(*bounds) {
@@ -303,7 +316,7 @@ fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) {
_ => ex.callee_id _ => ex.callee_id
}; };
let substs = fcx.node_ty_substs(callee_id); let substs = fcx.node_ty_substs(callee_id);
let vtbls = lookup_vtables(fcx, ex.span, bounds, let vtbls = lookup_vtables(fcx, ex, bounds,
&substs, false, is_early); &substs, false, is_early);
if !is_early { cx.vtable_map.insert(callee_id, vtbls); } if !is_early { cx.vtable_map.insert(callee_id, vtbls); }
} }
@@ -312,7 +325,6 @@ fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) {
} }
} }
ast::expr_cast(src, _) => { ast::expr_cast(src, _) => {
debug!("(vtable - resolving expr) resolving cast expr");
let target_ty = fcx.expr_ty(ex); let target_ty = fcx.expr_ty(ex);
match ty::get(target_ty).struct { match ty::get(target_ty).struct {
ty::ty_trait(*) => { ty::ty_trait(*) => {
@@ -320,7 +332,7 @@ fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) {
Look up vtables for the type we're casting to, Look up vtables for the type we're casting to,
passing in the source and target type passing in the source and target type
*/ */
let vtable = lookup_vtable(fcx, ex.span, fcx.expr_ty(src), let vtable = lookup_vtable(fcx, ex, fcx.expr_ty(src),
target_ty, true, is_early); target_ty, true, is_early);
/* /*
Map this expression to that vtable (that is: "ex has Map this expression to that vtable (that is: "ex has

View File

@@ -15,7 +15,7 @@ import middle::ty::{ty_float, ty_estr, ty_evec, ty_rec};
import middle::ty::{ty_fn, ty_trait, ty_tup, ty_var, ty_var_integral}; import middle::ty::{ty_fn, ty_trait, ty_tup, ty_var, ty_var_integral};
import middle::ty::{ty_param, ty_self, ty_type, ty_opaque_box}; import middle::ty::{ty_param, ty_self, ty_type, ty_opaque_box};
import middle::ty::{ty_opaque_closure_ptr, ty_unboxed_vec, type_is_var}; import middle::ty::{ty_opaque_closure_ptr, ty_unboxed_vec, type_is_var};
import middle::typeck::infer::{infer_ctxt, mk_subty}; import middle::typeck::infer::{infer_ctxt, can_mk_subty};
import middle::typeck::infer::{new_infer_ctxt, resolve_ivar, resolve_type}; import middle::typeck::infer::{new_infer_ctxt, resolve_ivar, resolve_type};
import syntax::ast::{crate, def_id, def_mod}; import syntax::ast::{crate, def_id, def_mod};
import syntax::ast::{item, item_class, item_const, item_enum, item_fn}; import syntax::ast::{item, item_class, item_const, item_enum, item_fn};
@@ -387,18 +387,22 @@ struct CoherenceChecker {
let monotype_a = self.universally_quantify_polytype(polytype_a); let monotype_a = self.universally_quantify_polytype(polytype_a);
let monotype_b = self.universally_quantify_polytype(polytype_b); let monotype_b = self.universally_quantify_polytype(polytype_b);
return return can_mk_subty(self.inference_context,
mk_subty(self.inference_context, monotype_a, monotype_b).is_ok() monotype_a, monotype_b).is_ok()
|| mk_subty(self.inference_context, monotype_b, monotype_a).is_ok(); || can_mk_subty(self.inference_context,
monotype_b, monotype_a).is_ok();
} }
// Converts a polytype to a monotype by replacing all parameters with // Converts a polytype to a monotype by replacing all parameters with
// type variables. // type variables.
fn universally_quantify_polytype(polytype: ty_param_bounds_and_ty) -> t { fn universally_quantify_polytype(polytype: ty_param_bounds_and_ty) -> t {
let self_region = // NDM--this span is bogus.
if !polytype.rp {none} let self_region = if !polytype.rp {
else {some(self.inference_context.next_region_var_nb())}; none
} else {
some(self.inference_context.next_region_var_nb(dummy_sp()))
};
let bounds_count = polytype.bounds.len(); let bounds_count = polytype.bounds.len();
let type_parameters = let type_parameters =

View File

@@ -298,7 +298,7 @@ fn compare_impl_method(tcx: ty::ctxt, sp: span,
ty::subst(tcx, &substs, trait_fty) ty::subst(tcx, &substs, trait_fty)
}; };
require_same_types( require_same_types(
tcx, none, sp, impl_fty, trait_fty, tcx, none, false, sp, impl_fty, trait_fty,
|| ~"method `" + *trait_m.ident + ~"` has an incompatible type"); || ~"method `" + *trait_m.ident + ~"` has an incompatible type");
return; return;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,198 @@
// ______________________________________________________________________
// Type assignment
//
// True if rvalues of type `a` can be assigned to lvalues of type `b`.
// This may cause borrowing to the region scope enclosing `a_node_id`.
//
// The strategy here is somewhat non-obvious. The problem is
// that the constraint we wish to contend with is not a subtyping
// constraint. Currently, for variables, we only track what it
// must be a subtype of, not what types it must be assignable to
// (or from). Possibly, we should track that, but I leave that
// refactoring for another day.
//
// Instead, we look at each variable involved and try to extract
// *some* sort of bound. Typically, the type a is the argument
// supplied to a call; it typically has a *lower bound* (which
// comes from having been assigned a value). What we'd actually
// *like* here is an upper-bound, but we generally don't have
// one. The type b is the expected type and it typically has a
// lower-bound too, which is good.
//
// The way we deal with the fact that we often don't have the
// bounds we need is to be a bit careful. We try to get *some*
// bound from each side, preferring the upper from a and the
// lower from b. If we fail to get a bound from both sides, then
// we just fall back to requiring that a <: b.
//
// Assuming we have a bound from both sides, we will then examine
// these bounds and see if they have the form (@M_a T_a, &rb.M_b T_b)
// (resp. ~M_a T_a, ~[M_a T_a], etc). If they do not, we fall back to
// subtyping.
//
// If they *do*, then we know that the two types could never be
// subtypes of one another. We will then construct a type @const T_b
// and ensure that type a is a subtype of that. This allows for the
// possibility of assigning from a type like (say) @~[mut T1] to a type
// &~[T2] where T1 <: T2. This might seem surprising, since the `@`
// points at mutable memory but the `&` points at immutable memory.
// This would in fact be unsound, except for the borrowck, which comes
// later and guarantees that such mutability conversions are safe.
// See borrowck for more details. Next we require that the region for
// the enclosing scope be a superregion of the region r.
//
// You might wonder why we don't make the type &e.const T_a where e is
// the enclosing region and check that &e.const T_a <: B. The reason
// is that the type of A is (generally) just a *lower-bound*, so this
// would be imposing that lower-bound also as the upper-bound on type
// A. But this upper-bound might be stricter than what is truly
// needed.
import to_str::to_str;
impl infer_ctxt {
fn assign_tys(anmnt: &assignment, a: ty::t, b: ty::t) -> ures {
fn select(fst: option<ty::t>, snd: option<ty::t>) -> option<ty::t> {
match fst {
some(t) => some(t),
none => match snd {
some(t) => some(t),
none => none
}
}
}
debug!{"assign_tys(anmnt=%?, %s -> %s)",
anmnt, a.to_str(self), b.to_str(self)};
let _r = indenter();
match (ty::get(a).struct, ty::get(b).struct) {
(ty::ty_bot, _) => {
uok()
}
(ty::ty_var(a_id), ty::ty_var(b_id)) => {
let nde_a = self.get(&self.ty_var_bindings, a_id);
let nde_b = self.get(&self.ty_var_bindings, b_id);
let a_bounds = nde_a.possible_types;
let b_bounds = nde_b.possible_types;
let a_bnd = select(a_bounds.ub, a_bounds.lb);
let b_bnd = select(b_bounds.lb, b_bounds.ub);
self.assign_tys_or_sub(anmnt, a, b, a_bnd, b_bnd)
}
(ty::ty_var(a_id), _) => {
let nde_a = self.get(&self.ty_var_bindings, a_id);
let a_bounds = nde_a.possible_types;
let a_bnd = select(a_bounds.ub, a_bounds.lb);
self.assign_tys_or_sub(anmnt, a, b, a_bnd, some(b))
}
(_, ty::ty_var(b_id)) => {
let nde_b = self.get(&self.ty_var_bindings, b_id);
let b_bounds = nde_b.possible_types;
let b_bnd = select(b_bounds.lb, b_bounds.ub);
self.assign_tys_or_sub(anmnt, a, b, some(a), b_bnd)
}
(_, _) => {
self.assign_tys_or_sub(anmnt, a, b, some(a), some(b))
}
}
}
fn assign_tys_or_sub(
anmnt: &assignment,
a: ty::t, b: ty::t,
+a_bnd: option<ty::t>, +b_bnd: option<ty::t>) -> ures {
debug!{"assign_tys_or_sub(anmnt=%?, %s -> %s, %s -> %s)",
anmnt, a.to_str(self), b.to_str(self),
a_bnd.to_str(self), b_bnd.to_str(self)};
let _r = indenter();
fn is_borrowable(v: ty::vstore) -> bool {
match v {
ty::vstore_fixed(_) | ty::vstore_uniq | ty::vstore_box => true,
ty::vstore_slice(_) => false
}
}
match (a_bnd, b_bnd) {
(some(a_bnd), some(b_bnd)) => {
match (ty::get(a_bnd).struct, ty::get(b_bnd).struct) {
(ty::ty_box(mt_a), ty::ty_rptr(r_b, mt_b)) => {
let nr_b = ty::mk_box(self.tcx, {ty: mt_b.ty,
mutbl: m_const});
self.crosspollinate(anmnt, a, nr_b, mt_b.mutbl, r_b)
}
(ty::ty_uniq(mt_a), ty::ty_rptr(r_b, mt_b)) => {
let nr_b = ty::mk_uniq(self.tcx, {ty: mt_b.ty,
mutbl: m_const});
self.crosspollinate(anmnt, a, nr_b, mt_b.mutbl, r_b)
}
(ty::ty_estr(vs_a),
ty::ty_estr(ty::vstore_slice(r_b)))
if is_borrowable(vs_a) => {
let nr_b = ty::mk_estr(self.tcx, vs_a);
self.crosspollinate(anmnt, a, nr_b, m_imm, r_b)
}
(ty::ty_evec(mt_a, vs_a),
ty::ty_evec(mt_b, ty::vstore_slice(r_b)))
if is_borrowable(vs_a) => {
let nr_b = ty::mk_evec(self.tcx, {ty: mt_b.ty,
mutbl: m_const}, vs_a);
self.crosspollinate(anmnt, a, nr_b, mt_b.mutbl, r_b)
}
_ => {
mk_sub(self, false, anmnt.span).tys(a, b).to_ures()
}
}
}
_ => {
mk_sub(self, false, anmnt.span).tys(a, b).to_ures()
}
}
}
fn crosspollinate(anmnt: &assignment,
a: ty::t,
nr_b: ty::t,
m: ast::mutability,
r_b: ty::region) -> ures {
debug!{"crosspollinate(anmnt=%?, a=%s, nr_b=%s, r_b=%s)",
anmnt, a.to_str(self), nr_b.to_str(self),
r_b.to_str(self)};
do indent {
let sub = mk_sub(self, false, anmnt.span);
do sub.tys(a, nr_b).chain |_t| {
// Create a fresh region variable `r_a` with the given
// borrow bounds:
let r_a = self.next_region_var(anmnt.span,
anmnt.borrow_lb);
debug!{"anmnt=%?", anmnt};
do sub.contraregions(r_a, r_b).chain |_r| {
// if successful, add an entry indicating that
// borrowing occurred
debug!{"borrowing expression #%?, scope=%?, m=%?",
anmnt, r_a, m};
self.borrowings.push({expr_id: anmnt.expr_id,
span: anmnt.span,
scope: r_a,
mutbl: m});
uok()
}
}
}
}
}

View File

@@ -0,0 +1,442 @@
// ______________________________________________________________________
// Type combining
//
// There are three type combiners: sub, lub, and glb. Each implements
// the trait `combine` and contains methods for combining two
// instances of various things and yielding a new instance. These
// combiner methods always yield a `result<T>`---failure is propagated
// upward using `chain()` methods.
//
// There is a lot of common code for these operations, which is
// abstracted out into functions named `super_X()` which take a combiner
// instance as the first parameter. This would be better implemented
// using traits. For this system to work properly, you should not
// call the `super_X(foo, ...)` functions directly, but rather call
// `foo.X(...)`. The implementation of `X()` can then choose to delegate
// to the `super` routine or to do other things.
//
// In reality, the sub operation is rather different from lub/glb, but
// they are combined into one trait to avoid duplication (they used to
// be separate but there were many bugs because there were two copies
// of most routines).
//
// The differences are:
//
// - when making two things have a sub relationship, the order of the
// arguments is significant (a <: b) and the return value of the
// combine functions is largely irrelevant. The important thing is
// whether the action succeeds or fails. If it succeeds, then side
// effects have been committed into the type variables.
//
// - for GLB/LUB, the order of arguments is not significant (GLB(a,b) ==
// GLB(b,a)) and the return value is important (it is the GLB). Of
// course GLB/LUB may also have side effects.
//
// Contravariance
//
// When you are relating two things which have a contravariant
// relationship, you should use `contratys()` or `contraregions()`,
// rather than inversing the order of arguments! This is necessary
// because the order of arguments is not relevant for LUB and GLB. It
// is also useful to track which value is the "expected" value in
// terms of error reporting, although we do not do that properly right
// now.
import to_str::to_str;
trait combine {
fn infcx() -> infer_ctxt;
fn tag() -> ~str;
fn a_is_expected() -> bool;
fn sub() -> Sub;
fn lub() -> Lub;
fn glb() -> Glb;
fn mts(a: ty::mt, b: ty::mt) -> cres<ty::mt>;
fn contratys(a: ty::t, b: ty::t) -> cres<ty::t>;
fn tys(a: ty::t, b: ty::t) -> cres<ty::t>;
fn tps(as: &[ty::t], bs: &[ty::t]) -> cres<~[ty::t]>;
fn self_tys(a: option<ty::t>, b: option<ty::t>) -> cres<option<ty::t>>;
fn substs(as: &ty::substs, bs: &ty::substs) -> cres<ty::substs>;
fn fns(a: &ty::fn_ty, b: &ty::fn_ty) -> cres<ty::fn_ty>;
fn flds(a: ty::field, b: ty::field) -> cres<ty::field>;
fn modes(a: ast::mode, b: ast::mode) -> cres<ast::mode>;
fn args(a: ty::arg, b: ty::arg) -> cres<ty::arg>;
fn protos(p1: ty::fn_proto, p2: ty::fn_proto) -> cres<ty::fn_proto>;
fn ret_styles(r1: ret_style, r2: ret_style) -> cres<ret_style>;
fn purities(a: purity, b: purity) -> cres<purity>;
fn contraregions(a: ty::region, b: ty::region) -> cres<ty::region>;
fn regions(a: ty::region, b: ty::region) -> cres<ty::region>;
fn vstores(vk: ty::terr_vstore_kind,
a: ty::vstore, b: ty::vstore) -> cres<ty::vstore>;
}
struct combine_fields {
infcx: infer_ctxt;
a_is_expected: bool;
span: span;
}
fn expected_found<C: combine,T>(
self: &C, +a: T, +b: T) -> ty::expected_found<T> {
if self.a_is_expected() {
ty::expected_found {expected: a, found: b}
} else {
ty::expected_found {expected: b, found: a}
}
}
fn eq_tys<C: combine>(self: &C, a: ty::t, b: ty::t) -> ures {
let suber = self.sub();
do self.infcx().try {
do suber.tys(a, b).chain |_ok| {
suber.contratys(a, b)
}.to_ures()
}
}
fn eq_regions<C: combine>(self: &C, a: ty::region, b: ty::region) -> ures {
debug!{"eq_regions(%s, %s)",
a.to_str(self.infcx()),
b.to_str(self.infcx())};
let sub = self.sub();
do indent {
self.infcx().try(|| {
do sub.regions(a, b).chain |_r| {
sub.contraregions(a, b)
}
}).chain_err(|e| {
// substitute a better error, but use the regions
// found in the original error
match e {
ty::terr_regions_does_not_outlive(a1, b1) =>
err(ty::terr_regions_not_same(a1, b1)),
_ => err(e)
}
}).to_ures()
}
}
fn eq_opt_regions<C:combine>(
self: &C,
a: option<ty::region>,
b: option<ty::region>) -> cres<option<ty::region>> {
match (a, b) {
(none, none) => {
ok(none)
}
(some(a), some(b)) => {
do eq_regions(self, a, b).then {
ok(some(a))
}
}
(_, _) => {
// If these two substitutions are for the same type (and
// they should be), then the type should either
// consistently have a region parameter or not have a
// region parameter.
self.infcx().tcx.sess.bug(
fmt!{"substitution a had opt_region %s and \
b had opt_region %s",
a.to_str(self.infcx()),
b.to_str(self.infcx())});
}
}
}
fn super_substs<C:combine>(
self: &C, a: &ty::substs, b: &ty::substs) -> cres<ty::substs> {
do self.tps(a.tps, b.tps).chain |tps| {
do self.self_tys(a.self_ty, b.self_ty).chain |self_ty| {
do eq_opt_regions(self, a.self_r, b.self_r).chain
|self_r| {
ok({self_r: self_r, self_ty: self_ty, tps: tps})
}
}
}
}
fn super_tps<C:combine>(
self: &C, as: &[ty::t], bs: &[ty::t]) -> cres<~[ty::t]> {
// Note: type parameters are always treated as *invariant*
// (otherwise the type system would be unsound). In the
// future we could allow type parameters to declare a
// variance.
if vec::same_length(as, bs) {
iter_vec2(as, bs, |a, b| {
eq_tys(self, a, b)
}).then(|| ok(as.to_vec()) )
} else {
err(ty::terr_ty_param_size(
expected_found(self, as.len(), bs.len())))
}
}
fn super_self_tys<C:combine>(
self: &C, a: option<ty::t>, b: option<ty::t>) -> cres<option<ty::t>> {
// Note: the self type parameter is (currently) always treated as
// *invariant* (otherwise the type system would be unsound).
match (a, b) {
(none, none) => {
ok(none)
}
(some(a), some(b)) => {
eq_tys(self, a, b).then(|| ok(some(a)) )
}
(none, some(_)) |
(some(_), none) => {
// I think it should never happen that we unify two substs and
// one of them has a self_ty and one doesn't...? I could be
// wrong about this.
err(ty::terr_self_substs)
}
}
}
fn super_flds<C:combine>(
self: &C, a: ty::field, b: ty::field) -> cres<ty::field> {
if a.ident == b.ident {
self.mts(a.mt, b.mt)
.chain(|mt| ok({ident: a.ident, mt: mt}) )
.chain_err(|e| err(ty::terr_in_field(@e, a.ident)) )
} else {
err(ty::terr_record_fields(
expected_found(self, a.ident, b.ident)))
}
}
fn super_modes<C:combine>(
self: &C, a: ast::mode, b: ast::mode)
-> cres<ast::mode> {
let tcx = self.infcx().tcx;
ty::unify_mode(tcx, expected_found(self, a, b))
}
fn super_args<C:combine>(
self: &C, a: ty::arg, b: ty::arg)
-> cres<ty::arg> {
do self.modes(a.mode, b.mode).chain |m| {
do self.contratys(a.ty, b.ty).chain |t| {
ok({mode: m, ty: t})
}
}
}
fn super_vstores<C:combine>(
self: &C, vk: ty::terr_vstore_kind,
a: ty::vstore, b: ty::vstore) -> cres<ty::vstore> {
match (a, b) {
(ty::vstore_slice(a_r), ty::vstore_slice(b_r)) => {
do self.contraregions(a_r, b_r).chain |r| {
ok(ty::vstore_slice(r))
}
}
_ if a == b => {
ok(a)
}
_ => {
err(ty::terr_vstores_differ(vk, expected_found(self, a, b)))
}
}
}
fn super_fns<C:combine>(
self: &C, a_f: &ty::fn_ty, b_f: &ty::fn_ty) -> cres<ty::fn_ty> {
fn argvecs<C:combine>(self: &C, a_args: ~[ty::arg],
b_args: ~[ty::arg]) -> cres<~[ty::arg]> {
if vec::same_length(a_args, b_args) {
map_vec2(a_args, b_args, |a, b| self.args(a, b) )
} else {
err(ty::terr_arg_count)
}
}
do self.protos(a_f.proto, b_f.proto).chain |p| {
do self.ret_styles(a_f.ret_style, b_f.ret_style).chain |rs| {
do argvecs(self, a_f.inputs, b_f.inputs).chain |inputs| {
do self.tys(a_f.output, b_f.output).chain |output| {
do self.purities(a_f.purity, b_f.purity).chain |purity| {
// FIXME: uncomment if #2588 doesn't get accepted:
// self.infcx().constrvecs(a_f.constraints,
// b_f.constraints).then {||
ok({purity: purity,
proto: p,
bounds: a_f.bounds, // XXX: This is wrong!
inputs: inputs,
output: output,
ret_style: rs})
// }
}
}
}
}
}
}
fn super_tys<C:combine>(
self: &C, a: ty::t, b: ty::t) -> cres<ty::t> {
let tcx = self.infcx().tcx;
match (ty::get(a).struct, ty::get(b).struct) {
// The "subtype" ought to be handling cases involving bot or var:
(ty::ty_bot, _) |
(_, ty::ty_bot) |
(ty::ty_var(_), _) |
(_, ty::ty_var(_)) => {
tcx.sess.bug(
fmt!{"%s: bot and var types should have been handled (%s,%s)",
self.tag(),
a.to_str(self.infcx()),
b.to_str(self.infcx())});
}
// Relate integral variables to other types
(ty::ty_var_integral(a_id), ty::ty_var_integral(b_id)) => {
self.infcx().vars_integral(a_id, b_id).then(|| ok(a) )
}
(ty::ty_var_integral(a_id), ty::ty_int(_)) |
(ty::ty_var_integral(a_id), ty::ty_uint(_)) => {
self.infcx().var_integral_sub_t(a_id, b).then(|| ok(a) )
}
(ty::ty_int(_), ty::ty_var_integral(b_id)) |
(ty::ty_uint(_), ty::ty_var_integral(b_id)) => {
self.infcx().t_sub_var_integral(a, b_id).then(|| ok(a) )
}
(ty::ty_int(_), _) |
(ty::ty_uint(_), _) |
(ty::ty_float(_), _) => {
let as = ty::get(a).struct;
let bs = ty::get(b).struct;
if as == bs {
ok(a)
} else {
err(ty::terr_sorts(expected_found(self, a, b)))
}
}
(ty::ty_nil, _) |
(ty::ty_bool, _) => {
let cfg = tcx.sess.targ_cfg;
if ty::mach_sty(cfg, a) == ty::mach_sty(cfg, b) {
ok(a)
} else {
err(ty::terr_sorts(expected_found(self, a, b)))
}
}
(ty::ty_param(a_p), ty::ty_param(b_p)) if a_p.idx == b_p.idx => {
ok(a)
}
(ty::ty_enum(a_id, ref a_substs),
ty::ty_enum(b_id, ref b_substs))
if a_id == b_id => {
do self.substs(a_substs, b_substs).chain |substs| {
ok(ty::mk_enum(tcx, a_id, substs))
}
}
(ty::ty_trait(a_id, ref a_substs, a_vstore),
ty::ty_trait(b_id, ref b_substs, b_vstore))
if a_id == b_id => {
do self.substs(a_substs, b_substs).chain |substs| {
do self.vstores(ty::terr_trait, a_vstore, b_vstore).chain |vs| {
ok(ty::mk_trait(tcx, a_id, substs, vs))
}
}
}
(ty::ty_class(a_id, ref a_substs), ty::ty_class(b_id, ref b_substs))
if a_id == b_id => {
do self.substs(a_substs, b_substs).chain |substs| {
ok(ty::mk_class(tcx, a_id, substs))
}
}
(ty::ty_box(a_mt), ty::ty_box(b_mt)) => {
do self.mts(a_mt, b_mt).chain |mt| {
ok(ty::mk_box(tcx, mt))
}
}
(ty::ty_uniq(a_mt), ty::ty_uniq(b_mt)) => {
do self.mts(a_mt, b_mt).chain |mt| {
ok(ty::mk_uniq(tcx, mt))
}
}
(ty::ty_ptr(a_mt), ty::ty_ptr(b_mt)) => {
do self.mts(a_mt, b_mt).chain |mt| {
ok(ty::mk_ptr(tcx, mt))
}
}
(ty::ty_rptr(a_r, a_mt), ty::ty_rptr(b_r, b_mt)) => {
do self.contraregions(a_r, b_r).chain |r| {
do self.mts(a_mt, b_mt).chain |mt| {
ok(ty::mk_rptr(tcx, r, mt))
}
}
}
(ty::ty_evec(a_mt, vs_a), ty::ty_evec(b_mt, vs_b)) => {
do self.mts(a_mt, b_mt).chain |mt| {
do self.vstores(ty::terr_vec, vs_a, vs_b).chain |vs| {
ok(ty::mk_evec(tcx, mt, vs))
}
}
}
(ty::ty_estr(vs_a), ty::ty_estr(vs_b)) => {
do self.vstores(ty::terr_str, vs_a, vs_b).chain |vs| {
ok(ty::mk_estr(tcx,vs))
}
}
(ty::ty_rec(as), ty::ty_rec(bs)) => {
if vec::same_length(as, bs) {
map_vec2(as, bs, |a,b| {
self.flds(a, b)
}).chain(|flds| ok(ty::mk_rec(tcx, flds)) )
} else {
err(ty::terr_record_size(expected_found(self, as.len(),
bs.len())))
}
}
(ty::ty_tup(as), ty::ty_tup(bs)) => {
if vec::same_length(as, bs) {
map_vec2(as, bs, |a, b| self.tys(a, b) )
.chain(|ts| ok(ty::mk_tup(tcx, ts)) )
} else {
err(ty::terr_tuple_size(expected_found(self, as.len(), bs.len())))
}
}
(ty::ty_fn(ref a_fty), ty::ty_fn(ref b_fty)) => {
do self.fns(a_fty, b_fty).chain |fty| {
ok(ty::mk_fn(tcx, fty))
}
}
_ => err(ty::terr_sorts(expected_found(self, a, b)))
}
}

View File

@@ -0,0 +1,166 @@
import combine::*;
import lattice::*;
import to_str::to_str;
enum Glb = combine_fields; // "greatest lower bound" (common subtype)
impl Glb: combine {
fn infcx() -> infer_ctxt { self.infcx }
fn tag() -> ~str { ~"glb" }
fn a_is_expected() -> bool { self.a_is_expected }
fn sub() -> Sub { Sub(*self) }
fn lub() -> Lub { Lub(*self) }
fn glb() -> Glb { Glb(*self) }
fn mts(a: ty::mt, b: ty::mt) -> cres<ty::mt> {
let tcx = self.infcx.tcx;
debug!{"%s.mts(%s, %s)",
self.tag(),
mt_to_str(tcx, a),
mt_to_str(tcx, b)};
match (a.mutbl, b.mutbl) {
// If one side or both is mut, then the GLB must use
// the precise type from the mut side.
(m_mutbl, m_const) => {
Sub(*self).tys(a.ty, b.ty).chain(|_t| {
ok({ty: a.ty, mutbl: m_mutbl})
})
}
(m_const, m_mutbl) => {
Sub(*self).tys(b.ty, a.ty).chain(|_t| {
ok({ty: b.ty, mutbl: m_mutbl})
})
}
(m_mutbl, m_mutbl) => {
eq_tys(&self, a.ty, b.ty).then(|| {
ok({ty: a.ty, mutbl: m_mutbl})
})
}
// If one side or both is immutable, we can use the GLB of
// both sides but mutbl must be `m_imm`.
(m_imm, m_const) |
(m_const, m_imm) |
(m_imm, m_imm) => {
self.tys(a.ty, b.ty).chain(|t| {
ok({ty: t, mutbl: m_imm})
})
}
// If both sides are const, then we can use GLB of both
// sides and mutbl of only `m_const`.
(m_const, m_const) => {
self.tys(a.ty, b.ty).chain(|t| {
ok({ty: t, mutbl: m_const})
})
}
// There is no mutual subtype of these combinations.
(m_mutbl, m_imm) |
(m_imm, m_mutbl) => {
err(ty::terr_mutability)
}
}
}
fn contratys(a: ty::t, b: ty::t) -> cres<ty::t> {
Lub(*self).tys(a, b)
}
fn protos(p1: ty::fn_proto, p2: ty::fn_proto) -> cres<ty::fn_proto> {
match (p1, p2) {
(ty::proto_vstore(ty::vstore_slice(_)), _) => ok(p2),
(_, ty::proto_vstore(ty::vstore_slice(_))) => ok(p1),
(ty::proto_vstore(v1), ty::proto_vstore(v2)) => {
self.infcx.try(|| {
do self.vstores(terr_fn, v1, v2).chain |vs| {
ok(ty::proto_vstore(vs))
}
}).chain_err(|_err| {
// XXX: Totally unsound, but fixed up later.
ok(ty::proto_bare)
})
}
_ => ok(ty::proto_bare)
}
}
fn purities(a: purity, b: purity) -> cres<purity> {
match (a, b) {
(pure_fn, _) | (_, pure_fn) => ok(pure_fn),
(extern_fn, _) | (_, extern_fn) => ok(extern_fn),
(impure_fn, _) | (_, impure_fn) => ok(impure_fn),
(unsafe_fn, unsafe_fn) => ok(unsafe_fn)
}
}
fn ret_styles(r1: ret_style, r2: ret_style) -> cres<ret_style> {
match (r1, r2) {
(ast::return_val, ast::return_val) => {
ok(ast::return_val)
}
(ast::noreturn, _) |
(_, ast::noreturn) => {
ok(ast::noreturn)
}
}
}
fn regions(a: ty::region, b: ty::region) -> cres<ty::region> {
debug!{"%s.regions(%?, %?)",
self.tag(),
a.to_str(self.infcx),
b.to_str(self.infcx)};
do indent {
self.infcx.region_vars.glb_regions(self.span, a, b)
}
}
fn contraregions(a: ty::region, b: ty::region) -> cres<ty::region> {
Lub(*self).regions(a, b)
}
fn tys(a: ty::t, b: ty::t) -> cres<ty::t> {
lattice_tys(&self, a, b)
}
// Traits please:
fn flds(a: ty::field, b: ty::field) -> cres<ty::field> {
super_flds(&self, a, b)
}
fn vstores(vk: ty::terr_vstore_kind,
a: ty::vstore, b: ty::vstore) -> cres<ty::vstore> {
super_vstores(&self, vk, a, b)
}
fn modes(a: ast::mode, b: ast::mode) -> cres<ast::mode> {
super_modes(&self, a, b)
}
fn args(a: ty::arg, b: ty::arg) -> cres<ty::arg> {
super_args(&self, a, b)
}
fn fns(a: &ty::fn_ty, b: &ty::fn_ty) -> cres<ty::fn_ty> {
super_fns(&self, a, b)
}
fn substs(as: &ty::substs, bs: &ty::substs) -> cres<ty::substs> {
super_substs(&self, as, bs)
}
fn tps(as: &[ty::t], bs: &[ty::t]) -> cres<~[ty::t]> {
super_tps(&self, as, bs)
}
fn self_tys(a: option<ty::t>, b: option<ty::t>) -> cres<option<ty::t>> {
super_self_tys(&self, a, b)
}
}

View File

@@ -0,0 +1,78 @@
/*!
Code related to integral type inference.
*/
import to_str::to_str;
// Bitvector to represent sets of integral types
enum int_ty_set = uint;
// Constants representing singleton sets containing each of the
// integral types
const INT_TY_SET_EMPTY : uint = 0b00_0000_0000u;
const INT_TY_SET_i8 : uint = 0b00_0000_0001u;
const INT_TY_SET_u8 : uint = 0b00_0000_0010u;
const INT_TY_SET_i16 : uint = 0b00_0000_0100u;
const INT_TY_SET_u16 : uint = 0b00_0000_1000u;
const INT_TY_SET_i32 : uint = 0b00_0001_0000u;
const INT_TY_SET_u32 : uint = 0b00_0010_0000u;
const INT_TY_SET_i64 : uint = 0b00_0100_0000u;
const INT_TY_SET_u64 : uint = 0b00_1000_0000u;
const INT_TY_SET_i : uint = 0b01_0000_0000u;
const INT_TY_SET_u : uint = 0b10_0000_0000u;
fn int_ty_set_all() -> int_ty_set {
int_ty_set(INT_TY_SET_i8 | INT_TY_SET_u8 |
INT_TY_SET_i16 | INT_TY_SET_u16 |
INT_TY_SET_i32 | INT_TY_SET_u32 |
INT_TY_SET_i64 | INT_TY_SET_u64 |
INT_TY_SET_i | INT_TY_SET_u)
}
fn intersection(a: int_ty_set, b: int_ty_set) -> int_ty_set {
int_ty_set(*a & *b)
}
fn single_type_contained_in(tcx: ty::ctxt, a: int_ty_set) ->
option<ty::t> {
debug!{"single_type_contained_in(a=%s)", uint::to_str(*a, 10u)};
if *a == INT_TY_SET_i8 { return some(ty::mk_i8(tcx)); }
if *a == INT_TY_SET_u8 { return some(ty::mk_u8(tcx)); }
if *a == INT_TY_SET_i16 { return some(ty::mk_i16(tcx)); }
if *a == INT_TY_SET_u16 { return some(ty::mk_u16(tcx)); }
if *a == INT_TY_SET_i32 { return some(ty::mk_i32(tcx)); }
if *a == INT_TY_SET_u32 { return some(ty::mk_u32(tcx)); }
if *a == INT_TY_SET_i64 { return some(ty::mk_i64(tcx)); }
if *a == INT_TY_SET_u64 { return some(ty::mk_u64(tcx)); }
if *a == INT_TY_SET_i { return some(ty::mk_int(tcx)); }
if *a == INT_TY_SET_u { return some(ty::mk_uint(tcx)); }
return none;
}
fn convert_integral_ty_to_int_ty_set(tcx: ty::ctxt, t: ty::t)
-> int_ty_set {
match get(t).struct {
ty_int(int_ty) => match int_ty {
ast::ty_i8 => int_ty_set(INT_TY_SET_i8),
ast::ty_i16 => int_ty_set(INT_TY_SET_i16),
ast::ty_i32 => int_ty_set(INT_TY_SET_i32),
ast::ty_i64 => int_ty_set(INT_TY_SET_i64),
ast::ty_i => int_ty_set(INT_TY_SET_i),
ast::ty_char => tcx.sess.bug(
~"char type passed to convert_integral_ty_to_int_ty_set()")
},
ty_uint(uint_ty) => match uint_ty {
ast::ty_u8 => int_ty_set(INT_TY_SET_u8),
ast::ty_u16 => int_ty_set(INT_TY_SET_u16),
ast::ty_u32 => int_ty_set(INT_TY_SET_u32),
ast::ty_u64 => int_ty_set(INT_TY_SET_u64),
ast::ty_u => int_ty_set(INT_TY_SET_u)
},
_ => tcx.sess.bug(~"non-integral type passed to \
convert_integral_ty_to_int_ty_set()")
}
}

View File

@@ -0,0 +1,148 @@
import combine::*;
import unify::*;
import to_str::to_str;
// ______________________________________________________________________
// Lattice operations on variables
//
// This is common code used by both LUB and GLB to compute the LUB/GLB
// for pairs of variables or for variables and values.
trait lattice_ops {
fn bnd(b: bounds<ty::t>) -> option<ty::t>;
fn with_bnd(b: bounds<ty::t>, t: ty::t) -> bounds<ty::t>;
fn ty_bot(t: ty::t) -> cres<ty::t>;
}
impl Lub: lattice_ops {
fn bnd(b: bounds<ty::t>) -> option<ty::t> { b.ub }
fn with_bnd(b: bounds<ty::t>, t: ty::t) -> bounds<ty::t> {
{ub: some(t) with b}
}
fn ty_bot(t: ty::t) -> cres<ty::t> {
ok(t)
}
}
impl Glb: lattice_ops {
fn bnd(b: bounds<ty::t>) -> option<ty::t> { b.lb }
fn with_bnd(b: bounds<ty::t>, t: ty::t) -> bounds<ty::t> {
{lb: some(t) with b}
}
fn ty_bot(_t: ty::t) -> cres<ty::t> {
ok(ty::mk_bot(self.infcx.tcx))
}
}
fn lattice_tys<L:lattice_ops combine>(
self: &L, a: ty::t, b: ty::t) -> cres<ty::t> {
debug!{"%s.lattice_tys(%s, %s)", self.tag(),
a.to_str(self.infcx()),
b.to_str(self.infcx())};
if a == b { return ok(a); }
do indent {
match (ty::get(a).struct, ty::get(b).struct) {
(ty::ty_bot, _) => self.ty_bot(b),
(_, ty::ty_bot) => self.ty_bot(a),
(ty::ty_var(a_id), ty::ty_var(b_id)) => {
lattice_vars(self, a, a_id, b_id,
|x, y| self.tys(x, y) )
}
(ty::ty_var(a_id), _) => {
lattice_var_and_t(self, a_id, b,
|x, y| self.tys(x, y) )
}
(_, ty::ty_var(b_id)) => {
lattice_var_and_t(self, b_id, a,
|x, y| self.tys(x, y) )
}
_ => {
super_tys(self, a, b)
}
}
}
}
fn lattice_vars<L:lattice_ops combine>(
self: &L, +a_t: ty::t, +a_vid: ty::tv_vid, +b_vid: ty::tv_vid,
c_ts: fn(ty::t, ty::t) -> cres<ty::t>) -> cres<ty::t> {
// The comments in this function are written for LUB and types,
// but they apply equally well to GLB and regions if you inverse
// upper/lower/sub/super/etc.
// Need to find a type that is a supertype of both a and b:
let vb = &self.infcx().ty_var_bindings;
let nde_a = self.infcx().get(vb, a_vid);
let nde_b = self.infcx().get(vb, b_vid);
let a_vid = nde_a.root;
let b_vid = nde_b.root;
let a_bounds = nde_a.possible_types;
let b_bounds = nde_b.possible_types;
debug!{"%s.lattice_vars(%s=%s <: %s=%s)",
self.tag(),
a_vid.to_str(), a_bounds.to_str(self.infcx()),
b_vid.to_str(), b_bounds.to_str(self.infcx())};
if a_vid == b_vid {
return ok(a_t);
}
// If both A and B have an UB type, then we can just compute the
// LUB of those types:
let a_bnd = self.bnd(a_bounds), b_bnd = self.bnd(b_bounds);
match (a_bnd, b_bnd) {
(some(a_ty), some(b_ty)) => {
match self.infcx().try(|| c_ts(a_ty, b_ty) ) {
ok(t) => return ok(t),
err(_) => { /*fallthrough */ }
}
}
_ => {/*fallthrough*/}
}
// Otherwise, we need to merge A and B into one variable. We can
// then use either variable as an upper bound:
var_sub_var(self, a_vid, b_vid).then(|| ok(a_t) )
}
fn lattice_var_and_t<L:lattice_ops combine>(
self: &L, a_id: ty::tv_vid, b: ty::t,
c_ts: fn(ty::t, ty::t) -> cres<ty::t>) -> cres<ty::t> {
let vb = &self.infcx().ty_var_bindings;
let nde_a = self.infcx().get(vb, a_id);
let a_id = nde_a.root;
let a_bounds = nde_a.possible_types;
// The comments in this function are written for LUB, but they
// apply equally well to GLB if you inverse upper/lower/sub/super/etc.
debug!{"%s.lattice_var_and_t(%s=%s <: %s)",
self.tag(),
a_id.to_str(), a_bounds.to_str(self.infcx()),
b.to_str(self.infcx())};
match self.bnd(a_bounds) {
some(a_bnd) => {
// If a has an upper bound, return the LUB(a.ub, b)
debug!{"bnd=some(%s)", a_bnd.to_str(self.infcx())};
return c_ts(a_bnd, b);
}
none => {
// If a does not have an upper bound, make b the upper bound of a
// and then return b.
debug!{"bnd=none"};
let a_bounds = self.with_bnd(a_bounds, b);
do bnds(self, a_bounds.lb, a_bounds.ub).then {
self.infcx().set(vb, a_id, root(a_bounds, nde_a.rank));
ok(b)
}
}
}
}

View File

@@ -0,0 +1,144 @@
import combine::*;
import lattice::*;
import to_str::to_str;
enum Lub = combine_fields; // "subtype", "subregion" etc
impl Lub: combine {
fn infcx() -> infer_ctxt { self.infcx }
fn tag() -> ~str { ~"lub" }
fn a_is_expected() -> bool { self.a_is_expected }
fn sub() -> Sub { Sub(*self) }
fn lub() -> Lub { Lub(*self) }
fn glb() -> Glb { Glb(*self) }
fn bot_ty(b: ty::t) -> cres<ty::t> { ok(b) }
fn ty_bot(b: ty::t) -> cres<ty::t> { self.bot_ty(b) } // commutative
fn mts(a: ty::mt, b: ty::mt) -> cres<ty::mt> {
let tcx = self.infcx.tcx;
debug!{"%s.mts(%s, %s)",
self.tag(),
mt_to_str(tcx, a),
mt_to_str(tcx, b)};
let m = if a.mutbl == b.mutbl {
a.mutbl
} else {
m_const
};
match m {
m_imm | m_const => {
self.tys(a.ty, b.ty).chain(|t| ok({ty: t, mutbl: m}) )
}
m_mutbl => {
self.infcx.try(|| {
eq_tys(&self, a.ty, b.ty).then(|| {
ok({ty: a.ty, mutbl: m})
})
}).chain_err(|_e| {
self.tys(a.ty, b.ty).chain(|t| {
ok({ty: t, mutbl: m_const})
})
})
}
}
}
fn contratys(a: ty::t, b: ty::t) -> cres<ty::t> {
Glb(*self).tys(a, b)
}
// XXX: Wrong.
fn protos(p1: ty::fn_proto, p2: ty::fn_proto) -> cres<ty::fn_proto> {
match (p1, p2) {
(ty::proto_bare, _) => ok(p2),
(_, ty::proto_bare) => ok(p1),
(ty::proto_vstore(v1), ty::proto_vstore(v2)) => {
self.infcx.try(|| {
do self.vstores(terr_fn, v1, v2).chain |vs| {
ok(ty::proto_vstore(vs))
}
}).chain_err(|_err| {
// XXX: Totally unsound, but fixed up later.
ok(ty::proto_vstore(ty::vstore_slice(ty::re_static)))
})
}
}
}
fn purities(a: purity, b: purity) -> cres<purity> {
match (a, b) {
(unsafe_fn, _) | (_, unsafe_fn) => ok(unsafe_fn),
(impure_fn, _) | (_, impure_fn) => ok(impure_fn),
(extern_fn, _) | (_, extern_fn) => ok(extern_fn),
(pure_fn, pure_fn) => ok(pure_fn)
}
}
fn ret_styles(r1: ret_style, r2: ret_style) -> cres<ret_style> {
match (r1, r2) {
(ast::return_val, _) |
(_, ast::return_val) => ok(ast::return_val),
(ast::noreturn, ast::noreturn) => ok(ast::noreturn)
}
}
fn contraregions(a: ty::region, b: ty::region) -> cres<ty::region> {
return Glb(*self).regions(a, b);
}
fn regions(a: ty::region, b: ty::region) -> cres<ty::region> {
debug!{"%s.regions(%?, %?)",
self.tag(),
a.to_str(self.infcx),
b.to_str(self.infcx)};
do indent {
self.infcx.region_vars.lub_regions(self.span, a, b)
}
}
// Traits please:
fn tys(a: ty::t, b: ty::t) -> cres<ty::t> {
lattice_tys(&self, a, b)
}
fn flds(a: ty::field, b: ty::field) -> cres<ty::field> {
super_flds(&self, a, b)
}
fn vstores(vk: ty::terr_vstore_kind,
a: ty::vstore, b: ty::vstore) -> cres<ty::vstore> {
super_vstores(&self, vk, a, b)
}
fn modes(a: ast::mode, b: ast::mode) -> cres<ast::mode> {
super_modes(&self, a, b)
}
fn args(a: ty::arg, b: ty::arg) -> cres<ty::arg> {
super_args(&self, a, b)
}
fn fns(a: &ty::fn_ty, b: &ty::fn_ty) -> cres<ty::fn_ty> {
super_fns(&self, a, b)
}
fn substs(as: &ty::substs, bs: &ty::substs) -> cres<ty::substs> {
super_substs(&self, as, bs)
}
fn tps(as: &[ty::t], bs: &[ty::t]) -> cres<~[ty::t]> {
super_tps(&self, as, bs)
}
fn self_tys(a: option<ty::t>, b: option<ty::t>) -> cres<option<ty::t>> {
super_self_tys(&self, a, b)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,234 @@
// Resolution is the process of removing type variables and replacing
// them with their inferred values. Unfortunately our inference has
// become fairly complex and so there are a number of options to
// control *just how much* you want to resolve and how you want to do
// it.
//
// # Controlling the scope of resolution
//
// The options resolve_* determine what kinds of variables get
// resolved. Generally resolution starts with a top-level type
// variable; we will always resolve this. However, once we have
// resolved that variable, we may end up with a type that still
// contains type variables. For example, if we resolve `<T0>` we may
// end up with something like `[<T1>]`. If the option
// `resolve_nested_tvar` is passed, we will then go and recursively
// resolve `<T1>`.
//
// The options `resolve_rvar` and `resolve_ivar` control whether we
// resolve region and integral variables, respectively.
//
// # What do if things are unconstrained
//
// Sometimes we will encounter a variable that has no constraints, and
// therefore cannot sensibly be mapped to any particular result. By
// default, we will leave such variables as is (so you will get back a
// variable in your result). The options force_* will cause the
// resolution to fail in this case intead, except for the case of
// integral variables, which resolve to `int` if forced.
//
// # resolve_all and force_all
//
// The options are a bit set, so you can use the *_all to resolve or
// force all kinds of variables (including those we may add in the
// future). If you want to resolve everything but one type, you are
// probably better off writing `resolve_all - resolve_ivar`.
import integral::*;
import to_str::to_str;
const resolve_nested_tvar: uint = 0b00000001;
const resolve_rvar: uint = 0b00000010;
const resolve_ivar: uint = 0b00000100;
const resolve_all: uint = 0b00000111;
const force_tvar: uint = 0b00010000;
const force_rvar: uint = 0b00100000;
const force_ivar: uint = 0b01000000;
const force_all: uint = 0b01110000;
const not_regions: uint = !(force_rvar | resolve_rvar);
const resolve_and_force_all_but_regions: uint =
(resolve_all | force_all) & not_regions;
type resolve_state_ = {
infcx: infer_ctxt,
modes: uint,
mut err: option<fixup_err>,
mut v_seen: ~[tv_vid]
};
enum resolve_state {
resolve_state_(@resolve_state_)
}
fn resolver(infcx: infer_ctxt, modes: uint) -> resolve_state {
resolve_state_(@{infcx: infcx,
modes: modes,
mut err: none,
mut v_seen: ~[]})
}
impl resolve_state {
fn should(mode: uint) -> bool {
(self.modes & mode) == mode
}
fn resolve_type_chk(typ: ty::t) -> fres<ty::t> {
self.err = none;
debug!{"Resolving %s (modes=%x)",
ty_to_str(self.infcx.tcx, typ),
self.modes};
// n.b. This is a hokey mess because the current fold doesn't
// allow us to pass back errors in any useful way.
assert vec::is_empty(self.v_seen);
let rty = indent(|| self.resolve_type(typ) );
assert vec::is_empty(self.v_seen);
match self.err {
none => {
debug!{"Resolved to %s (modes=%x)",
ty_to_str(self.infcx.tcx, rty),
self.modes};
return ok(rty);
}
some(e) => return err(e)
}
}
fn resolve_region_chk(orig: ty::region) -> fres<ty::region> {
self.err = none;
let resolved = indent(|| self.resolve_region(orig) );
match self.err {
none => ok(resolved),
some(e) => err(e)
}
}
fn resolve_type(typ: ty::t) -> ty::t {
debug!{"resolve_type(%s)", typ.to_str(self.infcx)};
indent(fn&() -> ty::t {
if !ty::type_needs_infer(typ) { return typ; }
match ty::get(typ).struct {
ty::ty_var(vid) => {
self.resolve_ty_var(vid)
}
ty::ty_var_integral(vid) => {
self.resolve_ty_var_integral(vid)
}
_ => {
if !self.should(resolve_rvar) &&
!self.should(resolve_nested_tvar) {
// shortcircuit for efficiency
typ
} else {
ty::fold_regions_and_ty(
self.infcx.tcx, typ,
|r| self.resolve_region(r),
|t| self.resolve_nested_tvar(t),
|t| self.resolve_nested_tvar(t))
}
}
}
})
}
fn resolve_nested_tvar(typ: ty::t) -> ty::t {
debug!{"Resolve_if_deep(%s)", typ.to_str(self.infcx)};
if !self.should(resolve_nested_tvar) {
typ
} else {
self.resolve_type(typ)
}
}
fn resolve_region(orig: ty::region) -> ty::region {
debug!{"Resolve_region(%s)", orig.to_str(self.infcx)};
match orig {
ty::re_var(rid) => self.resolve_region_var(rid),
_ => orig
}
}
fn resolve_region_var(rid: region_vid) -> ty::region {
if !self.should(resolve_rvar) {
return ty::re_var(rid)
}
self.infcx.region_vars.resolve_var(rid)
}
fn assert_not_rvar(rid: region_vid, r: ty::region) {
match r {
ty::re_var(rid2) => {
self.err = some(region_var_bound_by_region_var(rid, rid2));
}
_ => { }
}
}
fn resolve_ty_var(vid: tv_vid) -> ty::t {
if vec::contains(self.v_seen, vid) {
self.err = some(cyclic_ty(vid));
return ty::mk_var(self.infcx.tcx, vid);
} else {
vec::push(self.v_seen, vid);
let tcx = self.infcx.tcx;
// 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.
let nde = self.infcx.get(&self.infcx.ty_var_bindings, vid);
let bounds = nde.possible_types;
let t1 = match bounds {
{ ub:_, lb:some(t) } if !type_is_bot(t) => self.resolve_type(t),
{ ub:some(t), lb:_ } => self.resolve_type(t),
{ ub:_, lb:some(t) } => self.resolve_type(t),
{ ub:none, lb:none } => {
if self.should(force_tvar) {
self.err = some(unresolved_ty(vid));
}
ty::mk_var(tcx, vid)
}
};
vec::pop(self.v_seen);
return t1;
}
}
fn resolve_ty_var_integral(vid: tvi_vid) -> ty::t {
if !self.should(resolve_ivar) {
return ty::mk_var_integral(self.infcx.tcx, vid);
}
let nde = self.infcx.get(&self.infcx.ty_var_integral_bindings, vid);
let pt = nde.possible_types;
// If there's only one type in the set of possible types, then
// that's the answer.
match single_type_contained_in(self.infcx.tcx, pt) {
some(t) => t,
none => {
if self.should(force_ivar) {
// As a last resort, default to int.
let ty = ty::mk_int(self.infcx.tcx);
self.infcx.set(
&self.infcx.ty_var_integral_bindings, vid,
root(convert_integral_ty_to_int_ty_set(self.infcx.tcx,
ty),
nde.rank));
ty
} else {
ty::mk_var_integral(self.infcx.tcx, vid)
}
}
}
}
}

View File

@@ -0,0 +1,199 @@
import combine::*;
import unify::*;
import to_str::to_str;
enum Sub = combine_fields; // "subtype", "subregion" etc
impl Sub: combine {
fn infcx() -> infer_ctxt { self.infcx }
fn tag() -> ~str { ~"sub" }
fn a_is_expected() -> bool { self.a_is_expected }
fn sub() -> Sub { Sub(*self) }
fn lub() -> Lub { Lub(*self) }
fn glb() -> Glb { Glb(*self) }
fn contratys(a: ty::t, b: ty::t) -> cres<ty::t> {
let opp = combine_fields {
a_is_expected: !self.a_is_expected, with *self
};
Sub(opp).tys(b, a)
}
fn contraregions(a: ty::region, b: ty::region) -> cres<ty::region> {
let opp = combine_fields {
a_is_expected: !self.a_is_expected, with *self
};
Sub(opp).regions(b, a)
}
fn regions(a: ty::region, b: ty::region) -> cres<ty::region> {
debug!{"%s.regions(%s, %s)",
self.tag(),
a.to_str(self.infcx),
b.to_str(self.infcx)};
do indent {
match self.infcx.region_vars.make_subregion(self.span, a, b) {
ok(()) => ok(a),
err(e) => err(e)
}
}
}
fn mts(a: ty::mt, b: ty::mt) -> cres<ty::mt> {
debug!{"mts(%s <: %s)", a.to_str(self.infcx), b.to_str(self.infcx)};
if a.mutbl != b.mutbl && b.mutbl != m_const {
return err(ty::terr_mutability);
}
match b.mutbl {
m_mutbl => {
// If supertype is mut, subtype must match exactly
// (i.e., invariant if mut):
eq_tys(&self, a.ty, b.ty).then(|| ok(a) )
}
m_imm | m_const => {
// Otherwise we can be covariant:
self.tys(a.ty, b.ty).chain(|_t| ok(a) )
}
}
}
fn protos(a: ty::fn_proto, b: ty::fn_proto) -> cres<ty::fn_proto> {
match (a, b) {
(ty::proto_bare, _) =>
ok(ty::proto_bare),
(ty::proto_vstore(ty::vstore_box),
ty::proto_vstore(ty::vstore_slice(_))) =>
ok(ty::proto_vstore(ty::vstore_box)),
(ty::proto_vstore(ty::vstore_uniq),
ty::proto_vstore(ty::vstore_slice(_))) =>
ok(ty::proto_vstore(ty::vstore_uniq)),
(_, ty::proto_bare) =>
err(ty::terr_proto_mismatch(expected_found(&self, a, b))),
(ty::proto_vstore(vs_a), ty::proto_vstore(vs_b)) => {
do self.vstores(ty::terr_fn, vs_a, vs_b).chain |vs_c| {
ok(ty::proto_vstore(vs_c))
}
}
}
}
fn purities(a: purity, b: purity) -> cres<purity> {
self.lub().purities(a, b).compare(b, || {
ty::terr_purity_mismatch(expected_found(&self, a, b))
})
}
fn ret_styles(a: ret_style, b: ret_style) -> cres<ret_style> {
self.lub().ret_styles(a, b).compare(b, || {
ty::terr_ret_style_mismatch(expected_found(&self, a, b))
})
}
fn tys(a: ty::t, b: ty::t) -> cres<ty::t> {
debug!{"%s.tys(%s, %s)", self.tag(),
a.to_str(self.infcx), b.to_str(self.infcx)};
if a == b { return ok(a); }
do indent {
match (ty::get(a).struct, ty::get(b).struct) {
(ty::ty_bot, _) => {
ok(a)
}
(ty::ty_var(a_id), ty::ty_var(b_id)) => {
var_sub_var(&self, a_id, b_id).then(|| ok(a) )
}
(ty::ty_var(a_id), _) => {
var_sub_t(&self, a_id, b).then(|| ok(a) )
}
(_, ty::ty_var(b_id)) => {
t_sub_var(&self, a, b_id).then(|| ok(a) )
}
(_, ty::ty_bot) => {
err(ty::terr_sorts(expected_found(&self, a, b)))
}
_ => {
super_tys(&self, a, b)
}
}
}
}
fn fns(a: &ty::fn_ty, b: &ty::fn_ty) -> cres<ty::fn_ty> {
// Rather than checking the subtype relationship between `a` and `b`
// as-is, we need to do some extra work here in order to make sure
// that function subtyping works correctly with respect to regions
// (issue #2263).
// First, we instantiate each bound region in the subtype with a fresh
// region variable.
let {fn_ty: a_fn_ty, _} = {
do replace_bound_regions_in_fn_ty(self.infcx.tcx, @nil,
none, a) |br| {
// N.B.: The name of the bound region doesn't have
// anything to do with the region variable that's created
// for it. The only thing we're doing with `br` here is
// using it in the debug message.
//
// NDM--we should not be used dummy_sp() here, but
// rather passing in the span or something like that.
let rvar = self.infcx.next_region_var_nb(dummy_sp());
debug!{"Bound region %s maps to %s",
bound_region_to_str(self.infcx.tcx, br),
region_to_str(self.infcx.tcx, rvar)};
rvar
}
};
// Second, we instantiate each bound region in the supertype with a
// fresh concrete region.
let {fn_ty: b_fn_ty, _} = {
do replace_bound_regions_in_fn_ty(self.infcx.tcx, @nil,
none, b) |br| {
// FIXME: eventually re_skolemized (issue #2263)
ty::re_bound(br)
}
};
// Try to compare the supertype and subtype now that they've been
// instantiated.
super_fns(&self, &a_fn_ty, &b_fn_ty)
}
// Traits please:
fn flds(a: ty::field, b: ty::field) -> cres<ty::field> {
super_flds(&self, a, b)
}
fn vstores(vk: ty::terr_vstore_kind,
a: ty::vstore, b: ty::vstore) -> cres<ty::vstore> {
super_vstores(&self, vk, a, b)
}
fn modes(a: ast::mode, b: ast::mode) -> cres<ast::mode> {
super_modes(&self, a, b)
}
fn args(a: ty::arg, b: ty::arg) -> cres<ty::arg> {
super_args(&self, a, b)
}
fn substs(as: &ty::substs, bs: &ty::substs) -> cres<ty::substs> {
super_substs(&self, as, bs)
}
fn tps(as: &[ty::t], bs: &[ty::t]) -> cres<~[ty::t]> {
super_tps(&self, as, bs)
}
fn self_tys(a: option<ty::t>, b: option<ty::t>) -> cres<option<ty::t>> {
super_self_tys(&self, a, b)
}
}

View File

@@ -0,0 +1,60 @@
import integral::{int_ty_set};
import unify::{var_value, redirect, root};
trait to_str {
fn to_str(cx: infer_ctxt) -> ~str;
}
impl ty::t: to_str {
fn to_str(cx: infer_ctxt) -> ~str {
ty_to_str(cx.tcx, self)
}
}
impl ty::mt: to_str {
fn to_str(cx: infer_ctxt) -> ~str {
mt_to_str(cx.tcx, self)
}
}
impl ty::region: to_str {
fn to_str(cx: infer_ctxt) -> ~str {
util::ppaux::region_to_str(cx.tcx, self)
}
}
impl<V:copy to_str> bound<V>: to_str {
fn to_str(cx: infer_ctxt) -> ~str {
match self {
some(v) => v.to_str(cx),
none => ~"none"
}
}
}
impl<T:copy to_str> bounds<T>: to_str {
fn to_str(cx: infer_ctxt) -> ~str {
fmt!{"{%s <: %s}",
self.lb.to_str(cx),
self.ub.to_str(cx)}
}
}
impl int_ty_set: to_str {
fn to_str(_cx: infer_ctxt) -> ~str {
match self {
int_ty_set(v) => uint::to_str(v, 10u)
}
}
}
impl<V:copy vid, T:copy to_str> var_value<V, T>: to_str {
fn to_str(cx: infer_ctxt) -> ~str {
match self {
redirect(vid) => fmt!{"redirect(%s)", vid.to_str()},
root(pt, rk) => fmt!{"root(%s, %s)", pt.to_str(cx),
uint::to_str(rk, 10u)}
}
}
}

View File

@@ -0,0 +1,379 @@
import combine::combine;
import integral::*;
import to_str::to_str;
enum var_value<V:copy, T:copy> {
redirect(V),
root(T, uint),
}
struct vals_and_bindings<V:copy, T:copy> {
vals: smallintmap<var_value<V, T>>;
mut bindings: ~[(V, var_value<V, T>)];
}
struct node<V:copy, T:copy> {
root: V;
possible_types: T;
rank: uint;
}
impl infer_ctxt {
fn get<V:copy vid, T:copy>(
vb: &vals_and_bindings<V, T>, vid: V) -> node<V, T> {
let vid_u = vid.to_uint();
match vb.vals.find(vid_u) {
none => {
self.tcx.sess.bug(fmt!{"failed lookup of vid `%u`", vid_u});
}
some(var_val) => {
match var_val {
redirect(vid) => {
let node = self.get(vb, vid);
if node.root != vid {
// Path compression
vb.vals.insert(vid.to_uint(), redirect(node.root));
}
node
}
root(pt, rk) => {
node {root: vid, possible_types: pt, rank: rk}
}
}
}
}
}
fn set<V:copy vid, T:copy to_str>(
vb: &vals_and_bindings<V, T>, vid: V,
+new_v: var_value<V, T>) {
let old_v = vb.vals.get(vid.to_uint());
vec::push(vb.bindings, (vid, old_v));
vb.vals.insert(vid.to_uint(), new_v);
debug!{"Updating variable %s from %s to %s",
vid.to_str(), old_v.to_str(self), new_v.to_str(self)};
}
}
// Combines the two bounds into a more general bound.
fn merge_bnd<C: combine>(
self: &C, a: bound<ty::t>, b: bound<ty::t>,
merge_op: fn(ty::t,ty::t) -> cres<ty::t>) -> cres<bound<ty::t>> {
debug!("merge_bnd(%s,%s)",
a.to_str(self.infcx()),
b.to_str(self.infcx()));
let _r = indenter();
match (a, b) {
(none, none) => ok(none),
(some(_), none) => ok(a),
(none, some(_)) => ok(b),
(some(v_a), some(v_b)) => {
do merge_op(v_a, v_b).chain |v| {
ok(some(v))
}
}
}
}
fn merge_bnds<C: combine>(
self: &C, a: bounds<ty::t>, b: bounds<ty::t>,
lub: fn(ty::t,ty::t) -> cres<ty::t>,
glb: fn(ty::t,ty::t) -> cres<ty::t>) -> cres<bounds<ty::t>> {
let _r = indenter();
do merge_bnd(self, a.ub, b.ub, glb).chain |ub| {
debug!{"glb of ubs %s and %s is %s",
a.ub.to_str(self.infcx()),
b.ub.to_str(self.infcx()),
ub.to_str(self.infcx())};
do merge_bnd(self, a.lb, b.lb, lub).chain |lb| {
debug!{"lub of lbs %s and %s is %s",
a.lb.to_str(self.infcx()),
b.lb.to_str(self.infcx()),
lb.to_str(self.infcx())};
ok({lb: lb, ub: ub})
}
}
}
// Updates the bounds for the variable `v_id` to be the intersection
// of `a` and `b`. That is, the new bounds for `v_id` will be
// a bounds c such that:
// c.ub <: a.ub
// c.ub <: b.ub
// a.lb <: c.lb
// b.lb <: c.lb
// If this cannot be achieved, the result is failure.
fn set_var_to_merged_bounds<C: combine>(
self: &C,
v_id: ty::tv_vid,
a: bounds<ty::t>,
b: bounds<ty::t>,
rank: uint) -> ures {
let vb = &self.infcx().ty_var_bindings;
// Think of the two diamonds, we want to find the
// intersection. There are basically four possibilities (you
// can swap A/B in these pictures):
//
// A A
// / \ / \
// / B \ / B \
// / / \ \ / / \ \
// * * * * * / * *
// \ \ / / \ / /
// \ B / / \ / /
// \ / * \ /
// A \ / A
// B
debug!{"merge(%s,%s,%s)",
v_id.to_str(),
a.to_str(self.infcx()),
b.to_str(self.infcx())};
// First, relate the lower/upper bounds of A and B.
// Note that these relations *must* hold for us to
// to be able to merge A and B at all, and relating
// them explicitly gives the type inferencer more
// information and helps to produce tighter bounds
// when necessary.
do indent {
do bnds(self, a.lb, b.ub).then {
do bnds(self, b.lb, a.ub).then {
do merge_bnd(self, a.ub, b.ub,
|x, y| self.glb().tys(x, y)).chain |ub| {
do merge_bnd(self, a.lb, b.lb,
|x, y| self.lub().tys(x, y)).chain |lb| {
let bounds = {lb: lb, ub: ub};
debug!{"merge(%s): bounds=%s",
v_id.to_str(),
bounds.to_str(self.infcx())};
// the new bounds must themselves
// be relatable:
do bnds(self, bounds.lb, bounds.ub).then {
self.infcx().set(vb, v_id, root(bounds, rank));
uok()
}
}
}
}
}
}
}
/// Ensure that variable A is a subtype of variable B. This is a
/// subtle and tricky process, as described in detail at the top
/// of infer.rs
fn var_sub_var<C: combine>(self: &C,
a_id: ty::tv_vid,
b_id: ty::tv_vid) -> ures {
let vb = &self.infcx().ty_var_bindings;
// Need to make sub_id a subtype of sup_id.
let nde_a = self.infcx().get(vb, a_id);
let nde_b = self.infcx().get(vb, b_id);
let a_id = nde_a.root;
let b_id = nde_b.root;
let a_bounds = nde_a.possible_types;
let b_bounds = nde_b.possible_types;
debug!{"vars(%s=%s <: %s=%s)",
a_id.to_str(), a_bounds.to_str(self.infcx()),
b_id.to_str(), b_bounds.to_str(self.infcx())};
if a_id == b_id { return uok(); }
// If both A's UB and B's LB have already been bound to types,
// see if we can make those types subtypes.
match (a_bounds.ub, b_bounds.lb) {
(some(a_ub), some(b_lb)) => {
let r = self.infcx().try(|| self.sub().tys(a_ub, b_lb));
match r {
ok(_ty) => return result::ok(()),
err(_) => { /*fallthrough */ }
}
}
_ => { /*fallthrough*/ }
}
// Otherwise, we need to merge A and B so as to guarantee that
// A remains a subtype of B. Actually, there are other options,
// but that's the route we choose to take.
// Rank optimization
// Make the node with greater rank the parent of the node with
// smaller rank.
if nde_a.rank > nde_b.rank {
debug!{"vars(): a has smaller rank"};
// a has greater rank, so a should become b's parent,
// i.e., b should redirect to a.
self.infcx().set(vb, b_id, redirect(a_id));
set_var_to_merged_bounds(
self, a_id, a_bounds, b_bounds, nde_a.rank)
} else if nde_a.rank < nde_b.rank {
debug!{"vars(): b has smaller rank"};
// b has greater rank, so a should redirect to b.
self.infcx().set(vb, a_id, redirect(b_id));
set_var_to_merged_bounds(
self, b_id, a_bounds, b_bounds, nde_b.rank)
} else {
debug!{"vars(): a and b have equal rank"};
assert nde_a.rank == nde_b.rank;
// If equal, just redirect one to the other and increment
// the other's rank. We choose arbitrarily to redirect b
// to a and increment a's rank.
self.infcx().set(vb, b_id, redirect(a_id));
set_var_to_merged_bounds(
self, a_id, a_bounds, b_bounds, nde_a.rank + 1u
)
}
}
/// make variable a subtype of T
fn var_sub_t<C: combine>(self: &C, a_id: ty::tv_vid, b: ty::t) -> ures {
let vb = &self.infcx().ty_var_bindings;
let nde_a = self.infcx().get(vb, a_id);
let a_id = nde_a.root;
let a_bounds = nde_a.possible_types;
debug!{"var_sub_t(%s=%s <: %s)",
a_id.to_str(),
a_bounds.to_str(self.infcx()),
b.to_str(self.infcx())};
let b_bounds = {lb: none, ub: some(b)};
set_var_to_merged_bounds(self, a_id, a_bounds, b_bounds, nde_a.rank)
}
/// make T a subtype of variable
fn t_sub_var<C: combine>(self: &C, a: ty::t, b_id: ty::tv_vid) -> ures {
let vb = &self.infcx().ty_var_bindings;
let a_bounds = {lb: some(a), ub: none};
let nde_b = self.infcx().get(vb, b_id);
let b_id = nde_b.root;
let b_bounds = nde_b.possible_types;
debug!{"t_sub_var(%s <: %s=%s)",
a.to_str(self.infcx()),
b_id.to_str(),
b_bounds.to_str(self.infcx())};
set_var_to_merged_bounds(self, b_id, a_bounds, b_bounds, nde_b.rank)
}
fn bnds<C: combine>(
self: &C, a: bound<ty::t>, b: bound<ty::t>) -> ures {
debug!{"bnds(%s <: %s)", a.to_str(self.infcx()), b.to_str(self.infcx())};
do indent {
match (a, b) {
(none, none) |
(some(_), none) |
(none, some(_)) => {
uok()
}
(some(t_a), some(t_b)) => {
self.sub().tys(t_a, t_b).to_ures()
}
}
}
}
// ______________________________________________________________________
// Integral variables
impl infer_ctxt {
fn vars_integral(a_id: ty::tvi_vid, b_id: ty::tvi_vid) -> ures {
let vb = &self.ty_var_integral_bindings;
let nde_a = self.get(vb, a_id);
let nde_b = self.get(vb, b_id);
let a_id = nde_a.root;
let b_id = nde_b.root;
let a_pt = nde_a.possible_types;
let b_pt = nde_b.possible_types;
// If we're already dealing with the same two variables,
// there's nothing to do.
if a_id == b_id { return uok(); }
// Otherwise, take the intersection of the two sets of
// possible types.
let intersection = intersection(a_pt, b_pt);
if *intersection == INT_TY_SET_EMPTY {
return err(ty::terr_no_integral_type);
}
// Rank optimization
if nde_a.rank > nde_b.rank {
debug!{"vars_integral(): a has smaller rank"};
// a has greater rank, so a should become b's parent,
// i.e., b should redirect to a.
self.set(vb, a_id, root(intersection, nde_a.rank));
self.set(vb, b_id, redirect(a_id));
} else if nde_a.rank < nde_b.rank {
debug!{"vars_integral(): b has smaller rank"};
// b has greater rank, so a should redirect to b.
self.set(vb, b_id, root(intersection, nde_b.rank));
self.set(vb, a_id, redirect(b_id));
} else {
debug!{"vars_integral(): a and b have equal rank"};
assert nde_a.rank == nde_b.rank;
// If equal, just redirect one to the other and increment
// the other's rank. We choose arbitrarily to redirect b
// to a and increment a's rank.
self.set(vb, a_id, root(intersection, nde_a.rank + 1u));
self.set(vb, b_id, redirect(a_id));
};
uok()
}
fn var_integral_sub_t(a_id: ty::tvi_vid, b: ty::t) -> ures {
assert ty::type_is_integral(b);
let vb = &self.ty_var_integral_bindings;
let nde_a = self.get(vb, a_id);
let a_id = nde_a.root;
let a_pt = nde_a.possible_types;
let intersection =
intersection(a_pt,
convert_integral_ty_to_int_ty_set(self.tcx, b));
if *intersection == INT_TY_SET_EMPTY {
return err(ty::terr_no_integral_type);
}
self.set(vb, a_id, root(intersection, nde_a.rank));
uok()
}
fn t_sub_var_integral(a: ty::t, b_id: ty::tvi_vid) -> ures {
assert ty::type_is_integral(a);
let vb = &self.ty_var_integral_bindings;
let nde_b = self.get(vb, b_id);
let b_id = nde_b.root;
let b_pt = nde_b.possible_types;
let intersection =
intersection(b_pt,
convert_integral_ty_to_int_ty_set(self.tcx, a));
if *intersection == INT_TY_SET_EMPTY {
return err(ty::terr_no_integral_type);
}
self.set(vb, b_id, root(intersection, nde_b.rank));
uok()
}
}

View File

@@ -1,16 +1,16 @@
import result::result; import result::result;
trait region_scope { trait region_scope {
fn anon_region() -> result<ty::region, ~str>; fn anon_region(span: span) -> result<ty::region, ~str>;
fn named_region(id: ast::ident) -> result<ty::region, ~str>; fn named_region(span: span, id: ast::ident) -> result<ty::region, ~str>;
} }
enum empty_rscope { empty_rscope } enum empty_rscope { empty_rscope }
impl empty_rscope: region_scope { impl empty_rscope: region_scope {
fn anon_region() -> result<ty::region, ~str> { fn anon_region(_span: span) -> result<ty::region, ~str> {
result::ok(ty::re_static) result::ok(ty::re_static)
} }
fn named_region(id: ast::ident) -> result<ty::region, ~str> { fn named_region(_span: span, id: ast::ident) -> result<ty::region, ~str> {
if *id == ~"static" { result::ok(ty::re_static) } if *id == ~"static" { result::ok(ty::re_static) }
else { result::err(~"only the static region is allowed here") } else { result::err(~"only the static region is allowed here") }
} }
@@ -18,7 +18,7 @@ impl empty_rscope: region_scope {
enum type_rscope = bool; enum type_rscope = bool;
impl type_rscope: region_scope { impl type_rscope: region_scope {
fn anon_region() -> result<ty::region, ~str> { fn anon_region(_span: span) -> result<ty::region, ~str> {
if *self { if *self {
result::ok(ty::re_bound(ty::br_self)) result::ok(ty::re_bound(ty::br_self))
} else { } else {
@@ -26,10 +26,11 @@ impl type_rscope: region_scope {
must be declared with a region bound") must be declared with a region bound")
} }
} }
fn named_region(id: ast::ident) -> result<ty::region, ~str> { fn named_region(span: span, id: ast::ident) -> result<ty::region, ~str> {
do empty_rscope.named_region(id).chain_err |_e| { do empty_rscope.named_region(span, id).chain_err |_e| {
if *id == ~"self" { self.anon_region() } if *id == ~"self" {
else { self.anon_region(span)
} else {
result::err(~"named regions other than `self` are not \ result::err(~"named regions other than `self` are not \
allowed as part of a type declaration") allowed as part of a type declaration")
} }
@@ -43,11 +44,11 @@ fn in_anon_rscope<RS: region_scope copy owned>(self: RS, r: ty::region)
@anon_rscope({anon: r, base: self as region_scope}) @anon_rscope({anon: r, base: self as region_scope})
} }
impl @anon_rscope: region_scope { impl @anon_rscope: region_scope {
fn anon_region() -> result<ty::region, ~str> { fn anon_region(_span: span) -> result<ty::region, ~str> {
result::ok(self.anon) result::ok(self.anon)
} }
fn named_region(id: ast::ident) -> result<ty::region, ~str> { fn named_region(span: span, id: ast::ident) -> result<ty::region, ~str> {
self.base.named_region(id) self.base.named_region(span, id)
} }
} }
@@ -58,11 +59,11 @@ fn in_binding_rscope<RS: region_scope copy owned>(self: RS)
@binding_rscope({base: base}) @binding_rscope({base: base})
} }
impl @binding_rscope: region_scope { impl @binding_rscope: region_scope {
fn anon_region() -> result<ty::region, ~str> { fn anon_region(_span: span) -> result<ty::region, ~str> {
result::ok(ty::re_bound(ty::br_anon)) result::ok(ty::re_bound(ty::br_anon))
} }
fn named_region(id: ast::ident) -> result<ty::region, ~str> { fn named_region(span: span, id: ast::ident) -> result<ty::region, ~str> {
do self.base.named_region(id).chain_err |_e| { do self.base.named_region(span, id).chain_err |_e| {
result::ok(ty::re_bound(ty::br_named(id))) result::ok(ty::re_bound(ty::br_named(id)))
} }
} }

View File

@@ -66,7 +66,19 @@ mod middle {
} }
mod rscope; mod rscope;
mod astconv; mod astconv;
mod infer; mod infer {
mod assignment;
mod combine;
mod glb;
mod integral;
mod lattice;
mod lub;
mod region_var_bindings;
mod resolve;
mod sub;
mod to_str;
mod unify;
}
mod collect; mod collect;
mod coherence; mod coherence;
} }

View File

@@ -21,10 +21,31 @@ import syntax::{ast, ast_util};
import syntax::ast_map; import syntax::ast_map;
import driver::session::session; import driver::session::session;
/// Returns a string like "reference valid for the block at 27:31 in foo.rs" fn note_and_explain_region(cx: ctxt, prefix: ~str, region: ty::region) {
/// that attempts to explain a lifetime in a way it might plausibly be match explain_region_and_span(cx, region) {
/// understood. (str, some(span)) => {
cx.sess.span_note(
span,
fmt!("%s %s", prefix, str));
}
(str, none) => {
cx.sess.note(
fmt!("%s %s", prefix, str));
}
}
}
/// Returns a string like "the block at 27:31" that attempts to explain a
/// lifetime in a way it might plausibly be understood.
fn explain_region(cx: ctxt, region: ty::region) -> ~str { fn explain_region(cx: ctxt, region: ty::region) -> ~str {
let (res, _) = explain_region_and_span(cx, region);
return res;
}
fn explain_region_and_span(cx: ctxt, region: ty::region)
-> (~str, option<span>)
{
return match region { return match region {
re_scope(node_id) => { re_scope(node_id) => {
match cx.items.find(node_id) { match cx.items.find(node_id) {
@@ -33,14 +54,15 @@ fn explain_region(cx: ctxt, region: ty::region) -> ~str {
} }
some(ast_map::node_expr(expr)) => { some(ast_map::node_expr(expr)) => {
match expr.node { match expr.node {
ast::expr_call(*) => { explain_span(cx, ~"call", expr.span) } ast::expr_call(*) => explain_span(cx, ~"call", expr.span),
ast::expr_match(*) => { explain_span(cx, ~"alt", expr.span) } ast::expr_match(*) => explain_span(cx, ~"alt", expr.span),
_ => { explain_span(cx, ~"expression", expr.span) } _ => explain_span(cx, ~"expression", expr.span)
} }
} }
some(_) | none => { some(_) | none => {
// this really should not happen // this really should not happen
fmt!{"unknown scope: %d. Please report a bug.", node_id} (fmt!("unknown scope: %d. Please report a bug.", node_id),
none)
} }
} }
} }
@@ -48,30 +70,34 @@ fn explain_region(cx: ctxt, region: ty::region) -> ~str {
re_free(id, br) => { re_free(id, br) => {
match cx.items.find(id) { match cx.items.find(id) {
some(ast_map::node_block(blk)) => { some(ast_map::node_block(blk)) => {
fmt!{"the lifetime %s as defined on %s", let (msg, opt_span) = explain_span(cx, ~"block", blk.span);
bound_region_to_str(cx, br), (fmt!("the lifetime %s as defined on %s",
explain_span(cx, ~"block", blk.span)} bound_region_to_str(cx, br), msg),
opt_span)
} }
some(_) | none => { some(_) | none => {
// this really should not happen // this really should not happen
fmt!{"the lifetime %s as defined on node %d", (fmt!("the lifetime %s as defined on node %d",
bound_region_to_str(cx, br), id} bound_region_to_str(cx, br), id),
none)
} }
} }
} }
re_static => { ~"the static lifetime" } re_static => { (~"the static lifetime", none) }
// I believe these cases should not occur (except when debugging, // I believe these cases should not occur (except when debugging,
// perhaps) // perhaps)
re_var(_) | re_bound(_) => { re_var(_) | re_bound(_) => {
fmt!{"lifetime %?", region} (fmt!("lifetime %?", region), none)
} }
}; };
fn explain_span(cx: ctxt, heading: ~str, span: span) -> ~str { fn explain_span(cx: ctxt, heading: ~str, span: span)
-> (~str, option<span>)
{
let lo = codemap::lookup_char_pos_adj(cx.sess.codemap, span.lo); let lo = codemap::lookup_char_pos_adj(cx.sess.codemap, span.lo);
fmt!{"the %s at %u:%u", heading, lo.line, lo.col} (fmt!{"the %s at %u:%u", heading, lo.line, lo.col}, some(span))
} }
} }
@@ -133,30 +159,23 @@ fn re_scope_id_to_str(cx: ctxt, node_id: ast::node_id) -> ~str {
} }
} }
// In general, if you are giving a region error message,
// you should use `explain_region()` or, better yet,
// `note_and_explain_region()`
fn region_to_str(cx: ctxt, region: region) -> ~str { fn region_to_str(cx: ctxt, region: region) -> ~str {
match region {
re_scope(node_id) => {
if cx.sess.ppregions() { if cx.sess.ppregions() {
fmt!{"&%s", re_scope_id_to_str(cx, node_id)} return fmt!("&%?", region);
} else {
~"&"
}
}
re_bound(br) => {
bound_region_to_str(cx, br)
}
re_free(id, br) => {
if cx.sess.ppregions() {
// For debugging, this version is sometimes helpful:
fmt!{"{%d} %s", id, bound_region_to_str(cx, br)}
} else {
// But this version is what the user expects to see:
bound_region_to_str(cx, br)
}
} }
// These two should not be seen by end-users (very often, anyhow): // These printouts are concise. They do not contain all the information
re_var(id) => fmt!{"&%s", id.to_str()}, // the user might want to diagnose an error, but there is basically no way
// to fit that into a short string. Hence the recommendation to use
// `explain_region()` or `note_and_explain_region()`.
match region {
re_scope(node_id) => ~"&",
re_bound(br) => bound_region_to_str(cx, br),
re_free(id, br) => bound_region_to_str(cx, br),
re_var(id) => ~"&",
re_static => ~"&static" re_static => ~"&static"
} }
} }

View File

@@ -1,4 +1,3 @@
// error-pattern: reference is not valid outside of its lifetime
use std; use std;
import std::arc; import std::arc;
fn main() { fn main() {
@@ -6,6 +5,7 @@ fn main() {
let mut y = none; let mut y = none;
do x.write_downgrade |write_mode| { do x.write_downgrade |write_mode| {
y = some(x.downgrade(write_mode)); y = some(x.downgrade(write_mode));
//~^ ERROR cannot infer an appropriate lifetime
} }
// Adding this line causes a method unification failure instead // Adding this line causes a method unification failure instead
// do (&option::unwrap(y)).read |state| { assert *state == 1; } // do (&option::unwrap(y)).read |state| { assert *state == 1; }

View File

@@ -1,4 +1,4 @@
// error-pattern:expected `fn&<R0>()` but found `*u8` // error-pattern:expected `fn&()` but found `*u8`
extern fn f() { extern fn f() {
} }

View File

@@ -12,9 +12,9 @@ fn repeater<A:copy>(v: @A) -> repeat<A> {
fn main() { fn main() {
// Here, an error results as the type of y is inferred to // Here, an error results as the type of y is inferred to
// repeater<&lt/3> where lt is the block. // repeater<&lt/3> where lt is the block.
let y = { //~ ERROR reference is not valid outside of its lifetime let y = {
let x: &blk/int = &3; let x: &blk/int = &3; //~ ERROR cannot infer an appropriate lifetime
repeater(@x) repeater(@x)
}; };
assert 3 == *(y.get()); //~ ERROR reference is not valid outside of its lifetime assert 3 == *(y.get());
} }

View File

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

View File

@@ -4,10 +4,10 @@ fn foo(cond: bool) {
let mut z: &blk/int; let mut z: &blk/int;
if cond { if cond {
z = &x; z = &x; //~ ERROR cannot infer an appropriate lifetime due to conflicting requirements
} else { } else {
let w: &blk/int = &x; let w: &blk/int = &x;
z = w; //~ ERROR mismatched types z = w;
} }
} }

View File

@@ -4,7 +4,8 @@ enum ast {
} }
fn mk_add_bad1(x: &a/ast, y: &b/ast) -> ast/&a { fn mk_add_bad1(x: &a/ast, y: &b/ast) -> ast/&a {
add(x, y) //~ ERROR mismatched types: expected `&a/ast/&a` but found `&b/ast/&b` add(x, y) //~ ERROR cannot infer an appropriate lifetime
//~^ ERROR cannot infer an appropriate lifetime
} }
fn main() { fn main() {

View File

@@ -4,7 +4,9 @@ enum ast {
} }
fn mk_add_bad2(x: &a/ast, y: &a/ast, z: &ast) -> ast { fn mk_add_bad2(x: &a/ast, y: &a/ast, z: &ast) -> ast {
add(x, y) //~ ERROR mismatched types: expected `ast/&` but found `ast/&a` add(x, y)
//~^ ERROR cannot infer an appropriate lifetime
//~^^ ERROR cannot infer an appropriate lifetime
} }
fn main() { fn main() {

View File

@@ -0,0 +1,10 @@
fn with_int(f: fn(x: &int)) {
let x = 3;
f(&x);
}
fn main() {
let mut x = none;
//~^ ERROR reference is not valid outside of its lifetime
with_int(|y| x = some(y));
}

View File

@@ -0,0 +1,9 @@
fn with_int(f: fn(x: &int)) {
let x = 3;
f(&x);
}
fn main() {
let mut x: option<&int> = none; //~ ERROR cannot infer
with_int(|y| x = some(y));
}

View File

@@ -14,8 +14,9 @@ fn with<R: deref>(f: fn(x: &int) -> R) -> int {
fn return_it() -> int { fn return_it() -> int {
with(|o| o) with(|o| o)
//~^ ERROR reference is not valid outside of its lifetime, & //~^ ERROR reference is not valid outside of its lifetime
//~^^ ERROR reference is not valid outside of its lifetime, & //~^^ ERROR reference is not valid outside of its lifetime
//~^^^ ERROR cannot infer an appropriate lifetime
} }
fn main() { fn main() {

View File

@@ -17,7 +17,7 @@ mod argparse {
impl Flag { impl Flag {
fn set_desc(self, s: &str) -> Flag { fn set_desc(self, s: &str) -> Flag {
Flag { //~ ERROR mismatched types Flag { //~ ERROR cannot infer an appropriate lifetime
name: self.name, name: self.name,
desc: s, desc: s,
max_count: self.max_count, max_count: self.max_count,

View File

@@ -6,7 +6,7 @@ fn with<T>(f: fn(x: &int) -> T) -> T {
fn manip(x: &a/int) -> int { fn manip(x: &a/int) -> int {
let z = do with |y| { select(x, y) }; let z = do with |y| { select(x, y) };
//~^ ERROR reference is not valid outside of its lifetime //~^ ERROR cannot infer an appropriate lifetime
*z *z
} }

View File

@@ -2,17 +2,17 @@ fn ignore<T>(t: T) {}
fn nested(x: &x/int) { fn nested(x: &x/int) {
let y = 3; let y = 3;
let mut ay = &y; let mut ay = &y; //~ ERROR cannot infer an appropriate lifetime
ignore(fn&(z: &z/int) { ignore(fn&(z: &z/int) {
ay = x; ay = x;
ay = &y; ay = &y; //~ ERROR cannot infer an appropriate lifetime
ay = z; //~ ERROR mismatched types ay = z;
}); });
ignore(fn&(z: &z/int) -> &z/int { ignore(fn&(z: &z/int) -> &z/int {
if false { return x; } //~ ERROR mismatched types if false { return x; } //~ ERROR mismatched types
if false { return ay; } //~ ERROR mismatched types if false { return ay; }
return z; return z;
}); });
} }

View File

@@ -9,20 +9,14 @@ fn nested(x: &x/int) { // (1)
z: &z/int) -> &z/int) // A fresh region `z` (3) z: &z/int) -> &z/int) // A fresh region `z` (3)
-> &x/int { -> &x/int {
if false { return z(x, x, x); } //~ ERROR mismatched types: expected `&y/int` but found `&x/int`
if false { return z(x, x, y); } //~ ERROR mismatched types: expected `&y/int` but found `&x/int`
//~^ ERROR mismatched types: expected `&x/int` but found `&y/int`
if false { return z(x, y, x); } if false { return z(x, y, x); }
if false { return z(x, y, y); } //~ ERROR mismatched types: expected `&x/int` but found `&y/int`
if false { return z(y, x, x); } //~ ERROR mismatched types: expected `&x/int` but found `&y/int` if false { return z(x, y, y); }
//~^ ERROR mismatched types: expected `&y/int` but found `&x/int` //~^ ERROR cannot infer an appropriate lifetime
if false { return z(y, x, y); } //~ ERROR mismatched types: expected `&x/int` but found `&y/int`
//~^ ERROR mismatched types: expected `&y/int` but found `&x/int` return z(y, x, x);
//~^^ ERROR mismatched types: expected `&x/int` but found `&y/int`
if false { return z(y, y, x); } //~ ERROR mismatched types: expected `&x/int` but found `&y/int`
if false { return z(y, y, y); } //~ ERROR mismatched types: expected `&x/int` but found `&y/int`
//~^ ERROR mismatched types: expected `&x/int` but found `&y/int` //~^ ERROR mismatched types: expected `&x/int` but found `&y/int`
fail; //~^^ ERROR mismatched types: expected `&y/int` but found `&x/int`
} }
) |foo| { ) |foo| {
@@ -40,8 +34,13 @@ fn nested(x: &x/int) { // (1)
// //
// let f: &x/int = foo(&z, &z, |_x, _y, z| z ); // ERROR mismatched types: expected `&x/int` but found // let f: &x/int = foo(&z, &z, |_x, _y, z| z ); // ERROR mismatched types: expected `&x/int` but found
foo(x, &z, |x, _y, _z| x ); //~ ERROR mismatched types: expected `&z/int` but found `&x/int` foo(x, &z, |x, _y, _z| x); //~ ERROR mismatched types: expected `&z/int` but found `&x/int`
foo(x, &z, |_x, y, _z| y ); //~ ERROR mismatched types: expected `&z/int` but found `&
// Note: originally I had foo(x, &z, ...) here, but in that
// case the region inferencer deduced that this was valid if
// &y==&static, and so inference would succeed but borrow
// check would fail because the lifetime of &z is not &static.
foo(x, x, |_x, y, _z| y); //~ ERROR cannot infer an appropriate lifetime
} }
} }

View File

@@ -12,8 +12,8 @@ impl has_ctxt: get_ctxt {
fn make_gc() -> get_ctxt { fn make_gc() -> get_ctxt {
let ctxt = { v: 22u }; let ctxt = { v: 22u };
let hc = { c: &ctxt }; let hc = { c: &ctxt }; //~ ERROR illegal borrow
return hc as get_ctxt; //~ ERROR mismatched types: expected `@get_ctxt/&` return hc as get_ctxt;
} }
fn main() { fn main() {

View File

@@ -7,7 +7,7 @@ fn make_gc1(gc: get_ctxt/&a) -> get_ctxt/&b {
} }
fn make_gc2(gc: get_ctxt/&a) -> get_ctxt/&b { fn make_gc2(gc: get_ctxt/&a) -> get_ctxt/&b {
return gc as get_ctxt; //~ ERROR mismatched types: expected `@get_ctxt/&b` but found `@get_ctxt/&a` return gc as get_ctxt; //~ ERROR cannot infer an appropriate lifetime
} }
fn main() { fn main() {

View File

@@ -1,4 +1,4 @@
// error-pattern: reference is not valid outside of its lifetime // error-pattern: cannot infer an appropriate lifetime
use std; use std;
import std::sync; import std::sync;
fn main() { fn main() {

View File

@@ -3,7 +3,7 @@ type bar = {a: int, b: uint};
fn want_foo(f: foo) {} fn want_foo(f: foo) {}
fn have_bar(b: bar) { fn have_bar(b: bar) {
want_foo(b); //~ ERROR (in field `b`, int vs uint) want_foo(b); //~ ERROR (in field `b`, expected int but found uint)
} }
fn main() {} fn main() {}

View File

@@ -3,7 +3,7 @@ type bar = @foo;
fn want_foo(f: foo) {} fn want_foo(f: foo) {}
fn have_bar(b: bar) { fn have_bar(b: bar) {
want_foo(b); //~ ERROR (record vs @-ptr) want_foo(b); //~ ERROR (expected record but found @-ptr)
} }
fn main() {} fn main() {}

View File

@@ -17,8 +17,8 @@ fn main() {
let a = &"aaaa"; let a = &"aaaa";
let b = &"bbbb"; let b = &"bbbb";
// let c = &"cccc"; let c = &"cccc";
// let cc = &"ccccc"; let cc = &"ccccc";
log(debug, a); log(debug, a);
@@ -30,9 +30,6 @@ fn main() {
log(debug, b); log(debug, b);
// FIXME #3138: So then, why don't these ones work?
/*
assert a < c; assert a < c;
assert a <= c; assert a <= c;
assert a != c; assert a != c;
@@ -48,5 +45,4 @@ fn main() {
assert cc > c; assert cc > c;
log(debug, cc); log(debug, cc);
*/
} }

View File

@@ -1,10 +1,10 @@
fn test_fn() { fn test_fn() {
type t = extern fn() -> int; type t = fn@() -> int;
fn ten() -> int { return 10; } fn ten() -> int { return 10; }
let rs: t = { ten }; let rs: t = { ten };
assert (rs() == 10); //assert (rs() == 10);
} }
fn main() { test_fn(); } fn main() { test_fn(); }