infer the scope of borrows

This commit is contained in:
Niko Matsakis 2012-07-06 09:14:57 -07:00
parent 41a21f053c
commit 3ef7ff8b89
16 changed files with 378 additions and 203 deletions

View File

@ -74,8 +74,7 @@ import rscope::{anon_rscope, binding_rscope, empty_rscope, in_anon_rscope};
import rscope::{in_binding_rscope, region_scope, type_rscope};
import syntax::ast::ty_i;
import typeck::infer::{unify_methods}; // infcx.set()
import typeck::infer::{force_level, force_none, force_ty_vars_only,
force_all};
import typeck::infer::{resolve_type, force_tvar};
type fn_ctxt =
// var_bindings, locals and next_var_id are shared
@ -89,7 +88,22 @@ type fn_ctxt =
infcx: infer::infer_ctxt,
locals: hashmap<ast::node_id, tv_vid>,
mut blocks: ~[ast::node_id], // stack of blocks in scope, may be empty
// Sometimes we generate region pointers where the precise region
// to use is not known. For example, an expression like `&x.f`
// where `x` is of type `@T`: in this case, we will be rooting
// `x` onto the stack frame, and we could choose to root it until
// 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.
mut region_lb: ast::node_id,
mut region_ub: ast::node_id,
in_scope_regions: isr_alist,
node_types: smallintmap::smallintmap<ty::t>,
@ -98,7 +112,8 @@ type fn_ctxt =
ccx: @crate_ctxt};
// Used by check_const and check_enum_variants
fn blank_fn_ctxt(ccx: @crate_ctxt, rty: ty::t) -> @fn_ctxt {
fn blank_fn_ctxt(ccx: @crate_ctxt, rty: ty::t,
region_bnd: ast::node_id) -> @fn_ctxt {
// It's kind of a kludge to manufacture a fake function context
// and statement context, but we might as well do write the code only once
@{self_ty: none,
@ -107,7 +122,8 @@ fn blank_fn_ctxt(ccx: @crate_ctxt, rty: ty::t) -> @fn_ctxt {
purity: ast::pure_fn,
infcx: infer::new_infer_ctxt(ccx.tcx),
locals: int_hash(),
mut blocks: ~[],
mut region_lb: region_bnd,
mut region_ub: region_bnd,
in_scope_regions: @nil,
node_types: smallintmap::mk(),
node_type_substs: map::int_hash(),
@ -217,7 +233,8 @@ fn check_fn(ccx: @crate_ctxt,
purity: purity,
infcx: infcx,
locals: locals,
mut blocks: ~[],
mut region_lb: body.node.id,
mut region_ub: body.node.id,
in_scope_regions: isr,
node_types: node_types,
node_type_substs: node_type_substs,
@ -307,9 +324,12 @@ fn check_fn(ccx: @crate_ctxt,
};
let visit_block = fn@(b: ast::blk, &&e: (), v: visit::vt<()>) {
vec::push(fcx.blocks, b.node.id);
visit::visit_block(b, e, v);
vec::pop(fcx.blocks);
// non-obvious: the `blk` variable maps to region lb, so
// we have to keep this up-to-date. This
// is... unfortunate. It'd be nice to not need this.
do fcx.with_region_lb(b.node.id) {
visit::visit_block(b, e, v);
}
};
// Don't descend into fns and items
@ -430,7 +450,7 @@ impl of ast_conv for @fn_ctxt {
impl of region_scope for @fn_ctxt {
fn anon_region() -> result<ty::region, ~str> {
result::ok(self.infcx.next_region_var())
result::ok(self.infcx.next_region_var_nb())
}
fn named_region(id: ast::ident) -> result<ty::region, ~str> {
do empty_rscope.named_region(id).chain_err |_e| {
@ -448,10 +468,7 @@ impl of region_scope for @fn_ctxt {
impl methods for @fn_ctxt {
fn tag() -> ~str { #fmt["%x", ptr::addr_of(*self) as uint] }
fn block_region() -> result<ty::region, ~str> {
alt vec::last_opt(self.blocks) {
some(bid) { result::ok(ty::re_scope(bid)) }
none { result::err(~"no block is in scope here") }
}
result::ok(ty::re_scope(self.region_lb))
}
#[inline(always)]
fn write_ty(node_id: ast::node_id, ty: ty::t) {
@ -534,15 +551,17 @@ impl methods for @fn_ctxt {
infer::can_mk_subty(self.infcx, sub, sup)
}
fn mk_assignty(expr: @ast::expr, borrow_scope: ast::node_id,
fn mk_assignty(expr: @ast::expr, borrow_lb: ast::node_id,
sub: ty::t, sup: ty::t) -> result<(), ty::type_err> {
let anmnt = {expr_id: expr.id, borrow_scope: borrow_scope};
let anmnt = {expr_id: expr.id, borrow_lb: borrow_lb,
borrow_ub: self.region_ub};
infer::mk_assignty(self.infcx, anmnt, sub, sup)
}
fn can_mk_assignty(expr: @ast::expr, borrow_scope: ast::node_id,
fn can_mk_assignty(expr: @ast::expr, borrow_lb: ast::node_id,
sub: ty::t, sup: ty::t) -> result<(), ty::type_err> {
let anmnt = {expr_id: expr.id, borrow_scope: borrow_scope};
let anmnt = {expr_id: expr.id, borrow_lb: borrow_lb,
borrow_ub: self.region_ub};
infer::can_mk_assignty(self.infcx, anmnt, sub, sup)
}
@ -564,6 +583,20 @@ impl methods for @fn_ctxt {
}
}
}
fn with_region_lb<R>(lb: ast::node_id, f: fn() -> R) -> R {
let old_region_lb = self.region_lb;
self.region_lb = lb;
let v <- f();
self.region_lb = old_region_lb;
ret v;
}
fn with_region_ub<R>(ub: ast::node_id, f: fn() -> R) -> R {
let old_region_ub = self.region_ub;
self.region_ub = ub;
let v <- f();
self.region_ub = old_region_ub;
ret v;
}
}
fn do_autoderef(fcx: @fn_ctxt, sp: span, t: ty::t) -> ty::t {
@ -682,7 +715,7 @@ fn impl_self_ty(fcx: @fn_ctxt, did: ast::def_id) -> ty_param_substs_and_ty {
raw_ty: ity.ty}
};
let self_r = if rp {some(fcx.infcx.next_region_var())} else {none};
let self_r = if rp {some(fcx.infcx.next_region_var_nb())} else {none};
let tps = fcx.infcx.next_ty_vars(n_tps);
let substs = {self_r: self_r, self_ty: none, tps: tps};
@ -733,7 +766,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
sty @ ty::ty_fn(fn_ty) {
replace_bound_regions_in_fn_ty(
fcx.ccx.tcx, @nil, none, fn_ty,
|_br| fcx.infcx.next_region_var()).fn_ty
|_br| fcx.infcx.next_region_var_nb()).fn_ty
}
sty {
// I would like to make this span_err, but it's
@ -1017,7 +1050,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
-> option<O> {
alt expected {
some(t) {
alt infer::resolve_shallow(fcx.infcx, t, force_none) {
alt resolve_type(fcx.infcx, t, force_tvar) {
result::ok(t) { unpack(ty::get(t).struct) }
_ { none }
}
@ -1119,9 +1152,9 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
// this will be the call or block that immediately
// encloses the method call
let borrow_scope = fcx.tcx().region_map.get(expr.id);
let borrow_lb = fcx.tcx().region_map.get(expr.id);
let lkup = method::lookup(fcx, expr, base, borrow_scope,
let lkup = method::lookup(fcx, expr, base, borrow_lb,
expr.id, field, expr_t, tps,
is_self_ref);
alt lkup.method() {
@ -1364,13 +1397,17 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
}
ast::expr_while(cond, body) {
bot = check_expr_with(fcx, cond, ty::mk_bool(tcx));
check_block_no_value(fcx, body);
do fcx.with_region_ub(body.node.id) {
check_block_no_value(fcx, body);
}
fcx.write_ty(id, ty::mk_nil(tcx));
}
ast::expr_loop(body) {
check_block_no_value(fcx, body);
fcx.write_ty(id, ty::mk_nil(tcx));
bot = !may_break(body);
do fcx.with_region_ub(body.node.id) {
check_block_no_value(fcx, body);
}
fcx.write_ty(id, ty::mk_nil(tcx));
bot = !may_break(body);
}
ast::expr_alt(discrim, arms, _) {
bot = alt::check_alt(fcx, expr, discrim, arms);
@ -1754,44 +1791,44 @@ fn check_block(fcx0: @fn_ctxt, blk: ast::blk) -> bool {
ast::unsafe_blk { @{purity: ast::unsafe_fn with *fcx0} }
ast::default_blk { fcx0 }
};
vec::push(fcx.blocks, blk.node.id);
let mut bot = false;
let mut warned = false;
for blk.node.stmts.each |s| {
if bot && !warned &&
alt s.node {
ast::stmt_decl(@{node: ast::decl_local(_), _}, _) |
ast::stmt_expr(_, _) | ast::stmt_semi(_, _) {
true
}
_ { false }
} {
fcx.ccx.tcx.sess.span_warn(s.span, ~"unreachable statement");
warned = true;
do fcx.with_region_lb(blk.node.id) {
let mut bot = false;
let mut warned = false;
for blk.node.stmts.each |s| {
if bot && !warned &&
alt s.node {
ast::stmt_decl(@{node: ast::decl_local(_), _}, _) |
ast::stmt_expr(_, _) | ast::stmt_semi(_, _) {
true
}
_ { false }
} {
fcx.ccx.tcx.sess.span_warn(s.span, ~"unreachable statement");
warned = true;
}
bot |= check_stmt(fcx, s);
}
bot |= check_stmt(fcx, s);
}
alt blk.node.expr {
none { fcx.write_nil(blk.node.id); }
some(e) {
if bot && !warned {
fcx.ccx.tcx.sess.span_warn(e.span, ~"unreachable expression");
alt blk.node.expr {
none { fcx.write_nil(blk.node.id); }
some(e) {
if bot && !warned {
fcx.ccx.tcx.sess.span_warn(e.span, ~"unreachable expression");
}
bot |= check_expr(fcx, e, none);
let ety = fcx.expr_ty(e);
fcx.write_ty(blk.node.id, ety);
}
}
bot |= check_expr(fcx, e, none);
let ety = fcx.expr_ty(e);
fcx.write_ty(blk.node.id, ety);
}
if bot {
fcx.write_bot(blk.node.id);
}
bot
}
if bot {
fcx.write_bot(blk.node.id);
}
vec::pop(fcx.blocks);
ret bot;
}
fn check_const(ccx: @crate_ctxt, _sp: span, e: @ast::expr, id: ast::node_id) {
let rty = ty::node_id_to_type(ccx.tcx, id);
let fcx = blank_fn_ctxt(ccx, rty);
let fcx = blank_fn_ctxt(ccx, rty, e.id);
check_expr(fcx, e, none);
let cty = fcx.expr_ty(e);
let declty = fcx.ccx.tcx.tcache.get(local_def(id)).ty;
@ -1817,13 +1854,13 @@ fn check_enum_variants(ccx: @crate_ctxt,
vs: ~[ast::variant],
id: ast::node_id) {
let rty = ty::node_id_to_type(ccx.tcx, id);
let fcx = blank_fn_ctxt(ccx, rty);
let mut disr_vals: ~[int] = ~[];
let mut disr_val = 0;
let mut variants = ~[];
for vs.each |v| {
alt v.node.disr_expr {
some(e) {
let fcx = blank_fn_ctxt(ccx, rty, e.id);
check_expr(fcx, e, none);
let cty = fcx.expr_ty(e);
let declty = ty::mk_int(ccx.tcx);
@ -2003,7 +2040,7 @@ fn instantiate_path(fcx: @fn_ctxt,
some(ast_region_to_region(fcx, fcx, sp, r))
}
none if tpt.rp => {
some(fcx.infcx.next_region_var())
some(fcx.infcx.next_region_var_nb())
}
none => {
none
@ -2037,8 +2074,7 @@ fn instantiate_path(fcx: @fn_ctxt,
// Resolves `typ` by a single level if `typ` is a type variable. If no
// resolution is possible, then an error is reported.
fn structurally_resolved_type(fcx: @fn_ctxt, sp: span, tp: ty::t) -> ty::t {
alt infer::resolve_shallow(fcx.infcx, tp,
force_ty_vars_only) {
alt infer::resolve_type(fcx.infcx, tp, force_tvar) {
result::ok(t_s) if !ty::type_is_var(t_s) { ret t_s; }
_ {
fcx.ccx.tcx.sess.span_fatal

View File

@ -26,11 +26,10 @@ fn eqtype(fcx: @fn_ctxt, sp: span,
}
// Checks that the type `actual` can be assigned to `expected`.
fn assign(fcx: @fn_ctxt, sp: span, borrow_scope: ast::node_id,
fn assign(fcx: @fn_ctxt, sp: span, borrow_lb: ast::node_id,
expected: ty::t, expr: @ast::expr) {
let expr_ty = fcx.expr_ty(expr);
let anmnt = {expr_id: expr.id, borrow_scope: borrow_scope};
alt infer::mk_assignty(fcx.infcx, anmnt, expr_ty, expected) {
alt fcx.mk_assignty(expr, borrow_lb, expr_ty, expected) {
result::ok(()) { /* ok */ }
result::err(err) {
fcx.report_mismatched_types(sp, expected, expr_ty, err);

View File

@ -19,7 +19,7 @@ class lookup {
let fcx: @fn_ctxt;
let expr: @ast::expr;
let self_expr: @ast::expr;
let borrow_scope: ast::node_id;
let borrow_lb: ast::node_id;
let node_id: ast::node_id;
let m_name: ast::ident;
let mut self_ty: ty::t;
@ -32,7 +32,7 @@ class lookup {
new(fcx: @fn_ctxt,
expr: @ast::expr, //expr for a.b in a.b()
self_expr: @ast::expr, //a in a.b(...)
borrow_scope: ast::node_id, //scope to borrow the expr for
borrow_lb: ast::node_id, //scope to borrow the expr for
node_id: ast::node_id, //node id where to store type of fn
m_name: ast::ident, //b in a.b(...)
self_ty: ty::t, //type of a in a.b(...)
@ -42,7 +42,7 @@ class lookup {
self.fcx = fcx;
self.expr = expr;
self.self_expr = self_expr;
self.borrow_scope = borrow_scope;
self.borrow_lb = borrow_lb;
self.node_id = node_id;
self.m_name = m_name;
self.self_ty = self_ty;
@ -315,7 +315,7 @@ class lookup {
// type assignability. Collect the matches.
let matches = if use_assignability {
self.fcx.can_mk_assignty(
self.self_expr, self.borrow_scope,
self.self_expr, self.borrow_lb,
self.self_ty, impl_ty)
} else {
self.fcx.can_mk_subty(self.self_ty, impl_ty)
@ -377,7 +377,7 @@ class lookup {
// Make the actual receiver type (cand.self_ty) assignable to the
// required receiver type (cand.rcvr_ty). If this method is not
// from an impl, this'll basically be a no-nop.
alt self.fcx.mk_assignty(self.self_expr, self.borrow_scope,
alt self.fcx.mk_assignty(self.self_expr, self.borrow_lb,
cand.self_ty, cand.rcvr_ty) {
result::ok(_) {}
result::err(_) {

View File

@ -8,13 +8,19 @@ know whether a given type will be a region pointer or not until this
phase.
In particular, we ensure that, if the type of an expression or
variable is `&r.T`, then the expression or variable must occur within
the region scope `r`.
variable is `&r/T`, then the expression or variable must occur within
the region scope `r`. Note that in some cases `r` may still be a
region variable, so this gives us a chance to influence the value for
`r` that we infer to ensure we choose a value large enough to enclose
all uses. There is a lengthy comment in visit_node() that explains
this point a bit better.
*/
import util::ppaux;
import syntax::print::pprust;
import infer::{resolve_type, resolve_all, force_all,
resolve_rvar, force_rvar};
type rcx = @{fcx: @fn_ctxt, mut errors_reported: uint};
type rvt = visit::vt<rcx>;
@ -114,8 +120,31 @@ fn visit_node(id: ast::node_id, span: span, rcx: rcx) -> bool {
// Try to resolve the type. If we encounter an error, then typeck
// is going to fail anyway, so just stop here and let typeck
// report errors later on in the writeback phase.
//
// Note one important point: we do not attempt to resolve *region
// variables* here. This is because regionck is essentially adding
// constraints to those region variables and so may yet influence
// how they are resolved.
//
// Consider this silly example:
//
// fn borrow(x: &int) -> &int {x}
// fn foo(x: @int) -> int { /* block: B */
// let b = borrow(x); /* region: <R0> */
// *b
// }
//
// Here, the region of `b` will be `<R0>`. `<R0>` is constrainted
// to be some subregion of the block B and some superregion of
// the call. If we forced it now, we'd choose the smaller region
// (the call). But that would make the *b illegal. Since we don't
// resolve, the type of b will be `&<R0>.int` and then `*b` will require
// that `<R0>` be bigger than the let and the `*b` expression, so we
// will effectively resolve `<R0>` to be the block B.
let ty0 = fcx.node_ty(id);
let ty = alt infer::resolve_deep(fcx.infcx, ty0, force_none) {
let ty = alt resolve_type(fcx.infcx, ty0,
(resolve_all | force_all) -
(resolve_rvar | force_rvar)) {
result::err(_) { ret true; }
result::ok(ty) { ty }
};
@ -161,11 +190,12 @@ fn visit_node(id: ast::node_id, span: span, rcx: rcx) -> bool {
alt rcx.fcx.mk_subr(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, region)]);
ppaux::region_to_str(tcx, region1)]);
rcx.errors_reported += 1u;
}
result::ok(()) {

View File

@ -1,4 +1,5 @@
import check::{fn_ctxt, impl_self_ty, methods};
import infer::{resolve_type, resolve_all, force_all, fixup_err_to_str};
fn has_trait_bounds(tps: ~[ty::param_bounds]) -> bool {
vec::any(tps, |bs| {
@ -178,14 +179,14 @@ fn lookup_vtable(fcx: @fn_ctxt, isc: resolve::iscopes, sp: span,
fn fixup_ty(fcx: @fn_ctxt, sp: span, ty: ty::t) -> ty::t {
let tcx = fcx.ccx.tcx;
alt infer::resolve_deep(fcx.infcx, ty, force_all) {
alt resolve_type(fcx.infcx, ty, resolve_all | force_all) {
result::ok(new_type) { new_type }
result::err(e) {
tcx.sess.span_fatal(
sp,
#fmt["cannot determine a type \
for this bounded type parameter: %s",
infer::fixup_err_to_str(e)])
fixup_err_to_str(e)])
}
}
}

View File

@ -3,14 +3,14 @@
// substitutions.
import check::{fn_ctxt, lookup_local, methods};
import infer::{resolve_type, resolve_all, force_all};
export resolve_type_vars_in_fn;
export resolve_type_vars_in_expr;
fn resolve_type_vars_in_type(fcx: @fn_ctxt, sp: span, typ: ty::t) ->
option<ty::t> {
if !ty::type_needs_infer(typ) { ret some(typ); }
alt infer::resolve_deep(fcx.infcx, typ, force_all) {
alt resolve_type(fcx.infcx, typ, resolve_all | force_all) {
result::ok(new_type) { ret some(new_type); }
result::err(e) {
if !fcx.ccx.tcx.sess.has_errors() {
@ -130,7 +130,8 @@ fn visit_pat(p: @ast::pat, wbcx: wb_ctxt, v: wb_vt) {
fn visit_local(l: @ast::local, wbcx: wb_ctxt, v: wb_vt) {
if !wbcx.success { ret; }
let var_id = lookup_local(wbcx.fcx, l.span, l.node.id);
alt infer::resolve_deep_var(wbcx.fcx.infcx, var_id, force_all) {
let var_ty = ty::mk_var(wbcx.fcx.tcx(), var_id);
alt resolve_type(wbcx.fcx.infcx, var_ty, resolve_all | force_all) {
result::ok(lty) {
#debug["Type for local %s (id %d) resolved to %s",
pat_to_str(l.node.pat), l.node.id,
@ -166,6 +167,9 @@ fn resolve_type_vars_in_expr(fcx: @fn_ctxt, e: @ast::expr) -> bool {
let wbcx = {fcx: fcx, mut success: true};
let visit = mk_visitor();
visit.visit_expr(e, wbcx, visit);
if wbcx.success {
infer::resolve_borrowings(fcx.infcx);
}
ret wbcx.success;
}
@ -178,5 +182,8 @@ fn resolve_type_vars_in_fn(fcx: @fn_ctxt,
for decl.inputs.each |arg| {
resolve_type_vars_for_node(wbcx, arg.ty.span, arg.id);
}
if wbcx.success {
infer::resolve_borrowings(fcx.infcx);
}
ret wbcx.success;
}

View File

@ -242,7 +242,7 @@ class CoherenceChecker {
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())};
else {some(self.inference_context.next_region_var_nb())};
let bounds_count = polytype.bounds.len();
let type_parameters =

View File

@ -181,6 +181,7 @@ import driver::session::session;
import util::common::{indent, indenter};
import ast::{unsafe_fn, impure_fn, pure_fn, extern_fn};
import ast::{m_const, m_imm, m_mutbl};
import dvec::{dvec, extensions};
export infer_ctxt;
export new_infer_ctxt;
@ -188,16 +189,16 @@ export mk_subty, can_mk_subty;
export mk_subr;
export mk_eqty;
export mk_assignty, can_mk_assignty;
export resolve_shallow;
export resolve_deep;
export resolve_deep_var;
export resolve_nested_tvar, resolve_rvar, resolve_ivar, resolve_all;
export force_tvar, force_rvar, force_ivar, force_all;
export resolve_type, resolve_region;
export resolve_borrowings;
export methods; // for infer_ctxt
export unify_methods; // for infer_ctxt
export fixup_err, fixup_err_to_str;
export assignment;
export root, to_str;
export int_ty_set_all;
export force_level, force_none, force_ty_vars_only, force_all;
// Bitvector to represent sets of integral types
enum int_ty_set = uint;
@ -280,7 +281,8 @@ fn convert_integral_ty_to_int_ty_set(tcx: ty::ctxt, t: ty::t)
// value should be borrowed.
type assignment = {
expr_id: ast::node_id,
borrow_scope: ast::node_id
borrow_lb: ast::node_id,
borrow_ub: ast::node_id,
};
type bound<T:copy> = option<T>;
@ -321,6 +323,10 @@ enum infer_ctxt = @{
ty_var_counter: @mut uint,
ty_var_integral_counter: @mut uint,
region_var_counter: @mut uint,
borrowings: dvec<{expr_id: ast::node_id,
scope: ty::region,
mutbl: ast::mutability}>
};
enum fixup_err {
@ -328,7 +334,7 @@ enum fixup_err {
unresolved_ty(tv_vid),
cyclic_ty(tv_vid),
unresolved_region(region_vid),
cyclic_region(region_vid)
region_var_bound_by_region_var(region_vid, region_vid)
}
fn fixup_err_to_str(f: fixup_err) -> ~str {
@ -337,7 +343,10 @@ fn fixup_err_to_str(f: fixup_err) -> ~str {
unresolved_ty(_) { ~"unconstrained type" }
cyclic_ty(_) { ~"cyclic type of infinite size" }
unresolved_region(_) { ~"unconstrained region" }
cyclic_region(_) { ~"cyclic region" }
region_var_bound_by_region_var(r1, r2) {
#fmt["region var %? bound by another region var %?; this is \
a bug in rustc", r1, r2]
}
}
}
@ -351,7 +360,8 @@ fn new_infer_ctxt(tcx: ty::ctxt) -> infer_ctxt {
rb: {vals: smallintmap::mk(), mut bindings: ~[]},
ty_var_counter: @mut 0u,
ty_var_integral_counter: @mut 0u,
region_var_counter: @mut 0u})}
region_var_counter: @mut 0u,
borrowings: dvec()})}
fn mk_subty(cx: infer_ctxt, a: ty::t, b: ty::t) -> ures {
#debug["mk_subty(%s <: %s)", a.to_str(cx), b.to_str(cx)];
@ -387,10 +397,11 @@ fn can_mk_assignty(cx: infer_ctxt, anmnt: assignment,
#debug["can_mk_assignty(%? / %s <: %s)",
anmnt, a.to_str(cx), b.to_str(cx)];
// FIXME (#2593): this will not unroll any entries we make in the
// borrowings table. But this is OK for the moment because this is only
// used in method lookup, and there must be exactly one match or an
// error is reported. Still, it should be fixed.
// FIXME(#2593)---this will not unroll any entries we make in the
// borrowings table. But this is OK for the moment because this
// is only used in method lookup, and there must be exactly one
// match or an error is reported. Still, it should be fixed. (#2593)
// NDM OUTDATED
indent(|| cx.probe(||
cx.assign_tys(anmnt, a, b)
@ -398,21 +409,32 @@ fn can_mk_assignty(cx: infer_ctxt, anmnt: assignment,
}
// See comment on the type `resolve_state` below
fn resolve_shallow(cx: infer_ctxt, a: ty::t,
force_vars: force_level) -> fres<ty::t> {
resolver(cx, false, force_vars).resolve(a)
}
// See comment on the type `resolve_state` below
fn resolve_deep_var(cx: infer_ctxt, vid: tv_vid,
force_vars: force_level) -> fres<ty::t> {
resolver(cx, true, force_vars).resolve(ty::mk_var(cx.tcx, vid))
}
// See comment on the type `resolve_state` below
fn resolve_deep(cx: infer_ctxt, a: ty::t, force_vars: force_level)
fn resolve_type(cx: infer_ctxt, a: ty::t, modes: uint)
-> fres<ty::t> {
resolver(cx, true, force_vars).resolve(a)
resolver(cx, modes).resolve_type_chk(a)
}
fn resolve_region(cx: infer_ctxt, r: ty::region, modes: uint)
-> fres<ty::region> {
resolver(cx, modes).resolve_region_chk(r)
}
fn resolve_borrowings(cx: infer_ctxt) {
for cx.borrowings.each |item| {
alt resolve_region(cx, item.scope, resolve_all|force_all) {
ok(ty::re_scope(scope_id)) => {
#debug["borrowing for expr %d resolved to scope %d, mutbl %?",
item.expr_id, scope_id, item.mutbl];
cx.tcx.borrowings.insert(
item.expr_id, {scope_id: scope_id, mutbl: item.mutbl});
}
r => {
cx.tcx.sess.bug(
#fmt["borrowing resolved to %?, not a valid scope", r]);
}
}
}
}
impl methods for ures {
@ -567,6 +589,8 @@ impl transaction_methods for infer_ctxt {
let tvbl = self.tvb.bindings.len();
let rbl = self.rb.bindings.len();
let bl = self.borrowings.len();
#debug["try(tvbl=%u, rbl=%u)", tvbl, rbl];
let r <- f();
alt r {
@ -575,6 +599,7 @@ impl transaction_methods for infer_ctxt {
#debug["try--rollback"];
rollback_to(self.tvb, tvbl);
rollback_to(self.rb, rbl);
while self.borrowings.len() != bl { self.borrowings.pop(); }
}
}
ret r;
@ -621,16 +646,19 @@ impl methods for infer_ctxt {
ty::mk_var_integral(self.tcx, self.next_ty_var_integral_id())
}
fn next_region_var_id() -> region_vid {
fn next_region_var_id(bnds: bounds<ty::region>) -> region_vid {
let id = *self.region_var_counter;
*self.region_var_counter += 1u;
self.rb.vals.insert(id,
root({lb: none, ub: none}, 0u));
self.rb.vals.insert(id, root(bnds, 0));
ret region_vid(id);
}
fn next_region_var() -> ty::region {
ty::re_var(self.next_region_var_id())
fn next_region_var(bnds: bounds<ty::region>) -> ty::region {
ty::re_var(self.next_region_var_id(bnds))
}
fn next_region_var_nb() -> ty::region { // nb == "no bounds"
self.next_region_var({lb: none, ub: none})
}
fn ty_to_str(t: ty::t) -> ~str {
@ -639,11 +667,18 @@ impl methods for infer_ctxt {
}
fn resolve_type_vars_if_possible(typ: ty::t) -> ty::t {
alt infer::resolve_deep(self, typ, force_none) {
alt resolve_type(self, typ, resolve_all) {
result::ok(new_type) { ret new_type; }
result::err(_) { ret typ; }
}
}
fn resolve_region_if_possible(oldr: ty::region) -> ty::region {
alt resolve_region(self, oldr, resolve_all) {
result::ok(newr) { ret newr; }
result::err(_) { ret oldr; }
}
}
}
impl unify_methods for infer_ctxt {
@ -1029,67 +1064,70 @@ impl unify_methods for infer_ctxt {
// the behavior in the face of unconstrained type and region
// variables.
enum force_level {
// Any unconstrained variables are OK.
force_none,
// Unconstrained region vars and integral ty vars are OK;
// unconstrained general-purpose ty vars result in an error.
force_ty_vars_only,
// Any unconstrained variables result in an error.
force_all,
}
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;
type resolve_state = @{
infcx: infer_ctxt,
deep: bool,
force_vars: force_level,
modes: uint,
mut err: option<fixup_err>,
mut r_seen: ~[region_vid],
mut v_seen: ~[tv_vid]
};
fn resolver(infcx: infer_ctxt, deep: bool, fvars: force_level)
fn resolver(infcx: infer_ctxt, modes: uint)
-> resolve_state {
@{infcx: infcx,
deep: deep,
force_vars: fvars,
modes: modes,
mut err: none,
mut r_seen: ~[],
mut v_seen: ~[]}
}
impl methods for resolve_state {
fn resolve(typ: ty::t) -> fres<ty::t> {
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 (deep=%b, force_vars=%?)",
#debug["Resolving %s (modes=%x)",
ty_to_str(self.infcx.tcx, typ),
self.deep,
self.force_vars];
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) && vec::is_empty(self.r_seen);
let rty = indent(|| self.resolve1(typ) );
assert vec::is_empty(self.v_seen) && vec::is_empty(self.r_seen);
assert vec::is_empty(self.v_seen);
let rty = indent(|| self.resolve_type(typ) );
assert vec::is_empty(self.v_seen);
alt self.err {
none {
#debug["Resolved to %s (deep=%b, force_vars=%?)",
#debug["Resolved to %s (modes=%x)",
ty_to_str(self.infcx.tcx, rty),
self.deep,
self.force_vars];
self.modes];
ret ok(rty);
}
some(e) { ret err(e); }
}
}
fn resolve1(typ: ty::t) -> ty::t {
#debug("Resolve1(%s)", typ.to_str(self.infcx));
fn resolve_region_chk(orig: ty::region) -> fres<ty::region> {
self.err = none;
let resolved = indent(|| self.resolve_region(orig) );
alt 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) { ret typ; }
@ -1100,23 +1138,30 @@ impl methods for resolve_state {
ty::ty_var_integral(vid) {
self.resolve_ty_var_integral(vid)
}
_ if !ty::type_has_regions(typ) && !self.deep {
typ
}
_ {
ty::fold_regions_and_ty(
self.infcx.tcx, typ,
|r| self.resolve_region(r),
|t| self.resolve_if_deep(t),
|t| self.resolve_if_deep(t))
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_if_deep(typ: ty::t) -> ty::t {
fn resolve_nested_tvar(typ: ty::t) -> ty::t {
#debug("Resolve_if_deep(%s)", typ.to_str(self.infcx));
if !self.deep {typ} else {self.resolve1(typ)}
if !self.should(resolve_nested_tvar) {
typ
} else {
self.resolve_type(typ)
}
}
fn resolve_region(orig: ty::region) -> ty::region {
@ -1128,29 +1173,29 @@ impl methods for resolve_state {
}
fn resolve_region_var(rid: region_vid) -> ty::region {
if vec::contains(self.r_seen, rid) {
self.err = some(cyclic_region(rid));
ret ty::re_var(rid);
} else {
vec::push(self.r_seen, rid);
let nde = self.infcx.get(self.infcx.rb, rid);
let bounds = nde.possible_types;
if !self.should(resolve_rvar) {
ret ty::re_var(rid)
}
let nde = self.infcx.get(self.infcx.rb, rid);
let bounds = nde.possible_types;
alt bounds {
{ ub:_, lb:some(r) } => { self.assert_not_rvar(rid, r); r }
{ ub:some(r), lb:_ } => { self.assert_not_rvar(rid, r); r }
{ ub:none, lb:none } => {
if self.should(force_rvar) {
self.err = some(unresolved_region(rid));
}
ty::re_var(rid)
}
}
}
let r1 = alt bounds {
{ ub:_, lb:some(t) } { self.resolve_region(t) }
{ ub:some(t), lb:_ } { self.resolve_region(t) }
{ ub:none, lb:none } {
alt self.force_vars {
force_all {
self.err = some(unresolved_region(rid));
}
_ { /* ok */ }
}
ty::re_var(rid)
}
};
vec::pop(self.r_seen);
ret r1;
fn assert_not_rvar(rid: region_vid, r: ty::region) {
alt r {
ty::re_var(rid2) => {
self.err = some(region_var_bound_by_region_var(rid, rid2));
}
_ => { }
}
}
@ -1172,15 +1217,12 @@ impl methods for resolve_state {
let bounds = nde.possible_types;
let t1 = alt bounds {
{ ub:_, lb:some(t) } if !type_is_bot(t) { self.resolve1(t) }
{ ub:some(t), lb:_ } { self.resolve1(t) }
{ ub:_, lb:some(t) } { self.resolve1(t) }
{ 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 } {
alt self.force_vars {
force_ty_vars_only | force_all {
if self.should(force_tvar) {
self.err = some(unresolved_ty(vid));
}
force_none { /* ok */ }
}
ty::mk_var(tcx, vid)
}
@ -1191,6 +1233,10 @@ impl methods for resolve_state {
}
fn resolve_ty_var_integral(vid: tvi_vid) -> ty::t {
if !self.should(resolve_ivar) {
ret ty::mk_var_integral(self.infcx.tcx, vid);
}
let nde = self.infcx.get(self.infcx.tvib, vid);
let pt = nde.possible_types;
@ -1199,8 +1245,7 @@ impl methods for resolve_state {
alt single_type_contained_in(self.infcx.tcx, pt) {
some(t) { t }
none {
alt self.force_vars {
force_all {
if self.should(force_ivar) {
// As a last resort, default to int.
let ty = ty::mk_int(self.infcx.tcx);
self.infcx.set(
@ -1209,10 +1254,8 @@ impl methods for resolve_state {
ty),
nde.rank));
ty
}
force_none | force_ty_vars_only {
} else {
ty::mk_var_integral(self.infcx.tcx, vid)
}
}
}
}
@ -1393,15 +1436,22 @@ impl assignment for infer_ctxt {
do indent {
do self.sub_tys(a, nr_b).then {
let r_a = ty::re_scope(anmnt.borrow_scope);
// Create a fresh region variable `r_a` with the given
// borrow bounds:
let r_lb = ty::re_scope(anmnt.borrow_lb);
let r_ub = ty::re_scope(anmnt.borrow_ub);
let r_a = self.next_region_var({lb: some(r_lb),
ub: some(r_ub)});
#debug["anmnt=%?", anmnt];
do sub(self).contraregions(r_a, r_b).chain |_r| {
// if successful, add an entry indicating that
// borrowing occurred
#debug["borrowing expression #%?", anmnt];
let borrow = {scope_id: anmnt.borrow_scope,
mutbl: m};
self.tcx.borrowings.insert(anmnt.expr_id, borrow);
#debug["borrowing expression #%?, scope=%?, m=%?",
anmnt, r_a, m];
self.borrowings.push({expr_id: anmnt.expr_id,
scope: r_a,
mutbl: m});
uok()
}
}
@ -1921,7 +1971,7 @@ impl of combine for sub {
// 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.
let rvar = self.infcx().next_region_var();
let rvar = self.infcx().next_region_var_nb();
#debug["Bound region %s maps to %s",
bound_region_to_str(self.tcx, br),
region_to_str(self.tcx, rvar)];

View File

@ -43,7 +43,11 @@ fn re_scope_id_to_str(cx: ctxt, node_id: ast::node_id) -> ~str {
#fmt("<alt at %s>",
codemap::span_to_str(expr.span, cx.sess.codemap))
}
ast::expr_field(*) | ast::expr_unary(*) | ast::expr_binary(*) {
ast::expr_assign_op(*) |
ast::expr_field(*) |
ast::expr_unary(*) |
ast::expr_binary(*) |
ast::expr_index(*) {
#fmt("<method at %s>",
codemap::span_to_str(expr.span, cx.sess.codemap))
}

View File

@ -1,8 +0,0 @@
fn foo(x: &uint) -> &uint { x }
fn main() {
let p = @3u;
let r = foo(p);
//~^ ERROR reference is not valid
assert *p == *r;
}

View File

@ -0,0 +1,14 @@
type point = {x: int, y: int};
fn x_coord(p: &point) -> &int {
ret &p.x;
}
fn foo(p: @point) -> &int {
let xc = x_coord(p);
assert *xc == 3;
ret xc; //~ ERROR mismatched types: expected `&int` but found
}
fn main() {}

View File

@ -0,0 +1,11 @@
fn borrow<T>(x: &T) -> &T {x}
fn main() {
let x = @3;
let y: &int; //~ ERROR reference is not valid outside of its lifetime
while true {
y = borrow(x);
assert *x == *y;
}
assert *x == *y;
}

View File

@ -3,5 +3,5 @@ fn bar(x: &uint) -> uint { *x }
fn main() {
let p = @3u;
bar(foo(p)); //~ ERROR reference is not valid
assert bar(foo(p)) == 3;
}

View File

@ -0,0 +1,9 @@
fn view<T>(x: &[T]) -> &[T] {x}
fn main() {
let v = ~[1, 2, 3];
let x = view(v);
let y = view(x);
assert (v[0] == x[0]) && (v[0] == y[0]);
}

View File

@ -0,0 +1,10 @@
fn borrow<T>(x: &T) -> &T {x}
fn main() {
let x = @3;
loop {
let y = borrow(x);
assert *x == *y;
break;
}
}

View File

@ -0,0 +1,12 @@
type point = {x: int, y: int};
fn x_coord(p: &point) -> &int {
ret &p.x;
}
fn main() {
let p = @{x: 3, y: 4};
let xc = x_coord(p);
assert *xc == 3;
}