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)]
fn borrow<B>(f: fn(-~[mut A]) -> B) -> B {
fn check_out<B>(f: fn(-~[mut A]) -> B) -> B {
unsafe {
let mut data = unsafe::reinterpret_cast(null::<()>());
data <-> self.data;
@ -124,13 +124,13 @@ impl<A> DVec<A> {
*/
#[inline(always)]
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
pure fn len() -> uint {
unchecked {
do self.borrow |v| {
do self.check_out |v| {
let l = v.len();
self.give_back(v);
l
@ -146,7 +146,7 @@ impl<A> DVec<A> {
/// Remove and return the last element
fn pop() -> A {
do self.borrow |v| {
do self.check_out |v| {
let mut v <- v;
let result = vec::pop(v);
self.give_back(v);
@ -176,7 +176,7 @@ impl<A> DVec<A> {
/// Remove and return the first element
fn shift() -> A {
do self.borrow |v| {
do self.check_out |v| {
let mut v = vec::from_mut(v);
let result = vec::shift(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() {
do self.borrow |v| {
do self.check_out |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] {
unchecked {
do self.borrow |v| {
do self.check_out |v| {
let w = vec::from_mut(copy v);
self.give_back(v);
w

View File

@ -1187,7 +1187,9 @@ fn spawn_raw(+opts: TaskOpts, +f: fn~()) {
};
if result {
// 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.
result =
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.
fn with_ref(f: fn(v: &T)) {
let val = move self.take();
f(&val);
self.put_back(move val);
fn with_ref<R>(op: fn(v: &T) -> R) -> R {
let v = self.take();
let r = op(&v);
self.put_back(v);
return move r;
}
}

View File

@ -16,7 +16,7 @@ import middle::lint;
import middle::lint::{get_lint_level, allow};
import syntax::ast::*;
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 br_hashmap;
@ -119,6 +119,7 @@ export proto_kind, kind_lteq, type_kind;
export operators;
export type_err, terr_vstore_kind;
export type_err_to_str;
export expected_found;
export type_needs_drop;
export type_is_empty;
export type_is_integral;
@ -179,6 +180,7 @@ export fn_proto, proto_bare, proto_vstore;
export ast_proto_to_proto;
export is_blockish;
export method_call_bounds;
export hash_region;
// Data types
@ -368,7 +370,7 @@ enum region {
re_static,
/// A region variable. Should not exist after typeck.
re_var(region_vid),
re_var(region_vid)
}
enum bound_region {
@ -455,30 +457,35 @@ enum terr_vstore_kind {
terr_vec, terr_str, terr_fn, terr_trait
}
struct expected_found<T> {
expected: T;
found: T;
}
// Data structures used in type unification
enum type_err {
terr_mismatch,
terr_ret_style_mismatch(ast::ret_style, ast::ret_style),
terr_purity_mismatch(purity, purity),
terr_ret_style_mismatch(expected_found<ast::ret_style>),
terr_purity_mismatch(expected_found<purity>),
terr_mutability,
terr_proto_mismatch(ty::fn_proto, ty::fn_proto),
terr_proto_mismatch(expected_found<ty::fn_proto>),
terr_box_mutability,
terr_ptr_mutability,
terr_ref_mutability,
terr_vec_mutability,
terr_tuple_size(uint, uint),
terr_ty_param_size(uint, uint),
terr_record_size(uint, uint),
terr_tuple_size(expected_found<uint>),
terr_ty_param_size(expected_found<uint>),
terr_record_size(expected_found<uint>),
terr_record_mutability,
terr_record_fields(ast::ident, ast::ident),
terr_record_fields(expected_found<ident>),
terr_arg_count,
terr_mode_mismatch(mode, mode),
terr_mode_mismatch(expected_found<mode>),
terr_regions_does_not_outlive(region, region),
terr_regions_not_same(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_sorts(t, t),
terr_sorts(expected_found<t>),
terr_self_substs,
terr_no_integral_type,
}
@ -512,7 +519,7 @@ impl tvi_vid: vid {
impl region_vid: vid {
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 {
@ -2195,6 +2202,17 @@ fn br_hashmap<V:copy>() -> hashmap<bound_region, V> {
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.
pure fn hash_type_structure(st: &sty) -> uint {
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) }
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 {
let h = hash_subtys(h, substs.tps);
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) }
// 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> {
let m1 = modes.expected;
let m2 = modes.found;
match (canon_mode(cx, m1), canon_mode(cx, m2)) {
(m1, m2) if (m1 == m2) => {
result::ok(m1)
@ -2584,7 +2595,7 @@ fn unify_mode(cx: ctxt, m1: ast::mode, m2: ast::mode)
result::ok(m1)
}
(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 {
terr_mismatch => return ~"types differ",
terr_ret_style_mismatch(expect, actual) => {
terr_mismatch => ~"types differ",
terr_ret_style_mismatch(values) => {
fn to_str(s: ast::ret_style) -> ~str {
match s {
ast::noreturn => ~"non-returning",
ast::return_val => ~"return-by-value"
}
}
return to_str(actual) + ~" function found where " + to_str(expect) +
~" function was expected";
fmt!("expected %s function, found %s function",
to_str(values.expected),
to_str(values.expected))
}
terr_purity_mismatch(f1, f2) => {
return fmt!{"expected %s fn but found %s fn",
purity_to_str(f1), purity_to_str(f2)};
terr_purity_mismatch(values) => {
fmt!{"expected %s fn but found %s fn",
purity_to_str(values.expected),
purity_to_str(values.found)}
}
terr_proto_mismatch(e, a) => {
return fmt!{"closure protocol mismatch (%s vs %s)",
util::ppaux::proto_ty_to_str(cx, e),
util::ppaux::proto_ty_to_str(cx, a)};
terr_proto_mismatch(values) => {
fmt!{"expected %s closure, found %s closure",
proto_ty_to_str(cx, values.expected),
proto_ty_to_str(cx, values.found)}
}
terr_mutability => return ~"values differ in mutability",
terr_box_mutability => return ~"boxed values differ in mutability",
terr_vec_mutability => return ~"vectors differ in mutability",
terr_ptr_mutability => return ~"pointers differ in mutability",
terr_ref_mutability => return ~"references differ in mutability",
terr_ty_param_size(e_sz, a_sz) => {
return ~"expected a type with " + uint::to_str(e_sz, 10u) +
~" type params but found one with " + uint::to_str(a_sz, 10u) +
~" type params";
terr_mutability => ~"values differ in mutability",
terr_box_mutability => ~"boxed values differ in mutability",
terr_vec_mutability => ~"vectors differ in mutability",
terr_ptr_mutability => ~"pointers differ in mutability",
terr_ref_mutability => ~"references differ in mutability",
terr_ty_param_size(values) => {
fmt!("expected a type with %? type params \
but found one with %? type params",
values.expected, values.found)
}
terr_tuple_size(e_sz, a_sz) => {
return ~"expected a tuple with " + uint::to_str(e_sz, 10u) +
~" elements but found one with " + uint::to_str(a_sz, 10u) +
~" elements";
terr_tuple_size(values) => {
fmt!("expected a tuple with %? elements \
but found one with %? elements",
values.expected, values.found)
}
terr_record_size(e_sz, a_sz) => {
return ~"expected a record with " + uint::to_str(e_sz, 10u) +
~" fields but found one with " + uint::to_str(a_sz, 10u) +
~" fields";
terr_record_size(values) => {
fmt!("expected a record with %? fields \
but found one with %? fields",
values.expected, values.found)
}
terr_record_mutability => {
return ~"record elements differ in mutability";
~"record elements differ in mutability"
}
terr_record_fields(e_fld, a_fld) => {
return ~"expected a record with field `" + *e_fld +
~"` but found one with field `" + *a_fld + ~"`";
terr_record_fields(values) => {
fmt!("expected a record with field `%s` \
but found one with field `%s`",
*values.expected, *values.found)
}
terr_arg_count => return ~"incorrect number of function parameters",
terr_mode_mismatch(e_mode, a_mode) => {
return ~"expected argument mode " + mode_to_str(e_mode) +
~" but found " + mode_to_str(a_mode);
terr_arg_count => ~"incorrect number of function parameters",
terr_mode_mismatch(values) => {
fmt!("expected argument mode %s, but found %s",
mode_to_str(values.expected), mode_to_str(values.found))
}
terr_regions_does_not_outlive(subregion, superregion) => {
return fmt!{"%s does not necessarily outlive %s",
explain_region(cx, subregion),
explain_region(cx, superregion)};
fmt!{"%s does not necessarily outlive %s",
explain_region(cx, superregion),
explain_region(cx, subregion)}
}
terr_regions_not_same(region1, region2) => {
return fmt!{"%s is not the same as %s",
explain_region(cx, region1),
explain_region(cx, region2)};
fmt!{"%s is not the same as %s",
explain_region(cx, region1),
explain_region(cx, region2)}
}
terr_regions_no_overlap(region1, region2) => {
return fmt!{"%s does not intersect %s",
explain_region(cx, region1),
explain_region(cx, region2)};
fmt!("%s does not intersect %s",
explain_region(cx, region1),
explain_region(cx, region2))
}
terr_vstores_differ(k, e_vs, a_vs) => {
return fmt!{"%s storage differs: expected %s but found %s",
terr_vstore_kind_to_str(k),
vstore_to_str(cx, e_vs),
vstore_to_str(cx, a_vs)};
terr_vstores_differ(k, values) => {
fmt!("%s storage differs: expected %s but found %s",
terr_vstore_kind_to_str(k),
vstore_to_str(cx, values.expected),
vstore_to_str(cx, values.found))
}
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) => {
return fmt!{"%s vs %s", ty_sort_str(cx, exp), ty_sort_str(cx, act)};
terr_sorts(values) => {
fmt!{"expected %s but found %s",
ty_sort_str(cx, values.expected),
ty_sort_str(cx, values.found)}
}
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 => {
return ~"couldn't determine an appropriate integral type for integer \
literal";
~"couldn't determine an appropriate integral type for integer \
literal"
}
}
}

View File

@ -206,6 +206,7 @@ fn no_params(t: ty::t) -> ty::ty_param_bounds_and_ty {
fn require_same_types(
tcx: ty::ctxt,
maybe_infcx: option<infer::infer_ctxt>,
t1_is_expected: bool,
span: span,
t1: 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::err(ref terr) => {
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 {
let res = match a_r.node {
ast::re_anon => rscope.anon_region(),
ast::re_named(id) => rscope.named_region(id)
ast::re_anon => rscope.anon_region(span),
ast::re_named(id) => rscope.named_region(span, id)
};
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
}
(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);
some(r)
}
@ -409,8 +409,10 @@ fn ty_of_arg<AC: ast_conv, RS: region_scope copy owned>(
let mode = {
match a.mode {
ast::infer(_) if expected_ty.is_some() => {
result::get(ty::unify_mode(self.tcx(), a.mode,
expected_ty.get().mode))
result::get(ty::unify_mode(
self.tcx(),
ty::expected_found {expected: expected_ty.get().mode,
found: a.mode}))
}
ast::infer(_) => {
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:
_ => {
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 =>
ty::proto_vstore(ty::vstore_box),
ast::proto_block => {
let result = rscope.anon_region();
let result = rscope.anon_region(span);
let region = get_region_reporting_err(self.tcx(), span, result);
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
// want to pick the narrowest block that encompasses all uses.
//
// What we do in such cases is to generate a region variable and
// assign it the following two fields as bounds. The lower bound
// is always the innermost enclosing expression. The upper bound
// is the outermost enclosing expression that we could legally
// use. In practice, this is the innermost loop or function
// body.
// What we do in such cases is to generate a region variable with
// `region_lb` as a lower bound. The regionck pass then adds
// other constriants based on how the variable is used and region
// inference selects 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.
mut region_lb: ast::node_id,
in_scope_regions: isr_alist,
@ -292,20 +292,22 @@ fn check_fn(ccx: @crate_ctxt,
arg_tys: ~[ty::t]) {
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();
fcx.locals.insert(nid, var_id);
match ty_opt {
none => {/* nothing to do */ }
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.
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",
*input.ident, fcx.locals.get(input.id).to_str()};
}
@ -317,7 +319,7 @@ fn check_fn(ccx: @crate_ctxt,
ast::ty_infer => none,
_ => 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",
pat_to_str(local.node.pat),
fcx.locals.get(local.node.id).to_str()};
@ -329,7 +331,7 @@ fn check_fn(ccx: @crate_ctxt,
match p.node {
ast::pat_ident(_, path, _)
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",
*path.idents[0],
fcx.locals.get(p.id).to_str()};
@ -525,14 +527,14 @@ impl @fn_ctxt: ast_conv {
}
impl @fn_ctxt: region_scope {
fn anon_region() -> result<ty::region, ~str> {
result::ok(self.infcx.next_region_var_nb())
fn anon_region(span: span) -> result<ty::region, ~str> {
result::ok(self.infcx.next_region_var_nb(span))
}
fn named_region(id: ast::ident) -> result<ty::region, ~str> {
do empty_rscope.named_region(id).chain_err |_e| {
fn named_region(span: span, id: ast::ident) -> result<ty::region, ~str> {
do empty_rscope.named_region(span, id).chain_err |_e| {
match self.in_scope_regions.find(ty::br_named(id)) {
some(r) => result::ok(r),
none if *id == ~"blk" => self.block_region(),
none if *id == ~"blk" => result::ok(self.block_region()),
none => {
result::err(fmt!{"named region `%s` not in scope here", *id})
}
@ -543,8 +545,8 @@ impl @fn_ctxt: region_scope {
impl @fn_ctxt {
fn tag() -> ~str { fmt!{"%x", ptr::addr_of(*self) as uint} }
fn block_region() -> result<ty::region, ~str> {
result::ok(ty::re_scope(self.region_lb))
fn block_region() -> ty::region {
ty::re_scope(self.region_lb)
}
#[inline(always)]
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)});
}
fn mk_subty(sub: ty::t, sup: ty::t) -> result<(), ty::type_err> {
infer::mk_subty(self.infcx, sub, sup)
fn mk_subty(a_is_expected: bool, span: span,
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> {
@ -641,12 +644,14 @@ impl @fn_ctxt {
infer::can_mk_assignty(self.infcx, anmnt, sub, sup)
}
fn mk_eqty(sub: ty::t, sup: ty::t) -> result<(), ty::type_err> {
infer::mk_eqty(self.infcx, sub, sup)
fn mk_eqty(a_is_expected: bool, span: span,
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> {
infer::mk_subr(self.infcx, sub, sup)
fn mk_subr(a_is_expected: bool, span: span,
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) {
@ -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)]`
// would return ($0, $1) where $0 and $1 are freshly instantiated type
// variables.
fn impl_self_ty(fcx: @fn_ctxt, did: ast::def_id, require_rp: bool)
-> ty_param_substs_and_ty {
fn impl_self_ty(fcx: @fn_ctxt,
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 {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 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 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) => {
replace_bound_regions_in_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 => {
// 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|
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 oprnd_t = ty::mk_rptr(tcx, region, tm);
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 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_fail(expr_opt) => {
@ -1474,7 +1499,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
some(t) => t, none => fcx.ret_ty
};
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::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 (inner_ty, proto) = match expected_sty {
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::err(err) => {
tcx.sess.span_fatal(
@ -1809,7 +1836,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
// Generate the struct type.
let self_region;
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 {
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,
pth: @ast::path,
tpt: ty_param_bounds_and_ty,
sp: span,
id: ast::node_id) {
span: span,
node_id: ast::node_id,
region_lb: ty::region) {
let ty_param_count = vec::len(*tpt.bounds);
let ty_substs_len = vec::len(pth.types);
@ -2317,14 +2345,14 @@ fn instantiate_path(fcx: @fn_ctxt,
let self_r = match pth.rp {
some(r) if !tpt.rp => {
fcx.ccx.tcx.sess.span_err
(sp, ~"this item is not region-parameterized");
(span, ~"this item is not region-parameterized");
none
}
some(r) => {
some(ast_region_to_region(fcx, fcx, sp, r))
some(ast_region_to_region(fcx, fcx, span, r))
}
none if tpt.rp => {
some(fcx.infcx.next_region_var_nb())
some(fcx.infcx.next_region_var_with_lb(span, region_lb))
}
none => {
none
@ -2337,22 +2365,22 @@ fn instantiate_path(fcx: @fn_ctxt,
fcx.infcx.next_ty_vars(ty_param_count)
} else if ty_param_count == 0u {
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)
} else if ty_substs_len > ty_param_count {
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)
} else if ty_substs_len < ty_param_count {
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)
} else {
pth.types.map(|aty| fcx.to_ty(aty))
};
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
@ -2400,15 +2428,9 @@ fn ast_expr_vstore_to_vstore(fcx: @fn_ctxt, e: @ast::expr, n: uint,
}
ast::vstore_uniq => ty::vstore_uniq,
ast::vstore_box => ty::vstore_box,
ast::vstore_slice(a_r) => match fcx.block_region() {
result::ok(b_r) => {
let r = fcx.infcx.next_region_var_with_scope_lb(e.id);
ty::vstore_slice(r)
}
result::err(msg) => {
fcx.ccx.tcx.sess.span_err(e.span, msg);
ty::vstore_slice(ty::re_static)
}
ast::vstore_slice(a_r) => {
let r = fcx.infcx.next_region_var(e.span, e.id);
ty::vstore_slice(r)
}
}
}
@ -2523,7 +2545,7 @@ fn check_intrinsic_type(ccx: @crate_ctxt, it: @ast::foreign_item) {
expected %u", i_n_tps, n_tps});
} else {
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: \
expected `%s`",
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.
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`.
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 ending type: %?", e_ty};
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") {
// no-op
} 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
// and T is the expected type
let region_var =
fcx.infcx.next_region_var({lb: some(pcx.block_region),
ub: none});
fcx.infcx.next_region_var_with_lb(
pat.span, pcx.block_region);
let mt = {ty: expected, mutbl: mutbl};
let region_ty = ty::mk_rptr(tcx, region_var, mt);
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) {
// 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::err(ref 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,
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::err(ref 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
// variables for each parameter:
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(
self.tcx(), impl_substs.self_r,
@ -458,15 +458,17 @@ struct lookup {
self.self_ty,
impl_ty),
immutable_reference_mode => {
let region = self.fcx.infcx.next_region_var_with_scope_lb
(self.self_expr.id);
let region = self.fcx.infcx.next_region_var(
self.self_expr.span,
self.self_expr.id);
let tm = { ty: self.self_ty, mutbl: ast::m_imm };
let ref_ty = ty::mk_rptr(self.tcx(), region, tm);
matches = self.fcx.can_mk_subty(ref_ty, impl_ty);
}
mutable_reference_mode => {
let region = self.fcx.infcx.next_region_var_with_scope_lb
(self.self_expr.id);
let region = self.fcx.infcx.next_region_var(
self.self_expr.span,
self.self_expr.id);
let tm = { ty: self.self_ty, mutbl: ast::m_mutbl };
let ref_ty = ty::mk_rptr(self.tcx(), region, tm);
matches = self.fcx.can_mk_subty(ref_ty, impl_ty);
@ -609,8 +611,9 @@ struct lookup {
}
immutable_reference_mode => {
// Borrow as an immutable reference.
let region_var = self.fcx.infcx.next_region_var_with_scope_lb
(self.self_expr.id);
let region_var = self.fcx.infcx.next_region_var(
self.self_expr.span,
self.self_expr.id);
self.fcx.infcx.borrowings.push({expr_id: self.self_expr.id,
span: self.self_expr.span,
scope: region_var,
@ -618,8 +621,9 @@ struct lookup {
}
mutable_reference_mode => {
// Borrow as a mutable reference.
let region_var = self.fcx.infcx.next_region_var_with_scope_lb
(self.self_expr.id);
let region_var = self.fcx.infcx.next_region_var(
self.self_expr.span,
self.self_expr.id);
self.fcx.infcx.borrowings.push({expr_id: self.self_expr.id,
span: self.self_expr.span,
scope: region_var,

View File

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

View File

@ -1,6 +1,8 @@
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 syntax::print::pprust;
// vtable resolution looks for places where trait bounds are
// 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,
sp: span,
expr: @ast::expr,
bounds: @~[ty::param_bounds],
substs: &ty::substs,
allow_unsafe: bool,
@ -39,7 +41,7 @@ fn lookup_vtables(fcx: @fn_ctxt,
match bound {
ty::bound_trait(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));
}
_ => ()
@ -50,31 +52,36 @@ fn lookup_vtables(fcx: @fn_ctxt,
@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,
is_early: bool) -> option<ty::substs> {
let tcx = fcx.ccx.tcx;
// 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));
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 {
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) {
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>
as if it has type <trait_ty>
*/
fn lookup_vtable(fcx: @fn_ctxt, sp: span, ty: ty::t, trait_ty: ty::t,
allow_unsafe: bool, is_early: bool)
-> vtable_origin {
fn lookup_vtable(fcx: @fn_ctxt,
expr: @ast::expr,
ty: ty::t,
trait_ty: ty::t,
allow_unsafe: bool,
is_early: bool)
-> vtable_origin
{
debug!{"lookup_vtable(ty=%s, trait_ty=%s)",
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 {
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,
none => {
// 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 {
debug!{"(checking vtable) @0 relating ty to trait ty
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);
}
}
@ -126,17 +133,19 @@ 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 %?",
did};
relate_trait_tys(fcx, sp, trait_ty, ty);
relate_trait_tys(fcx, expr, trait_ty, ty);
if !allow_unsafe && !is_early {
for vec::each(*ty::trait_methods(tcx, did)) |m| {
if ty::type_has_self(ty::mk_fn(tcx, m.fty)) {
tcx.sess.span_err(
sp, ~"a boxed trait with self types may not be \
passed as a bounded type");
expr.span,
~"a boxed trait with self types may not be \
passed as a bounded type");
} else if (*m.tps).len() > 0u {
tcx.sess.span_err(
sp, ~"a boxed trait with generic methods may not \
be passed as a bounded type");
expr.span,
~"a boxed trait with generic methods may not \
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
// that the impl is for, and continue if not
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;
match fcx.mk_subty(ty, for_ty) {
match fcx.mk_subty(false, expr.span, ty, for_ty) {
result::err(_) => again,
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(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.
let trait_tps = trait_substs.tps;
// 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) {
some(substs) => substs,
none => {
@ -204,10 +213,11 @@ 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);
let subres = lookup_vtables(fcx, sp, im_bs, &substs_f,
false, is_early);
let subres = lookup_vtables(
fcx, expr, im_bs, &substs_f,
false, is_early);
vec::push(found,
vtable_static(im.did, substs_f.tps,
subres));
@ -222,7 +232,8 @@ fn lookup_vtable(fcx: @fn_ctxt, sp: span, ty: ty::t, trait_ty: ty::t,
_ => {
if !is_early {
fcx.ccx.tcx.sess.span_err(
sp, ~"multiple applicable methods in scope");
expr.span,
~"multiple applicable methods in scope");
}
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(
sp, ~"failed to find an implementation of trait " +
ty_to_str(tcx, trait_ty) + ~" for " +
ty_to_str(tcx, ty));
expr.span,
fmt!("failed to find an implementation of trait %s for %s",
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)
-> option<ty::t> {
fn fixup_ty(fcx: @fn_ctxt,
expr: @ast::expr,
ty: ty::t,
is_early: bool) -> option<ty::t>
{
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::err(e) if !is_early => {
tcx.sess.span_fatal(
sp,
expr.span,
fmt!{"cannot determine a type \
for this bounded type parameter: %s",
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) {
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 {
ty::ty_trait(_, substs, _) => {
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) {
debug!("vtable: early_resolve_expr() ex with id %?: %s",
ex.id, expr_to_str(ex));
let cx = fcx.ccx;
match ex.node {
ast::expr_path(*) => {
debug!("(vtable - resolving expr) resolving path expr");
match fcx.opt_node_ty_substs(ex.id) {
some(ref substs) => {
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);
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);
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_unary(*) | ast::expr_assign_op(*) |
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) {
some(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
};
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);
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, _) => {
debug!("(vtable - resolving expr) resolving cast expr");
let target_ty = fcx.expr_ty(ex);
match ty::get(target_ty).struct {
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,
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);
/*
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_param, ty_self, ty_type, ty_opaque_box};
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 syntax::ast::{crate, def_id, def_mod};
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_b = self.universally_quantify_polytype(polytype_b);
return
mk_subty(self.inference_context, monotype_a, monotype_b).is_ok()
|| mk_subty(self.inference_context, monotype_b, monotype_a).is_ok();
return can_mk_subty(self.inference_context,
monotype_a, monotype_b).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
// type variables.
fn universally_quantify_polytype(polytype: ty_param_bounds_and_ty) -> t {
let self_region =
if !polytype.rp {none}
else {some(self.inference_context.next_region_var_nb())};
// NDM--this span is bogus.
let self_region = if !polytype.rp {
none
} else {
some(self.inference_context.next_region_var_nb(dummy_sp()))
};
let bounds_count = polytype.bounds.len();
let type_parameters =

View File

@ -298,7 +298,7 @@ fn compare_impl_method(tcx: ty::ctxt, sp: span,
ty::subst(tcx, &substs, trait_fty)
};
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");
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;
trait region_scope {
fn anon_region() -> result<ty::region, ~str>;
fn named_region(id: ast::ident) -> result<ty::region, ~str>;
fn anon_region(span: span) -> result<ty::region, ~str>;
fn named_region(span: span, id: ast::ident) -> result<ty::region, ~str>;
}
enum empty_rscope { empty_rscope }
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)
}
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) }
else { result::err(~"only the static region is allowed here") }
}
@ -18,7 +18,7 @@ impl empty_rscope: region_scope {
enum type_rscope = bool;
impl type_rscope: region_scope {
fn anon_region() -> result<ty::region, ~str> {
fn anon_region(_span: span) -> result<ty::region, ~str> {
if *self {
result::ok(ty::re_bound(ty::br_self))
} else {
@ -26,10 +26,11 @@ impl type_rscope: region_scope {
must be declared with a region bound")
}
}
fn named_region(id: ast::ident) -> result<ty::region, ~str> {
do empty_rscope.named_region(id).chain_err |_e| {
if *id == ~"self" { self.anon_region() }
else {
fn named_region(span: span, id: ast::ident) -> result<ty::region, ~str> {
do empty_rscope.named_region(span, id).chain_err |_e| {
if *id == ~"self" {
self.anon_region(span)
} else {
result::err(~"named regions other than `self` are not \
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})
}
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)
}
fn named_region(id: ast::ident) -> result<ty::region, ~str> {
self.base.named_region(id)
fn named_region(span: span, id: ast::ident) -> result<ty::region, ~str> {
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})
}
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))
}
fn named_region(id: ast::ident) -> result<ty::region, ~str> {
do self.base.named_region(id).chain_err |_e| {
fn named_region(span: span, id: ast::ident) -> result<ty::region, ~str> {
do self.base.named_region(span, id).chain_err |_e| {
result::ok(ty::re_bound(ty::br_named(id)))
}
}

View File

@ -66,7 +66,19 @@ mod middle {
}
mod rscope;
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 coherence;
}

View File

@ -21,10 +21,31 @@ import syntax::{ast, ast_util};
import syntax::ast_map;
import driver::session::session;
/// Returns a string like "reference valid for the block at 27:31 in foo.rs"
/// that attempts to explain a lifetime in a way it might plausibly be
/// understood.
fn note_and_explain_region(cx: ctxt, prefix: ~str, region: ty::region) {
match explain_region_and_span(cx, region) {
(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 {
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 {
re_scope(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)) => {
match expr.node {
ast::expr_call(*) => { explain_span(cx, ~"call", expr.span) }
ast::expr_match(*) => { explain_span(cx, ~"alt", expr.span) }
_ => { explain_span(cx, ~"expression", expr.span) }
ast::expr_call(*) => explain_span(cx, ~"call", expr.span),
ast::expr_match(*) => explain_span(cx, ~"alt", expr.span),
_ => explain_span(cx, ~"expression", expr.span)
}
}
some(_) | none => {
// 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) => {
match cx.items.find(id) {
some(ast_map::node_block(blk)) => {
fmt!{"the lifetime %s as defined on %s",
bound_region_to_str(cx, br),
explain_span(cx, ~"block", blk.span)}
let (msg, opt_span) = explain_span(cx, ~"block", blk.span);
(fmt!("the lifetime %s as defined on %s",
bound_region_to_str(cx, br), msg),
opt_span)
}
some(_) | none => {
// this really should not happen
fmt!{"the lifetime %s as defined on node %d",
bound_region_to_str(cx, br), id}
(fmt!("the lifetime %s as defined on node %d",
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,
// perhaps)
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);
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 {
match region {
re_scope(node_id) => {
if cx.sess.ppregions() {
fmt!{"&%s", re_scope_id_to_str(cx, node_id)}
} 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)
}
}
if cx.sess.ppregions() {
return fmt!("&%?", region);
}
// These two should not be seen by end-users (very often, anyhow):
re_var(id) => fmt!{"&%s", id.to_str()},
// These printouts are concise. They do not contain all the information
// 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"
}
}

View File

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

View File

@ -12,9 +12,9 @@ fn repeater<A:copy>(v: @A) -> repeat<A> {
fn main() {
// Here, an error results as the type of y is inferred to
// repeater<&lt/3> where lt is the block.
let y = { //~ ERROR reference is not valid outside of its lifetime
let x: &blk/int = &3;
let y = {
let x: &blk/int = &3; //~ ERROR cannot infer an appropriate lifetime
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() {
let f = {|i| i};
assert apply_int(f, 2) == 2;
assert apply(f, 2) == 2; //~ ERROR expected argument mode ++
assert apply(f, 2) == 2; //~ ERROR expected argument mode &&
}

View File

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

View File

@ -4,8 +4,9 @@ enum ast {
}
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() {
}
}

View File

@ -4,8 +4,10 @@ enum 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() {
}
}

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

View File

@ -17,7 +17,7 @@ mod argparse {
impl Flag {
fn set_desc(self, s: &str) -> Flag {
Flag { //~ ERROR mismatched types
Flag { //~ ERROR cannot infer an appropriate lifetime
name: self.name,
desc: s,
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 {
let z = do with |y| { select(x, y) };
//~^ ERROR reference is not valid outside of its lifetime
//~^ ERROR cannot infer an appropriate lifetime
*z
}

View File

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

View File

@ -9,20 +9,14 @@ fn nested(x: &x/int) { // (1)
z: &z/int) -> &z/int) // A fresh region `z` (3)
-> &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, 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`
//~^ ERROR mismatched types: expected `&y/int` but found `&x/int`
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`
//~^^ 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`
fail;
if false { return z(x, y, y); }
//~^ ERROR cannot infer an appropriate lifetime
return z(y, x, x);
//~^ ERROR mismatched types: expected `&x/int` but found `&y/int`
//~^^ ERROR mismatched types: expected `&y/int` but found `&x/int`
}
) |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
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 `&
foo(x, &z, |x, _y, _z| x); //~ ERROR mismatched types: expected `&z/int` but found `&x/int`
// 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 {
let ctxt = { v: 22u };
let hc = { c: &ctxt };
return hc as get_ctxt; //~ ERROR mismatched types: expected `@get_ctxt/&`
let hc = { c: &ctxt }; //~ ERROR illegal borrow
return hc as get_ctxt;
}
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 {
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() {

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;
import std::sync;
fn main() {

View File

@ -3,7 +3,7 @@ type bar = {a: int, b: uint};
fn want_foo(f: foo) {}
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() {}

View File

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

View File

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

View File

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