mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-28 17:53:56 +00:00
infer the scope of borrows
This commit is contained in:
parent
41a21f053c
commit
3ef7ff8b89
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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(_) {
|
||||
|
@ -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(()) {
|
||||
|
@ -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)])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 =
|
||||
|
@ -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)];
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
14
src/test/compile-fail/regions-infer-borrow-scope-too-big.rs
Normal file
14
src/test/compile-fail/regions-infer-borrow-scope-too-big.rs
Normal 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() {}
|
||||
|
@ -0,0 +1,11 @@
|
||||
fn borrow<T>(x: &T) -> &T {x}
|
||||
|
||||
fn main() {
|
||||
let x = @3;
|
||||
let y: ∫ //~ ERROR reference is not valid outside of its lifetime
|
||||
while true {
|
||||
y = borrow(x);
|
||||
assert *x == *y;
|
||||
}
|
||||
assert *x == *y;
|
||||
}
|
@ -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;
|
||||
}
|
9
src/test/run-pass/regions-infer-borrow-scope-view.rs
Normal file
9
src/test/run-pass/regions-infer-borrow-scope-view.rs
Normal 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]);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
12
src/test/run-pass/regions-infer-borrow-scope.rs
Normal file
12
src/test/run-pass/regions-infer-borrow-scope.rs
Normal 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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user