2012-12-03 18:48:01 -06:00
|
|
|
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
2012-06-01 12:46:17 -05:00
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
// Gathering loans
|
|
|
|
//
|
|
|
|
// The borrow check proceeds in two phases. In phase one, we gather the full
|
|
|
|
// set of loans that are required at any point. These are sorted according to
|
|
|
|
// their associated scopes. In phase two, checking loans, we will then make
|
|
|
|
// sure that all of these loans are honored.
|
|
|
|
|
2013-01-08 21:37:25 -06:00
|
|
|
use core::prelude::*;
|
2013-01-07 16:16:52 -06:00
|
|
|
|
2013-01-10 12:59:58 -06:00
|
|
|
use middle::borrowck::preserve::{PreserveCondition, PcOk, PcIfPure};
|
|
|
|
use middle::borrowck::{Loan, bckerr, bckres, BorrowckCtxt, err_mutbl};
|
2013-02-09 00:21:45 -06:00
|
|
|
use middle::borrowck::{LoanKind, TotalFreeze, PartialFreeze,
|
|
|
|
TotalTake, PartialTake, Immobile};
|
2013-02-19 01:40:42 -06:00
|
|
|
use middle::borrowck::ReqMaps;
|
2013-02-09 00:21:45 -06:00
|
|
|
use middle::borrowck::loan;
|
2013-03-26 15:38:07 -05:00
|
|
|
use middle::mem_categorization::{cmt, mem_categorization_ctxt};
|
2012-12-23 16:41:37 -06:00
|
|
|
use middle::pat_util;
|
2012-12-13 15:05:22 -06:00
|
|
|
use middle::ty::{ty_region};
|
2012-12-23 16:41:37 -06:00
|
|
|
use middle::ty;
|
2013-01-08 21:37:25 -06:00
|
|
|
use util::common::indenter;
|
Cleanup substitutions and treatment of generics around traits in a number of ways.
- In a TraitRef, use the self type consistently to refer to the Self type:
- trait ref in `impl Trait<A,B,C> for S` has a self type of `S`.
- trait ref in `A:Trait` has the self type `A`
- trait ref associated with a trait decl has self type `Self`
- trait ref associated with a supertype has self type `Self`
- trait ref in an object type `@Trait` has no self type
- Rewrite `each_bound_traits_and_supertraits` to perform
substitutions as it goes, and thus yield a series of trait refs
that are always in the same 'namespace' as the type parameter
bound given as input. Before, we left this to the caller, but
this doesn't work because the caller lacks adequare information
to perform the type substitutions correctly.
- For provided methods, substitute the generics involved in the provided
method correctly.
- Introduce TypeParameterDef, which tracks the bounds declared on a type
parameter and brings them together with the def_id and (in the future)
other information (maybe even the parameter's name!).
- Introduce Subst trait, which helps to cleanup a lot of the
repetitive code involved with doing type substitution.
- Introduce Repr trait, which makes debug printouts far more convenient.
Fixes #4183. Needed for #5656.
2013-04-09 00:54:49 -05:00
|
|
|
use util::ppaux::{Repr, region_to_str};
|
2012-06-01 12:46:17 -05:00
|
|
|
|
2013-04-03 08:28:36 -05:00
|
|
|
use core::hashmap::{HashSet, HashMap};
|
2012-12-23 16:41:37 -06:00
|
|
|
use core::vec;
|
2013-01-08 21:37:25 -06:00
|
|
|
use syntax::ast::{m_const, m_imm, m_mutbl};
|
2012-12-23 16:41:37 -06:00
|
|
|
use syntax::ast;
|
2013-01-08 21:37:25 -06:00
|
|
|
use syntax::codemap::span;
|
2012-12-23 16:41:37 -06:00
|
|
|
use syntax::print::pprust;
|
|
|
|
use syntax::visit;
|
2012-11-28 15:51:50 -06:00
|
|
|
|
2012-07-26 10:51:57 -05:00
|
|
|
/// Context used while gathering loans:
|
|
|
|
///
|
|
|
|
/// - `bccx`: the the borrow check context
|
|
|
|
/// - `req_maps`: the maps computed by `gather_loans()`, see def'n of the
|
2013-02-19 01:40:42 -06:00
|
|
|
/// struct `ReqMaps` for more info
|
2012-07-26 10:51:57 -05:00
|
|
|
/// - `item_ub`: the id of the block for the enclosing fn/method item
|
|
|
|
/// - `root_ub`: the id of the outermost block for which we can root
|
|
|
|
/// an `@T`. This is the id of the innermost enclosing
|
|
|
|
/// loop or function body.
|
|
|
|
///
|
|
|
|
/// The role of `root_ub` is to prevent us from having to accumulate
|
|
|
|
/// vectors of rooted items at runtime. Consider this case:
|
|
|
|
///
|
|
|
|
/// fn foo(...) -> int {
|
|
|
|
/// let mut ptr: ∫
|
|
|
|
/// while some_cond {
|
|
|
|
/// let x: @int = ...;
|
|
|
|
/// ptr = &*x;
|
|
|
|
/// }
|
|
|
|
/// *ptr
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// If we are not careful here, we would infer the scope of the borrow `&*x`
|
|
|
|
/// to be the body of the function `foo()` as a whole. We would then
|
|
|
|
/// have root each `@int` that is produced, which is an unbounded number.
|
|
|
|
/// No good. Instead what will happen is that `root_ub` will be set to the
|
|
|
|
/// body of the while loop and we will refuse to root the pointer `&*x`
|
|
|
|
/// because it would have to be rooted for a region greater than `root_ub`.
|
2013-02-04 16:02:01 -06:00
|
|
|
struct GatherLoanCtxt {
|
|
|
|
bccx: @BorrowckCtxt,
|
2013-02-19 01:40:42 -06:00
|
|
|
req_maps: ReqMaps,
|
2013-02-04 16:02:01 -06:00
|
|
|
item_ub: ast::node_id,
|
|
|
|
root_ub: ast::node_id,
|
2013-04-03 08:28:36 -05:00
|
|
|
ignore_adjustments: HashSet<ast::node_id>
|
2013-02-04 16:02:01 -06:00
|
|
|
}
|
2012-06-01 12:46:17 -05:00
|
|
|
|
2013-02-19 01:40:42 -06:00
|
|
|
pub fn gather_loans(bccx: @BorrowckCtxt, crate: @ast::crate) -> ReqMaps {
|
2013-02-04 16:02:01 -06:00
|
|
|
let glcx = @mut GatherLoanCtxt {
|
|
|
|
bccx: bccx,
|
2013-04-03 08:28:36 -05:00
|
|
|
req_maps: ReqMaps { req_loan_map: HashMap::new(),
|
|
|
|
pure_map: HashMap::new() },
|
2013-02-04 16:02:01 -06:00
|
|
|
item_ub: 0,
|
|
|
|
root_ub: 0,
|
2013-04-03 08:28:36 -05:00
|
|
|
ignore_adjustments: HashSet::new()
|
2013-02-04 16:02:01 -06:00
|
|
|
};
|
2013-01-08 16:00:45 -06:00
|
|
|
let v = visit::mk_vt(@visit::Visitor {visit_expr: req_loans_in_expr,
|
|
|
|
visit_fn: req_loans_in_fn,
|
2013-01-13 18:51:18 -06:00
|
|
|
visit_stmt: add_stmt_to_map,
|
2013-01-08 16:00:45 -06:00
|
|
|
.. *visit::default_visitor()});
|
2013-04-17 11:15:37 -05:00
|
|
|
visit::visit_crate(crate, glcx, v);
|
2013-03-22 21:26:41 -05:00
|
|
|
let @GatherLoanCtxt{req_maps, _} = glcx;
|
|
|
|
return req_maps;
|
2012-06-01 12:46:17 -05:00
|
|
|
}
|
|
|
|
|
2013-02-18 23:25:44 -06:00
|
|
|
fn req_loans_in_fn(fk: &visit::fn_kind,
|
2013-02-18 00:20:36 -06:00
|
|
|
decl: &ast::fn_decl,
|
|
|
|
body: &ast::blk,
|
2012-07-26 10:51:57 -05:00
|
|
|
sp: span,
|
|
|
|
id: ast::node_id,
|
2013-04-17 11:15:37 -05:00
|
|
|
self: @mut GatherLoanCtxt,
|
2013-02-04 16:02:01 -06:00
|
|
|
v: visit::vt<@mut GatherLoanCtxt>) {
|
2012-07-26 10:51:57 -05:00
|
|
|
// see explanation attached to the `root_ub` field:
|
|
|
|
let old_item_id = self.item_ub;
|
|
|
|
let old_root_ub = self.root_ub;
|
|
|
|
self.root_ub = body.node.id;
|
|
|
|
|
2013-02-18 23:25:44 -06:00
|
|
|
match *fk {
|
2012-10-08 13:49:01 -05:00
|
|
|
visit::fk_anon(*) | visit::fk_fn_block(*) => {}
|
|
|
|
visit::fk_item_fn(*) | visit::fk_method(*) |
|
|
|
|
visit::fk_dtor(*) => {
|
|
|
|
self.item_ub = body.node.id;
|
|
|
|
}
|
2012-07-26 10:51:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
visit::visit_fn(fk, decl, body, sp, id, self, v);
|
|
|
|
self.root_ub = old_root_ub;
|
|
|
|
self.item_ub = old_item_id;
|
|
|
|
}
|
|
|
|
|
2012-06-01 12:46:17 -05:00
|
|
|
fn req_loans_in_expr(ex: @ast::expr,
|
2013-04-17 11:15:37 -05:00
|
|
|
self: @mut GatherLoanCtxt,
|
2013-02-04 16:02:01 -06:00
|
|
|
vt: visit::vt<@mut GatherLoanCtxt>) {
|
2012-06-01 12:46:17 -05:00
|
|
|
let bccx = self.bccx;
|
|
|
|
let tcx = bccx.tcx;
|
2012-07-26 10:51:57 -05:00
|
|
|
let old_root_ub = self.root_ub;
|
2012-06-01 12:46:17 -05:00
|
|
|
|
2012-08-28 17:54:45 -05:00
|
|
|
debug!("req_loans_in_expr(expr=%?/%s)",
|
|
|
|
ex.id, pprust::expr_to_str(ex, tcx.sess.intr()));
|
2012-06-01 17:46:32 -05:00
|
|
|
|
2012-06-01 12:46:17 -05:00
|
|
|
// If this expression is borrowed, have to ensure it remains valid:
|
2013-03-16 13:11:31 -05:00
|
|
|
{
|
|
|
|
let mut this = &mut *self;
|
|
|
|
if !this.ignore_adjustments.contains(&ex.id) {
|
2013-03-22 21:26:41 -05:00
|
|
|
for tcx.adjustments.find(&ex.id).each |&adjustments| {
|
2013-03-16 13:11:31 -05:00
|
|
|
this.guarantee_adjustments(ex, *adjustments);
|
|
|
|
}
|
2012-11-28 15:51:50 -06:00
|
|
|
}
|
2012-06-01 12:46:17 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Special checks for various kinds of expressions:
|
2013-03-20 00:17:42 -05:00
|
|
|
match ex.node {
|
2012-08-03 21:59:04 -05:00
|
|
|
ast::expr_addr_of(mutbl, base) => {
|
2012-06-01 12:46:17 -05:00
|
|
|
let base_cmt = self.bccx.cat_expr(base);
|
|
|
|
|
|
|
|
// make sure that the thing we are pointing out stays valid
|
|
|
|
// for the lifetime `scope_r` of the resulting ptr:
|
2013-03-25 16:39:15 -05:00
|
|
|
let scope_r = ty_region(tcx, ex.span, tcx.ty(ex));
|
2012-06-01 12:46:17 -05:00
|
|
|
self.guarantee_valid(base_cmt, mutbl, scope_r);
|
2012-07-26 10:51:57 -05:00
|
|
|
visit::visit_expr(ex, self, vt);
|
2012-06-01 12:46:17 -05:00
|
|
|
}
|
|
|
|
|
2013-03-20 00:17:42 -05:00
|
|
|
ast::expr_call(f, ref args, _) => {
|
2012-06-01 12:46:17 -05:00
|
|
|
let arg_tys = ty::ty_fn_args(ty::expr_ty(self.tcx(), f));
|
|
|
|
let scope_r = ty::re_scope(ex.id);
|
2013-03-20 00:17:42 -05:00
|
|
|
for vec::each2(*args, arg_tys) |arg, arg_ty| {
|
2012-08-06 14:34:08 -05:00
|
|
|
match ty::resolved_mode(self.tcx(), arg_ty.mode) {
|
2013-01-10 12:59:58 -06:00
|
|
|
ast::by_ref => {
|
|
|
|
let arg_cmt = self.bccx.cat_expr(*arg);
|
|
|
|
self.guarantee_valid(arg_cmt, m_imm, scope_r);
|
|
|
|
}
|
2013-03-10 10:02:16 -05:00
|
|
|
ast::by_copy => {}
|
2012-06-01 12:46:17 -05:00
|
|
|
}
|
|
|
|
}
|
2012-07-26 10:51:57 -05:00
|
|
|
visit::visit_expr(ex, self, vt);
|
2012-06-01 12:46:17 -05:00
|
|
|
}
|
|
|
|
|
2013-03-26 15:38:07 -05:00
|
|
|
ast::expr_method_call(_, _, _, ref args, _) => {
|
2012-11-30 13:18:25 -06:00
|
|
|
let arg_tys = ty::ty_fn_args(ty::node_id_to_type(self.tcx(),
|
|
|
|
ex.callee_id));
|
|
|
|
let scope_r = ty::re_scope(ex.id);
|
2013-03-20 00:17:42 -05:00
|
|
|
for vec::each2(*args, arg_tys) |arg, arg_ty| {
|
2012-11-30 13:18:25 -06:00
|
|
|
match ty::resolved_mode(self.tcx(), arg_ty.mode) {
|
2013-01-10 12:59:58 -06:00
|
|
|
ast::by_ref => {
|
|
|
|
let arg_cmt = self.bccx.cat_expr(*arg);
|
|
|
|
self.guarantee_valid(arg_cmt, m_imm, scope_r);
|
|
|
|
}
|
2013-03-10 10:02:16 -05:00
|
|
|
ast::by_copy => {}
|
2012-11-30 13:18:25 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
visit::visit_expr(ex, self, vt);
|
|
|
|
}
|
|
|
|
|
2012-12-04 12:50:00 -06:00
|
|
|
ast::expr_match(ex_v, ref arms) => {
|
2012-06-01 12:46:17 -05:00
|
|
|
let cmt = self.bccx.cat_expr(ex_v);
|
2012-12-04 12:50:00 -06:00
|
|
|
for (*arms).each |arm| {
|
2012-06-30 18:19:07 -05:00
|
|
|
for arm.pats.each |pat| {
|
2012-09-19 18:55:01 -05:00
|
|
|
self.gather_pat(cmt, *pat, arm.body.node.id, ex.id);
|
2012-06-01 12:46:17 -05:00
|
|
|
}
|
|
|
|
}
|
2012-07-26 10:51:57 -05:00
|
|
|
visit::visit_expr(ex, self, vt);
|
2012-06-01 12:46:17 -05:00
|
|
|
}
|
|
|
|
|
2012-06-01 23:54:38 -05:00
|
|
|
ast::expr_index(rcvr, _) |
|
2012-06-01 17:46:32 -05:00
|
|
|
ast::expr_binary(_, rcvr, _) |
|
2012-11-28 15:51:50 -06:00
|
|
|
ast::expr_unary(_, rcvr) |
|
|
|
|
ast::expr_assign_op(_, rcvr, _)
|
2013-02-08 16:08:02 -06:00
|
|
|
if self.bccx.method_map.contains_key(&ex.id) => {
|
2012-06-01 17:46:32 -05:00
|
|
|
// Receivers in method calls are always passed by ref.
|
|
|
|
//
|
2012-06-01 23:54:38 -05:00
|
|
|
// Here, in an overloaded operator, the call is this expression,
|
|
|
|
// and hence the scope of the borrow is this call.
|
|
|
|
//
|
2012-06-21 18:44:10 -05:00
|
|
|
// FIX? / NOT REALLY---technically we should check the other
|
2012-06-01 23:54:38 -05:00
|
|
|
// argument and consider the argument mode. But how annoying.
|
|
|
|
// And this problem when goes away when argument modes are
|
|
|
|
// phased out. So I elect to leave this undone.
|
|
|
|
let scope_r = ty::re_scope(ex.id);
|
|
|
|
let rcvr_cmt = self.bccx.cat_expr(rcvr);
|
|
|
|
self.guarantee_valid(rcvr_cmt, m_imm, scope_r);
|
2012-11-28 15:51:50 -06:00
|
|
|
|
|
|
|
// FIXME (#3387): Total hack: Ignore adjustments for the left-hand
|
|
|
|
// side. Their regions will be inferred to be too large.
|
2013-01-22 20:59:28 -06:00
|
|
|
self.ignore_adjustments.insert(rcvr.id);
|
2012-11-28 15:51:50 -06:00
|
|
|
|
2012-07-26 10:51:57 -05:00
|
|
|
visit::visit_expr(ex, self, vt);
|
2012-06-01 23:54:38 -05:00
|
|
|
}
|
|
|
|
|
2012-08-28 17:54:45 -05:00
|
|
|
// FIXME--#3387
|
|
|
|
// ast::expr_binary(_, lhs, rhs) => {
|
|
|
|
// // Universal comparison operators like ==, >=, etc
|
|
|
|
// // take their arguments by reference.
|
|
|
|
// let lhs_ty = ty::expr_ty(self.tcx(), lhs);
|
|
|
|
// if !ty::type_is_scalar(lhs_ty) {
|
|
|
|
// let scope_r = ty::re_scope(ex.id);
|
|
|
|
// let lhs_cmt = self.bccx.cat_expr(lhs);
|
|
|
|
// self.guarantee_valid(lhs_cmt, m_imm, scope_r);
|
|
|
|
// let rhs_cmt = self.bccx.cat_expr(rhs);
|
|
|
|
// self.guarantee_valid(rhs_cmt, m_imm, scope_r);
|
|
|
|
// }
|
|
|
|
// visit::visit_expr(ex, self, vt);
|
|
|
|
// }
|
|
|
|
|
2012-06-01 23:54:38 -05:00
|
|
|
ast::expr_field(rcvr, _, _)
|
2013-02-08 16:08:02 -06:00
|
|
|
if self.bccx.method_map.contains_key(&ex.id) => {
|
2012-06-01 23:54:38 -05:00
|
|
|
// Receivers in method calls are always passed by ref.
|
|
|
|
//
|
|
|
|
// Here, the field a.b is in fact a closure. Eventually, this
|
2013-03-01 14:11:07 -06:00
|
|
|
// should be an &fn, but for now it's an @fn. In any case,
|
2012-06-01 23:54:38 -05:00
|
|
|
// the enclosing scope is either the call where it is a rcvr
|
|
|
|
// (if used like `a.b(...)`), the call where it's an argument
|
|
|
|
// (if used like `x(a.b)`), or the block (if used like `let x
|
|
|
|
// = a.b`).
|
2013-04-02 00:32:37 -05:00
|
|
|
let scope_r = self.tcx().region_maps.encl_region(ex.id);
|
2012-06-01 17:46:32 -05:00
|
|
|
let rcvr_cmt = self.bccx.cat_expr(rcvr);
|
|
|
|
self.guarantee_valid(rcvr_cmt, m_imm, scope_r);
|
2012-07-26 10:51:57 -05:00
|
|
|
visit::visit_expr(ex, self, vt);
|
2012-06-01 17:46:32 -05:00
|
|
|
}
|
|
|
|
|
2012-07-26 10:51:57 -05:00
|
|
|
// see explanation attached to the `root_ub` field:
|
2012-12-04 12:50:00 -06:00
|
|
|
ast::expr_while(cond, ref body) => {
|
2012-07-26 10:51:57 -05:00
|
|
|
// during the condition, can only root for the condition
|
|
|
|
self.root_ub = cond.id;
|
2012-11-29 19:51:16 -06:00
|
|
|
(vt.visit_expr)(cond, self, vt);
|
2012-07-26 10:51:57 -05:00
|
|
|
|
|
|
|
// during body, can only root for the body
|
2013-02-18 00:20:36 -06:00
|
|
|
self.root_ub = body.node.id;
|
|
|
|
(vt.visit_block)(body, self, vt);
|
2012-07-26 10:51:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// see explanation attached to the `root_ub` field:
|
2012-12-04 12:50:00 -06:00
|
|
|
ast::expr_loop(ref body, _) => {
|
2013-02-18 00:20:36 -06:00
|
|
|
self.root_ub = body.node.id;
|
2012-07-26 10:51:57 -05:00
|
|
|
visit::visit_expr(ex, self, vt);
|
|
|
|
}
|
|
|
|
|
|
|
|
_ => {
|
|
|
|
visit::visit_expr(ex, self, vt);
|
|
|
|
}
|
2012-06-01 12:46:17 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check any contained expressions:
|
2012-07-26 10:51:57 -05:00
|
|
|
|
|
|
|
self.root_ub = old_root_ub;
|
2012-06-01 12:46:17 -05:00
|
|
|
}
|
|
|
|
|
2013-02-26 19:47:41 -06:00
|
|
|
pub impl GatherLoanCtxt {
|
2013-03-16 13:11:31 -05:00
|
|
|
fn tcx(&mut self) -> ty::ctxt { self.bccx.tcx }
|
2012-06-01 12:46:17 -05:00
|
|
|
|
2013-03-16 13:11:31 -05:00
|
|
|
fn guarantee_adjustments(&mut self,
|
2012-10-14 15:39:17 -05:00
|
|
|
expr: @ast::expr,
|
2012-09-11 23:25:01 -05:00
|
|
|
adjustment: &ty::AutoAdjustment) {
|
|
|
|
debug!("guarantee_adjustments(expr=%s, adjustment=%?)",
|
Cleanup substitutions and treatment of generics around traits in a number of ways.
- In a TraitRef, use the self type consistently to refer to the Self type:
- trait ref in `impl Trait<A,B,C> for S` has a self type of `S`.
- trait ref in `A:Trait` has the self type `A`
- trait ref associated with a trait decl has self type `Self`
- trait ref associated with a supertype has self type `Self`
- trait ref in an object type `@Trait` has no self type
- Rewrite `each_bound_traits_and_supertraits` to perform
substitutions as it goes, and thus yield a series of trait refs
that are always in the same 'namespace' as the type parameter
bound given as input. Before, we left this to the caller, but
this doesn't work because the caller lacks adequare information
to perform the type substitutions correctly.
- For provided methods, substitute the generics involved in the provided
method correctly.
- Introduce TypeParameterDef, which tracks the bounds declared on a type
parameter and brings them together with the def_id and (in the future)
other information (maybe even the parameter's name!).
- Introduce Subst trait, which helps to cleanup a lot of the
repetitive code involved with doing type substitution.
- Introduce Repr trait, which makes debug printouts far more convenient.
Fixes #4183. Needed for #5656.
2013-04-09 00:54:49 -05:00
|
|
|
expr.repr(self.tcx()), adjustment);
|
2012-09-11 23:25:01 -05:00
|
|
|
let _i = indenter();
|
|
|
|
|
2013-02-27 18:28:37 -06:00
|
|
|
match *adjustment {
|
|
|
|
ty::AutoAddEnv(*) => {
|
|
|
|
debug!("autoaddenv -- no autoref");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ty::AutoDerefRef(
|
|
|
|
ty::AutoDerefRef {
|
|
|
|
autoref: None, _ }) => {
|
2012-09-11 23:25:01 -05:00
|
|
|
debug!("no autoref");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-02-27 18:28:37 -06:00
|
|
|
ty::AutoDerefRef(
|
|
|
|
ty::AutoDerefRef {
|
|
|
|
autoref: Some(ref autoref),
|
|
|
|
autoderefs: autoderefs}) => {
|
2012-09-11 23:25:01 -05:00
|
|
|
let mcx = &mem_categorization_ctxt {
|
|
|
|
tcx: self.tcx(),
|
|
|
|
method_map: self.bccx.method_map};
|
2013-02-27 18:28:37 -06:00
|
|
|
let mut cmt = mcx.cat_expr_autoderefd(expr, autoderefs);
|
2012-09-11 23:25:01 -05:00
|
|
|
debug!("after autoderef, cmt=%s", self.bccx.cmt_to_repr(cmt));
|
|
|
|
|
|
|
|
match autoref.kind {
|
|
|
|
ty::AutoPtr => {
|
|
|
|
self.guarantee_valid(cmt,
|
|
|
|
autoref.mutbl,
|
|
|
|
autoref.region)
|
|
|
|
}
|
2012-12-06 18:29:17 -06:00
|
|
|
ty::AutoBorrowVec | ty::AutoBorrowVecRef => {
|
2012-09-11 23:25:01 -05:00
|
|
|
let cmt_index = mcx.cat_index(expr, cmt);
|
|
|
|
self.guarantee_valid(cmt_index,
|
|
|
|
autoref.mutbl,
|
|
|
|
autoref.region)
|
|
|
|
}
|
2012-11-04 22:41:00 -06:00
|
|
|
ty::AutoBorrowFn => {
|
|
|
|
let cmt_deref = mcx.cat_deref_fn(expr, cmt, 0);
|
|
|
|
self.guarantee_valid(cmt_deref,
|
|
|
|
autoref.mutbl,
|
|
|
|
autoref.region)
|
|
|
|
}
|
2012-09-11 23:25:01 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-01 12:46:17 -05:00
|
|
|
// guarantees that addr_of(cmt) will be valid for the duration of
|
|
|
|
// `static_scope_r`, or reports an error. This may entail taking
|
|
|
|
// out loans, which will be added to the `req_loan_map`. This can
|
|
|
|
// also entail "rooting" GC'd pointers, which means ensuring
|
|
|
|
// dynamically that they are not freed.
|
2013-03-16 13:11:31 -05:00
|
|
|
fn guarantee_valid(&mut self,
|
2012-10-14 15:39:17 -05:00
|
|
|
cmt: cmt,
|
2012-06-01 12:46:17 -05:00
|
|
|
req_mutbl: ast::mutability,
|
2013-02-09 00:21:45 -06:00
|
|
|
scope_r: ty::Region)
|
|
|
|
{
|
|
|
|
|
|
|
|
let loan_kind = match req_mutbl {
|
|
|
|
m_mutbl => TotalTake,
|
|
|
|
m_imm => TotalFreeze,
|
|
|
|
m_const => Immobile
|
|
|
|
};
|
2012-06-01 12:46:17 -05:00
|
|
|
|
2013-02-04 16:02:01 -06:00
|
|
|
self.bccx.stats.guaranteed_paths += 1;
|
2012-07-05 14:59:03 -05:00
|
|
|
|
2013-02-09 00:21:45 -06:00
|
|
|
debug!("guarantee_valid(cmt=%s, req_mutbl=%?, \
|
|
|
|
loan_kind=%?, scope_r=%s)",
|
2012-06-01 12:46:17 -05:00
|
|
|
self.bccx.cmt_to_repr(cmt),
|
2013-02-09 00:21:45 -06:00
|
|
|
req_mutbl,
|
|
|
|
loan_kind,
|
2012-08-22 19:24:52 -05:00
|
|
|
region_to_str(self.tcx(), scope_r));
|
2012-06-01 12:46:17 -05:00
|
|
|
let _i = indenter();
|
|
|
|
|
2012-08-06 14:34:08 -05:00
|
|
|
match cmt.lp {
|
2012-07-05 14:59:03 -05:00
|
|
|
// If this expression is a loanable path, we MUST take out a
|
|
|
|
// loan. This is somewhat non-obvious. You might think,
|
|
|
|
// for example, that if we have an immutable local variable
|
|
|
|
// `x` whose value is being borrowed, we could rely on `x`
|
|
|
|
// not to change. This is not so, however, because even
|
|
|
|
// immutable locals can be moved. So we take out a loan on
|
|
|
|
// `x`, guaranteeing that it remains immutable for the
|
|
|
|
// duration of the reference: if there is an attempt to move
|
|
|
|
// it within that scope, the loan will be detected and an
|
|
|
|
// error will be reported.
|
2012-08-20 14:23:37 -05:00
|
|
|
Some(_) => {
|
2013-02-09 00:21:45 -06:00
|
|
|
match loan::loan(self.bccx, cmt, scope_r, loan_kind) {
|
2012-12-04 12:50:00 -06:00
|
|
|
Err(ref e) => { self.bccx.report((*e)); }
|
2013-02-15 03:14:34 -06:00
|
|
|
Ok(loans) => {
|
|
|
|
self.add_loans(cmt, loan_kind, scope_r, loans);
|
2012-07-26 10:51:57 -05:00
|
|
|
}
|
2012-06-01 12:46:17 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-26 10:51:57 -05:00
|
|
|
// The path is not loanable: in that case, we must try and
|
|
|
|
// preserve it dynamically (or see that it is preserved by
|
|
|
|
// virtue of being rooted in some immutable path). We must
|
|
|
|
// also check that the mutability of the desired pointer
|
|
|
|
// matches with the actual mutability (but if an immutable
|
|
|
|
// pointer is desired, that is ok as long as we are pure)
|
2012-08-20 14:23:37 -05:00
|
|
|
None => {
|
2013-01-10 12:59:58 -06:00
|
|
|
let result: bckres<PreserveCondition> = {
|
2013-02-09 00:21:45 -06:00
|
|
|
do self.check_mutbl(loan_kind, cmt).chain |pc1| {
|
2012-07-26 10:51:57 -05:00
|
|
|
do self.bccx.preserve(cmt, scope_r,
|
|
|
|
self.item_ub,
|
|
|
|
self.root_ub).chain |pc2| {
|
2012-08-26 18:54:31 -05:00
|
|
|
Ok(pc1.combine(pc2))
|
2012-07-26 10:51:57 -05:00
|
|
|
}
|
2012-06-01 12:46:17 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2012-08-06 14:34:08 -05:00
|
|
|
match result {
|
2013-01-10 12:59:58 -06:00
|
|
|
Ok(PcOk) => {
|
|
|
|
debug!("result of preserve: PcOk");
|
2012-09-14 23:58:04 -05:00
|
|
|
|
|
|
|
// we were able guarantee the validity of the ptr,
|
|
|
|
// perhaps by rooting or because it is immutably
|
|
|
|
// rooted. good.
|
2013-02-04 16:02:01 -06:00
|
|
|
self.bccx.stats.stable_paths += 1;
|
2012-09-14 23:58:04 -05:00
|
|
|
}
|
2013-01-10 12:59:58 -06:00
|
|
|
Ok(PcIfPure(ref e)) => {
|
|
|
|
debug!("result of preserve: %?", PcIfPure((*e)));
|
2012-09-14 23:58:04 -05:00
|
|
|
|
|
|
|
// we are only able to guarantee the validity if
|
|
|
|
// the scope is pure
|
|
|
|
match scope_r {
|
|
|
|
ty::re_scope(pure_id) => {
|
|
|
|
// if the scope is some block/expr in the
|
|
|
|
// fn, then just require that this scope
|
|
|
|
// be pure
|
2013-03-22 21:26:41 -05:00
|
|
|
self.req_maps.pure_map.insert(pure_id, *e);
|
2013-02-04 16:02:01 -06:00
|
|
|
self.bccx.stats.req_pure_paths += 1;
|
2012-09-14 23:58:04 -05:00
|
|
|
|
|
|
|
debug!("requiring purity for scope %?",
|
|
|
|
scope_r);
|
|
|
|
|
|
|
|
if self.tcx().sess.borrowck_note_pure() {
|
|
|
|
self.bccx.span_note(
|
|
|
|
cmt.span,
|
|
|
|
fmt!("purity required"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
// otherwise, we can't enforce purity for
|
|
|
|
// that scope, so give up and report an
|
|
|
|
// error
|
2012-12-04 12:50:00 -06:00
|
|
|
self.bccx.report((*e));
|
2012-09-14 23:58:04 -05:00
|
|
|
}
|
2012-07-06 17:03:18 -05:00
|
|
|
}
|
2012-09-14 23:58:04 -05:00
|
|
|
}
|
2012-12-04 12:50:00 -06:00
|
|
|
Err(ref e) => {
|
2012-09-14 23:58:04 -05:00
|
|
|
// we cannot guarantee the validity of this pointer
|
|
|
|
debug!("result of preserve: error");
|
2012-12-04 12:50:00 -06:00
|
|
|
self.bccx.report((*e));
|
2012-06-01 12:46:17 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that the pat `cmt` is compatible with the required
|
|
|
|
// mutability, presuming that it can be preserved to stay alive
|
|
|
|
// long enough.
|
|
|
|
//
|
|
|
|
// For example, if you have an expression like `&x.f` where `x`
|
|
|
|
// has type `@mut{f:int}`, this check might fail because `&x.f`
|
|
|
|
// reqires an immutable pointer, but `f` lives in (aliased)
|
|
|
|
// mutable memory.
|
2013-03-16 13:11:31 -05:00
|
|
|
fn check_mutbl(&mut self,
|
2013-02-09 00:21:45 -06:00
|
|
|
loan_kind: LoanKind,
|
2013-02-04 16:02:01 -06:00
|
|
|
cmt: cmt)
|
|
|
|
-> bckres<PreserveCondition> {
|
2013-02-09 00:21:45 -06:00
|
|
|
debug!("check_mutbl(loan_kind=%?, cmt.mutbl=%?)",
|
|
|
|
loan_kind, cmt.mutbl);
|
2012-09-14 23:58:04 -05:00
|
|
|
|
2013-02-09 00:21:45 -06:00
|
|
|
match loan_kind {
|
|
|
|
Immobile => Ok(PcOk),
|
|
|
|
|
|
|
|
TotalTake | PartialTake => {
|
|
|
|
if cmt.mutbl.is_mutable() {
|
2013-01-10 12:59:58 -06:00
|
|
|
Ok(PcOk)
|
2013-01-11 23:01:42 -06:00
|
|
|
} else {
|
2013-02-09 00:21:45 -06:00
|
|
|
Err(bckerr { cmt: cmt, code: err_mutbl(loan_kind) })
|
|
|
|
}
|
|
|
|
}
|
2013-01-11 23:01:42 -06:00
|
|
|
|
2013-02-09 00:21:45 -06:00
|
|
|
TotalFreeze | PartialFreeze => {
|
|
|
|
if cmt.mutbl.is_immutable() {
|
|
|
|
Ok(PcOk)
|
|
|
|
} else if cmt.cat.is_mutable_box() {
|
|
|
|
Ok(PcOk)
|
|
|
|
} else {
|
|
|
|
// Eventually:
|
|
|
|
let e = bckerr {cmt: cmt,
|
|
|
|
code: err_mutbl(loan_kind)};
|
2013-01-10 12:59:58 -06:00
|
|
|
Ok(PcIfPure(e))
|
2013-01-11 23:01:42 -06:00
|
|
|
}
|
2012-07-26 10:51:57 -05:00
|
|
|
}
|
2012-06-01 12:46:17 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-16 13:11:31 -05:00
|
|
|
fn add_loans(&mut self,
|
2012-10-14 15:39:17 -05:00
|
|
|
cmt: cmt,
|
2013-02-09 00:21:45 -06:00
|
|
|
loan_kind: LoanKind,
|
2012-10-15 16:56:42 -05:00
|
|
|
scope_r: ty::Region,
|
2013-04-17 11:15:37 -05:00
|
|
|
loans: ~[Loan]) {
|
2012-10-14 15:39:17 -05:00
|
|
|
if loans.len() == 0 {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-01-23 20:15:06 -06:00
|
|
|
// Normally we wouldn't allow `re_free` here. However, in this case
|
|
|
|
// it should be sound. Below is nmatsakis' reasoning:
|
|
|
|
//
|
|
|
|
// Perhaps [this permits] a function kind of like this one here, which
|
|
|
|
// consumes one mut pointer and returns a narrower one:
|
|
|
|
//
|
|
|
|
// struct Foo { f: int }
|
2013-03-14 13:22:51 -05:00
|
|
|
// fn foo(p: &'v mut Foo) -> &'v mut int { &mut p.f }
|
2013-01-23 20:15:06 -06:00
|
|
|
//
|
|
|
|
// I think this should work fine but there is more subtlety to it than
|
|
|
|
// I at first imagined. Unfortunately it's a very important use case,
|
|
|
|
// I think, so it really ought to work. The changes you [pcwalton]
|
|
|
|
// made to permit re_free() do permit this case, I think, but I'm not
|
|
|
|
// sure what else they permit. I have to think that over a bit.
|
|
|
|
//
|
|
|
|
// Ordinarily, a loan with scope re_free wouldn't make sense, because
|
|
|
|
// you couldn't enforce it. But in this case, your function signature
|
|
|
|
// informs the caller that you demand exclusive access to p and its
|
|
|
|
// contents for the lifetime v. Since borrowed pointers are
|
|
|
|
// non-copyable, they must have (a) made a borrow which will enforce
|
|
|
|
// those conditions and then (b) given you the resulting pointer.
|
|
|
|
// Therefore, they should be respecting the loan. So it actually seems
|
|
|
|
// that it's ok in this case to have a loan with re_free, so long as
|
|
|
|
// the scope of the loan is no greater than the region pointer on
|
|
|
|
// which it is based. Neat but not something I had previously
|
|
|
|
// considered all the way through. (Note that we already rely on
|
|
|
|
// similar reasoning to permit you to return borrowed pointers into
|
|
|
|
// immutable structures, this is just the converse I suppose)
|
|
|
|
|
2012-10-14 15:39:17 -05:00
|
|
|
let scope_id = match scope_r {
|
2013-04-02 00:32:37 -05:00
|
|
|
ty::re_scope(scope_id) |
|
|
|
|
ty::re_free(ty::FreeRegion {scope_id, _}) => {
|
|
|
|
scope_id
|
|
|
|
}
|
2012-10-14 15:39:17 -05:00
|
|
|
_ => {
|
|
|
|
self.bccx.tcx.sess.span_bug(
|
|
|
|
cmt.span,
|
2013-01-23 20:15:06 -06:00
|
|
|
fmt!("loans required but scope is scope_region is %s \
|
|
|
|
(%?)",
|
|
|
|
region_to_str(self.tcx(), scope_r),
|
|
|
|
scope_r));
|
2012-10-14 15:39:17 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-02-15 03:14:34 -06:00
|
|
|
self.add_loans_to_scope_id(scope_id, loans);
|
2012-10-14 15:39:17 -05:00
|
|
|
|
2013-02-09 00:21:45 -06:00
|
|
|
if loan_kind.is_freeze() && !cmt.mutbl.is_immutable() {
|
2013-02-04 16:02:01 -06:00
|
|
|
self.bccx.stats.loaned_paths_imm += 1;
|
2012-10-14 15:39:17 -05:00
|
|
|
|
|
|
|
if self.tcx().sess.borrowck_note_loan() {
|
|
|
|
self.bccx.span_note(
|
|
|
|
cmt.span,
|
|
|
|
fmt!("immutable loan required"));
|
|
|
|
}
|
|
|
|
} else {
|
2013-02-04 16:02:01 -06:00
|
|
|
self.bccx.stats.loaned_paths_same += 1;
|
2012-10-14 15:39:17 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-16 13:11:31 -05:00
|
|
|
fn add_loans_to_scope_id(&mut self,
|
2013-02-04 16:02:01 -06:00
|
|
|
scope_id: ast::node_id,
|
2013-04-17 11:15:37 -05:00
|
|
|
loans: ~[Loan]) {
|
2013-02-09 00:21:45 -06:00
|
|
|
debug!("adding %u loans to scope_id %?: %s",
|
|
|
|
loans.len(), scope_id,
|
|
|
|
str::connect(loans.map(|l| self.bccx.loan_to_repr(l)), ", "));
|
2013-02-05 21:41:45 -06:00
|
|
|
match self.req_maps.req_loan_map.find(&scope_id) {
|
2012-10-14 15:39:17 -05:00
|
|
|
Some(req_loans) => {
|
|
|
|
req_loans.push_all(loans);
|
2013-03-22 21:26:41 -05:00
|
|
|
return;
|
2012-08-28 17:54:45 -05:00
|
|
|
}
|
2013-03-22 21:26:41 -05:00
|
|
|
None => {}
|
2012-06-01 12:46:17 -05:00
|
|
|
}
|
2013-03-22 21:26:41 -05:00
|
|
|
self.req_maps.req_loan_map.insert(scope_id, @mut loans);
|
2012-06-01 12:46:17 -05:00
|
|
|
}
|
|
|
|
|
2013-02-04 16:02:01 -06:00
|
|
|
fn gather_pat(@mut self,
|
2012-10-14 15:39:17 -05:00
|
|
|
discr_cmt: cmt,
|
|
|
|
root_pat: @ast::pat,
|
|
|
|
arm_id: ast::node_id,
|
2013-01-04 08:52:07 -06:00
|
|
|
match_id: ast::node_id) {
|
2012-08-08 10:15:32 -05:00
|
|
|
do self.bccx.cat_pattern(discr_cmt, root_pat) |cmt, pat| {
|
|
|
|
match pat.node {
|
2012-11-13 00:10:15 -06:00
|
|
|
ast::pat_ident(bm, _, _) if self.pat_is_binding(pat) => {
|
2012-08-08 10:15:32 -05:00
|
|
|
match bm {
|
|
|
|
ast::bind_by_ref(mutbl) => {
|
|
|
|
// ref x or ref x @ p --- creates a ptr which must
|
2013-01-04 08:52:07 -06:00
|
|
|
// remain valid for the scope of the match
|
2012-08-08 10:15:32 -05:00
|
|
|
|
|
|
|
// find the region of the resulting pointer (note that
|
|
|
|
// the type of such a pattern will *always* be a
|
|
|
|
// region pointer)
|
2013-03-25 16:39:15 -05:00
|
|
|
let scope_r = ty_region(self.tcx(), pat.span,
|
|
|
|
self.tcx().ty(pat));
|
2012-08-08 10:15:32 -05:00
|
|
|
|
|
|
|
// if the scope of the region ptr turns out to be
|
|
|
|
// specific to this arm, wrap the categorization with
|
|
|
|
// a cat_discr() node. There is a detailed discussion
|
|
|
|
// of the function of this node in method preserve():
|
|
|
|
let arm_scope = ty::re_scope(arm_id);
|
|
|
|
if self.bccx.is_subregion_of(scope_r, arm_scope) {
|
2013-01-04 08:52:07 -06:00
|
|
|
let cmt_discr = self.bccx.cat_discr(cmt, match_id);
|
2012-08-08 10:15:32 -05:00
|
|
|
self.guarantee_valid(cmt_discr, mutbl, scope_r);
|
|
|
|
} else {
|
|
|
|
self.guarantee_valid(cmt, mutbl, scope_r);
|
|
|
|
}
|
|
|
|
}
|
2013-01-10 12:59:58 -06:00
|
|
|
ast::bind_by_copy | ast::bind_infer => {
|
|
|
|
// Nothing to do here; neither copies nor moves induce
|
|
|
|
// borrows.
|
2012-08-08 10:15:32 -05:00
|
|
|
}
|
2012-08-06 20:14:46 -05:00
|
|
|
}
|
|
|
|
}
|
2012-06-01 12:46:17 -05:00
|
|
|
|
2013-02-26 12:58:46 -06:00
|
|
|
ast::pat_vec(_, Some(slice_pat), _) => {
|
|
|
|
// The `slice_pat` here creates a slice into the
|
2013-01-24 21:33:48 -06:00
|
|
|
// original vector. This is effectively a borrow of
|
|
|
|
// the elements of the vector being matched.
|
|
|
|
|
2013-02-26 12:58:46 -06:00
|
|
|
let slice_ty = self.tcx().ty(slice_pat);
|
|
|
|
let (slice_mutbl, slice_r) =
|
|
|
|
self.vec_slice_info(slice_pat, slice_ty);
|
2013-01-24 21:33:48 -06:00
|
|
|
let mcx = self.bccx.mc_ctxt();
|
2013-02-26 12:58:46 -06:00
|
|
|
let cmt_index = mcx.cat_index(slice_pat, cmt);
|
|
|
|
self.guarantee_valid(cmt_index, slice_mutbl, slice_r);
|
2013-01-24 21:33:48 -06:00
|
|
|
}
|
|
|
|
|
2012-08-08 10:15:32 -05:00
|
|
|
_ => {}
|
2012-06-01 12:46:17 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-04 16:02:01 -06:00
|
|
|
fn vec_slice_info(@mut self,
|
2013-01-24 21:33:48 -06:00
|
|
|
pat: @ast::pat,
|
2013-02-26 12:58:46 -06:00
|
|
|
slice_ty: ty::t) -> (ast::mutability, ty::Region) {
|
2013-01-24 21:33:48 -06:00
|
|
|
/*!
|
|
|
|
*
|
|
|
|
* In a pattern like [a, b, ..c], normally `c` has slice type,
|
|
|
|
* but if you have [a, b, ..ref c], then the type of `ref c`
|
|
|
|
* will be `&&[]`, so to extract the slice details we have
|
|
|
|
* to recurse through rptrs.
|
|
|
|
*/
|
|
|
|
|
2013-02-26 12:58:46 -06:00
|
|
|
match ty::get(slice_ty).sty {
|
|
|
|
ty::ty_evec(slice_mt, ty::vstore_slice(slice_r)) => {
|
|
|
|
(slice_mt.mutbl, slice_r)
|
2013-01-24 21:33:48 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
ty::ty_rptr(_, ref mt) => {
|
|
|
|
self.vec_slice_info(pat, mt.ty)
|
|
|
|
}
|
|
|
|
|
|
|
|
_ => {
|
|
|
|
self.tcx().sess.span_bug(
|
|
|
|
pat.span,
|
2013-02-26 12:58:46 -06:00
|
|
|
fmt!("Type of slice pattern is not a slice"));
|
2013-01-24 21:33:48 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-04 16:02:01 -06:00
|
|
|
fn pat_is_variant_or_struct(@mut self, pat: @ast::pat) -> bool {
|
2012-10-30 17:53:06 -05:00
|
|
|
pat_util::pat_is_variant_or_struct(self.bccx.tcx.def_map, pat)
|
2012-06-01 12:46:17 -05:00
|
|
|
}
|
2012-11-13 00:10:15 -06:00
|
|
|
|
2013-02-04 16:02:01 -06:00
|
|
|
fn pat_is_binding(@mut self, pat: @ast::pat) -> bool {
|
2012-11-13 00:10:15 -06:00
|
|
|
pat_util::pat_is_binding(self.bccx.tcx.def_map, pat)
|
|
|
|
}
|
2012-06-01 12:46:17 -05:00
|
|
|
}
|
|
|
|
|
2013-01-13 18:51:18 -06:00
|
|
|
// Setting up info that preserve needs.
|
|
|
|
// This is just the most convenient place to do it.
|
|
|
|
fn add_stmt_to_map(stmt: @ast::stmt,
|
2013-04-17 11:15:37 -05:00
|
|
|
self: @mut GatherLoanCtxt,
|
2013-02-04 16:02:01 -06:00
|
|
|
vt: visit::vt<@mut GatherLoanCtxt>) {
|
2013-01-13 18:51:18 -06:00
|
|
|
match stmt.node {
|
|
|
|
ast::stmt_expr(_, id) | ast::stmt_semi(_, id) => {
|
2013-03-22 21:26:41 -05:00
|
|
|
self.bccx.stmt_map.insert(id);
|
2013-01-13 18:51:18 -06:00
|
|
|
}
|
|
|
|
_ => ()
|
|
|
|
}
|
|
|
|
visit::visit_stmt(stmt, self, vt);
|
|
|
|
}
|
2013-02-04 16:02:01 -06:00
|
|
|
|