Auto merge of #66246 - matthewjasper:simplify-mem-cat, r=pnkfelix
Simplify memory categorization With AST borrowck gone, mem_categorization can be simplified, a lot. * `cmt_` is now called `Place`. Most local variable names have been updated to reflect this, but the `cat_*` methods retain their names. * `MemCategorizationContext` no longer needs a `ScopeTree` and always needs an `InferCtxt`. * `Place` now uses a similar representation to `mir::Place` with a `Vec` of projections. * `Upvar` places don't include the implicit environment and capture derefs. These are now handled by `regionck` when needed. * Various types, methods and variants only used by AST borrowck have been removed. * `ExprUseVisitor` now lives in `rustc_typeck::expr_use_visitor`. * `MemCategorizationContext` and `Place` live in `rustc_typeck::mem_categorization`. * `Place` is re-exported in `rustc_typeck::expr_use_visitor` so that Clippy can access it. The loss of an error in `issue-4335.rs` is due to a change in capture inference in ill-formed programs. If any projection from a variable is moved from then we capture that variable by move, whether or not the place being moved from allows this. Closes #66270
This commit is contained in:
commit
4752c05af4
11 changed files with 992 additions and 1956 deletions
|
|
@ -96,7 +96,6 @@ pub mod infer;
|
|||
pub mod lint;
|
||||
|
||||
pub mod middle {
|
||||
pub mod expr_use_visitor;
|
||||
pub mod cstore;
|
||||
pub mod dependency_format;
|
||||
pub mod diagnostic_items;
|
||||
|
|
@ -104,7 +103,6 @@ pub mod middle {
|
|||
pub mod free_region;
|
||||
pub mod lib_features;
|
||||
pub mod lang_items;
|
||||
pub mod mem_categorization;
|
||||
pub mod privacy;
|
||||
pub mod reachable;
|
||||
pub mod region;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -74,8 +74,7 @@
|
|||
|
||||
use crate::check::dropck;
|
||||
use crate::check::FnCtxt;
|
||||
use crate::middle::mem_categorization as mc;
|
||||
use crate::middle::mem_categorization::Categorization;
|
||||
use crate::mem_categorization as mc;
|
||||
use crate::middle::region;
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::infer::outlives::env::OutlivesEnvironment;
|
||||
|
|
@ -88,7 +87,6 @@ use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
|
|||
use rustc::hir::{self, PatKind};
|
||||
use std::mem;
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
use syntax_pos::Span;
|
||||
|
||||
// a variation on try that just returns unit
|
||||
|
|
@ -814,18 +812,17 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
|
|||
where
|
||||
F: for<'b> FnOnce(mc::MemCategorizationContext<'b, 'tcx>) -> R,
|
||||
{
|
||||
f(mc::MemCategorizationContext::with_infer(
|
||||
f(mc::MemCategorizationContext::new(
|
||||
&self.infcx,
|
||||
self.outlives_environment.param_env,
|
||||
self.body_owner,
|
||||
&self.region_scope_tree,
|
||||
&self.tables.borrow(),
|
||||
))
|
||||
}
|
||||
|
||||
/// Invoked on any adjustments that occur. Checks that if this is a region pointer being
|
||||
/// dereferenced, the lifetime of the pointer includes the deref expr.
|
||||
fn constrain_adjustments(&mut self, expr: &hir::Expr) -> mc::McResult<mc::cmt_<'tcx>> {
|
||||
fn constrain_adjustments(&mut self, expr: &hir::Expr) -> mc::McResult<mc::Place<'tcx>> {
|
||||
debug!("constrain_adjustments(expr={:?})", expr);
|
||||
|
||||
let mut cmt = self.with_mc(|mc| mc.cat_expr_unadjusted(expr))?;
|
||||
|
|
@ -899,10 +896,6 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
|
|||
}
|
||||
|
||||
cmt = self.with_mc(|mc| mc.cat_expr_adjusted(expr, cmt, &adjustment))?;
|
||||
|
||||
if let Categorization::Deref(_, mc::BorrowedPtr(_, r_ptr)) = cmt.cat {
|
||||
self.mk_subregion_due_to_dereference(expr.span, expr_region, r_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(cmt)
|
||||
|
|
@ -921,16 +914,22 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
|
|||
)
|
||||
}
|
||||
|
||||
fn check_safety_of_rvalue_destructor_if_necessary(&mut self, cmt: &mc::cmt_<'tcx>, span: Span) {
|
||||
if let Categorization::Rvalue = cmt.cat {
|
||||
let typ = self.resolve_type(cmt.ty);
|
||||
let body_id = self.body_id;
|
||||
let _ = dropck::check_drop_obligations(
|
||||
self,
|
||||
typ,
|
||||
span,
|
||||
body_id,
|
||||
);
|
||||
fn check_safety_of_rvalue_destructor_if_necessary(
|
||||
&mut self,
|
||||
place: &mc::Place<'tcx>,
|
||||
span: Span,
|
||||
) {
|
||||
if let mc::PlaceBase::Rvalue = place.base {
|
||||
if place.projections.is_empty() {
|
||||
let typ = self.resolve_type(place.ty);
|
||||
let body_id = self.body_id;
|
||||
let _ = dropck::check_drop_obligations(
|
||||
self,
|
||||
typ,
|
||||
span,
|
||||
body_id,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1035,7 +1034,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
|
|||
}
|
||||
Some(ref expr) => &**expr,
|
||||
};
|
||||
let discr_cmt = Rc::new(ignore_err!(self.with_mc(|mc| mc.cat_expr(init_expr))));
|
||||
let discr_cmt = ignore_err!(self.with_mc(|mc| mc.cat_expr(init_expr)));
|
||||
self.link_pattern(discr_cmt, &local.pat);
|
||||
}
|
||||
|
||||
|
|
@ -1044,7 +1043,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
|
|||
/// linked to the lifetime of its guarantor (if any).
|
||||
fn link_match(&self, discr: &hir::Expr, arms: &[hir::Arm]) {
|
||||
debug!("regionck::for_match()");
|
||||
let discr_cmt = Rc::new(ignore_err!(self.with_mc(|mc| mc.cat_expr(discr))));
|
||||
let discr_cmt = ignore_err!(self.with_mc(|mc| mc.cat_expr(discr)));
|
||||
debug!("discr_cmt={:?}", discr_cmt);
|
||||
for arm in arms {
|
||||
self.link_pattern(discr_cmt.clone(), &arm.pat);
|
||||
|
|
@ -1058,7 +1057,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
|
|||
for param in params {
|
||||
let param_ty = self.node_ty(param.hir_id);
|
||||
let param_cmt = self.with_mc(|mc| {
|
||||
Rc::new(mc.cat_rvalue(param.hir_id, param.pat.span, param_ty))
|
||||
mc.cat_rvalue(param.hir_id, param.pat.span, param_ty)
|
||||
});
|
||||
debug!("param_ty={:?} param_cmt={:?} param={:?}", param_ty, param_cmt, param);
|
||||
self.link_pattern(param_cmt, ¶m.pat);
|
||||
|
|
@ -1067,7 +1066,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
|
|||
|
||||
/// Link lifetimes of any ref bindings in `root_pat` to the pointers found
|
||||
/// in the discriminant, if needed.
|
||||
fn link_pattern(&self, discr_cmt: mc::cmt<'tcx>, root_pat: &hir::Pat) {
|
||||
fn link_pattern(&self, discr_cmt: mc::Place<'tcx>, root_pat: &hir::Pat) {
|
||||
debug!(
|
||||
"link_pattern(discr_cmt={:?}, root_pat={:?})",
|
||||
discr_cmt, root_pat
|
||||
|
|
@ -1100,7 +1099,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
|
|||
fn link_autoref(
|
||||
&self,
|
||||
expr: &hir::Expr,
|
||||
expr_cmt: &mc::cmt_<'tcx>,
|
||||
expr_cmt: &mc::Place<'tcx>,
|
||||
autoref: &adjustment::AutoBorrow<'tcx>,
|
||||
) {
|
||||
debug!(
|
||||
|
|
@ -1130,7 +1129,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
|
|||
span: Span,
|
||||
id: hir::HirId,
|
||||
mutbl: hir::Mutability,
|
||||
cmt_borrowed: &mc::cmt_<'tcx>,
|
||||
cmt_borrowed: &mc::Place<'tcx>,
|
||||
) {
|
||||
debug!(
|
||||
"link_region_from_node_type(id={:?}, mutbl={:?}, cmt_borrowed={:?})",
|
||||
|
|
@ -1153,61 +1152,35 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
|
|||
span: Span,
|
||||
borrow_region: ty::Region<'tcx>,
|
||||
borrow_kind: ty::BorrowKind,
|
||||
borrow_cmt: &mc::cmt_<'tcx>,
|
||||
borrow_place: &mc::Place<'tcx>,
|
||||
) {
|
||||
let origin = infer::DataBorrowed(borrow_cmt.ty, span);
|
||||
self.type_must_outlive(origin, borrow_cmt.ty, borrow_region);
|
||||
let origin = infer::DataBorrowed(borrow_place.ty, span);
|
||||
self.type_must_outlive(origin, borrow_place.ty, borrow_region);
|
||||
|
||||
let mut borrow_kind = borrow_kind;
|
||||
let mut borrow_cmt_cat = borrow_cmt.cat.clone();
|
||||
|
||||
loop {
|
||||
for pointer_ty in borrow_place.deref_tys() {
|
||||
debug!(
|
||||
"link_region(borrow_region={:?}, borrow_kind={:?}, borrow_cmt={:?})",
|
||||
borrow_region, borrow_kind, borrow_cmt
|
||||
"link_region(borrow_region={:?}, borrow_kind={:?}, pointer_ty={:?})",
|
||||
borrow_region, borrow_kind, borrow_place
|
||||
);
|
||||
match borrow_cmt_cat {
|
||||
Categorization::Deref(ref_cmt, mc::BorrowedPtr(ref_kind, ref_region)) => {
|
||||
match self.link_reborrowed_region(
|
||||
match pointer_ty.kind {
|
||||
ty::RawPtr(_) => return,
|
||||
ty::Ref(ref_region, _, ref_mutability) => {
|
||||
if self.link_reborrowed_region(
|
||||
span,
|
||||
borrow_region,
|
||||
borrow_kind,
|
||||
ref_cmt,
|
||||
ref_region,
|
||||
ref_kind,
|
||||
borrow_cmt.note,
|
||||
ref_mutability,
|
||||
) {
|
||||
Some((c, k)) => {
|
||||
borrow_cmt_cat = c.cat.clone();
|
||||
borrow_kind = k;
|
||||
}
|
||||
None => {
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Categorization::Downcast(cmt_base, _)
|
||||
| Categorization::Deref(cmt_base, mc::Unique)
|
||||
| Categorization::Interior(cmt_base, _) => {
|
||||
// Borrowing interior or owned data requires the base
|
||||
// to be valid and borrowable in the same fashion.
|
||||
borrow_cmt_cat = cmt_base.cat.clone();
|
||||
borrow_kind = borrow_kind;
|
||||
}
|
||||
|
||||
Categorization::Deref(_, mc::UnsafePtr(..))
|
||||
| Categorization::StaticItem
|
||||
| Categorization::Upvar(..)
|
||||
| Categorization::Local(..)
|
||||
| Categorization::ThreadLocal
|
||||
| Categorization::Rvalue => {
|
||||
// These are all "base cases" with independent lifetimes
|
||||
// that are not subject to inference
|
||||
return;
|
||||
}
|
||||
_ => assert!(pointer_ty.is_box(), "unexpected built-in deref type {}", pointer_ty)
|
||||
}
|
||||
}
|
||||
if let mc::PlaceBase::Upvar(upvar_id) = borrow_place.base {
|
||||
self.link_upvar_region(span, borrow_region, upvar_id);
|
||||
}
|
||||
}
|
||||
|
||||
/// This is the most complicated case: the path being borrowed is
|
||||
|
|
@ -1231,83 +1204,28 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
|
|||
///
|
||||
/// Here `bk` stands for some borrow-kind (e.g., `mut`, `uniq`, etc).
|
||||
///
|
||||
/// Unfortunately, there are some complications beyond the simple
|
||||
/// scenario I just painted:
|
||||
/// There is a complication beyond the simple scenario I just painted: there
|
||||
/// may in fact be more levels of reborrowing. In the example, I said the
|
||||
/// borrow was like `&'z *r`, but it might in fact be a borrow like
|
||||
/// `&'z **q` where `q` has type `&'a &'b mut T`. In that case, we want to
|
||||
/// ensure that `'z <= 'a` and `'z <= 'b`.
|
||||
///
|
||||
/// 1. The reference `r` might in fact be a "by-ref" upvar. In that
|
||||
/// case, we have two jobs. First, we are inferring whether this reference
|
||||
/// should be an `&T`, `&mut T`, or `&uniq T` reference, and we must
|
||||
/// adjust that based on this borrow (e.g., if this is an `&mut` borrow,
|
||||
/// then `r` must be an `&mut` reference). Second, whenever we link
|
||||
/// two regions (here, `'z <= 'a`), we supply a *cause*, and in this
|
||||
/// case we adjust the cause to indicate that the reference being
|
||||
/// "reborrowed" is itself an upvar. This provides a nicer error message
|
||||
/// should something go wrong.
|
||||
/// The return value of this function indicates whether we *don't* need to
|
||||
/// the recurse to the next reference up.
|
||||
///
|
||||
/// 2. There may in fact be more levels of reborrowing. In the
|
||||
/// example, I said the borrow was like `&'z *r`, but it might
|
||||
/// in fact be a borrow like `&'z **q` where `q` has type `&'a
|
||||
/// &'b mut T`. In that case, we want to ensure that `'z <= 'a`
|
||||
/// and `'z <= 'b`. This is explained more below.
|
||||
///
|
||||
/// The return value of this function indicates whether we need to
|
||||
/// recurse and process `ref_cmt` (see case 2 above).
|
||||
/// This is explained more below.
|
||||
fn link_reborrowed_region(
|
||||
&self,
|
||||
span: Span,
|
||||
borrow_region: ty::Region<'tcx>,
|
||||
borrow_kind: ty::BorrowKind,
|
||||
ref_cmt: mc::cmt<'tcx>,
|
||||
ref_region: ty::Region<'tcx>,
|
||||
mut ref_kind: ty::BorrowKind,
|
||||
note: mc::Note,
|
||||
) -> Option<(mc::cmt<'tcx>, ty::BorrowKind)> {
|
||||
// Possible upvar ID we may need later to create an entry in the
|
||||
// maybe link map.
|
||||
|
||||
// Detect by-ref upvar `x`:
|
||||
let cause = match note {
|
||||
mc::NoteUpvarRef(ref upvar_id) => {
|
||||
match self.tables.borrow().upvar_capture_map.get(upvar_id) {
|
||||
Some(&ty::UpvarCapture::ByRef(ref upvar_borrow)) => {
|
||||
// The mutability of the upvar may have been modified
|
||||
// by the above adjustment, so update our local variable.
|
||||
ref_kind = upvar_borrow.kind;
|
||||
|
||||
infer::ReborrowUpvar(span, *upvar_id)
|
||||
}
|
||||
_ => {
|
||||
span_bug!(span, "Illegal upvar id: {:?}", upvar_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
mc::NoteClosureEnv(ref upvar_id) => {
|
||||
// We don't have any mutability changes to propagate, but
|
||||
// we do want to note that an upvar reborrow caused this
|
||||
// link
|
||||
infer::ReborrowUpvar(span, *upvar_id)
|
||||
}
|
||||
_ => infer::Reborrow(span),
|
||||
};
|
||||
|
||||
ref_mutability: hir::Mutability,
|
||||
) -> bool {
|
||||
debug!(
|
||||
"link_reborrowed_region: {:?} <= {:?}",
|
||||
borrow_region, ref_region
|
||||
);
|
||||
self.sub_regions(cause, borrow_region, ref_region);
|
||||
|
||||
// If we end up needing to recurse and establish a region link
|
||||
// with `ref_cmt`, calculate what borrow kind we will end up
|
||||
// needing. This will be used below.
|
||||
//
|
||||
// One interesting twist is that we can weaken the borrow kind
|
||||
// when we recurse: to reborrow an `&mut` referent as mutable,
|
||||
// borrowck requires a unique path to the `&mut` reference but not
|
||||
// necessarily a *mutable* path.
|
||||
let new_borrow_kind = match borrow_kind {
|
||||
ty::ImmBorrow => ty::ImmBorrow,
|
||||
ty::MutBorrow | ty::UniqueImmBorrow => ty::UniqueImmBorrow,
|
||||
};
|
||||
self.sub_regions(infer::Reborrow(span), borrow_region, ref_region);
|
||||
|
||||
// Decide whether we need to recurse and link any regions within
|
||||
// the `ref_cmt`. This is concerned for the case where the value
|
||||
|
|
@ -1336,24 +1254,83 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
|
|||
// (Note that since we have not examined `ref_cmt.cat`, we don't
|
||||
// know whether this scenario has occurred; but I wanted to show
|
||||
// how all the types get adjusted.)
|
||||
match ref_kind {
|
||||
ty::ImmBorrow => {
|
||||
match ref_mutability {
|
||||
hir::Mutability::Immutable => {
|
||||
// The reference being reborrowed is a shareable ref of
|
||||
// type `&'a T`. In this case, it doesn't matter where we
|
||||
// *found* the `&T` pointer, the memory it references will
|
||||
// be valid and immutable for `'a`. So we can stop here.
|
||||
//
|
||||
// (Note that the `borrow_kind` must also be ImmBorrow or
|
||||
// else the user is borrowed imm memory as mut memory,
|
||||
// which means they'll get an error downstream in borrowck
|
||||
// anyhow.)
|
||||
return None;
|
||||
true
|
||||
}
|
||||
|
||||
ty::MutBorrow | ty::UniqueImmBorrow => {
|
||||
// The reference being reborrowed is either an `&mut T` or
|
||||
// `&uniq T`. This is the case where recursion is needed.
|
||||
return Some((ref_cmt, new_borrow_kind));
|
||||
hir::Mutability::Mutable => {
|
||||
// The reference being reborrowed is either an `&mut T`. This is
|
||||
// the case where recursion is needed.
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An upvar may be behind up to 2 references:
|
||||
///
|
||||
/// * One can come from the reference to a "by-reference" upvar.
|
||||
/// * Another one can come from the reference to the closure itself if it's
|
||||
/// a `FnMut` or `Fn` closure.
|
||||
///
|
||||
/// This function links the lifetimes of those references to the lifetime
|
||||
/// of the borrow that's provided. See [link_reborrowed_region] for some
|
||||
/// more explanation of this in the general case.
|
||||
///
|
||||
/// We also supply a *cause*, and in this case we set the cause to
|
||||
/// indicate that the reference being "reborrowed" is itself an upvar. This
|
||||
/// provides a nicer error message should something go wrong.
|
||||
fn link_upvar_region(
|
||||
&self,
|
||||
span: Span,
|
||||
borrow_region: ty::Region<'tcx>,
|
||||
upvar_id: ty::UpvarId,
|
||||
) {
|
||||
debug!("link_upvar_region(borrorw_region={:?}, upvar_id={:?}", borrow_region, upvar_id);
|
||||
// A by-reference upvar can't be borrowed for longer than the
|
||||
// upvar is borrowed from the environment.
|
||||
match self.tables.borrow().upvar_capture(upvar_id) {
|
||||
ty::UpvarCapture::ByRef(upvar_borrow) => {
|
||||
self.sub_regions(
|
||||
infer::ReborrowUpvar(span, upvar_id),
|
||||
borrow_region,
|
||||
upvar_borrow.region,
|
||||
);
|
||||
if let ty::ImmBorrow = upvar_borrow.kind {
|
||||
debug!("link_upvar_region: capture by shared ref");
|
||||
return;
|
||||
}
|
||||
}
|
||||
ty::UpvarCapture::ByValue => {}
|
||||
}
|
||||
let fn_hir_id = self.tcx.hir().local_def_id_to_hir_id(upvar_id.closure_expr_id);
|
||||
let ty = self.resolve_node_type(fn_hir_id);
|
||||
debug!("link_upvar_region: ty={:?}", ty);
|
||||
|
||||
// A closure capture can't be borrowed for longer than the
|
||||
// reference to the closure.
|
||||
if let ty::Closure(closure_def_id, substs) = ty.kind {
|
||||
match self.infcx.closure_kind(closure_def_id, substs) {
|
||||
Some(ty::ClosureKind::Fn) | Some(ty::ClosureKind::FnMut) => {
|
||||
// Region of environment pointer
|
||||
let env_region = self.tcx.mk_region(ty::ReFree(ty::FreeRegion {
|
||||
scope: upvar_id.closure_expr_id.to_def_id(),
|
||||
bound_region: ty::BrEnv
|
||||
}));
|
||||
self.sub_regions(
|
||||
infer::ReborrowUpvar(span, upvar_id),
|
||||
borrow_region,
|
||||
env_region,
|
||||
);
|
||||
}
|
||||
Some(ty::ClosureKind::FnOnce) => {}
|
||||
None => {
|
||||
span_bug!(span, "Have not inferred closure kind before regionck");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,9 +32,9 @@
|
|||
|
||||
use super::FnCtxt;
|
||||
|
||||
use crate::middle::expr_use_visitor as euv;
|
||||
use crate::middle::mem_categorization as mc;
|
||||
use crate::middle::mem_categorization::Categorization;
|
||||
use crate::expr_use_visitor as euv;
|
||||
use crate::mem_categorization as mc;
|
||||
use crate::mem_categorization::PlaceBase;
|
||||
use rustc::hir;
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::hir::def_id::LocalDefId;
|
||||
|
|
@ -76,6 +76,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InferBorrowKindVisitor<'a, 'tcx> {
|
|||
}
|
||||
|
||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
/// Analysis starting point.
|
||||
fn analyze_closure(
|
||||
&self,
|
||||
closure_hir_id: hir::HirId,
|
||||
|
|
@ -83,9 +84,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
body: &hir::Body,
|
||||
capture_clause: hir::CaptureBy,
|
||||
) {
|
||||
/*!
|
||||
* Analysis starting point.
|
||||
*/
|
||||
|
||||
debug!(
|
||||
"analyze_closure(id={:?}, body.id={:?})",
|
||||
|
|
@ -171,20 +169,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
|
||||
let body_owner_def_id = self.tcx.hir().body_owner_def_id(body.id());
|
||||
assert_eq!(body_owner_def_id, closure_def_id);
|
||||
let region_scope_tree = &self.tcx.region_scope_tree(body_owner_def_id);
|
||||
let mut delegate = InferBorrowKind {
|
||||
fcx: self,
|
||||
closure_def_id: closure_def_id,
|
||||
closure_def_id,
|
||||
current_closure_kind: ty::ClosureKind::LATTICE_BOTTOM,
|
||||
current_origin: None,
|
||||
adjust_upvar_captures: ty::UpvarCaptureMap::default(),
|
||||
};
|
||||
euv::ExprUseVisitor::with_infer(
|
||||
euv::ExprUseVisitor::new(
|
||||
&mut delegate,
|
||||
&self.infcx,
|
||||
body_owner_def_id,
|
||||
self.param_env,
|
||||
region_scope_tree,
|
||||
&self.tables.borrow(),
|
||||
)
|
||||
.consume_body(body);
|
||||
|
|
@ -312,13 +308,10 @@ struct InferBorrowKind<'a, 'tcx> {
|
|||
impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
|
||||
fn adjust_upvar_borrow_kind_for_consume(
|
||||
&mut self,
|
||||
cmt: &mc::cmt_<'tcx>,
|
||||
place: &mc::Place<'tcx>,
|
||||
mode: euv::ConsumeMode,
|
||||
) {
|
||||
debug!(
|
||||
"adjust_upvar_borrow_kind_for_consume(cmt={:?}, mode={:?})",
|
||||
cmt, mode
|
||||
);
|
||||
debug!("adjust_upvar_borrow_kind_for_consume(place={:?}, mode={:?})", place, mode);
|
||||
|
||||
// we only care about moves
|
||||
match mode {
|
||||
|
|
@ -329,132 +322,68 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
|
|||
}
|
||||
|
||||
let tcx = self.fcx.tcx;
|
||||
let upvar_id = if let PlaceBase::Upvar(upvar_id) = place.base {
|
||||
upvar_id
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
// watch out for a move of the deref of a borrowed pointer;
|
||||
// for that to be legal, the upvar would have to be borrowed
|
||||
// by value instead
|
||||
let guarantor = cmt.guarantor();
|
||||
debug!(
|
||||
"adjust_upvar_borrow_kind_for_consume: guarantor={:?}",
|
||||
guarantor
|
||||
debug!("adjust_upvar_borrow_kind_for_consume: upvar={:?}", upvar_id);
|
||||
|
||||
// To move out of an upvar, this must be a FnOnce closure
|
||||
self.adjust_closure_kind(
|
||||
upvar_id.closure_expr_id,
|
||||
ty::ClosureKind::FnOnce,
|
||||
place.span,
|
||||
var_name(tcx, upvar_id.var_path.hir_id),
|
||||
);
|
||||
debug!(
|
||||
"adjust_upvar_borrow_kind_for_consume: guarantor.cat={:?}",
|
||||
guarantor.cat
|
||||
);
|
||||
if let Categorization::Deref(_, mc::BorrowedPtr(..)) = guarantor.cat {
|
||||
debug!(
|
||||
"adjust_upvar_borrow_kind_for_consume: found deref with note {:?}",
|
||||
cmt.note
|
||||
);
|
||||
match guarantor.note {
|
||||
mc::NoteUpvarRef(upvar_id) => {
|
||||
debug!(
|
||||
"adjust_upvar_borrow_kind_for_consume: \
|
||||
setting upvar_id={:?} to by value",
|
||||
upvar_id
|
||||
);
|
||||
|
||||
// to move out of an upvar, this must be a FnOnce closure
|
||||
self.adjust_closure_kind(
|
||||
upvar_id.closure_expr_id,
|
||||
ty::ClosureKind::FnOnce,
|
||||
guarantor.span,
|
||||
var_name(tcx, upvar_id.var_path.hir_id),
|
||||
);
|
||||
|
||||
self.adjust_upvar_captures
|
||||
.insert(upvar_id, ty::UpvarCapture::ByValue);
|
||||
}
|
||||
mc::NoteClosureEnv(upvar_id) => {
|
||||
// we get just a closureenv ref if this is a
|
||||
// `move` closure, or if the upvar has already
|
||||
// been inferred to by-value. In any case, we
|
||||
// must still adjust the kind of the closure
|
||||
// to be a FnOnce closure to permit moves out
|
||||
// of the environment.
|
||||
self.adjust_closure_kind(
|
||||
upvar_id.closure_expr_id,
|
||||
ty::ClosureKind::FnOnce,
|
||||
guarantor.span,
|
||||
var_name(tcx, upvar_id.var_path.hir_id),
|
||||
);
|
||||
}
|
||||
mc::NoteIndex | mc::NoteNone => {}
|
||||
}
|
||||
}
|
||||
self.adjust_upvar_captures.insert(upvar_id, ty::UpvarCapture::ByValue);
|
||||
}
|
||||
|
||||
/// 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` borrow.
|
||||
fn adjust_upvar_borrow_kind_for_mut(&mut self, cmt: &mc::cmt_<'tcx>) {
|
||||
debug!("adjust_upvar_borrow_kind_for_mut(cmt={:?})", cmt);
|
||||
/// Indicates that `place` is being directly mutated (e.g., assigned
|
||||
/// to). If the place is based on a by-ref upvar, this implies that
|
||||
/// the upvar must be borrowed using an `&mut` borrow.
|
||||
fn adjust_upvar_borrow_kind_for_mut(&mut self, place: &mc::Place<'tcx>) {
|
||||
debug!("adjust_upvar_borrow_kind_for_mut(place={:?})", place);
|
||||
|
||||
match cmt.cat.clone() {
|
||||
Categorization::Deref(base, mc::Unique)
|
||||
| Categorization::Interior(base, _)
|
||||
| Categorization::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);
|
||||
}
|
||||
|
||||
Categorization::Deref(base, mc::BorrowedPtr(..)) => {
|
||||
if !self.try_adjust_upvar_deref(cmt, ty::MutBorrow) {
|
||||
if let PlaceBase::Upvar(upvar_id) = place.base {
|
||||
let mut borrow_kind = ty::MutBorrow;
|
||||
for pointer_ty in place.deref_tys() {
|
||||
match pointer_ty.kind {
|
||||
// Raw pointers don't inherit mutability.
|
||||
ty::RawPtr(_) => return,
|
||||
// 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);
|
||||
ty::Ref(.., hir::Mutability::Mutable) => borrow_kind = ty::UniqueImmBorrow,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
self.adjust_upvar_deref(upvar_id, place.span, borrow_kind);
|
||||
}
|
||||
}
|
||||
|
||||
Categorization::Deref(_, mc::UnsafePtr(..))
|
||||
| Categorization::StaticItem
|
||||
| Categorization::ThreadLocal
|
||||
| Categorization::Rvalue
|
||||
| Categorization::Local(_)
|
||||
| Categorization::Upvar(..) => {
|
||||
fn adjust_upvar_borrow_kind_for_unique(&mut self, place: &mc::Place<'tcx>) {
|
||||
debug!("adjust_upvar_borrow_kind_for_unique(place={:?})", place);
|
||||
|
||||
if let PlaceBase::Upvar(upvar_id) = place.base {
|
||||
if place.deref_tys().any(ty::TyS::is_unsafe_ptr) {
|
||||
// Raw pointers don't inherit mutability.
|
||||
return;
|
||||
}
|
||||
// for a borrowed pointer to be unique, its base must be unique
|
||||
self.adjust_upvar_deref(upvar_id, place.span, ty::UniqueImmBorrow);
|
||||
}
|
||||
}
|
||||
|
||||
fn adjust_upvar_borrow_kind_for_unique(&mut self, cmt: &mc::cmt_<'tcx>) {
|
||||
debug!("adjust_upvar_borrow_kind_for_unique(cmt={:?})", cmt);
|
||||
|
||||
match cmt.cat.clone() {
|
||||
Categorization::Deref(base, mc::Unique)
|
||||
| Categorization::Interior(base, _)
|
||||
| Categorization::Downcast(base, _) => {
|
||||
// Interior or owned data is unique if base is
|
||||
// unique.
|
||||
self.adjust_upvar_borrow_kind_for_unique(&base);
|
||||
}
|
||||
|
||||
Categorization::Deref(base, mc::BorrowedPtr(..)) => {
|
||||
if !self.try_adjust_upvar_deref(cmt, ty::UniqueImmBorrow) {
|
||||
// for a borrowed pointer to be unique, its
|
||||
// base must be unique
|
||||
self.adjust_upvar_borrow_kind_for_unique(&base);
|
||||
}
|
||||
}
|
||||
|
||||
Categorization::Deref(_, mc::UnsafePtr(..))
|
||||
| Categorization::StaticItem
|
||||
| Categorization::ThreadLocal
|
||||
| Categorization::Rvalue
|
||||
| Categorization::Local(_)
|
||||
| Categorization::Upvar(..) => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn try_adjust_upvar_deref(
|
||||
fn adjust_upvar_deref(
|
||||
&mut self,
|
||||
cmt: &mc::cmt_<'tcx>,
|
||||
upvar_id: ty::UpvarId,
|
||||
place_span: Span,
|
||||
borrow_kind: ty::BorrowKind,
|
||||
) -> bool {
|
||||
) {
|
||||
assert!(match borrow_kind {
|
||||
ty::MutBorrow => true,
|
||||
ty::UniqueImmBorrow => true,
|
||||
|
|
@ -465,39 +394,19 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
|
|||
|
||||
let tcx = self.fcx.tcx;
|
||||
|
||||
match cmt.note {
|
||||
mc::NoteUpvarRef(upvar_id) => {
|
||||
// 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
|
||||
self.adjust_upvar_borrow_kind(upvar_id, borrow_kind);
|
||||
// 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
|
||||
self.adjust_upvar_borrow_kind(upvar_id, borrow_kind);
|
||||
|
||||
// also need to be in an FnMut closure since this is not an ImmBorrow
|
||||
self.adjust_closure_kind(
|
||||
upvar_id.closure_expr_id,
|
||||
ty::ClosureKind::FnMut,
|
||||
cmt.span,
|
||||
var_name(tcx, upvar_id.var_path.hir_id),
|
||||
);
|
||||
|
||||
true
|
||||
}
|
||||
mc::NoteClosureEnv(upvar_id) => {
|
||||
// this kind of deref occurs in a `move` closure, or
|
||||
// for a by-value upvar; in either case, to mutate an
|
||||
// upvar, we need to be an FnMut closure
|
||||
self.adjust_closure_kind(
|
||||
upvar_id.closure_expr_id,
|
||||
ty::ClosureKind::FnMut,
|
||||
cmt.span,
|
||||
var_name(tcx, upvar_id.var_path.hir_id),
|
||||
);
|
||||
|
||||
true
|
||||
}
|
||||
mc::NoteIndex | mc::NoteNone => false,
|
||||
}
|
||||
// also need to be in an FnMut closure since this is not an ImmBorrow
|
||||
self.adjust_closure_kind(
|
||||
upvar_id.closure_expr_id,
|
||||
ty::ClosureKind::FnMut,
|
||||
place_span,
|
||||
var_name(tcx, upvar_id.var_path.hir_id),
|
||||
);
|
||||
}
|
||||
|
||||
/// We infer the borrow_kind with which to borrow upvars in a stack closure.
|
||||
|
|
@ -509,7 +418,7 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
|
|||
let upvar_capture = self
|
||||
.adjust_upvar_captures
|
||||
.get(&upvar_id)
|
||||
.cloned()
|
||||
.copied()
|
||||
.unwrap_or_else(|| self.fcx.tables.borrow().upvar_capture(upvar_id));
|
||||
debug!(
|
||||
"adjust_upvar_borrow_kind(upvar_id={:?}, upvar_capture={:?}, kind={:?})",
|
||||
|
|
@ -586,29 +495,29 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
|
|||
}
|
||||
|
||||
impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
|
||||
fn consume(&mut self, cmt: &mc::cmt_<'tcx>,mode: euv::ConsumeMode) {
|
||||
debug!("consume(cmt={:?},mode={:?})", cmt, mode);
|
||||
self.adjust_upvar_borrow_kind_for_consume(cmt, mode);
|
||||
fn consume(&mut self, place: &mc::Place<'tcx>,mode: euv::ConsumeMode) {
|
||||
debug!("consume(place={:?},mode={:?})", place, mode);
|
||||
self.adjust_upvar_borrow_kind_for_consume(place, mode);
|
||||
}
|
||||
|
||||
fn borrow(&mut self, cmt: &mc::cmt_<'tcx>, bk: ty::BorrowKind) {
|
||||
debug!("borrow(cmt={:?}, bk={:?})", cmt, bk);
|
||||
fn borrow(&mut self, place: &mc::Place<'tcx>, bk: ty::BorrowKind) {
|
||||
debug!("borrow(place={:?}, bk={:?})", place, bk);
|
||||
|
||||
match bk {
|
||||
ty::ImmBorrow => {}
|
||||
ty::UniqueImmBorrow => {
|
||||
self.adjust_upvar_borrow_kind_for_unique(cmt);
|
||||
self.adjust_upvar_borrow_kind_for_unique(place);
|
||||
}
|
||||
ty::MutBorrow => {
|
||||
self.adjust_upvar_borrow_kind_for_mut(cmt);
|
||||
self.adjust_upvar_borrow_kind_for_mut(place);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn mutate(&mut self, assignee_cmt: &mc::cmt_<'tcx>) {
|
||||
debug!("mutate(assignee_cmt={:?})", assignee_cmt);
|
||||
fn mutate(&mut self, assignee_place: &mc::Place<'tcx>) {
|
||||
debug!("mutate(assignee_place={:?})", assignee_place);
|
||||
|
||||
self.adjust_upvar_borrow_kind_for_mut(assignee_cmt);
|
||||
self.adjust_upvar_borrow_kind_for_mut(assignee_place);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,16 +5,17 @@
|
|||
pub use self::ConsumeMode::*;
|
||||
use self::OverloadedCallType::*;
|
||||
|
||||
use crate::hir::def::Res;
|
||||
use crate::hir::def_id::DefId;
|
||||
use crate::hir::ptr::P;
|
||||
use crate::infer::InferCtxt;
|
||||
use crate::middle::mem_categorization as mc;
|
||||
use crate::middle::region;
|
||||
use crate::ty::{self, TyCtxt, adjustment};
|
||||
// Export these here so that Clippy can use them.
|
||||
pub use mc::{PlaceBase, Place, Projection};
|
||||
|
||||
use crate::hir::{self, PatKind};
|
||||
use std::rc::Rc;
|
||||
use rustc::hir::{self, PatKind};
|
||||
use rustc::hir::def::Res;
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::hir::ptr::P;
|
||||
use rustc::infer::InferCtxt;
|
||||
use rustc::ty::{self, TyCtxt, adjustment};
|
||||
|
||||
use crate::mem_categorization as mc;
|
||||
use syntax_pos::Span;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -23,15 +24,15 @@ use syntax_pos::Span;
|
|||
/// This trait defines the callbacks you can expect to receive when
|
||||
/// employing the ExprUseVisitor.
|
||||
pub trait Delegate<'tcx> {
|
||||
// The value found at `cmt` is either copied or moved, depending
|
||||
// The value found at `place` is either copied or moved, depending
|
||||
// on mode.
|
||||
fn consume(&mut self, cmt: &mc::cmt_<'tcx>, mode: ConsumeMode);
|
||||
fn consume(&mut self, place: &mc::Place<'tcx>, mode: ConsumeMode);
|
||||
|
||||
// The value found at `cmt` is being borrowed with kind `bk`.
|
||||
fn borrow(&mut self, cmt: &mc::cmt_<'tcx>, bk: ty::BorrowKind);
|
||||
// The value found at `place` is being borrowed with kind `bk`.
|
||||
fn borrow(&mut self, place: &mc::Place<'tcx>, bk: ty::BorrowKind);
|
||||
|
||||
// The path at `cmt` is being assigned to.
|
||||
fn mutate(&mut self, assignee_cmt: &mc::cmt_<'tcx>);
|
||||
// The path at `place` is being assigned to.
|
||||
fn mutate(&mut self, assignee_place: &mc::Place<'tcx>);
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
|
|
@ -85,7 +86,6 @@ impl OverloadedCallType {
|
|||
pub struct ExprUseVisitor<'a, 'tcx> {
|
||||
mc: mc::MemCategorizationContext<'a, 'tcx>,
|
||||
delegate: &'a mut dyn Delegate<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
}
|
||||
|
||||
// If the MC results in an error, it's because the type check
|
||||
|
|
@ -112,49 +112,22 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
|||
///
|
||||
/// - `delegate` -- who receives the callbacks
|
||||
/// - `param_env` --- parameter environment for trait lookups (esp. pertaining to `Copy`)
|
||||
/// - `region_scope_tree` --- region scope tree for the code being analyzed
|
||||
/// - `tables` --- typeck results for the code being analyzed
|
||||
///
|
||||
/// See also `with_infer`, which is used *during* typeck.
|
||||
pub fn new(
|
||||
delegate: &'a mut (dyn Delegate<'tcx> + 'a),
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body_owner: DefId,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
region_scope_tree: &'a region::ScopeTree,
|
||||
tables: &'a ty::TypeckTables<'tcx>,
|
||||
) -> Self {
|
||||
ExprUseVisitor {
|
||||
mc: mc::MemCategorizationContext::new(tcx,
|
||||
param_env,
|
||||
body_owner,
|
||||
region_scope_tree,
|
||||
tables),
|
||||
delegate,
|
||||
param_env,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
||||
pub fn with_infer(
|
||||
delegate: &'a mut (dyn Delegate<'tcx> + 'a),
|
||||
infcx: &'a InferCtxt<'a, 'tcx>,
|
||||
body_owner: DefId,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
region_scope_tree: &'a region::ScopeTree,
|
||||
tables: &'a ty::TypeckTables<'tcx>,
|
||||
) -> Self {
|
||||
ExprUseVisitor {
|
||||
mc: mc::MemCategorizationContext::with_infer(
|
||||
mc: mc::MemCategorizationContext::new(
|
||||
infcx,
|
||||
param_env,
|
||||
body_owner,
|
||||
region_scope_tree,
|
||||
tables,
|
||||
),
|
||||
delegate,
|
||||
param_env,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -165,26 +138,23 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
|||
let param_ty = return_if_err!(self.mc.pat_ty_adjusted(¶m.pat));
|
||||
debug!("consume_body: param_ty = {:?}", param_ty);
|
||||
|
||||
let param_cmt = Rc::new(self.mc.cat_rvalue(
|
||||
param.hir_id,
|
||||
param.pat.span,
|
||||
param_ty));
|
||||
let param_place = self.mc.cat_rvalue(param.hir_id, param.pat.span, param_ty);
|
||||
|
||||
self.walk_irrefutable_pat(param_cmt, ¶m.pat);
|
||||
self.walk_irrefutable_pat(¶m_place, ¶m.pat);
|
||||
}
|
||||
|
||||
self.consume_expr(&body.value);
|
||||
}
|
||||
|
||||
fn tcx(&self) -> TyCtxt<'tcx> {
|
||||
self.mc.tcx
|
||||
self.mc.tcx()
|
||||
}
|
||||
|
||||
fn delegate_consume(&mut self, cmt: &mc::cmt_<'tcx>) {
|
||||
debug!("delegate_consume(cmt={:?})", cmt);
|
||||
fn delegate_consume(&mut self, place: &Place<'tcx>) {
|
||||
debug!("delegate_consume(place={:?})", place);
|
||||
|
||||
let mode = copy_or_move(&self.mc, self.param_env, cmt);
|
||||
self.delegate.consume(cmt, mode);
|
||||
let mode = copy_or_move(&self.mc, place);
|
||||
self.delegate.consume(place, mode);
|
||||
}
|
||||
|
||||
fn consume_exprs(&mut self, exprs: &[hir::Expr]) {
|
||||
|
|
@ -196,22 +166,22 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
|||
pub fn consume_expr(&mut self, expr: &hir::Expr) {
|
||||
debug!("consume_expr(expr={:?})", expr);
|
||||
|
||||
let cmt = return_if_err!(self.mc.cat_expr(expr));
|
||||
self.delegate_consume(&cmt);
|
||||
let place = return_if_err!(self.mc.cat_expr(expr));
|
||||
self.delegate_consume(&place);
|
||||
self.walk_expr(expr);
|
||||
}
|
||||
|
||||
fn mutate_expr(&mut self, expr: &hir::Expr) {
|
||||
let cmt = return_if_err!(self.mc.cat_expr(expr));
|
||||
self.delegate.mutate(&cmt);
|
||||
let place = return_if_err!(self.mc.cat_expr(expr));
|
||||
self.delegate.mutate(&place);
|
||||
self.walk_expr(expr);
|
||||
}
|
||||
|
||||
fn borrow_expr(&mut self, expr: &hir::Expr, bk: ty::BorrowKind) {
|
||||
debug!("borrow_expr(expr={:?}, bk={:?})", expr, bk);
|
||||
|
||||
let cmt = return_if_err!(self.mc.cat_expr(expr));
|
||||
self.delegate.borrow(&cmt, bk);
|
||||
let place = return_if_err!(self.mc.cat_expr(expr));
|
||||
self.delegate.borrow(&place, bk);
|
||||
|
||||
self.walk_expr(expr)
|
||||
}
|
||||
|
|
@ -263,12 +233,12 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
|||
}
|
||||
|
||||
hir::ExprKind::Match(ref discr, ref arms, _) => {
|
||||
let discr_cmt = Rc::new(return_if_err!(self.mc.cat_expr(&discr)));
|
||||
let discr_place = return_if_err!(self.mc.cat_expr(&discr));
|
||||
self.borrow_expr(&discr, ty::ImmBorrow);
|
||||
|
||||
// treatment of the discriminant is handled while walking the arms.
|
||||
for arm in arms {
|
||||
self.walk_arm(discr_cmt.clone(), arm);
|
||||
self.walk_arm(&discr_place, arm);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -414,8 +384,8 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
|||
// "assigns", which is handled by
|
||||
// `walk_pat`:
|
||||
self.walk_expr(&expr);
|
||||
let init_cmt = Rc::new(return_if_err!(self.mc.cat_expr(&expr)));
|
||||
self.walk_irrefutable_pat(init_cmt, &local.pat);
|
||||
let init_place = return_if_err!(self.mc.cat_expr(&expr));
|
||||
self.walk_irrefutable_pat(&init_place, &local.pat);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -446,11 +416,11 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
|||
None => { return; }
|
||||
};
|
||||
|
||||
let with_cmt = Rc::new(return_if_err!(self.mc.cat_expr(&with_expr)));
|
||||
let with_place = return_if_err!(self.mc.cat_expr(&with_expr));
|
||||
|
||||
// Select just those fields of the `with`
|
||||
// expression that will actually be used
|
||||
match with_cmt.ty.kind {
|
||||
match with_place.ty.kind {
|
||||
ty::Adt(adt, substs) if adt.is_struct() => {
|
||||
// Consume those fields of the with expression that are needed.
|
||||
for (f_index, with_field) in adt.non_enum_variant().fields.iter().enumerate() {
|
||||
|
|
@ -458,14 +428,12 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
|||
self.tcx().field_index(f.hir_id, self.mc.tables) == f_index
|
||||
});
|
||||
if !is_mentioned {
|
||||
let cmt_field = self.mc.cat_field(
|
||||
let field_place = self.mc.cat_projection(
|
||||
&*with_expr,
|
||||
with_cmt.clone(),
|
||||
f_index,
|
||||
with_field.ident,
|
||||
with_field.ty(self.tcx(), substs)
|
||||
with_place.clone(),
|
||||
with_field.ty(self.tcx(), substs),
|
||||
);
|
||||
self.delegate_consume(&cmt_field);
|
||||
self.delegate_consume(&field_place);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -492,7 +460,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
|||
// process.
|
||||
fn walk_adjustment(&mut self, expr: &hir::Expr) {
|
||||
let adjustments = self.mc.tables.expr_adjustments(expr);
|
||||
let mut cmt = return_if_err!(self.mc.cat_expr_unadjusted(expr));
|
||||
let mut place = return_if_err!(self.mc.cat_expr_unadjusted(expr));
|
||||
for adjustment in adjustments {
|
||||
debug!("walk_adjustment expr={:?} adj={:?}", expr, adjustment);
|
||||
match adjustment.kind {
|
||||
|
|
@ -500,7 +468,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
|||
adjustment::Adjust::Pointer(_) => {
|
||||
// Creating a closure/fn-pointer or unsizing consumes
|
||||
// the input and stores it into the resulting rvalue.
|
||||
self.delegate_consume(&cmt);
|
||||
self.delegate_consume(&place);
|
||||
}
|
||||
|
||||
adjustment::Adjust::Deref(None) => {}
|
||||
|
|
@ -512,47 +480,47 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
|||
// this is an autoref of `x`.
|
||||
adjustment::Adjust::Deref(Some(ref deref)) => {
|
||||
let bk = ty::BorrowKind::from_mutbl(deref.mutbl);
|
||||
self.delegate.borrow(&cmt, bk);
|
||||
self.delegate.borrow(&place, bk);
|
||||
}
|
||||
|
||||
adjustment::Adjust::Borrow(ref autoref) => {
|
||||
self.walk_autoref(expr, &cmt, autoref);
|
||||
self.walk_autoref(expr, &place, autoref);
|
||||
}
|
||||
}
|
||||
cmt = return_if_err!(self.mc.cat_expr_adjusted(expr, cmt, &adjustment));
|
||||
place = return_if_err!(self.mc.cat_expr_adjusted(expr, place, &adjustment));
|
||||
}
|
||||
}
|
||||
|
||||
/// Walks the autoref `autoref` applied to the autoderef'd
|
||||
/// `expr`. `cmt_base` is the mem-categorized form of `expr`
|
||||
/// `expr`. `base_place` is the mem-categorized form of `expr`
|
||||
/// after all relevant autoderefs have occurred.
|
||||
fn walk_autoref(&mut self,
|
||||
expr: &hir::Expr,
|
||||
cmt_base: &mc::cmt_<'tcx>,
|
||||
base_place: &mc::Place<'tcx>,
|
||||
autoref: &adjustment::AutoBorrow<'tcx>) {
|
||||
debug!("walk_autoref(expr.hir_id={} cmt_base={:?} autoref={:?})",
|
||||
debug!("walk_autoref(expr.hir_id={} base_place={:?} autoref={:?})",
|
||||
expr.hir_id,
|
||||
cmt_base,
|
||||
base_place,
|
||||
autoref);
|
||||
|
||||
match *autoref {
|
||||
adjustment::AutoBorrow::Ref(_, m) => {
|
||||
self.delegate.borrow(cmt_base, ty::BorrowKind::from_mutbl(m.into()));
|
||||
self.delegate.borrow(base_place, ty::BorrowKind::from_mutbl(m.into()));
|
||||
}
|
||||
|
||||
adjustment::AutoBorrow::RawPtr(m) => {
|
||||
debug!("walk_autoref: expr.hir_id={} cmt_base={:?}",
|
||||
debug!("walk_autoref: expr.hir_id={} base_place={:?}",
|
||||
expr.hir_id,
|
||||
cmt_base);
|
||||
base_place);
|
||||
|
||||
|
||||
self.delegate.borrow(cmt_base, ty::BorrowKind::from_mutbl(m));
|
||||
self.delegate.borrow(base_place, ty::BorrowKind::from_mutbl(m));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn walk_arm(&mut self, discr_cmt: mc::cmt<'tcx>, arm: &hir::Arm) {
|
||||
self.walk_pat(discr_cmt.clone(), &arm.pat);
|
||||
fn walk_arm(&mut self, discr_place: &Place<'tcx>, arm: &hir::Arm) {
|
||||
self.walk_pat(discr_place, &arm.pat);
|
||||
|
||||
if let Some(hir::Guard::If(ref e)) = arm.guard {
|
||||
self.consume_expr(e)
|
||||
|
|
@ -563,22 +531,22 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
|||
|
||||
/// Walks a pat that occurs in isolation (i.e., top-level of fn argument or
|
||||
/// let binding, and *not* a match arm or nested pat.)
|
||||
fn walk_irrefutable_pat(&mut self, cmt_discr: mc::cmt<'tcx>, pat: &hir::Pat) {
|
||||
self.walk_pat(cmt_discr, pat);
|
||||
fn walk_irrefutable_pat(&mut self, discr_place: &Place<'tcx>, pat: &hir::Pat) {
|
||||
self.walk_pat(discr_place, pat);
|
||||
}
|
||||
|
||||
|
||||
/// The core driver for walking a pattern
|
||||
fn walk_pat(&mut self, cmt_discr: mc::cmt<'tcx>, pat: &hir::Pat) {
|
||||
debug!("walk_pat(cmt_discr={:?}, pat={:?})", cmt_discr, pat);
|
||||
fn walk_pat(&mut self, discr_place: &Place<'tcx>, pat: &hir::Pat) {
|
||||
debug!("walk_pat(discr_place={:?}, pat={:?})", discr_place, pat);
|
||||
|
||||
let tcx = self.tcx();
|
||||
let ExprUseVisitor { ref mc, ref mut delegate, param_env } = *self;
|
||||
return_if_err!(mc.cat_pattern(cmt_discr.clone(), pat, |cmt_pat, pat| {
|
||||
let ExprUseVisitor { ref mc, ref mut delegate } = *self;
|
||||
return_if_err!(mc.cat_pattern(discr_place.clone(), pat, |place, pat| {
|
||||
if let PatKind::Binding(_, canonical_id, ..) = pat.kind {
|
||||
debug!(
|
||||
"walk_pat: binding cmt_pat={:?} pat={:?}",
|
||||
cmt_pat,
|
||||
"walk_pat: binding place={:?} pat={:?}",
|
||||
place,
|
||||
pat,
|
||||
);
|
||||
if let Some(&bm) = mc.tables.pat_binding_modes().get(pat.hir_id) {
|
||||
|
|
@ -591,20 +559,20 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
|||
// Each match binding is effectively an assignment to the
|
||||
// binding being produced.
|
||||
let def = Res::Local(canonical_id);
|
||||
if let Ok(ref binding_cmt) = mc.cat_res(pat.hir_id, pat.span, pat_ty, def) {
|
||||
delegate.mutate(binding_cmt);
|
||||
if let Ok(ref binding_place) = mc.cat_res(pat.hir_id, pat.span, pat_ty, def) {
|
||||
delegate.mutate(binding_place);
|
||||
}
|
||||
|
||||
// It is also a borrow or copy/move of the value being matched.
|
||||
match bm {
|
||||
ty::BindByReference(m) => {
|
||||
let bk = ty::BorrowKind::from_mutbl(m);
|
||||
delegate.borrow(&cmt_pat, bk);
|
||||
delegate.borrow(place, bk);
|
||||
}
|
||||
ty::BindByValue(..) => {
|
||||
let mode = copy_or_move(mc, param_env, &cmt_pat);
|
||||
let mode = copy_or_move(mc, place);
|
||||
debug!("walk_pat binding consuming pat");
|
||||
delegate.consume(&cmt_pat, mode);
|
||||
delegate.consume(place, mode);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -625,16 +593,18 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
|||
closure_expr_id: closure_def_id.to_local(),
|
||||
};
|
||||
let upvar_capture = self.mc.tables.upvar_capture(upvar_id);
|
||||
let cmt_var = return_if_err!(self.cat_captured_var(closure_expr.hir_id,
|
||||
fn_decl_span,
|
||||
var_id));
|
||||
let captured_place = return_if_err!(self.cat_captured_var(
|
||||
closure_expr.hir_id,
|
||||
fn_decl_span,
|
||||
var_id,
|
||||
));
|
||||
match upvar_capture {
|
||||
ty::UpvarCapture::ByValue => {
|
||||
let mode = copy_or_move(&self.mc, self.param_env, &cmt_var);
|
||||
self.delegate.consume(&cmt_var, mode);
|
||||
let mode = copy_or_move(&self.mc, &captured_place);
|
||||
self.delegate.consume(&captured_place, mode);
|
||||
}
|
||||
ty::UpvarCapture::ByRef(upvar_borrow) => {
|
||||
self.delegate.borrow(&cmt_var, upvar_borrow.kind);
|
||||
self.delegate.borrow(&captured_place, upvar_borrow.kind);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -645,8 +615,8 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
|||
closure_hir_id: hir::HirId,
|
||||
closure_span: Span,
|
||||
var_id: hir::HirId)
|
||||
-> mc::McResult<mc::cmt_<'tcx>> {
|
||||
// Create the cmt for the variable being borrowed, from the
|
||||
-> mc::McResult<mc::Place<'tcx>> {
|
||||
// Create the place for the variable being borrowed, from the
|
||||
// perspective of the creator (parent) of the closure.
|
||||
let var_ty = self.mc.node_ty(var_id)?;
|
||||
self.mc.cat_res(closure_hir_id, closure_span, var_ty, Res::Local(var_id))
|
||||
|
|
@ -655,10 +625,9 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
|||
|
||||
fn copy_or_move<'a, 'tcx>(
|
||||
mc: &mc::MemCategorizationContext<'a, 'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
cmt: &mc::cmt_<'tcx>,
|
||||
place: &Place<'tcx>,
|
||||
) -> ConsumeMode {
|
||||
if !mc.type_is_copy_modulo_regions(param_env, cmt.ty, cmt.span) {
|
||||
if !mc.type_is_copy_modulo_regions(place.ty, place.span) {
|
||||
Move
|
||||
} else {
|
||||
Copy
|
||||
|
|
@ -75,6 +75,9 @@ This API is completely unstable and subject to change.
|
|||
|
||||
#[macro_use] extern crate rustc;
|
||||
|
||||
// This is used by Clippy.
|
||||
pub mod expr_use_visitor;
|
||||
|
||||
mod astconv;
|
||||
mod check;
|
||||
mod check_unused;
|
||||
|
|
@ -83,6 +86,7 @@ mod collect;
|
|||
mod constrained_generic_params;
|
||||
mod structured_errors;
|
||||
mod impl_wf_check;
|
||||
mod mem_categorization;
|
||||
mod namespace;
|
||||
mod outlives;
|
||||
mod variance;
|
||||
|
|
|
|||
707
src/librustc_typeck/mem_categorization.rs
Normal file
707
src/librustc_typeck/mem_categorization.rs
Normal file
|
|
@ -0,0 +1,707 @@
|
|||
//! # Categorization
|
||||
//!
|
||||
//! The job of the categorization module is to analyze an expression to
|
||||
//! determine what kind of memory is used in evaluating it (for example,
|
||||
//! where dereferences occur and what kind of pointer is dereferenced;
|
||||
//! whether the memory is mutable, etc.).
|
||||
//!
|
||||
//! Categorization effectively transforms all of our expressions into
|
||||
//! expressions of the following forms (the actual enum has many more
|
||||
//! possibilities, naturally, but they are all variants of these base
|
||||
//! forms):
|
||||
//!
|
||||
//! E = rvalue // some computed rvalue
|
||||
//! | x // address of a local variable or argument
|
||||
//! | *E // deref of a ptr
|
||||
//! | E.comp // access to an interior component
|
||||
//!
|
||||
//! Imagine a routine ToAddr(Expr) that evaluates an expression and returns an
|
||||
//! address where the result is to be found. If Expr is a place, then this
|
||||
//! is the address of the place. If `Expr` is an rvalue, this is the address of
|
||||
//! some temporary spot in memory where the result is stored.
|
||||
//!
|
||||
//! Now, `cat_expr()` classifies the expression `Expr` and the address `A = ToAddr(Expr)`
|
||||
//! as follows:
|
||||
//!
|
||||
//! - `cat`: what kind of expression was this? This is a subset of the
|
||||
//! full expression forms which only includes those that we care about
|
||||
//! for the purpose of the analysis.
|
||||
//! - `mutbl`: mutability of the address `A`.
|
||||
//! - `ty`: the type of data found at the address `A`.
|
||||
//!
|
||||
//! The resulting categorization tree differs somewhat from the expressions
|
||||
//! themselves. For example, auto-derefs are explicit. Also, an index a[b] is
|
||||
//! decomposed into two operations: a dereference to reach the array data and
|
||||
//! then an index to jump forward to the relevant item.
|
||||
//!
|
||||
//! ## By-reference upvars
|
||||
//!
|
||||
//! One part of the codegen which may be non-obvious is that we translate
|
||||
//! closure upvars into the dereference of a borrowed pointer; this more closely
|
||||
//! resembles the runtime codegen. So, for example, if we had:
|
||||
//!
|
||||
//! let mut x = 3;
|
||||
//! let y = 5;
|
||||
//! let inc = || x += y;
|
||||
//!
|
||||
//! Then when we categorize `x` (*within* the closure) we would yield a
|
||||
//! result of `*x'`, effectively, where `x'` is a `Categorization::Upvar` reference
|
||||
//! tied to `x`. The type of `x'` will be a borrowed pointer.
|
||||
|
||||
use rustc::hir;
|
||||
use rustc::hir::PatKind;
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::hir::def::{Res, DefKind};
|
||||
use rustc::infer::InferCtxt;
|
||||
use rustc::ty::adjustment;
|
||||
use rustc::ty::{self, Ty, TyCtxt};
|
||||
use rustc::ty::fold::TypeFoldable;
|
||||
|
||||
use syntax_pos::Span;
|
||||
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum PlaceBase {
|
||||
/// A temporary variable
|
||||
Rvalue,
|
||||
/// A named `static` item
|
||||
StaticItem,
|
||||
/// A named local variable
|
||||
Local(hir::HirId),
|
||||
/// An upvar referenced by closure env
|
||||
Upvar(ty::UpvarId),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Projection<'tcx> {
|
||||
/// A dereference of a pointer, reference or `Box<T>` of the given type
|
||||
Deref(Ty<'tcx>),
|
||||
/// An index or a field
|
||||
Other,
|
||||
}
|
||||
|
||||
/// A `Place` represents how a value is located in memory.
|
||||
///
|
||||
/// This is an HIR version of `mir::Place`
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Place<'tcx> {
|
||||
/// `HirId` of the expression or pattern producing this value.
|
||||
pub hir_id: hir::HirId,
|
||||
/// The `Span` of the expression or pattern producing this value.
|
||||
pub span: Span,
|
||||
/// The type of the `Place`
|
||||
pub ty: Ty<'tcx>,
|
||||
/// The "outermost" place that holds this value.
|
||||
pub base: PlaceBase,
|
||||
/// How this place is derived from the base place.
|
||||
pub projections: Vec<Projection<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'tcx> Place<'tcx> {
|
||||
/// Returns an iterator of the types that have to be dereferenced to access
|
||||
/// the `Place`.
|
||||
///
|
||||
/// The types are in the reverse order that they are applied. So if
|
||||
/// `x: &*const u32` and the `Place` is `**x`, then the types returned are
|
||||
///`*const u32` then `&*const u32`.
|
||||
crate fn deref_tys(&self) -> impl Iterator<Item=Ty<'tcx>> + '_ {
|
||||
self.projections.iter().rev().filter_map(|proj| if let Projection::Deref(deref_ty) = *proj {
|
||||
Some(deref_ty)
|
||||
} else {
|
||||
None
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
crate trait HirNode {
|
||||
fn hir_id(&self) -> hir::HirId;
|
||||
fn span(&self) -> Span;
|
||||
}
|
||||
|
||||
impl HirNode for hir::Expr {
|
||||
fn hir_id(&self) -> hir::HirId { self.hir_id }
|
||||
fn span(&self) -> Span { self.span }
|
||||
}
|
||||
|
||||
impl HirNode for hir::Pat {
|
||||
fn hir_id(&self) -> hir::HirId { self.hir_id }
|
||||
fn span(&self) -> Span { self.span }
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
crate struct MemCategorizationContext<'a, 'tcx> {
|
||||
crate tables: &'a ty::TypeckTables<'tcx>,
|
||||
infcx: &'a InferCtxt<'a, 'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
body_owner: DefId,
|
||||
upvars: Option<&'tcx FxIndexMap<hir::HirId, hir::Upvar>>,
|
||||
}
|
||||
|
||||
crate type McResult<T> = Result<T, ()>;
|
||||
|
||||
impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
|
||||
/// Creates a `MemCategorizationContext`.
|
||||
crate fn new(
|
||||
infcx: &'a InferCtxt<'a, 'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
body_owner: DefId,
|
||||
tables: &'a ty::TypeckTables<'tcx>,
|
||||
) -> MemCategorizationContext<'a, 'tcx> {
|
||||
MemCategorizationContext {
|
||||
tables,
|
||||
infcx,
|
||||
param_env,
|
||||
body_owner,
|
||||
upvars: infcx.tcx.upvars(body_owner),
|
||||
}
|
||||
}
|
||||
|
||||
crate fn tcx(&self) -> TyCtxt<'tcx> {
|
||||
self.infcx.tcx
|
||||
}
|
||||
|
||||
crate fn type_is_copy_modulo_regions(
|
||||
&self,
|
||||
ty: Ty<'tcx>,
|
||||
span: Span,
|
||||
) -> bool {
|
||||
self.infcx.type_is_copy_modulo_regions(self.param_env, ty, span)
|
||||
}
|
||||
|
||||
fn resolve_vars_if_possible<T>(&self, value: &T) -> T
|
||||
where T: TypeFoldable<'tcx>
|
||||
{
|
||||
self.infcx.resolve_vars_if_possible(value)
|
||||
}
|
||||
|
||||
fn is_tainted_by_errors(&self) -> bool {
|
||||
self.infcx.is_tainted_by_errors()
|
||||
}
|
||||
|
||||
fn resolve_type_vars_or_error(&self,
|
||||
id: hir::HirId,
|
||||
ty: Option<Ty<'tcx>>)
|
||||
-> McResult<Ty<'tcx>> {
|
||||
match ty {
|
||||
Some(ty) => {
|
||||
let ty = self.resolve_vars_if_possible(&ty);
|
||||
if ty.references_error() || ty.is_ty_var() {
|
||||
debug!("resolve_type_vars_or_error: error from {:?}", ty);
|
||||
Err(())
|
||||
} else {
|
||||
Ok(ty)
|
||||
}
|
||||
}
|
||||
// FIXME
|
||||
None if self.is_tainted_by_errors() => Err(()),
|
||||
None => {
|
||||
bug!("no type for node {}: {} in mem_categorization",
|
||||
id, self.tcx().hir().node_to_string(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
crate fn node_ty(&self, hir_id: hir::HirId) -> McResult<Ty<'tcx>> {
|
||||
self.resolve_type_vars_or_error(hir_id, self.tables.node_type_opt(hir_id))
|
||||
}
|
||||
|
||||
fn expr_ty(&self, expr: &hir::Expr) -> McResult<Ty<'tcx>> {
|
||||
self.resolve_type_vars_or_error(expr.hir_id, self.tables.expr_ty_opt(expr))
|
||||
}
|
||||
|
||||
crate fn expr_ty_adjusted(&self, expr: &hir::Expr) -> McResult<Ty<'tcx>> {
|
||||
self.resolve_type_vars_or_error(expr.hir_id, self.tables.expr_ty_adjusted_opt(expr))
|
||||
}
|
||||
|
||||
/// Returns the type of value that this pattern matches against.
|
||||
/// Some non-obvious cases:
|
||||
///
|
||||
/// - a `ref x` binding matches against a value of type `T` and gives
|
||||
/// `x` the type `&T`; we return `T`.
|
||||
/// - a pattern with implicit derefs (thanks to default binding
|
||||
/// modes #42640) may look like `Some(x)` but in fact have
|
||||
/// implicit deref patterns attached (e.g., it is really
|
||||
/// `&Some(x)`). In that case, we return the "outermost" type
|
||||
/// (e.g., `&Option<T>).
|
||||
crate fn pat_ty_adjusted(&self, pat: &hir::Pat) -> McResult<Ty<'tcx>> {
|
||||
// Check for implicit `&` types wrapping the pattern; note
|
||||
// that these are never attached to binding patterns, so
|
||||
// actually this is somewhat "disjoint" from the code below
|
||||
// that aims to account for `ref x`.
|
||||
if let Some(vec) = self.tables.pat_adjustments().get(pat.hir_id) {
|
||||
if let Some(first_ty) = vec.first() {
|
||||
debug!("pat_ty(pat={:?}) found adjusted ty `{:?}`", pat, first_ty);
|
||||
return Ok(first_ty);
|
||||
}
|
||||
}
|
||||
|
||||
self.pat_ty_unadjusted(pat)
|
||||
}
|
||||
|
||||
|
||||
/// Like `pat_ty`, but ignores implicit `&` patterns.
|
||||
fn pat_ty_unadjusted(&self, pat: &hir::Pat) -> McResult<Ty<'tcx>> {
|
||||
let base_ty = self.node_ty(pat.hir_id)?;
|
||||
debug!("pat_ty(pat={:?}) base_ty={:?}", pat, base_ty);
|
||||
|
||||
// This code detects whether we are looking at a `ref x`,
|
||||
// and if so, figures out what the type *being borrowed* is.
|
||||
let ret_ty = match pat.kind {
|
||||
PatKind::Binding(..) => {
|
||||
let bm = *self.tables
|
||||
.pat_binding_modes()
|
||||
.get(pat.hir_id)
|
||||
.expect("missing binding mode");
|
||||
|
||||
if let ty::BindByReference(_) = bm {
|
||||
// a bind-by-ref means that the base_ty will be the type of the ident itself,
|
||||
// but what we want here is the type of the underlying value being borrowed.
|
||||
// So peel off one-level, turning the &T into T.
|
||||
match base_ty.builtin_deref(false) {
|
||||
Some(t) => t.ty,
|
||||
None => {
|
||||
debug!("By-ref binding of non-derefable type {:?}", base_ty);
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
base_ty
|
||||
}
|
||||
}
|
||||
_ => base_ty,
|
||||
};
|
||||
debug!("pat_ty(pat={:?}) ret_ty={:?}", pat, ret_ty);
|
||||
|
||||
Ok(ret_ty)
|
||||
}
|
||||
|
||||
crate fn cat_expr(&self, expr: &hir::Expr) -> McResult<Place<'tcx>> {
|
||||
// This recursion helper avoids going through *too many*
|
||||
// adjustments, since *only* non-overloaded deref recurses.
|
||||
fn helper<'a, 'tcx>(
|
||||
mc: &MemCategorizationContext<'a, 'tcx>,
|
||||
expr: &hir::Expr,
|
||||
adjustments: &[adjustment::Adjustment<'tcx>],
|
||||
) -> McResult<Place<'tcx>> {
|
||||
match adjustments.split_last() {
|
||||
None => mc.cat_expr_unadjusted(expr),
|
||||
Some((adjustment, previous)) => {
|
||||
mc.cat_expr_adjusted_with(expr, || helper(mc, expr, previous), adjustment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
helper(self, expr, self.tables.expr_adjustments(expr))
|
||||
}
|
||||
|
||||
crate fn cat_expr_adjusted(&self, expr: &hir::Expr,
|
||||
previous: Place<'tcx>,
|
||||
adjustment: &adjustment::Adjustment<'tcx>)
|
||||
-> McResult<Place<'tcx>> {
|
||||
self.cat_expr_adjusted_with(expr, || Ok(previous), adjustment)
|
||||
}
|
||||
|
||||
fn cat_expr_adjusted_with<F>(&self, expr: &hir::Expr,
|
||||
previous: F,
|
||||
adjustment: &adjustment::Adjustment<'tcx>)
|
||||
-> McResult<Place<'tcx>>
|
||||
where F: FnOnce() -> McResult<Place<'tcx>>
|
||||
{
|
||||
debug!("cat_expr_adjusted_with({:?}): {:?}", adjustment, expr);
|
||||
let target = self.resolve_vars_if_possible(&adjustment.target);
|
||||
match adjustment.kind {
|
||||
adjustment::Adjust::Deref(overloaded) => {
|
||||
// Equivalent to *expr or something similar.
|
||||
let base = if let Some(deref) = overloaded {
|
||||
let ref_ty = self.tcx().mk_ref(deref.region, ty::TypeAndMut {
|
||||
ty: target,
|
||||
mutbl: deref.mutbl,
|
||||
});
|
||||
self.cat_rvalue(expr.hir_id, expr.span, ref_ty)
|
||||
} else {
|
||||
previous()?
|
||||
};
|
||||
self.cat_deref(expr, base)
|
||||
}
|
||||
|
||||
adjustment::Adjust::NeverToAny |
|
||||
adjustment::Adjust::Pointer(_) |
|
||||
adjustment::Adjust::Borrow(_) => {
|
||||
// Result is an rvalue.
|
||||
Ok(self.cat_rvalue(expr.hir_id, expr.span, target))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
crate fn cat_expr_unadjusted(&self, expr: &hir::Expr) -> McResult<Place<'tcx>> {
|
||||
debug!("cat_expr: id={} expr={:?}", expr.hir_id, expr);
|
||||
|
||||
let expr_ty = self.expr_ty(expr)?;
|
||||
match expr.kind {
|
||||
hir::ExprKind::Unary(hir::UnDeref, ref e_base) => {
|
||||
if self.tables.is_method_call(expr) {
|
||||
self.cat_overloaded_place(expr, e_base)
|
||||
} else {
|
||||
let base = self.cat_expr(&e_base)?;
|
||||
self.cat_deref(expr, base)
|
||||
}
|
||||
}
|
||||
|
||||
hir::ExprKind::Field(ref base, _) => {
|
||||
let base = self.cat_expr(&base)?;
|
||||
debug!("cat_expr(cat_field): id={} expr={:?} base={:?}",
|
||||
expr.hir_id,
|
||||
expr,
|
||||
base);
|
||||
Ok(self.cat_projection(expr, base, expr_ty))
|
||||
}
|
||||
|
||||
hir::ExprKind::Index(ref base, _) => {
|
||||
if self.tables.is_method_call(expr) {
|
||||
// If this is an index implemented by a method call, then it
|
||||
// will include an implicit deref of the result.
|
||||
// The call to index() returns a `&T` value, which
|
||||
// is an rvalue. That is what we will be
|
||||
// dereferencing.
|
||||
self.cat_overloaded_place(expr, base)
|
||||
} else {
|
||||
let base = self.cat_expr(&base)?;
|
||||
Ok(self.cat_projection(expr, base, expr_ty))
|
||||
}
|
||||
}
|
||||
|
||||
hir::ExprKind::Path(ref qpath) => {
|
||||
let res = self.tables.qpath_res(qpath, expr.hir_id);
|
||||
self.cat_res(expr.hir_id, expr.span, expr_ty, res)
|
||||
}
|
||||
|
||||
hir::ExprKind::Type(ref e, _) => {
|
||||
self.cat_expr(&e)
|
||||
}
|
||||
|
||||
hir::ExprKind::AddrOf(..) | hir::ExprKind::Call(..) |
|
||||
hir::ExprKind::Assign(..) | hir::ExprKind::AssignOp(..) |
|
||||
hir::ExprKind::Closure(..) | hir::ExprKind::Ret(..) |
|
||||
hir::ExprKind::Unary(..) | hir::ExprKind::Yield(..) |
|
||||
hir::ExprKind::MethodCall(..) | hir::ExprKind::Cast(..) | hir::ExprKind::DropTemps(..) |
|
||||
hir::ExprKind::Array(..) | hir::ExprKind::Tup(..) |
|
||||
hir::ExprKind::Binary(..) |
|
||||
hir::ExprKind::Block(..) | hir::ExprKind::Loop(..) | hir::ExprKind::Match(..) |
|
||||
hir::ExprKind::Lit(..) | hir::ExprKind::Break(..) |
|
||||
hir::ExprKind::Continue(..) | hir::ExprKind::Struct(..) | hir::ExprKind::Repeat(..) |
|
||||
hir::ExprKind::InlineAsm(..) | hir::ExprKind::Box(..) | hir::ExprKind::Err => {
|
||||
Ok(self.cat_rvalue(expr.hir_id, expr.span, expr_ty))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
crate fn cat_res(&self,
|
||||
hir_id: hir::HirId,
|
||||
span: Span,
|
||||
expr_ty: Ty<'tcx>,
|
||||
res: Res)
|
||||
-> McResult<Place<'tcx>> {
|
||||
debug!("cat_res: id={:?} expr={:?} def={:?}",
|
||||
hir_id, expr_ty, res);
|
||||
|
||||
match res {
|
||||
Res::Def(DefKind::Ctor(..), _)
|
||||
| Res::Def(DefKind::Const, _)
|
||||
| Res::Def(DefKind::ConstParam, _)
|
||||
| Res::Def(DefKind::AssocConst, _)
|
||||
| Res::Def(DefKind::Fn, _)
|
||||
| Res::Def(DefKind::Method, _)
|
||||
| Res::SelfCtor(..) => {
|
||||
Ok(self.cat_rvalue(hir_id, span, expr_ty))
|
||||
}
|
||||
|
||||
Res::Def(DefKind::Static, _) => {
|
||||
Ok(Place {
|
||||
hir_id,
|
||||
span,
|
||||
ty: expr_ty,
|
||||
base: PlaceBase::StaticItem,
|
||||
projections: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
Res::Local(var_id) => {
|
||||
if self.upvars.map_or(false, |upvars| upvars.contains_key(&var_id)) {
|
||||
self.cat_upvar(hir_id, span, var_id)
|
||||
} else {
|
||||
Ok(Place {
|
||||
hir_id,
|
||||
span,
|
||||
ty: expr_ty,
|
||||
base: PlaceBase::Local(var_id),
|
||||
projections: Vec::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
def => span_bug!(span, "unexpected definition in memory categorization: {:?}", def)
|
||||
}
|
||||
}
|
||||
|
||||
/// Categorize an upvar.
|
||||
///
|
||||
/// Note: the actual upvar access contains invisible derefs of closure
|
||||
/// environment and upvar reference as appropriate. Only regionck cares
|
||||
/// about these dereferences, so we let it compute them as needed.
|
||||
fn cat_upvar(
|
||||
&self,
|
||||
hir_id: hir::HirId,
|
||||
span: Span,
|
||||
var_id: hir::HirId,
|
||||
) -> McResult<Place<'tcx>> {
|
||||
let closure_expr_def_id = self.body_owner;
|
||||
|
||||
let upvar_id = ty::UpvarId {
|
||||
var_path: ty::UpvarPath { hir_id: var_id },
|
||||
closure_expr_id: closure_expr_def_id.to_local(),
|
||||
};
|
||||
let var_ty = self.node_ty(var_id)?;
|
||||
|
||||
let ret = Place {
|
||||
hir_id,
|
||||
span,
|
||||
ty: var_ty,
|
||||
base: PlaceBase::Upvar(upvar_id),
|
||||
projections: Vec::new(),
|
||||
};
|
||||
|
||||
debug!("cat_upvar ret={:?}", ret);
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
crate fn cat_rvalue(&self, hir_id: hir::HirId, span: Span, expr_ty: Ty<'tcx>) -> Place<'tcx> {
|
||||
debug!("cat_rvalue hir_id={:?}, expr_ty={:?}, span={:?}", hir_id, expr_ty, span);
|
||||
let ret = Place {
|
||||
hir_id,
|
||||
span,
|
||||
base: PlaceBase::Rvalue,
|
||||
projections: Vec::new(),
|
||||
ty: expr_ty,
|
||||
};
|
||||
debug!("cat_rvalue ret={:?}", ret);
|
||||
ret
|
||||
}
|
||||
|
||||
crate fn cat_projection<N: HirNode>(
|
||||
&self,
|
||||
node: &N,
|
||||
base_place: Place<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> Place<'tcx> {
|
||||
let mut projections = base_place.projections;
|
||||
projections.push(Projection::Other);
|
||||
let ret = Place {
|
||||
hir_id: node.hir_id(),
|
||||
span: node.span(),
|
||||
ty,
|
||||
base: base_place.base,
|
||||
projections,
|
||||
};
|
||||
debug!("cat_field ret {:?}", ret);
|
||||
ret
|
||||
}
|
||||
|
||||
fn cat_overloaded_place(
|
||||
&self,
|
||||
expr: &hir::Expr,
|
||||
base: &hir::Expr,
|
||||
) -> McResult<Place<'tcx>> {
|
||||
debug!("cat_overloaded_place(expr={:?}, base={:?})", expr, base);
|
||||
|
||||
// Reconstruct the output assuming it's a reference with the
|
||||
// same region and mutability as the receiver. This holds for
|
||||
// `Deref(Mut)::Deref(_mut)` and `Index(Mut)::index(_mut)`.
|
||||
let place_ty = self.expr_ty(expr)?;
|
||||
let base_ty = self.expr_ty_adjusted(base)?;
|
||||
|
||||
let (region, mutbl) = match base_ty.kind {
|
||||
ty::Ref(region, _, mutbl) => (region, mutbl),
|
||||
_ => span_bug!(expr.span, "cat_overloaded_place: base is not a reference")
|
||||
};
|
||||
let ref_ty = self.tcx().mk_ref(region, ty::TypeAndMut {
|
||||
ty: place_ty,
|
||||
mutbl,
|
||||
});
|
||||
|
||||
let base = self.cat_rvalue(expr.hir_id, expr.span, ref_ty);
|
||||
self.cat_deref(expr, base)
|
||||
}
|
||||
|
||||
fn cat_deref(
|
||||
&self,
|
||||
node: &impl HirNode,
|
||||
base_place: Place<'tcx>,
|
||||
) -> McResult<Place<'tcx>> {
|
||||
debug!("cat_deref: base_place={:?}", base_place);
|
||||
|
||||
let base_ty = base_place.ty;
|
||||
let deref_ty = match base_ty.builtin_deref(true) {
|
||||
Some(mt) => mt.ty,
|
||||
None => {
|
||||
debug!("explicit deref of non-derefable type: {:?}", base_ty);
|
||||
return Err(());
|
||||
}
|
||||
};
|
||||
let mut projections = base_place.projections;
|
||||
projections.push(Projection::Deref(base_ty));
|
||||
|
||||
let ret = Place {
|
||||
hir_id: node.hir_id(),
|
||||
span: node.span(),
|
||||
ty: deref_ty,
|
||||
base: base_place.base,
|
||||
projections,
|
||||
};
|
||||
debug!("cat_deref ret {:?}", ret);
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
crate fn cat_pattern<F>(&self, place: Place<'tcx>, pat: &hir::Pat, mut op: F) -> McResult<()>
|
||||
where F: FnMut(&Place<'tcx>, &hir::Pat),
|
||||
{
|
||||
self.cat_pattern_(place, pat, &mut op)
|
||||
}
|
||||
|
||||
// FIXME(#19596) This is a workaround, but there should be a better way to do this
|
||||
fn cat_pattern_<F>(&self, mut place: Place<'tcx>, pat: &hir::Pat, op: &mut F) -> McResult<()>
|
||||
where F: FnMut(&Place<'tcx>, &hir::Pat)
|
||||
{
|
||||
// Here, `place` is the `Place` being matched and pat is the pattern it
|
||||
// is being matched against.
|
||||
//
|
||||
// In general, the way that this works is that we walk down the pattern,
|
||||
// constructing a `Place` that represents the path that will be taken
|
||||
// to reach the value being matched.
|
||||
|
||||
debug!("cat_pattern(pat={:?}, place={:?})", pat, place);
|
||||
|
||||
// If (pattern) adjustments are active for this pattern, adjust the `Place` correspondingly.
|
||||
// `Place`s are constructed differently from patterns. For example, in
|
||||
//
|
||||
// ```
|
||||
// match foo {
|
||||
// &&Some(x, ) => { ... },
|
||||
// _ => { ... },
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// the pattern `&&Some(x,)` is represented as `Ref { Ref { TupleStruct }}`. To build the
|
||||
// corresponding `Place` we start with the `Place` for `foo`, and then, by traversing the
|
||||
// pattern, try to answer the question: given the address of `foo`, how is `x` reached?
|
||||
//
|
||||
// `&&Some(x,)` `place_foo`
|
||||
// `&Some(x,)` `deref { place_foo}`
|
||||
// `Some(x,)` `deref { deref { place_foo }}`
|
||||
// (x,)` `field0 { deref { deref { place_foo }}}` <- resulting place
|
||||
//
|
||||
// The above example has no adjustments. If the code were instead the (after adjustments,
|
||||
// equivalent) version
|
||||
//
|
||||
// ```
|
||||
// match foo {
|
||||
// Some(x, ) => { ... },
|
||||
// _ => { ... },
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// Then we see that to get the same result, we must start with
|
||||
// `deref { deref { place_foo }}` instead of `place_foo` since the pattern is now `Some(x,)`
|
||||
// and not `&&Some(x,)`, even though its assigned type is that of `&&Some(x,)`.
|
||||
for _ in 0..self.tables
|
||||
.pat_adjustments()
|
||||
.get(pat.hir_id)
|
||||
.map(|v| v.len())
|
||||
.unwrap_or(0)
|
||||
{
|
||||
debug!("cat_pattern: applying adjustment to place={:?}", place);
|
||||
place = self.cat_deref(pat, place)?;
|
||||
}
|
||||
let place = place; // lose mutability
|
||||
debug!("cat_pattern: applied adjustment derefs to get place={:?}", place);
|
||||
|
||||
// Invoke the callback, but only now, after the `place` has adjusted.
|
||||
//
|
||||
// To see that this makes sense, consider `match &Some(3) { Some(x) => { ... }}`. In that
|
||||
// case, the initial `place` will be that for `&Some(3)` and the pattern is `Some(x)`. We
|
||||
// don't want to call `op` with these incompatible values. As written, what happens instead
|
||||
// is that `op` is called with the adjusted place (that for `*&Some(3)`) and the pattern
|
||||
// `Some(x)` (which matches). Recursing once more, `*&Some(3)` and the pattern `Some(x)`
|
||||
// result in the place `Downcast<Some>(*&Some(3)).0` associated to `x` and invoke `op` with
|
||||
// that (where the `ref` on `x` is implied).
|
||||
op(&place, pat);
|
||||
|
||||
match pat.kind {
|
||||
PatKind::TupleStruct(_, ref subpats, _)
|
||||
| PatKind::Tuple(ref subpats, _) => {
|
||||
// S(p1, ..., pN) or (p1, ..., pN)
|
||||
for subpat in subpats.iter() {
|
||||
let subpat_ty = self.pat_ty_adjusted(&subpat)?;
|
||||
let sub_place = self.cat_projection(pat, place.clone(), subpat_ty);
|
||||
self.cat_pattern_(sub_place, &subpat, op)?;
|
||||
}
|
||||
}
|
||||
|
||||
PatKind::Struct(_, ref field_pats, _) => {
|
||||
// S { f1: p1, ..., fN: pN }
|
||||
for fp in field_pats {
|
||||
let field_ty = self.pat_ty_adjusted(&fp.pat)?;
|
||||
let field_place = self.cat_projection(pat, place.clone(), field_ty);
|
||||
self.cat_pattern_(field_place, &fp.pat, op)?;
|
||||
}
|
||||
}
|
||||
|
||||
PatKind::Or(ref pats) => {
|
||||
for pat in pats {
|
||||
self.cat_pattern_(place.clone(), &pat, op)?;
|
||||
}
|
||||
}
|
||||
|
||||
PatKind::Binding(.., Some(ref subpat)) => {
|
||||
self.cat_pattern_(place, &subpat, op)?;
|
||||
}
|
||||
|
||||
PatKind::Box(ref subpat) | PatKind::Ref(ref subpat, _) => {
|
||||
// box p1, &p1, &mut p1. we can ignore the mutability of
|
||||
// PatKind::Ref since that information is already contained
|
||||
// in the type.
|
||||
let subplace = self.cat_deref(pat, place)?;
|
||||
self.cat_pattern_(subplace, &subpat, op)?;
|
||||
}
|
||||
|
||||
PatKind::Slice(ref before, ref slice, ref after) => {
|
||||
let element_ty = match place.ty.builtin_index() {
|
||||
Some(ty) => ty,
|
||||
None => {
|
||||
debug!("explicit index of non-indexable type {:?}", place);
|
||||
return Err(());
|
||||
}
|
||||
};
|
||||
let elt_place = self.cat_projection(pat, place.clone(), element_ty);
|
||||
for before_pat in before {
|
||||
self.cat_pattern_(elt_place.clone(), &before_pat, op)?;
|
||||
}
|
||||
if let Some(ref slice_pat) = *slice {
|
||||
let slice_pat_ty = self.pat_ty_adjusted(&slice_pat)?;
|
||||
let slice_place = self.cat_projection(pat, place, slice_pat_ty);
|
||||
self.cat_pattern_(slice_place, &slice_pat, op)?;
|
||||
}
|
||||
for after_pat in after {
|
||||
self.cat_pattern_(elt_place.clone(), &after_pat, op)?;
|
||||
}
|
||||
}
|
||||
|
||||
PatKind::Path(_) | PatKind::Binding(.., None) |
|
||||
PatKind::Lit(..) | PatKind::Range(..) | PatKind::Wild => {
|
||||
// always ok
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -4,8 +4,7 @@ fn id<T>(t: T) -> T { t }
|
|||
|
||||
fn f<'r, T>(v: &'r T) -> Box<dyn FnMut() -> T + 'r> {
|
||||
id(Box::new(|| *v))
|
||||
//~^ ERROR E0373
|
||||
//~| ERROR E0507
|
||||
//~^ ERROR E0507
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
|
|
|||
|
|
@ -6,25 +6,6 @@ LL | fn f<'r, T>(v: &'r T) -> Box<dyn FnMut() -> T + 'r> {
|
|||
LL | id(Box::new(|| *v))
|
||||
| ^^ move occurs because `*v` has type `T`, which does not implement the `Copy` trait
|
||||
|
||||
error[E0373]: closure may outlive the current function, but it borrows `v`, which is owned by the current function
|
||||
--> $DIR/issue-4335.rs:6:17
|
||||
|
|
||||
LL | id(Box::new(|| *v))
|
||||
| ^^ - `v` is borrowed here
|
||||
| |
|
||||
| may outlive borrowed value `v`
|
||||
|
|
||||
note: closure is returned here
|
||||
--> $DIR/issue-4335.rs:6:5
|
||||
|
|
||||
LL | id(Box::new(|| *v))
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
help: to force the closure to take ownership of `v` (and any other referenced variables), use the `move` keyword
|
||||
|
|
||||
LL | id(Box::new(move || *v))
|
||||
| ^^^^^^^
|
||||
error: aborting due to previous error
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0373, E0507.
|
||||
For more information about an error, try `rustc --explain E0373`.
|
||||
For more information about this error, try `rustc --explain E0507`.
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ note: first, the lifetime cannot outlive the lifetime `'_` as defined on the bod
|
|||
|
|
||||
LL | let _f = || {
|
||||
| ^^
|
||||
note: ...so that reference does not outlive borrowed content
|
||||
note: ...so that closure can access `self`
|
||||
--> $DIR/regions-addr-of-upvar-self.rs:10:41
|
||||
|
|
||||
LL | let p: &'static mut usize = &mut self.food;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ error[E0525]: expected a closure that implements the `Fn` trait, but this closur
|
|||
--> $DIR/unboxed-closures-infer-fn-once-move-from-projection.rs:14:13
|
||||
|
|
||||
LL | let c = || drop(y.0);
|
||||
| ^^^^^^^^-^^^
|
||||
| ^^^^^^^^---^
|
||||
| | |
|
||||
| | closure is `FnOnce` because it moves the variable `y` out of its environment
|
||||
| this closure implements `FnOnce`, not `Fn`
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue