Move upvar checking into its own pre-pass that occurs before regionck

and which uses EUV. For now, upvar inference is not any smarter than
it ever was, but regionck is simpler because it doesn't have to do as
many things at once.
This commit is contained in:
Niko Matsakis 2015-01-02 04:12:25 -05:00
parent 7474be0660
commit ecd9c10e1a
4 changed files with 385 additions and 339 deletions

View File

@ -126,6 +126,12 @@ impl TraitItemKind {
}
impl Def {
pub fn local_node_id(&self) -> ast::NodeId {
let def_id = self.def_id();
assert_eq!(def_id.krate, ast::LOCAL_CRATE);
def_id.node
}
pub fn def_id(&self) -> ast::DefId {
match *self {
DefFn(id, _) | DefStaticMethod(id, _) | DefMod(id) |

View File

@ -130,6 +130,7 @@ pub mod regionmanip;
pub mod regionck;
pub mod demand;
pub mod method;
mod upvar;
pub mod wf;
mod closure;
mod callee;
@ -482,6 +483,7 @@ fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
decl, id, body, &inh);
vtable::select_all_fcx_obligations_or_error(&fcx);
upvar::closure_analyze_fn(&fcx, id, decl, body);
regionck::regionck_fn(&fcx, id, decl, body);
writeback::resolve_type_vars_in_fn(&fcx, decl, body);
}

View File

@ -81,38 +81,6 @@
//! traversed. This is essentially the same as the ownership
//! relation, except that a borrowed pointer never owns its
//! contents.
//!
//! ### Inferring borrow kinds for upvars
//!
//! Whenever there is a closure expression, we need to determine how each
//! upvar is used. We do this by initially assigning each upvar an
//! immutable "borrow kind" (see `ty::BorrowKind` for details) and then
//! "escalating" the kind as needed. The borrow kind proceeds according to
//! the following lattice:
//!
//! ty::ImmBorrow -> ty::UniqueImmBorrow -> ty::MutBorrow
//!
//! So, for example, if we see an assignment `x = 5` to an upvar `x`, we
//! will promote its borrow kind to mutable borrow. If we see an `&mut x`
//! we'll do the same. Naturally, this applies not just to the upvar, but
//! to everything owned by `x`, so the result is the same for something
//! like `x.f = 5` and so on (presuming `x` is not a borrowed pointer to a
//! struct). These adjustments are performed in
//! `adjust_upvar_borrow_kind()` (you can trace backwards through the code
//! from there).
//!
//! The fact that we are inferring borrow kinds as we go results in a
//! semi-hacky interaction with mem-categorization. In particular,
//! mem-categorization will query the current borrow kind as it
//! categorizes, and we'll return the *current* value, but this may get
//! adjusted later. Therefore, in this module, we generally ignore the
//! borrow kind (and derived mutabilities) that are returned from
//! mem-categorization, since they may be inaccurate. (Another option
//! would be to use a unification scheme, where instead of returning a
//! concrete borrow kind like `ty::ImmBorrow`, we return a
//! `ty::InferBorrow(upvar_id)` or something like that, but this would
//! then mean that all later passes would have to check for these figments
//! and report an error, and it just seems like more mess in the end.)
use astconv::AstConv;
use check::FnCtxt;
@ -126,7 +94,6 @@ use middle::ty::{ReScope};
use middle::ty::{mod, Ty, MethodCall};
use middle::infer;
use middle::pat_util;
use util::nodemap::{FnvHashMap};
use util::ppaux::{ty_to_string, Repr};
use syntax::{ast, ast_util};
@ -134,9 +101,6 @@ use syntax::codemap::Span;
use syntax::visit;
use syntax::visit::Visitor;
use std::cell::{RefCell};
use std::collections::hash_map::Entry::{Vacant, Occupied};
use self::RepeatingScope::Repeating;
use self::SubjectNode::Subject;
@ -197,19 +161,6 @@ pub fn regionck_ensure_component_tys_wf<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
///////////////////////////////////////////////////////////////////////////
// INTERNALS
// Stores parameters for a potential call to link_region()
// to perform if an upvar reference is marked unique/mutable after
// it has already been processed before.
struct MaybeLink<'tcx> {
span: Span,
borrow_region: ty::Region,
borrow_kind: ty::BorrowKind,
borrow_cmt: mc::cmt<'tcx>
}
// A map associating an upvar ID to a vector of the above
type MaybeLinkMap<'tcx> = RefCell<FnvHashMap<ty::UpvarId, Vec<MaybeLink<'tcx>>>>;
pub struct Rcx<'a, 'tcx: 'a> {
fcx: &'a FnCtxt<'a, 'tcx>,
@ -220,10 +171,6 @@ pub struct Rcx<'a, 'tcx: 'a> {
// id of AST node being analyzed (the subject of the analysis).
subject: SubjectNode,
// Possible region links we will establish if an upvar
// turns out to be unique/mutable
maybe_links: MaybeLinkMap<'tcx>
}
/// Returns the validity region of `def` -- that is, how long is `def` valid?
@ -258,8 +205,7 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> {
Rcx { fcx: fcx,
repeating_scope: initial_repeating_scope,
subject: subject,
region_param_pairs: Vec::new(),
maybe_links: RefCell::new(FnvHashMap::new()) }
region_param_pairs: Vec::new() }
}
pub fn tcx(&self) -> &'a ty::ctxt<'tcx> {
@ -590,19 +536,12 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) {
visit::walk_expr(rcx, expr);
}
ast::ExprAssign(ref lhs, _) => {
adjust_borrow_kind_for_assignment_lhs(rcx, &**lhs);
visit::walk_expr(rcx, expr);
}
ast::ExprAssignOp(_, ref lhs, ref rhs) => {
if has_method_map {
constrain_call(rcx, expr, Some(&**lhs),
Some(&**rhs).into_iter(), true);
}
adjust_borrow_kind_for_assignment_lhs(rcx, &**lhs);
visit::walk_expr(rcx, expr);
}
@ -838,22 +777,6 @@ fn check_expr_fn_block(rcx: &mut Rcx,
visit::walk_expr(rcx, expr);
rcx.set_repeating_scope(repeating_scope);
match function_type.sty {
ty::ty_closure(box ty::ClosureTy { store: ty::RegionTraitStore(..), .. }) => {
ty::with_freevars(tcx, expr.id, |freevars| {
propagate_upupvar_borrow_kind(rcx, expr, freevars);
})
}
ty::ty_unboxed_closure(..) => {
if tcx.capture_modes.borrow()[expr.id].clone() == ast::CaptureByRef {
ty::with_freevars(tcx, expr.id, |freevars| {
propagate_upupvar_borrow_kind(rcx, expr, freevars);
});
}
}
_ => {}
}
match function_type.sty {
ty::ty_closure(box ty::ClosureTy {ref bounds, ..}) => {
ty::with_freevars(tcx, expr.id, |freevars| {
@ -930,7 +853,6 @@ fn check_expr_fn_block(rcx: &mut Rcx,
freevars: &[ty::Freevar])
{
let tcx = rcx.fcx.ccx.tcx;
let infcx = rcx.fcx.infcx();
debug!("constrain_free_variables({}, {})",
region_bound.repr(tcx), expr.repr(tcx));
for freevar in freevars.iter() {
@ -946,20 +868,10 @@ fn check_expr_fn_block(rcx: &mut Rcx,
let upvar_id = ty::UpvarId { var_id: var_node_id,
closure_expr_id: expr.id };
// Create a region variable to represent this borrow. This borrow
// must outlive the region on the closure.
let origin = infer::UpvarRegion(upvar_id, expr.span);
let freevar_region = infcx.next_region_var(origin);
rcx.fcx.mk_subr(infer::FreeVariable(freevar.span, var_node_id),
region_bound, freevar_region);
let upvar_borrow = rcx.fcx.inh.upvar_borrow_map.borrow()[upvar_id];
// Create a UpvarBorrow entry. Note that we begin with a
// const borrow_kind, but change it to either mut or
// immutable as dictated by the uses.
let upvar_borrow = ty::UpvarBorrow { kind: ty::ImmBorrow,
region: freevar_region };
rcx.fcx.inh.upvar_borrow_map.borrow_mut().insert(upvar_id,
upvar_borrow);
rcx.fcx.mk_subr(infer::FreeVariable(freevar.span, var_node_id),
region_bound, upvar_borrow.region);
// Guarantee that the closure does not outlive the variable itself.
let enclosing_region = region_of_def(rcx.fcx, def);
@ -968,52 +880,6 @@ fn check_expr_fn_block(rcx: &mut Rcx,
region_bound, enclosing_region);
}
}
fn propagate_upupvar_borrow_kind(rcx: &mut Rcx,
expr: &ast::Expr,
freevars: &[ty::Freevar]) {
let tcx = rcx.fcx.ccx.tcx;
debug!("propagate_upupvar_borrow_kind({})", expr.repr(tcx));
for freevar in freevars.iter() {
// Because of the semi-hokey way that we are doing
// borrow_kind inference, we need to check for
// indirect dependencies, like so:
//
// let mut x = 0;
// outer_call(|| {
// inner_call(|| {
// x = 1;
// });
// });
//
// Here, the `inner_call` is basically "reborrowing" the
// outer pointer. With no other changes, `inner_call`
// would infer that it requires a mutable borrow, but
// `outer_call` would infer that a const borrow is
// sufficient. This is because we haven't linked the
// borrow_kind of the borrow that occurs in the inner
// closure to the borrow_kind of the borrow in the outer
// closure. Note that regions *are* naturally linked
// because we have a proper inference scheme there.
//
// Anyway, for borrow_kind, we basically go back over now
// after checking the inner closure (and hence
// determining the final borrow_kind) and propagate that as
// a constraint on the outer closure.
if let def::DefUpvar(var_id, outer_closure_id, _) = freevar.def {
// thing being captured is itself an upvar:
let outer_upvar_id = ty::UpvarId {
var_id: var_id,
closure_expr_id: outer_closure_id };
let inner_upvar_id = ty::UpvarId {
var_id: var_id,
closure_expr_id: expr.id };
link_upvar_borrow_kind_for_nested_closures(rcx,
inner_upvar_id,
outer_upvar_id);
}
}
}
}
fn constrain_callee(rcx: &mut Rcx,
@ -1491,14 +1357,6 @@ fn link_reborrowed_region<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
rcx.fcx.inh.upvar_borrow_map.borrow_mut();
match upvar_borrow_map.get_mut(upvar_id) {
Some(upvar_borrow) => {
// Adjust mutability that we infer for the upvar
// so it can accommodate being borrowed with
// mutability `kind`:
adjust_upvar_borrow_kind_for_loan(rcx,
*upvar_id,
upvar_borrow,
borrow_kind);
// The mutability of the upvar may have been modified
// by the above adjustment, so update our local variable.
ref_kind = upvar_borrow.kind;
@ -1583,27 +1441,6 @@ fn link_reborrowed_region<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
// else the user is borrowed imm memory as mut memory,
// which means they'll get an error downstream in borrowck
// anyhow.)
//
// If mutability was inferred from an upvar, we may be
// forced to revisit this decision later if processing
// another borrow or nested closure ends up converting the
// upvar borrow kind to mutable/unique. Record the
// information needed to perform the recursive link in the
// maybe link map.
if let mc::NoteUpvarRef(upvar_id) = note {
let link = MaybeLink {
span: span,
borrow_region: borrow_region,
borrow_kind: new_borrow_kind,
borrow_cmt: ref_cmt
};
match rcx.maybe_links.borrow_mut().entry(upvar_id) {
Vacant(entry) => { entry.set(vec![link]); }
Occupied(entry) => { entry.into_mut().push(link); }
}
}
return None;
}
@ -1615,178 +1452,6 @@ fn link_reborrowed_region<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
}
}
/// Adjusts the inferred borrow_kind as needed to account for upvars that are assigned to in an
/// assignment expression.
fn adjust_borrow_kind_for_assignment_lhs(rcx: &Rcx,
lhs: &ast::Expr) {
let mc = mc::MemCategorizationContext::new(rcx.fcx);
let cmt = mc.cat_expr(lhs);
adjust_upvar_borrow_kind_for_mut(rcx, cmt);
}
/// Indicates that `cmt` is being directly mutated (e.g., assigned to). If cmt contains any by-ref
/// upvars, this implies that those upvars must be borrowed using an `&mut` borow.
fn adjust_upvar_borrow_kind_for_mut<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
cmt: mc::cmt<'tcx>) {
let mut cmt = cmt;
loop {
debug!("adjust_upvar_borrow_kind_for_mut(cmt={})",
cmt.repr(rcx.tcx()));
match cmt.cat.clone() {
mc::cat_deref(base, _, mc::Unique) |
mc::cat_interior(base, _) |
mc::cat_downcast(base, _) => {
// Interior or owned data is mutable if base is
// mutable, so iterate to the base.
cmt = base;
continue;
}
mc::cat_deref(base, _, mc::BorrowedPtr(..)) |
mc::cat_deref(base, _, mc::Implicit(..)) => {
if let mc::NoteUpvarRef(ref upvar_id) = cmt.note {
// if this is an implicit deref of an
// upvar, then we need to modify the
// borrow_kind of the upvar to make sure it
// is inferred to mutable if necessary
let mut upvar_borrow_map =
rcx.fcx.inh.upvar_borrow_map.borrow_mut();
let ub = &mut (*upvar_borrow_map)[*upvar_id];
return adjust_upvar_borrow_kind(rcx, *upvar_id, ub, ty::MutBorrow);
}
// assignment to deref of an `&mut`
// borrowed pointer implies that the
// pointer itself must be unique, but not
// necessarily *mutable*
return adjust_upvar_borrow_kind_for_unique(rcx, base);
}
mc::cat_deref(_, _, mc::UnsafePtr(..)) |
mc::cat_static_item |
mc::cat_rvalue(_) |
mc::cat_local(_) |
mc::cat_upvar(..) => {
return;
}
}
}
}
fn adjust_upvar_borrow_kind_for_unique<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>, cmt: mc::cmt<'tcx>) {
let mut cmt = cmt;
loop {
debug!("adjust_upvar_borrow_kind_for_unique(cmt={})",
cmt.repr(rcx.tcx()));
match cmt.cat.clone() {
mc::cat_deref(base, _, mc::Unique) |
mc::cat_interior(base, _) |
mc::cat_downcast(base, _) => {
// Interior or owned data is unique if base is
// unique.
cmt = base;
continue;
}
mc::cat_deref(base, _, mc::BorrowedPtr(..)) |
mc::cat_deref(base, _, mc::Implicit(..)) => {
if let mc::NoteUpvarRef(ref upvar_id) = cmt.note {
// if this is an implicit deref of an
// upvar, then we need to modify the
// borrow_kind of the upvar to make sure it
// is inferred to unique if necessary
let mut ub = rcx.fcx.inh.upvar_borrow_map.borrow_mut();
let ub = &mut (*ub)[*upvar_id];
return adjust_upvar_borrow_kind(rcx, *upvar_id, ub, ty::UniqueImmBorrow);
}
// for a borrowed pointer to be unique, its
// base must be unique
return adjust_upvar_borrow_kind_for_unique(rcx, base);
}
mc::cat_deref(_, _, mc::UnsafePtr(..)) |
mc::cat_static_item |
mc::cat_rvalue(_) |
mc::cat_local(_) |
mc::cat_upvar(..) => {
return;
}
}
}
}
/// Indicates that the borrow_kind of `outer_upvar_id` must permit a reborrowing with the
/// borrow_kind of `inner_upvar_id`. This occurs in nested closures, see comment above at the call
/// to this function.
fn link_upvar_borrow_kind_for_nested_closures(rcx: &mut Rcx,
inner_upvar_id: ty::UpvarId,
outer_upvar_id: ty::UpvarId) {
debug!("link_upvar_borrow_kind: inner_upvar_id={} outer_upvar_id={}",
inner_upvar_id, outer_upvar_id);
let mut upvar_borrow_map = rcx.fcx.inh.upvar_borrow_map.borrow_mut();
let inner_borrow = upvar_borrow_map[inner_upvar_id].clone();
match upvar_borrow_map.get_mut(&outer_upvar_id) {
Some(outer_borrow) => {
adjust_upvar_borrow_kind(rcx, outer_upvar_id, outer_borrow, inner_borrow.kind);
}
None => { /* outer closure is not a stack closure */ }
}
}
fn adjust_upvar_borrow_kind_for_loan(rcx: &Rcx,
upvar_id: ty::UpvarId,
upvar_borrow: &mut ty::UpvarBorrow,
kind: ty::BorrowKind) {
debug!("adjust_upvar_borrow_kind_for_loan: upvar_id={} kind={} -> {}",
upvar_id, upvar_borrow.kind, kind);
adjust_upvar_borrow_kind(rcx, upvar_id, upvar_borrow, kind)
}
/// We infer the borrow_kind with which to borrow upvars in a stack closure. The borrow_kind
/// basically follows a lattice of `imm < unique-imm < mut`, moving from left to right as needed
/// (but never right to left). Here the argument `mutbl` is the borrow_kind that is required by
/// some particular use.
fn adjust_upvar_borrow_kind(rcx: &Rcx,
upvar_id: ty::UpvarId,
upvar_borrow: &mut ty::UpvarBorrow,
kind: ty::BorrowKind) {
debug!("adjust_upvar_borrow_kind: id={} kind=({} -> {})",
upvar_id, upvar_borrow.kind, kind);
match (upvar_borrow.kind, kind) {
// Take RHS:
(ty::ImmBorrow, ty::UniqueImmBorrow) |
(ty::ImmBorrow, ty::MutBorrow) |
(ty::UniqueImmBorrow, ty::MutBorrow) => {
upvar_borrow.kind = kind;
// Check if there are any region links we now need to
// establish due to adjusting the borrow kind of the upvar
match rcx.maybe_links.borrow_mut().entry(upvar_id) {
Occupied(entry) => {
for MaybeLink { span, borrow_region,
borrow_kind, borrow_cmt } in entry.take().into_iter()
{
link_region(rcx, span, borrow_region, borrow_kind, borrow_cmt);
}
}
Vacant(_) => {}
}
}
// Take LHS:
(ty::ImmBorrow, ty::ImmBorrow) |
(ty::UniqueImmBorrow, ty::ImmBorrow) |
(ty::UniqueImmBorrow, ty::UniqueImmBorrow) |
(ty::MutBorrow, _) => {
}
}
}
/// Ensures that all borrowed data reachable via `ty` outlives `region`.
fn type_must_outlive<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>,
origin: infer::SubregionOrigin<'tcx>,

View File

@ -0,0 +1,373 @@
// Copyright 2012-2014 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.
//! ### Inferring borrow kinds for upvars
//!
//! Whenever there is a closure expression, we need to determine how each
//! upvar is used. We do this by initially assigning each upvar an
//! immutable "borrow kind" (see `ty::BorrowKind` for details) and then
//! "escalating" the kind as needed. The borrow kind proceeds according to
//! the following lattice:
//!
//! ty::ImmBorrow -> ty::UniqueImmBorrow -> ty::MutBorrow
//!
//! So, for example, if we see an assignment `x = 5` to an upvar `x`, we
//! will promote its borrow kind to mutable borrow. If we see an `&mut x`
//! we'll do the same. Naturally, this applies not just to the upvar, but
//! to everything owned by `x`, so the result is the same for something
//! like `x.f = 5` and so on (presuming `x` is not a borrowed pointer to a
//! struct). These adjustments are performed in
//! `adjust_upvar_borrow_kind()` (you can trace backwards through the code
//! from there).
//!
//! The fact that we are inferring borrow kinds as we go results in a
//! semi-hacky interaction with mem-categorization. In particular,
//! mem-categorization will query the current borrow kind as it
//! categorizes, and we'll return the *current* value, but this may get
//! adjusted later. Therefore, in this module, we generally ignore the
//! borrow kind (and derived mutabilities) that are returned from
//! mem-categorization, since they may be inaccurate. (Another option
//! would be to use a unification scheme, where instead of returning a
//! concrete borrow kind like `ty::ImmBorrow`, we return a
//! `ty::InferBorrow(upvar_id)` or something like that, but this would
//! then mean that all later passes would have to check for these figments
//! and report an error, and it just seems like more mess in the end.)
use super::FnCtxt;
use middle::expr_use_visitor as euv;
use middle::mem_categorization as mc;
use middle::ty::{mod};
use middle::infer::{InferCtxt, UpvarRegion};
use syntax::ast;
use syntax::codemap::Span;
use syntax::visit::{mod, Visitor};
use util::ppaux::Repr;
///////////////////////////////////////////////////////////////////////////
// PUBLIC ENTRY POINTS
pub fn closure_analyze_fn(fcx: &FnCtxt,
_id: ast::NodeId,
decl: &ast::FnDecl,
body: &ast::Block) {
let mut seed = SeedBorrowKind::new(fcx);
seed.visit_block(body);
let mut adjust = AdjustBorrowKind::new(fcx);
adjust.analyze_fn(decl, body);
}
///////////////////////////////////////////////////////////////////////////
// SEED BORROW KIND
struct SeedBorrowKind<'a,'tcx:'a> {
fcx: &'a FnCtxt<'a,'tcx>,
}
impl<'a, 'tcx, 'v> Visitor<'v> for SeedBorrowKind<'a, 'tcx> {
fn visit_expr(&mut self, expr: &ast::Expr) {
match expr.node {
ast::ExprClosure(cc, _, _, ref body) => {
self.check_closure(expr, cc, &**body);
}
_ => { }
}
visit::walk_expr(self, expr);
}
fn visit_fn(&mut self,
fn_kind: visit::FnKind<'v>,
decl: &'v ast::FnDecl,
block: &'v ast::Block,
span: Span,
_id: ast::NodeId)
{
match fn_kind {
visit::FkItemFn(..) | visit::FkMethod(..) => {
// ignore nested fn items
}
visit::FkFnBlock => {
visit::walk_fn(self, fn_kind, decl, block, span);
}
}
}
}
impl<'a,'tcx> SeedBorrowKind<'a,'tcx> {
fn new(fcx: &'a FnCtxt<'a,'tcx>) -> SeedBorrowKind<'a,'tcx> {
SeedBorrowKind { fcx: fcx }
}
fn tcx(&self) -> &'a ty::ctxt<'tcx> {
self.fcx.tcx()
}
fn infcx(&self) -> &'a InferCtxt<'a,'tcx> {
self.fcx.infcx()
}
fn check_closure(&mut self,
expr: &ast::Expr,
capture_clause: ast::CaptureClause,
_body: &ast::Block)
{
let is_old_skool_closure = match self.fcx.expr_ty(expr).sty {
ty::ty_closure(..) => true,
_ => false,
};
match capture_clause {
ast::CaptureByValue if !is_old_skool_closure => {
}
_ => {
ty::with_freevars(self.tcx(), expr.id, |freevars| {
for freevar in freevars.iter() {
let var_node_id = freevar.def.local_node_id();
let upvar_id = ty::UpvarId { var_id: var_node_id,
closure_expr_id: expr.id };
debug!("seed upvar_id {}", upvar_id);
let origin = UpvarRegion(upvar_id, expr.span);
let freevar_region = self.infcx().next_region_var(origin);
let upvar_borrow = ty::UpvarBorrow { kind: ty::ImmBorrow,
region: freevar_region };
self.fcx.inh.upvar_borrow_map.borrow_mut().insert(upvar_id,
upvar_borrow);
}
});
}
}
}
}
///////////////////////////////////////////////////////////////////////////
// ADJUST BORROW KIND
struct AdjustBorrowKind<'a,'tcx:'a> {
fcx: &'a FnCtxt<'a,'tcx>
}
impl<'a,'tcx> AdjustBorrowKind<'a,'tcx>{
fn new(fcx: &'a FnCtxt<'a,'tcx>) -> AdjustBorrowKind<'a,'tcx> {
AdjustBorrowKind { fcx: fcx }
}
fn tcx(&self) -> &'a ty::ctxt<'tcx> {
self.fcx.tcx()
}
fn analyze_fn(&mut self, decl: &ast::FnDecl, body: &ast::Block) {
/*!
* Analysis starting point.
*/
self.visit_block(body);
debug!("analyzing fn body with id {}", body.id);
let mut euv = euv::ExprUseVisitor::new(self, self.fcx);
euv.walk_fn(decl, body);
}
/// Indicates that `cmt` is being directly mutated (e.g., assigned
/// to). If cmt contains any by-ref upvars, this implies that
/// those upvars must be borrowed using an `&mut` borow.
fn adjust_upvar_borrow_kind_for_mut(&mut self, cmt: mc::cmt<'tcx>) {
debug!("adjust_upvar_borrow_kind_for_mut(cmt={})",
cmt.repr(self.tcx()));
match cmt.cat.clone() {
mc::cat_deref(base, _, mc::Unique) |
mc::cat_interior(base, _) |
mc::cat_downcast(base, _) => {
// Interior or owned data is mutable if base is
// mutable, so iterate to the base.
self.adjust_upvar_borrow_kind_for_mut(base);
}
mc::cat_deref(base, _, mc::BorrowedPtr(..)) |
mc::cat_deref(base, _, mc::Implicit(..)) => {
if let mc::NoteUpvarRef(upvar_id) = cmt.note {
// if this is an implicit deref of an
// upvar, then we need to modify the
// borrow_kind of the upvar to make sure it
// is inferred to mutable if necessary
let mut upvar_borrow_map = self.fcx.inh.upvar_borrow_map.borrow_mut();
let ub = &mut upvar_borrow_map[upvar_id];
self.adjust_upvar_borrow_kind(upvar_id, ub, ty::MutBorrow);
} else {
// assignment to deref of an `&mut`
// borrowed pointer implies that the
// pointer itself must be unique, but not
// necessarily *mutable*
self.adjust_upvar_borrow_kind_for_unique(base);
}
}
mc::cat_deref(_, _, mc::UnsafePtr(..)) |
mc::cat_static_item |
mc::cat_rvalue(_) |
mc::cat_local(_) |
mc::cat_upvar(..) => {
return;
}
}
}
fn adjust_upvar_borrow_kind_for_unique(&self, cmt: mc::cmt<'tcx>) {
debug!("adjust_upvar_borrow_kind_for_unique(cmt={})",
cmt.repr(self.tcx()));
match cmt.cat.clone() {
mc::cat_deref(base, _, mc::Unique) |
mc::cat_interior(base, _) |
mc::cat_downcast(base, _) => {
// Interior or owned data is unique if base is
// unique.
self.adjust_upvar_borrow_kind_for_unique(base);
}
mc::cat_deref(base, _, mc::BorrowedPtr(..)) |
mc::cat_deref(base, _, mc::Implicit(..)) => {
if let mc::NoteUpvarRef(upvar_id) = cmt.note {
// if this is an implicit deref of an
// upvar, then we need to modify the
// borrow_kind of the upvar to make sure it
// is inferred to unique if necessary
let mut ub = self.fcx.inh.upvar_borrow_map.borrow_mut();
let ub = &mut ub[upvar_id];
self.adjust_upvar_borrow_kind(upvar_id, ub, ty::UniqueImmBorrow);
} else {
// for a borrowed pointer to be unique, its
// base must be unique
self.adjust_upvar_borrow_kind_for_unique(base);
}
}
mc::cat_deref(_, _, mc::UnsafePtr(..)) |
mc::cat_static_item |
mc::cat_rvalue(_) |
mc::cat_local(_) |
mc::cat_upvar(..) => {
}
}
}
/// We infer the borrow_kind with which to borrow upvars in a stack closure. The borrow_kind
/// basically follows a lattice of `imm < unique-imm < mut`, moving from left to right as needed
/// (but never right to left). Here the argument `mutbl` is the borrow_kind that is required by
/// some particular use.
fn adjust_upvar_borrow_kind(&self,
upvar_id: ty::UpvarId,
upvar_borrow: &mut ty::UpvarBorrow,
kind: ty::BorrowKind) {
debug!("adjust_upvar_borrow_kind: id={} kind=({} -> {})",
upvar_id, upvar_borrow.kind, kind);
match (upvar_borrow.kind, kind) {
// Take RHS:
(ty::ImmBorrow, ty::UniqueImmBorrow) |
(ty::ImmBorrow, ty::MutBorrow) |
(ty::UniqueImmBorrow, ty::MutBorrow) => {
upvar_borrow.kind = kind;
}
// Take LHS:
(ty::ImmBorrow, ty::ImmBorrow) |
(ty::UniqueImmBorrow, ty::ImmBorrow) |
(ty::UniqueImmBorrow, ty::UniqueImmBorrow) |
(ty::MutBorrow, _) => {
}
}
}
}
impl<'a, 'tcx, 'v> Visitor<'v> for AdjustBorrowKind<'a, 'tcx> {
fn visit_fn(&mut self,
fn_kind: visit::FnKind<'v>,
decl: &'v ast::FnDecl,
body: &'v ast::Block,
span: Span,
_id: ast::NodeId)
{
match fn_kind {
visit::FkItemFn(..) | visit::FkMethod(..) => {
// ignore nested fn items
}
visit::FkFnBlock => {
self.analyze_fn(decl, body);
visit::walk_fn(self, fn_kind, decl, body, span);
}
}
}
}
impl<'a,'tcx> euv::Delegate<'tcx> for AdjustBorrowKind<'a,'tcx> {
fn consume(&mut self,
_consume_id: ast::NodeId,
_consume_span: Span,
_cmt: mc::cmt<'tcx>,
_mode: euv::ConsumeMode)
{}
fn matched_pat(&mut self,
_matched_pat: &ast::Pat,
_cmt: mc::cmt<'tcx>,
_mode: euv::MatchMode)
{}
fn consume_pat(&mut self,
_consume_pat: &ast::Pat,
_cmt: mc::cmt<'tcx>,
_mode: euv::ConsumeMode)
{}
fn borrow(&mut self,
borrow_id: ast::NodeId,
_borrow_span: Span,
cmt: mc::cmt<'tcx>,
_loan_region: ty::Region,
bk: ty::BorrowKind,
_loan_cause: euv::LoanCause)
{
debug!("borrow(borrow_id={}, cmt={}, bk={})",
borrow_id, cmt.repr(self.tcx()), bk);
match bk {
ty::ImmBorrow => { }
ty::UniqueImmBorrow => {
self.adjust_upvar_borrow_kind_for_unique(cmt);
}
ty::MutBorrow => {
self.adjust_upvar_borrow_kind_for_mut(cmt);
}
}
}
fn decl_without_init(&mut self,
_id: ast::NodeId,
_span: Span)
{}
fn mutate(&mut self,
_assignment_id: ast::NodeId,
_assignment_span: Span,
assignee_cmt: mc::cmt<'tcx>,
_mode: euv::MutateMode)
{
debug!("mutate(assignee_cmt={})",
assignee_cmt.repr(self.tcx()));
self.adjust_upvar_borrow_kind_for_mut(assignee_cmt);
}
}