diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 1fc157eca3e89..481ae3b93291e 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -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; diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs deleted file mode 100644 index 3510fe4d12359..0000000000000 --- a/src/librustc/middle/mem_categorization.rs +++ /dev/null @@ -1,1508 +0,0 @@ -//! # 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. - -#![allow(non_camel_case_types)] - -pub use self::PointerKind::*; -pub use self::InteriorKind::*; -pub use self::MutabilityCategory::*; -pub use self::AliasableReason::*; -pub use self::Note::*; - -use self::Aliasability::*; - -use crate::middle::region; -use crate::hir::def_id::{DefId, LocalDefId}; -use crate::hir::Node; -use crate::infer::InferCtxt; -use crate::hir::def::{CtorOf, Res, DefKind, CtorKind}; -use crate::ty::adjustment; -use crate::ty::{self, DefIdTree, Ty, TyCtxt}; -use crate::ty::fold::TypeFoldable; - -use crate::hir::{Mutability, PatKind}; -use crate::hir::pat_util::EnumerateAndAdjustIterator; -use crate::hir; -use syntax::ast::{self, Name}; -use syntax::symbol::sym; -use syntax_pos::Span; - -use std::borrow::Cow; -use std::fmt; -use std::hash::{Hash, Hasher}; -use rustc_data_structures::fx::FxIndexMap; -use std::rc::Rc; - -#[derive(Clone, Debug, PartialEq)] -pub enum Categorization<'tcx> { - Rvalue, // temporary val - ThreadLocal, // value that cannot move, but still restricted in scope - StaticItem, - Upvar(Upvar), // upvar referenced by closure env - Local(hir::HirId), // local variable - Deref(cmt<'tcx>, PointerKind<'tcx>), // deref of a ptr - Interior(cmt<'tcx>, InteriorKind), // something interior: field, tuple, etc - Downcast(cmt<'tcx>, DefId), // selects a particular enum variant (*1) - - // (*1) downcast is only required if the enum has more than one variant -} - -// Represents any kind of upvar -#[derive(Clone, Copy, PartialEq)] -pub struct Upvar { - pub id: ty::UpvarId, - pub kind: ty::ClosureKind -} - -// different kinds of pointers: -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum PointerKind<'tcx> { - /// `Box` - Unique, - - /// `&T` - BorrowedPtr(ty::BorrowKind, ty::Region<'tcx>), - - /// `*T` - UnsafePtr(hir::Mutability), -} - -// We use the term "interior" to mean "something reachable from the -// base without a pointer dereference", e.g., a field -#[derive(Clone, PartialEq)] -pub enum InteriorKind { - InteriorField(FieldIndex), - InteriorElement(InteriorOffsetKind), -} - -// Contains index of a field that is actually used for loan path comparisons and -// string representation of the field that should be used only for diagnostics. -#[derive(Clone, Copy, Eq)] -pub struct FieldIndex(pub usize, pub Name); - -impl PartialEq for FieldIndex { - fn eq(&self, rhs: &Self) -> bool { - self.0 == rhs.0 - } -} - -impl Hash for FieldIndex { - fn hash(&self, h: &mut H) { - self.0.hash(h) - } -} - -#[derive(Clone, PartialEq)] -pub enum InteriorOffsetKind { - Index, // e.g., `array_expr[index_expr]` - Pattern, // e.g., `fn foo([_, a, _, _]: [A; 4]) { ... }` -} - -#[derive(Clone, Copy, PartialEq, Debug)] -pub enum MutabilityCategory { - McImmutable, // Immutable. - McDeclared, // Directly declared as mutable. - McInherited, // Inherited from the fact that owner is mutable. -} - -// A note about the provenance of a `cmt`. This is used for -// special-case handling of upvars such as mutability inference. -// Upvar categorization can generate a variable number of nested -// derefs. The note allows detecting them without deep pattern -// matching on the categorization. -#[derive(Clone, Copy, PartialEq, Debug)] -pub enum Note { - NoteClosureEnv(ty::UpvarId), // Deref through closure env - NoteUpvarRef(ty::UpvarId), // Deref through by-ref upvar - NoteIndex, // Deref as part of desugaring `x[]` into its two components - NoteNone // Nothing special -} - -// `cmt`: "Category, Mutability, and Type". -// -// a complete categorization of a value indicating where it originated -// and how it is located, as well as the mutability of the memory in -// which the value is stored. -// -// *WARNING* The field `cmt.type` is NOT necessarily the same as the -// result of `node_type(cmt.id)`. -// -// (FIXME: rewrite the following comment given that `@x` managed -// pointers have been obsolete for quite some time.) -// -// This is because the `id` is always the `id` of the node producing the -// type; in an expression like `*x`, the type of this deref node is the -// deref'd type (`T`), but in a pattern like `@x`, the `@x` pattern is -// again a dereference, but its type is the type *before* the -// dereference (`@T`). So use `cmt.ty` to find the type of the value in -// a consistent fashion. For more details, see the method `cat_pattern` -#[derive(Clone, Debug, PartialEq)] -pub struct cmt_<'tcx> { - pub hir_id: hir::HirId, // HIR id of expr/pat producing this value - pub span: Span, // span of same expr/pat - pub cat: Categorization<'tcx>, // categorization of expr - pub mutbl: MutabilityCategory, // mutability of expr as place - pub ty: Ty<'tcx>, // type of the expr (*see WARNING above*) - pub note: Note, // Note about the provenance of this cmt -} - -pub type cmt<'tcx> = Rc>; - -pub 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)] -pub struct MemCategorizationContext<'a, 'tcx> { - pub tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - pub body_owner: DefId, - pub upvars: Option<&'tcx FxIndexMap>, - pub region_scope_tree: &'a region::ScopeTree, - pub tables: &'a ty::TypeckTables<'tcx>, - infcx: Option<&'a InferCtxt<'a, 'tcx>>, -} - -pub type McResult = Result; - -impl MutabilityCategory { - pub fn from_mutbl(m: hir::Mutability) -> MutabilityCategory { - let ret = match m { - Mutability::Immutable => McImmutable, - Mutability::Mutable => McDeclared - }; - debug!("MutabilityCategory::{}({:?}) => {:?}", - "from_mutbl", m, ret); - ret - } - - pub fn from_borrow_kind(borrow_kind: ty::BorrowKind) -> MutabilityCategory { - let ret = match borrow_kind { - ty::ImmBorrow => McImmutable, - ty::UniqueImmBorrow => McImmutable, - ty::MutBorrow => McDeclared, - }; - debug!("MutabilityCategory::{}({:?}) => {:?}", - "from_borrow_kind", borrow_kind, ret); - ret - } - - fn from_pointer_kind(base_mutbl: MutabilityCategory, - ptr: PointerKind<'_>) -> MutabilityCategory { - let ret = match ptr { - Unique => { - base_mutbl.inherit() - } - BorrowedPtr(borrow_kind, _) => { - MutabilityCategory::from_borrow_kind(borrow_kind) - } - UnsafePtr(m) => { - MutabilityCategory::from_mutbl(m) - } - }; - debug!("MutabilityCategory::{}({:?}, {:?}) => {:?}", - "from_pointer_kind", base_mutbl, ptr, ret); - ret - } - - fn from_local( - tcx: TyCtxt<'_>, - tables: &ty::TypeckTables<'_>, - id: hir::HirId, - ) -> MutabilityCategory { - let ret = match tcx.hir().get(id) { - Node::Binding(p) => match p.kind { - PatKind::Binding(..) => { - let bm = *tables.pat_binding_modes() - .get(p.hir_id) - .expect("missing binding mode"); - if bm == ty::BindByValue(Mutability::Mutable) { - McDeclared - } else { - McImmutable - } - } - _ => span_bug!(p.span, "expected identifier pattern") - }, - _ => span_bug!(tcx.hir().span(id), "expected identifier pattern") - }; - debug!("MutabilityCategory::{}(tcx, id={:?}) => {:?}", - "from_local", id, ret); - ret - } - - pub fn inherit(&self) -> MutabilityCategory { - let ret = match *self { - McImmutable => McImmutable, - McDeclared => McInherited, - McInherited => McInherited, - }; - debug!("{:?}.inherit() => {:?}", self, ret); - ret - } - - pub fn is_mutable(&self) -> bool { - let ret = match *self { - McImmutable => false, - McInherited => true, - McDeclared => true, - }; - debug!("{:?}.is_mutable() => {:?}", self, ret); - ret - } - - pub fn is_immutable(&self) -> bool { - let ret = match *self { - McImmutable => true, - McDeclared | McInherited => false - }; - debug!("{:?}.is_immutable() => {:?}", self, ret); - ret - } - - pub fn to_user_str(&self) -> &'static str { - match *self { - McDeclared | McInherited => "mutable", - McImmutable => "immutable", - } - } -} - -impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { - pub fn new( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - body_owner: DefId, - region_scope_tree: &'a region::ScopeTree, - tables: &'a ty::TypeckTables<'tcx>, - ) -> MemCategorizationContext<'a, 'tcx> { - MemCategorizationContext { - tcx, - body_owner, - upvars: tcx.upvars(body_owner), - region_scope_tree, - tables, - infcx: None, - param_env, - } - } -} - -impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { - /// Creates a `MemCategorizationContext` during type inference. - /// This is used during upvar analysis and a few other places. - /// Because the typeck tables are not yet complete, the results - /// from the analysis must be used with caution: - /// - /// - rvalue promotions are not known, so the lifetimes of - /// temporaries may be overly conservative; - /// - similarly, as the results of upvar analysis are not yet - /// known, the results around upvar accesses may be incorrect. - pub fn with_infer( - infcx: &'a InferCtxt<'a, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - body_owner: DefId, - region_scope_tree: &'a region::ScopeTree, - tables: &'a ty::TypeckTables<'tcx>, - ) -> MemCategorizationContext<'a, 'tcx> { - let tcx = infcx.tcx; - - MemCategorizationContext { - tcx, - body_owner, - upvars: tcx.upvars(body_owner), - region_scope_tree, - tables, - infcx: Some(infcx), - param_env, - } - } - - pub fn type_is_copy_modulo_regions( - &self, - param_env: ty::ParamEnv<'tcx>, - ty: Ty<'tcx>, - span: Span, - ) -> bool { - self.infcx.map(|infcx| infcx.type_is_copy_modulo_regions(param_env, ty, span)) - .or_else(|| { - if (param_env, ty).has_local_value() { - None - } else { - Some(ty.is_copy_modulo_regions(self.tcx, param_env, span)) - } - }) - .unwrap_or(true) - } - - fn resolve_vars_if_possible(&self, value: &T) -> T - where T: TypeFoldable<'tcx> - { - self.infcx.map(|infcx| infcx.resolve_vars_if_possible(value)) - .unwrap_or_else(|| value.clone()) - } - - fn is_tainted_by_errors(&self) -> bool { - self.infcx.map_or(false, |infcx| infcx.is_tainted_by_errors()) - } - - fn resolve_type_vars_or_error(&self, - id: hir::HirId, - ty: Option>) - -> McResult> { - 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)); - } - } - } - - pub fn node_ty(&self, - hir_id: hir::HirId) - -> McResult> { - self.resolve_type_vars_or_error(hir_id, - self.tables.node_type_opt(hir_id)) - } - - pub fn expr_ty(&self, expr: &hir::Expr) -> McResult> { - self.resolve_type_vars_or_error(expr.hir_id, self.tables.expr_ty_opt(expr)) - } - - pub fn expr_ty_adjusted(&self, expr: &hir::Expr) -> McResult> { - 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). - pub fn pat_ty_adjusted(&self, pat: &hir::Pat) -> McResult> { - // 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> { - 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) - } - - pub fn cat_expr(&self, expr: &hir::Expr) -> McResult> { - // 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> { - 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)) - } - - pub fn cat_expr_adjusted(&self, expr: &hir::Expr, - previous: cmt_<'tcx>, - adjustment: &adjustment::Adjustment<'tcx>) - -> McResult> { - self.cat_expr_adjusted_with(expr, || Ok(previous), adjustment) - } - - fn cat_expr_adjusted_with(&self, expr: &hir::Expr, - previous: F, - adjustment: &adjustment::Adjustment<'tcx>) - -> McResult> - where F: FnOnce() -> McResult> - { - 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 = Rc::new(if let Some(deref) = overloaded { - let ref_ty = self.tcx.mk_ref(deref.region, ty::TypeAndMut { - ty: target, - mutbl: deref.mutbl, - }); - self.cat_rvalue_node(expr.hir_id, expr.span, ref_ty) - } else { - previous()? - }); - self.cat_deref(expr, base, NoteNone) - } - - adjustment::Adjust::NeverToAny | - adjustment::Adjust::Pointer(_) | - adjustment::Adjust::Borrow(_) => { - // Result is an rvalue. - Ok(self.cat_rvalue_node(expr.hir_id, expr.span, target)) - } - } - } - - pub fn cat_expr_unadjusted(&self, expr: &hir::Expr) -> McResult> { - 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, NoteNone) - } else { - let base_cmt = Rc::new(self.cat_expr(&e_base)?); - self.cat_deref(expr, base_cmt, NoteNone) - } - } - - hir::ExprKind::Field(ref base, f_ident) => { - let base_cmt = Rc::new(self.cat_expr(&base)?); - debug!("cat_expr(cat_field): id={} expr={:?} base={:?}", - expr.hir_id, - expr, - base_cmt); - let f_index = self.tcx.field_index(expr.hir_id, self.tables); - Ok(self.cat_field(expr, base_cmt, f_index, f_ident, 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, NoteIndex) - } else { - let base_cmt = Rc::new(self.cat_expr(&base)?); - self.cat_index(expr, base_cmt, expr_ty, InteriorOffsetKind::Index) - } - } - - 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_node(expr.hir_id, expr.span, expr_ty)) - } - } - } - - pub fn cat_res(&self, - hir_id: hir::HirId, - span: Span, - expr_ty: Ty<'tcx>, - res: Res) - -> McResult> { - 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_node(hir_id, span, expr_ty)) - } - - Res::Def(DefKind::Static, def_id) => { - // `#[thread_local]` statics may not outlive the current function, but - // they also cannot be moved out of. - let is_thread_local = self.tcx.get_attrs(def_id)[..] - .iter() - .any(|attr| attr.check_name(sym::thread_local)); - - let cat = if is_thread_local { - Categorization::ThreadLocal - } else { - Categorization::StaticItem - }; - - Ok(cmt_ { - hir_id, - span, - cat, - mutbl: match self.tcx.static_mutability(def_id).unwrap() { - Mutability::Immutable => McImmutable, - Mutability::Mutable => McDeclared, - }, - ty:expr_ty, - note: NoteNone - }) - } - - 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(cmt_ { - hir_id, - span, - cat: Categorization::Local(var_id), - mutbl: MutabilityCategory::from_local(self.tcx, self.tables, var_id), - ty: expr_ty, - note: NoteNone - }) - } - } - - def => span_bug!(span, "unexpected definition in memory categorization: {:?}", def) - } - } - - // Categorize an upvar, complete with invisible derefs of closure - // environment and upvar reference as appropriate. - fn cat_upvar( - &self, - hir_id: hir::HirId, - span: Span, - var_id: hir::HirId, - ) -> McResult> { - // An upvar can have up to 3 components. We translate first to a - // `Categorization::Upvar`, which is itself a fiction -- it represents the reference to the - // field from the environment. - // - // `Categorization::Upvar`. Next, we add a deref through the implicit - // environment pointer with an anonymous free region 'env and - // appropriate borrow kind for closure kinds that take self by - // reference. Finally, if the upvar was captured - // by-reference, we add a deref through that reference. The - // region of this reference is an inference variable 'up that - // was previously generated and recorded in the upvar borrow - // map. The borrow kind bk is inferred by based on how the - // upvar is used. - // - // This results in the following table for concrete closure - // types: - // - // | move | ref - // ---------------+----------------------+------------------------------- - // Fn | copied -> &'env | upvar -> &'env -> &'up bk - // FnMut | copied -> &'env mut | upvar -> &'env mut -> &'up bk - // FnOnce | copied | upvar -> &'up bk - - let closure_expr_def_id = self.body_owner; - let fn_hir_id = self.tcx.hir().local_def_id_to_hir_id( - LocalDefId::from_def_id(closure_expr_def_id), - ); - let ty = self.node_ty(fn_hir_id)?; - let kind = match ty.kind { - ty::Generator(..) => ty::ClosureKind::FnOnce, - ty::Closure(closure_def_id, substs) => { - match self.infcx { - // During upvar inference we may not know the - // closure kind, just use the LATTICE_BOTTOM value. - Some(infcx) => - infcx.closure_kind( - closure_def_id, - substs - ).unwrap_or(ty::ClosureKind::LATTICE_BOTTOM), - - None => - substs.as_closure().kind(closure_def_id, self.tcx), - } - } - _ => span_bug!(span, "unexpected type for fn in mem_categorization: {:?}", ty), - }; - - 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)?; - - // Mutability of original variable itself - let var_mutbl = MutabilityCategory::from_local(self.tcx, self.tables, var_id); - - // Construct the upvar. This represents access to the field - // from the environment (perhaps we should eventually desugar - // this field further, but it will do for now). - let cmt_result = cmt_ { - hir_id, - span, - cat: Categorization::Upvar(Upvar {id: upvar_id, kind: kind}), - mutbl: var_mutbl, - ty: var_ty, - note: NoteNone - }; - - // If this is a `FnMut` or `Fn` closure, then the above is - // conceptually a `&mut` or `&` reference, so we have to add a - // deref. - let cmt_result = match kind { - ty::ClosureKind::FnOnce => { - cmt_result - } - ty::ClosureKind::FnMut => { - self.env_deref(hir_id, span, upvar_id, var_mutbl, ty::MutBorrow, cmt_result) - } - ty::ClosureKind::Fn => { - self.env_deref(hir_id, span, upvar_id, var_mutbl, ty::ImmBorrow, cmt_result) - } - }; - - // If this is a by-ref capture, then the upvar we loaded is - // actually a reference, so we have to add an implicit deref - // for that. - let upvar_capture = self.tables.upvar_capture(upvar_id); - let cmt_result = match upvar_capture { - ty::UpvarCapture::ByValue => { - cmt_result - } - ty::UpvarCapture::ByRef(upvar_borrow) => { - let ptr = BorrowedPtr(upvar_borrow.kind, upvar_borrow.region); - cmt_ { - hir_id, - span, - cat: Categorization::Deref(Rc::new(cmt_result), ptr), - mutbl: MutabilityCategory::from_borrow_kind(upvar_borrow.kind), - ty: var_ty, - note: NoteUpvarRef(upvar_id) - } - } - }; - - let ret = cmt_result; - debug!("cat_upvar ret={:?}", ret); - Ok(ret) - } - - fn env_deref(&self, - hir_id: hir::HirId, - span: Span, - upvar_id: ty::UpvarId, - upvar_mutbl: MutabilityCategory, - env_borrow_kind: ty::BorrowKind, - cmt_result: cmt_<'tcx>) - -> cmt_<'tcx> - { - // Region of environment pointer - let env_region = self.tcx.mk_region(ty::ReFree(ty::FreeRegion { - // The environment of a closure is guaranteed to - // outlive any bindings introduced in the body of the - // closure itself. - scope: upvar_id.closure_expr_id.to_def_id(), - bound_region: ty::BrEnv - })); - - let env_ptr = BorrowedPtr(env_borrow_kind, env_region); - - let var_ty = cmt_result.ty; - - // We need to add the env deref. This means - // that the above is actually immutable and - // has a ref type. However, nothing should - // actually look at the type, so we can get - // away with stuffing a `Error` in there - // instead of bothering to construct a proper - // one. - let cmt_result = cmt_ { - mutbl: McImmutable, - ty: self.tcx.types.err, - ..cmt_result - }; - - let mut deref_mutbl = MutabilityCategory::from_borrow_kind(env_borrow_kind); - - // Issue #18335. If variable is declared as immutable, override the - // mutability from the environment and substitute an `&T` anyway. - match upvar_mutbl { - McImmutable => { deref_mutbl = McImmutable; } - McDeclared | McInherited => { } - } - - let ret = cmt_ { - hir_id, - span, - cat: Categorization::Deref(Rc::new(cmt_result), env_ptr), - mutbl: deref_mutbl, - ty: var_ty, - note: NoteClosureEnv(upvar_id) - }; - - debug!("env_deref ret {:?}", ret); - - ret - } - - pub fn cat_rvalue_node(&self, - hir_id: hir::HirId, - span: Span, - expr_ty: Ty<'tcx>) - -> cmt_<'tcx> { - debug!("cat_rvalue_node(id={:?}, span={:?}, expr_ty={:?})", - hir_id, span, expr_ty); - - let ret = self.cat_rvalue(hir_id, span, expr_ty); - debug!("cat_rvalue_node ret {:?}", ret); - ret - } - - pub fn cat_rvalue(&self, - cmt_hir_id: hir::HirId, - span: Span, - expr_ty: Ty<'tcx>) -> cmt_<'tcx> { - let ret = cmt_ { - hir_id: cmt_hir_id, - span:span, - cat:Categorization::Rvalue, - mutbl:McDeclared, - ty:expr_ty, - note: NoteNone - }; - debug!("cat_rvalue ret {:?}", ret); - ret - } - - pub fn cat_field(&self, - node: &N, - base_cmt: cmt<'tcx>, - f_index: usize, - f_ident: ast::Ident, - f_ty: Ty<'tcx>) - -> cmt_<'tcx> { - let ret = cmt_ { - hir_id: node.hir_id(), - span: node.span(), - mutbl: base_cmt.mutbl.inherit(), - cat: Categorization::Interior(base_cmt, - InteriorField(FieldIndex(f_index, f_ident.name))), - ty: f_ty, - note: NoteNone - }; - debug!("cat_field ret {:?}", ret); - ret - } - - fn cat_overloaded_place( - &self, - expr: &hir::Expr, - base: &hir::Expr, - note: Note, - ) -> McResult> { - debug!("cat_overloaded_place(expr={:?}, base={:?}, note={:?})", - expr, - base, - note); - - // 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_cmt = Rc::new(self.cat_rvalue_node(expr.hir_id, expr.span, ref_ty)); - self.cat_deref(expr, base_cmt, note) - } - - pub fn cat_deref( - &self, - node: &impl HirNode, - base_cmt: cmt<'tcx>, - note: Note, - ) -> McResult> { - debug!("cat_deref: base_cmt={:?}", base_cmt); - - let base_cmt_ty = base_cmt.ty; - let deref_ty = match base_cmt_ty.builtin_deref(true) { - Some(mt) => mt.ty, - None => { - debug!("explicit deref of non-derefable type: {:?}", base_cmt_ty); - return Err(()); - } - }; - - let ptr = match base_cmt.ty.kind { - ty::Adt(def, ..) if def.is_box() => Unique, - ty::RawPtr(ref mt) => UnsafePtr(mt.mutbl), - ty::Ref(r, _, mutbl) => { - let bk = ty::BorrowKind::from_mutbl(mutbl); - BorrowedPtr(bk, r) - } - _ => bug!("unexpected type in cat_deref: {:?}", base_cmt.ty) - }; - let ret = cmt_ { - hir_id: node.hir_id(), - span: node.span(), - // For unique ptrs, we inherit mutability from the owning reference. - mutbl: MutabilityCategory::from_pointer_kind(base_cmt.mutbl, ptr), - cat: Categorization::Deref(base_cmt, ptr), - ty: deref_ty, - note: note, - }; - debug!("cat_deref ret {:?}", ret); - Ok(ret) - } - - fn cat_index(&self, - elt: &N, - base_cmt: cmt<'tcx>, - element_ty: Ty<'tcx>, - context: InteriorOffsetKind) - -> McResult> { - //! Creates a cmt for an indexing operation (`[]`). - //! - //! One subtle aspect of indexing that may not be - //! immediately obvious: for anything other than a fixed-length - //! vector, an operation like `x[y]` actually consists of two - //! disjoint (from the point of view of borrowck) operations. - //! The first is a deref of `x` to create a pointer `p` that points - //! at the first element in the array. The second operation is - //! an index which adds `y*sizeof(T)` to `p` to obtain the - //! pointer to `x[y]`. `cat_index` will produce a resulting - //! cmt containing both this deref and the indexing, - //! presuming that `base_cmt` is not of fixed-length type. - //! - //! # Parameters - //! - `elt`: the HIR node being indexed - //! - `base_cmt`: the cmt of `elt` - - let interior_elem = InteriorElement(context); - let ret = self.cat_imm_interior(elt, base_cmt, element_ty, interior_elem); - debug!("cat_index ret {:?}", ret); - return Ok(ret); - } - - pub fn cat_imm_interior(&self, - node: &N, - base_cmt: cmt<'tcx>, - interior_ty: Ty<'tcx>, - interior: InteriorKind) - -> cmt_<'tcx> { - let ret = cmt_ { - hir_id: node.hir_id(), - span: node.span(), - mutbl: base_cmt.mutbl.inherit(), - cat: Categorization::Interior(base_cmt, interior), - ty: interior_ty, - note: NoteNone - }; - debug!("cat_imm_interior ret={:?}", ret); - ret - } - - pub fn cat_downcast_if_needed(&self, - node: &N, - base_cmt: cmt<'tcx>, - variant_did: DefId) - -> cmt<'tcx> { - // univariant enums do not need downcasts - let base_did = self.tcx.parent(variant_did).unwrap(); - if self.tcx.adt_def(base_did).variants.len() != 1 { - let base_ty = base_cmt.ty; - let ret = Rc::new(cmt_ { - hir_id: node.hir_id(), - span: node.span(), - mutbl: base_cmt.mutbl.inherit(), - cat: Categorization::Downcast(base_cmt, variant_did), - ty: base_ty, - note: NoteNone - }); - debug!("cat_downcast ret={:?}", ret); - ret - } else { - debug!("cat_downcast univariant={:?}", base_cmt); - base_cmt - } - } - - pub fn cat_pattern(&self, cmt: cmt<'tcx>, pat: &hir::Pat, mut op: F) -> McResult<()> - where F: FnMut(cmt<'tcx>, &hir::Pat), - { - self.cat_pattern_(cmt, pat, &mut op) - } - - // FIXME(#19596) This is a workaround, but there should be a better way to do this - fn cat_pattern_(&self, mut cmt: cmt<'tcx>, pat: &hir::Pat, op: &mut F) -> McResult<()> - where F : FnMut(cmt<'tcx>, &hir::Pat) - { - // Here, `cmt` is the categorization for the value 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 cmt that represents the path - // that will be taken to reach the value being matched. - // - // When we encounter named bindings, we take the cmt that has - // been built up and pass it off to guarantee_valid() so that - // we can be sure that the binding will remain valid for the - // duration of the arm. - // - // (*2) There is subtlety concerning the correspondence between - // pattern ids and types as compared to *expression* ids and - // types. This is explained briefly. on the definition of the - // type `cmt`, so go off and read what it says there, then - // come back and I'll dive into a bit more detail here. :) OK, - // back? - // - // In general, the id of the cmt should be the node that - // "produces" the value---patterns aren't executable code - // exactly, but I consider them to "execute" when they match a - // value, and I consider them to produce the value that was - // matched. So if you have something like: - // - // (FIXME: `@@3` is not legal code anymore!) - // - // let x = @@3; - // match x { - // @@y { ... } - // } - // - // In this case, the cmt and the relevant ids would be: - // - // CMT Id Type of Id Type of cmt - // - // local(x)->@->@ - // ^~~~~~~^ `x` from discr @@int @@int - // ^~~~~~~~~~^ `@@y` pattern node @@int @int - // ^~~~~~~~~~~~~^ `@y` pattern node @int int - // - // You can see that the types of the id and the cmt are in - // sync in the first line, because that id is actually the id - // of an expression. But once we get to pattern ids, the types - // step out of sync again. So you'll see below that we always - // get the type of the *subpattern* and use that. - - debug!("cat_pattern(pat={:?}, cmt={:?})", pat, cmt); - - // If (pattern) adjustments are active for this pattern, adjust the `cmt` correspondingly. - // `cmt`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 `cmt` we start with a `cmt` for `foo`, and then, by traversing the - // pattern, try to answer the question: given the address of `foo`, how is `x` reached? - // - // `&&Some(x,)` `cmt_foo` - // `&Some(x,)` `deref { cmt_foo}` - // `Some(x,)` `deref { deref { cmt_foo }}` - // (x,)` `field0 { deref { deref { cmt_foo }}}` <- resulting cmt - // - // 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 { cmt_foo }}` - // instead of `cmt_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 cmt={:?}", cmt); - cmt = Rc::new(self.cat_deref(pat, cmt, NoteNone)?); - } - let cmt = cmt; // lose mutability - debug!("cat_pattern: applied adjustment derefs to get cmt={:?}", cmt); - - // Invoke the callback, but only now, after the `cmt` has adjusted. - // - // To see that this makes sense, consider `match &Some(3) { Some(x) => { ... }}`. In that - // case, the initial `cmt` 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 cmt (that for `*&Some(3)`) and the pattern - // `Some(x)` (which matches). Recursing once more, `*&Some(3)` and the pattern `Some(x)` - // result in the cmt `Downcast(*&Some(3)).0` associated to `x` and invoke `op` with - // that (where the `ref` on `x` is implied). - op(cmt.clone(), pat); - - match pat.kind { - PatKind::TupleStruct(ref qpath, ref subpats, ddpos) => { - let res = self.tables.qpath_res(qpath, pat.hir_id); - let (cmt, expected_len) = match res { - Res::Err => { - debug!("access to unresolvable pattern {:?}", pat); - return Err(()) - } - Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), variant_ctor_did) => { - let variant_did = self.tcx.parent(variant_ctor_did).unwrap(); - let enum_did = self.tcx.parent(variant_did).unwrap(); - (self.cat_downcast_if_needed(pat, cmt, variant_did), - self.tcx.adt_def(enum_did) - .variant_with_ctor_id(variant_ctor_did).fields.len()) - } - Res::Def(DefKind::Ctor(CtorOf::Struct, CtorKind::Fn), _) - | Res::SelfCtor(..) => { - let ty = self.pat_ty_unadjusted(&pat)?; - match ty.kind { - ty::Adt(adt_def, _) => { - (cmt, adt_def.non_enum_variant().fields.len()) - } - _ => { - span_bug!(pat.span, - "tuple struct pattern unexpected type {:?}", ty); - } - } - } - def => { - debug!( - "tuple struct pattern didn't resolve to variant or struct {:?} at {:?}", - def, - pat.span, - ); - self.tcx.sess.delay_span_bug(pat.span, &format!( - "tuple struct pattern didn't resolve to variant or struct {:?}", - def, - )); - return Err(()); - } - }; - - for (i, subpat) in subpats.iter().enumerate_and_adjust(expected_len, ddpos) { - let subpat_ty = self.pat_ty_adjusted(&subpat)?; // see (*2) - let interior = InteriorField(FieldIndex(i, sym::integer(i))); - let subcmt = Rc::new( - self.cat_imm_interior(pat, cmt.clone(), subpat_ty, interior)); - self.cat_pattern_(subcmt, &subpat, op)?; - } - } - - PatKind::Struct(ref qpath, ref field_pats, _) => { - // {f1: p1, ..., fN: pN} - let res = self.tables.qpath_res(qpath, pat.hir_id); - let cmt = match res { - Res::Err => { - debug!("access to unresolvable pattern {:?}", pat); - return Err(()) - } - Res::Def(DefKind::Ctor(CtorOf::Variant, _), variant_ctor_did) => { - let variant_did = self.tcx.parent(variant_ctor_did).unwrap(); - self.cat_downcast_if_needed(pat, cmt, variant_did) - } - Res::Def(DefKind::Variant, variant_did) => { - self.cat_downcast_if_needed(pat, cmt, variant_did) - } - _ => cmt, - }; - - for fp in field_pats { - let field_ty = self.pat_ty_adjusted(&fp.pat)?; // see (*2) - let f_index = self.tcx.field_index(fp.hir_id, self.tables); - let cmt_field = Rc::new(self.cat_field(pat, cmt.clone(), f_index, - fp.ident, field_ty)); - self.cat_pattern_(cmt_field, &fp.pat, op)?; - } - } - - PatKind::Or(ref pats) => { - for pat in pats { - self.cat_pattern_(cmt.clone(), &pat, op)?; - } - } - - PatKind::Binding(.., Some(ref subpat)) => { - self.cat_pattern_(cmt, &subpat, op)?; - } - - PatKind::Tuple(ref subpats, ddpos) => { - // (p1, ..., pN) - let ty = self.pat_ty_unadjusted(&pat)?; - let expected_len = match ty.kind { - ty::Tuple(ref tys) => tys.len(), - _ => span_bug!(pat.span, "tuple pattern unexpected type {:?}", ty), - }; - for (i, subpat) in subpats.iter().enumerate_and_adjust(expected_len, ddpos) { - let subpat_ty = self.pat_ty_adjusted(&subpat)?; // see (*2) - let interior = InteriorField(FieldIndex(i, sym::integer(i))); - let subcmt = Rc::new( - self.cat_imm_interior(pat, cmt.clone(), subpat_ty, interior)); - self.cat_pattern_(subcmt, &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 subcmt = Rc::new(self.cat_deref(pat, cmt, NoteNone)?); - self.cat_pattern_(subcmt, &subpat, op)?; - } - - PatKind::Slice(ref before, ref slice, ref after) => { - let element_ty = match cmt.ty.builtin_index() { - Some(ty) => ty, - None => { - debug!("explicit index of non-indexable type {:?}", cmt); - return Err(()); - } - }; - let context = InteriorOffsetKind::Pattern; - let elt_cmt = Rc::new(self.cat_index(pat, cmt, element_ty, context)?); - for before_pat in before { - self.cat_pattern_(elt_cmt.clone(), &before_pat, op)?; - } - if let Some(ref slice_pat) = *slice { - self.cat_pattern_(elt_cmt.clone(), &slice_pat, op)?; - } - for after_pat in after { - self.cat_pattern_(elt_cmt.clone(), &after_pat, op)?; - } - } - - PatKind::Path(_) | PatKind::Binding(.., None) | - PatKind::Lit(..) | PatKind::Range(..) | PatKind::Wild => { - // always ok - } - } - - Ok(()) - } -} - -#[derive(Clone, Debug)] -pub enum Aliasability { - FreelyAliasable(AliasableReason), - NonAliasable, - ImmutableUnique(Box), -} - -#[derive(Copy, Clone, Debug)] -pub enum AliasableReason { - AliasableBorrowed, - AliasableStatic, - AliasableStaticMut, -} - -impl<'tcx> cmt_<'tcx> { - pub fn guarantor(&self) -> cmt_<'tcx> { - //! Returns `self` after stripping away any derefs or - //! interior content. The return value is basically the `cmt` which - //! determines how long the value in `self` remains live. - - match self.cat { - Categorization::Rvalue | - Categorization::StaticItem | - Categorization::ThreadLocal | - Categorization::Local(..) | - Categorization::Deref(_, UnsafePtr(..)) | - Categorization::Deref(_, BorrowedPtr(..)) | - Categorization::Upvar(..) => { - (*self).clone() - } - Categorization::Downcast(ref b, _) | - Categorization::Interior(ref b, _) | - Categorization::Deref(ref b, Unique) => { - b.guarantor() - } - } - } - - /// Returns `FreelyAliasable(_)` if this place represents a freely aliasable pointer type. - pub fn freely_aliasable(&self) -> Aliasability { - // Maybe non-obvious: copied upvars can only be considered - // non-aliasable in once closures, since any other kind can be - // aliased and eventually recused. - - match self.cat { - Categorization::Deref(ref b, BorrowedPtr(ty::MutBorrow, _)) | - Categorization::Deref(ref b, BorrowedPtr(ty::UniqueImmBorrow, _)) | - Categorization::Deref(ref b, Unique) | - Categorization::Downcast(ref b, _) | - Categorization::Interior(ref b, _) => { - // Aliasability depends on base cmt - b.freely_aliasable() - } - - Categorization::Rvalue | - Categorization::ThreadLocal | - Categorization::Local(..) | - Categorization::Upvar(..) | - Categorization::Deref(_, UnsafePtr(..)) => { // yes, it's aliasable, but... - NonAliasable - } - - Categorization::StaticItem => { - if self.mutbl.is_mutable() { - FreelyAliasable(AliasableStaticMut) - } else { - FreelyAliasable(AliasableStatic) - } - } - - Categorization::Deref(_, BorrowedPtr(ty::ImmBorrow, _)) => { - FreelyAliasable(AliasableBorrowed) - } - } - } - - // Digs down through one or two layers of deref and grabs the - // Categorization of the cmt for the upvar if a note indicates there is - // one. - pub fn upvar_cat(&self) -> Option<&Categorization<'tcx>> { - match self.note { - NoteClosureEnv(..) | NoteUpvarRef(..) => { - Some(match self.cat { - Categorization::Deref(ref inner, _) => { - match inner.cat { - Categorization::Deref(ref inner, _) => &inner.cat, - Categorization::Upvar(..) => &inner.cat, - _ => bug!() - } - } - _ => bug!() - }) - } - NoteIndex | NoteNone => None - } - } - - pub fn descriptive_string(&self, tcx: TyCtxt<'_>) -> Cow<'static, str> { - match self.cat { - Categorization::StaticItem => { - "static item".into() - } - Categorization::ThreadLocal => { - "thread-local static item".into() - } - Categorization::Rvalue => { - "non-place".into() - } - Categorization::Local(vid) => { - if tcx.hir().is_argument(vid) { - "argument" - } else { - "local variable" - }.into() - } - Categorization::Deref(_, pk) => { - match self.upvar_cat() { - Some(&Categorization::Upvar(ref var)) => { - var.to_string().into() - } - Some(_) => bug!(), - None => { - match pk { - Unique => { - "`Box` content" - } - UnsafePtr(..) => { - "dereference of raw pointer" - } - BorrowedPtr(..) => { - match self.note { - NoteIndex => "indexed content", - _ => "borrowed content" - } - } - }.into() - } - } - } - Categorization::Interior(_, InteriorField(..)) => { - "field".into() - } - Categorization::Interior(_, InteriorElement(InteriorOffsetKind::Index)) => { - "indexed content".into() - } - Categorization::Interior(_, InteriorElement(InteriorOffsetKind::Pattern)) => { - "pattern-bound indexed content".into() - } - Categorization::Upvar(ref var) => { - var.to_string().into() - } - Categorization::Downcast(ref cmt, _) => { - cmt.descriptive_string(tcx).into() - } - } - } -} - -pub fn ptr_sigil(ptr: PointerKind<'_>) -> &'static str { - match ptr { - Unique => "Box", - BorrowedPtr(ty::ImmBorrow, _) => "&", - BorrowedPtr(ty::MutBorrow, _) => "&mut", - BorrowedPtr(ty::UniqueImmBorrow, _) => "&unique", - UnsafePtr(_) => "*", - } -} - -impl fmt::Debug for InteriorKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - InteriorField(FieldIndex(_, info)) => write!(f, "{}", info), - InteriorElement(..) => write!(f, "[]"), - } - } -} - -impl fmt::Debug for Upvar { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}/{:?}", self.id, self.kind) - } -} - -impl fmt::Display for Upvar { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let kind = match self.kind { - ty::ClosureKind::Fn => "Fn", - ty::ClosureKind::FnMut => "FnMut", - ty::ClosureKind::FnOnce => "FnOnce", - }; - write!(f, "captured outer variable in an `{}` closure", kind) - } -} diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index f4fdc2882e702..8ceb21748520e 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -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> { + fn constrain_adjustments(&mut self, expr: &hir::Expr) -> mc::McResult> { 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: - /// - /// 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. + /// 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`. /// - /// 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 *don't* need to + /// the recurse to the next reference up. /// - /// 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 + } + + hir::Mutability::Mutable => { + // The reference being reborrowed is either an `&mut T`. This is + // the case where recursion is needed. + false } + } + } - 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)); + /// 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"); + } } } } diff --git a/src/librustc_typeck/check/upvar.rs b/src/librustc_typeck/check/upvar.rs index 390ee0d0f8380..3002459d56f3c 100644 --- a/src/librustc_typeck/check/upvar.rs +++ b/src/librustc_typeck/check/upvar.rs @@ -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: 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 - ); + 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, - guarantor.span, - var_name(tcx, upvar_id.var_path.hir_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), + ); - 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); - - 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) { + /// 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); + + 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, + _ => (), } } - - Categorization::Deref(_, mc::UnsafePtr(..)) - | Categorization::StaticItem - | Categorization::ThreadLocal - | Categorization::Rvalue - | Categorization::Local(_) - | Categorization::Upvar(..) => { - return; - } + self.adjust_upvar_deref(upvar_id, place.span, borrow_kind); } } - fn adjust_upvar_borrow_kind_for_unique(&mut self, cmt: &mc::cmt_<'tcx>) { - debug!("adjust_upvar_borrow_kind_for_unique(cmt={:?})", cmt); + fn adjust_upvar_borrow_kind_for_unique(&mut self, place: &mc::Place<'tcx>) { + debug!("adjust_upvar_borrow_kind_for_unique(place={:?})", place); - 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); - } + 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; } - - Categorization::Deref(_, mc::UnsafePtr(..)) - | Categorization::StaticItem - | Categorization::ThreadLocal - | Categorization::Rvalue - | Categorization::Local(_) - | Categorization::Upvar(..) => {} + // for a borrowed pointer to be unique, its base must be unique + self.adjust_upvar_deref(upvar_id, place.span, ty::UniqueImmBorrow); } } - 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); - - // 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, - } + // 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, + 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); } } diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc_typeck/expr_use_visitor.rs similarity index 76% rename from src/librustc/middle/expr_use_visitor.rs rename to src/librustc_typeck/expr_use_visitor.rs index 00bddf50c293e..03d7ab2d63377 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc_typeck/expr_use_visitor.rs @@ -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}; - -use crate::hir::{self, PatKind}; -use std::rc::Rc; +// Export these here so that Clippy can use them. +pub use mc::{PlaceBase, Place, Projection}; + +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> { - // Create the cmt for the variable being borrowed, from the + -> mc::McResult> { + // 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 diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 0f147f92b5f7e..c606feab08727 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -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; diff --git a/src/librustc_typeck/mem_categorization.rs b/src/librustc_typeck/mem_categorization.rs new file mode 100644 index 0000000000000..84e5d234da9d7 --- /dev/null +++ b/src/librustc_typeck/mem_categorization.rs @@ -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` 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>, +} + +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> + '_ { + 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>, +} + +crate type McResult = Result; + +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(&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>) + -> McResult> { + 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> { + self.resolve_type_vars_or_error(hir_id, self.tables.node_type_opt(hir_id)) + } + + fn expr_ty(&self, expr: &hir::Expr) -> McResult> { + 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> { + 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). + crate fn pat_ty_adjusted(&self, pat: &hir::Pat) -> McResult> { + // 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> { + 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> { + // 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> { + 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> { + self.cat_expr_adjusted_with(expr, || Ok(previous), adjustment) + } + + fn cat_expr_adjusted_with(&self, expr: &hir::Expr, + previous: F, + adjustment: &adjustment::Adjustment<'tcx>) + -> McResult> + where F: FnOnce() -> McResult> + { + 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> { + 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> { + 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> { + 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( + &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> { + 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> { + 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(&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_(&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(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(()) + } +} diff --git a/src/test/ui/issues/issue-4335.rs b/src/test/ui/issues/issue-4335.rs index a10ae9a1243a2..c5914a17cf924 100644 --- a/src/test/ui/issues/issue-4335.rs +++ b/src/test/ui/issues/issue-4335.rs @@ -4,8 +4,7 @@ fn id(t: T) -> T { t } fn f<'r, T>(v: &'r T) -> Box T + 'r> { id(Box::new(|| *v)) - //~^ ERROR E0373 - //~| ERROR E0507 + //~^ ERROR E0507 } fn main() { diff --git a/src/test/ui/issues/issue-4335.stderr b/src/test/ui/issues/issue-4335.stderr index ca1c0b68d2a5f..f187969ff4e86 100644 --- a/src/test/ui/issues/issue-4335.stderr +++ b/src/test/ui/issues/issue-4335.stderr @@ -6,25 +6,6 @@ LL | fn f<'r, T>(v: &'r T) -> Box 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 2 previous errors +error: aborting due to previous error -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`. diff --git a/src/test/ui/regions/regions-addr-of-upvar-self.stderr b/src/test/ui/regions/regions-addr-of-upvar-self.stderr index 7a051b8ac835d..005800d50a457 100644 --- a/src/test/ui/regions/regions-addr-of-upvar-self.stderr +++ b/src/test/ui/regions/regions-addr-of-upvar-self.stderr @@ -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; diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-fn-once-move-from-projection.stderr b/src/test/ui/unboxed-closures/unboxed-closures-infer-fn-once-move-from-projection.stderr index 9287353245e98..68ec8d2ba8287 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-infer-fn-once-move-from-projection.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-fn-once-move-from-projection.stderr @@ -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`