1 //! See The Book chapter on the borrow checker for more details.
3 #![allow(non_camel_case_types)]
5 pub use LoanPathKind::*;
6 pub use LoanPathElem::*;
10 use rustc::hir::HirId;
12 use rustc::middle::borrowck::{BorrowCheckResult, SignalledError};
13 use rustc::hir::def_id::{DefId, LocalDefId};
14 use rustc::middle::mem_categorization as mc;
15 use rustc::middle::mem_categorization::Categorization;
16 use rustc::middle::region;
17 use rustc::middle::free_region::RegionRelations;
18 use rustc::ty::{self, Ty, TyCtxt};
19 use rustc::ty::query::Providers;
22 use std::cell::{Cell};
25 use std::hash::{Hash, Hasher};
31 use crate::dataflow::{DataFlowContext, BitwiseOperator, DataFlowOperator, KillFrom};
39 #[derive(Clone, Copy)]
40 pub struct LoanDataFlowOperator;
42 pub type LoanDataFlow<'tcx> = DataFlowContext<'tcx, LoanDataFlowOperator>;
44 pub fn check_crate(tcx: TyCtxt<'_>) {
45 tcx.par_body_owners(|body_owner_def_id| {
46 tcx.ensure().borrowck(body_owner_def_id);
50 pub fn provide(providers: &mut Providers<'_>) {
51 *providers = Providers {
57 /// Collection of conclusions determined via borrow checker analyses.
58 pub struct AnalysisData<'tcx> {
59 pub all_loans: Vec<Loan<'tcx>>,
60 pub loans: DataFlowContext<'tcx, LoanDataFlowOperator>,
61 pub move_data: move_data::FlowedMoveData<'tcx>,
64 fn borrowck(tcx: TyCtxt<'_>, owner_def_id: DefId) -> &BorrowCheckResult {
65 assert!(tcx.use_ast_borrowck() || tcx.migrate_borrowck());
67 debug!("borrowck(body_owner_def_id={:?})", owner_def_id);
69 let signalled_error = tcx.check_match(owner_def_id);
70 if let SignalledError::SawSomeError = signalled_error {
71 return tcx.arena.alloc(BorrowCheckResult {
72 signalled_any_error: SignalledError::SawSomeError,
76 let owner_id = tcx.hir().as_local_hir_id(owner_def_id).unwrap();
78 match tcx.hir().get(owner_id) {
80 // We get invoked with anything that has MIR, but some of
81 // those things (notably the synthesized constructors from
82 // tuple structs/variants) do not have an associated body
83 // and do not need borrowchecking.
84 return tcx.arena.alloc(BorrowCheckResult {
85 signalled_any_error: SignalledError::NoErrorsSeen,
91 let body_id = tcx.hir().body_owned_by(owner_id);
92 let tables = tcx.typeck_tables_of(owner_def_id);
93 let region_scope_tree = tcx.region_scope_tree(owner_def_id);
94 let body = tcx.hir().body(body_id);
95 let mut bccx = BorrowckCtxt {
101 signalled_any_error: Cell::new(SignalledError::NoErrorsSeen),
104 // Eventually, borrowck will always read the MIR, but at the
105 // moment we do not. So, for now, we always force MIR to be
106 // constructed for a given fn, since this may result in errors
107 // being reported and we want that to happen.
109 // Note that `mir_validated` is a "stealable" result; the
110 // thief, `optimized_mir()`, forces borrowck, so we know that
111 // is not yet stolen.
112 tcx.ensure().mir_validated(owner_def_id);
114 // option dance because you can't capture an uninitialized variable
117 if let Some(AnalysisData { all_loans,
119 move_data: flowed_moves }) =
120 build_borrowck_dataflow_data(&mut bccx, false, body_id,
122 cfg = Some(cfg::CFG::new(bccx.tcx, &body));
123 cfg.as_mut().unwrap()
126 check_loans::check_loans(&mut bccx, &loan_dfcx, &flowed_moves, &all_loans, body);
129 tcx.arena.alloc(BorrowCheckResult {
130 signalled_any_error: bccx.signalled_any_error.into_inner(),
134 fn build_borrowck_dataflow_data<'a, 'c, 'tcx, F>(this: &mut BorrowckCtxt<'a, 'tcx>,
135 force_analysis: bool,
136 body_id: hir::BodyId,
138 -> Option<AnalysisData<'tcx>>
139 where F: FnOnce(&mut BorrowckCtxt<'a, 'tcx>) -> &'c cfg::CFG
141 // Check the body of fn items.
142 let (all_loans, move_data) =
143 gather_loans::gather_loans_in_fn(this, body_id);
145 if !force_analysis && move_data.is_empty() && all_loans.is_empty() {
146 // large arrays of data inserted as constants can take a lot of
147 // time and memory to borrow-check - see issue #36799. However,
148 // they don't have places, so no borrow-check is actually needed.
149 // Recognize that case and skip borrow-checking.
150 debug!("skipping loan propagation for {:?} because of no loans", body_id);
153 debug!("propagating loans in {:?}", body_id);
156 let cfg = get_cfg(this);
158 DataFlowContext::new(this.tcx,
162 LoanDataFlowOperator,
164 for (loan_idx, loan) in all_loans.iter().enumerate() {
165 loan_dfcx.add_gen(loan.gen_scope.item_local_id(), loan_idx);
166 loan_dfcx.add_kill(KillFrom::ScopeEnd,
167 loan.kill_scope.item_local_id(),
170 loan_dfcx.add_kills_from_flow_exits(cfg);
171 loan_dfcx.propagate(cfg, this.body);
173 let flowed_moves = move_data::FlowedMoveData::new(move_data,
178 Some(AnalysisData { all_loans,
180 move_data:flowed_moves })
183 /// Accessor for introspective clients inspecting `AnalysisData` and
184 /// the `BorrowckCtxt` itself , e.g., the flowgraph visualizer.
185 pub fn build_borrowck_dataflow_data_for_fn<'a, 'tcx>(
187 body_id: hir::BodyId,
189 -> (BorrowckCtxt<'a, 'tcx>, AnalysisData<'tcx>)
191 let owner_id = tcx.hir().body_owner(body_id);
192 let owner_def_id = tcx.hir().local_def_id(owner_id);
193 let tables = tcx.typeck_tables_of(owner_def_id);
194 let region_scope_tree = tcx.region_scope_tree(owner_def_id);
195 let body = tcx.hir().body(body_id);
196 let mut bccx = BorrowckCtxt {
202 signalled_any_error: Cell::new(SignalledError::NoErrorsSeen),
205 let dataflow_data = build_borrowck_dataflow_data(&mut bccx, true, body_id, |_| cfg);
206 (bccx, dataflow_data.unwrap())
209 // ----------------------------------------------------------------------
212 pub struct BorrowckCtxt<'a, 'tcx> {
215 // tables for the current thing we are checking; set to
216 // Some in `borrowck_fn` and cleared later
217 tables: &'a ty::TypeckTables<'tcx>,
219 region_scope_tree: &'tcx region::ScopeTree,
223 body: &'tcx hir::Body,
225 signalled_any_error: Cell<SignalledError>,
229 impl<'a, 'tcx: 'a> BorrowckCtxt<'a, 'tcx> {
230 fn signal_error(&self) {
231 self.signalled_any_error.set(SignalledError::SawSomeError);
235 ///////////////////////////////////////////////////////////////////////////
236 // Loans and loan paths
238 /// Record of a loan that was issued.
239 pub struct Loan<'tcx> {
241 loan_path: Rc<LoanPath<'tcx>>,
242 kind: ty::BorrowKind,
243 restricted_paths: Vec<Rc<LoanPath<'tcx>>>,
245 /// gen_scope indicates where loan is introduced. Typically the
246 /// loan is introduced at the point of the borrow, but in some
247 /// cases, notably method arguments, the loan may be introduced
248 /// only later, once it comes into scope. See also
249 /// `GatherLoanCtxt::compute_gen_scope`.
250 gen_scope: region::Scope,
252 /// kill_scope indicates when the loan goes out of scope. This is
253 /// either when the lifetime expires or when the local variable
254 /// which roots the loan-path goes out of scope, whichever happens
255 /// faster. See also `GatherLoanCtxt::compute_kill_scope`.
256 kill_scope: region::Scope,
259 impl<'tcx> Loan<'tcx> {
260 pub fn loan_path(&self) -> Rc<LoanPath<'tcx>> {
261 self.loan_path.clone()
266 pub struct LoanPath<'tcx> {
267 kind: LoanPathKind<'tcx>,
271 impl<'tcx> PartialEq for LoanPath<'tcx> {
272 fn eq(&self, that: &LoanPath<'tcx>) -> bool {
273 self.kind == that.kind
277 impl<'tcx> Hash for LoanPath<'tcx> {
278 fn hash<H: Hasher>(&self, state: &mut H) {
279 self.kind.hash(state);
283 #[derive(PartialEq, Eq, Hash, Debug)]
284 pub enum LoanPathKind<'tcx> {
285 LpVar(hir::HirId), // `x` in README.md
286 LpUpvar(ty::UpvarId), // `x` captured by-value into closure
287 LpDowncast(Rc<LoanPath<'tcx>>, DefId), // `x` downcast to particular enum variant
288 LpExtend(Rc<LoanPath<'tcx>>, mc::MutabilityCategory, LoanPathElem<'tcx>)
291 impl<'tcx> LoanPath<'tcx> {
292 fn new(kind: LoanPathKind<'tcx>, ty: Ty<'tcx>) -> LoanPath<'tcx> {
293 LoanPath { kind: kind, ty: ty }
296 fn to_type(&self) -> Ty<'tcx> { self.ty }
299 // FIXME (pnkfelix): See discussion here
300 // https://github.com/pnkfelix/rust/commit/
301 // b2b39e8700e37ad32b486b9a8409b50a8a53aa51#commitcomment-7892003
302 const DOWNCAST_PRINTED_OPERATOR: &'static str = " as ";
304 // A local, "cleaned" version of `mc::InteriorKind` that drops
305 // information that is not relevant to loan-path analysis. (In
306 // particular, the distinction between how precisely an array-element
307 // is tracked is irrelevant here.)
308 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
309 pub enum InteriorKind {
310 InteriorField(mc::FieldIndex),
314 trait ToInteriorKind { fn cleaned(self) -> InteriorKind; }
315 impl ToInteriorKind for mc::InteriorKind {
316 fn cleaned(self) -> InteriorKind {
318 mc::InteriorField(name) => InteriorField(name),
319 mc::InteriorElement(_) => InteriorElement,
325 // - a pointer dereference (`*P` in README.md)
326 // - a field reference, with an optional definition of the containing
327 // enum variant (`P.f` in README.md)
328 // `DefId` is present when the field is part of struct that is in
329 // a variant of an enum. For instance in:
330 // `enum E { X { foo: u32 }, Y { foo: u32 }}`
331 // each `foo` is qualified by the definitition id of the variant (`X` or `Y`).
332 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
333 pub enum LoanPathElem<'tcx> {
334 LpDeref(mc::PointerKind<'tcx>),
335 LpInterior(Option<DefId>, InteriorKind),
338 fn closure_to_block(closure_id: LocalDefId, tcx: TyCtxt<'_>) -> HirId {
339 let closure_id = tcx.hir().local_def_id_to_hir_id(closure_id);
340 match tcx.hir().get(closure_id) {
341 Node::Expr(expr) => match expr.node {
342 hir::ExprKind::Closure(.., body_id, _, _) => {
346 bug!("encountered non-closure id: {}", closure_id)
349 _ => bug!("encountered non-expr id: {}", closure_id)
353 impl<'a, 'tcx> LoanPath<'tcx> {
354 pub fn kill_scope(&self, bccx: &BorrowckCtxt<'a, 'tcx>) -> region::Scope {
357 bccx.region_scope_tree.var_scope(hir_id.local_id)
359 LpUpvar(upvar_id) => {
360 let block_id = closure_to_block(upvar_id.closure_expr_id, bccx.tcx);
361 region::Scope { id: block_id.local_id, data: region::ScopeData::Node }
363 LpDowncast(ref base, _) |
364 LpExtend(ref base, ..) => base.kill_scope(bccx),
369 // Avoid "cannot borrow immutable field `self.x` as mutable" as that implies that a field *can* be
370 // mutable independently of the struct it belongs to. (#35937)
371 pub fn opt_loan_path_is_field<'tcx>(cmt: &mc::cmt_<'tcx>) -> (Option<Rc<LoanPath<'tcx>>>, bool) {
372 let new_lp = |v: LoanPathKind<'tcx>| Rc::new(LoanPath::new(v, cmt.ty));
375 Categorization::Rvalue(..) |
376 Categorization::ThreadLocal(..) |
377 Categorization::StaticItem => {
381 Categorization::Local(id) => {
382 (Some(new_lp(LpVar(id))), false)
385 Categorization::Upvar(mc::Upvar { id, .. }) => {
386 (Some(new_lp(LpUpvar(id))), false)
389 Categorization::Deref(ref cmt_base, pk) => {
390 let lp = opt_loan_path_is_field(cmt_base);
392 new_lp(LpExtend(lp, cmt.mutbl, LpDeref(pk)))
396 Categorization::Interior(ref cmt_base, ik) => {
397 (opt_loan_path(cmt_base).map(|lp| {
398 let opt_variant_id = match cmt_base.cat {
399 Categorization::Downcast(_, did) => Some(did),
402 new_lp(LpExtend(lp, cmt.mutbl, LpInterior(opt_variant_id, ik.cleaned())))
406 Categorization::Downcast(ref cmt_base, variant_def_id) => {
407 let lp = opt_loan_path_is_field(cmt_base);
409 new_lp(LpDowncast(lp, variant_def_id))
415 /// Computes the `LoanPath` (if any) for a `cmt`.
416 /// Note that this logic is somewhat duplicated in
417 /// the method `compute()` found in `gather_loans::restrictions`,
418 /// which allows it to share common loan path pieces as it
419 /// traverses the CMT.
420 pub fn opt_loan_path<'tcx>(cmt: &mc::cmt_<'tcx>) -> Option<Rc<LoanPath<'tcx>>> {
421 opt_loan_path_is_field(cmt).0
424 ///////////////////////////////////////////////////////////////////////////
427 impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
428 pub fn is_subregion_of(&self,
429 r_sub: ty::Region<'tcx>,
430 r_sup: ty::Region<'tcx>)
433 let region_rels = RegionRelations::new(self.tcx,
435 &self.region_scope_tree,
436 &self.tables.free_region_map);
437 region_rels.is_subregion_of(r_sub, r_sup)
440 pub fn append_loan_path_to_string(&self,
441 loan_path: &LoanPath<'tcx>,
443 match loan_path.kind {
444 LpUpvar(ty::UpvarId { var_path: ty::UpvarPath { hir_id: id }, closure_expr_id: _ }) => {
445 out.push_str(&self.tcx.hir().name(id).as_str());
448 out.push_str(&self.tcx.hir().name(id).as_str());
451 LpDowncast(ref lp_base, variant_def_id) => {
453 self.append_loan_path_to_string(&lp_base, out);
454 out.push_str(DOWNCAST_PRINTED_OPERATOR);
455 out.push_str(&self.tcx.def_path_str(variant_def_id));
459 LpExtend(ref lp_base, _, LpInterior(_, InteriorField(mc::FieldIndex(_, info)))) => {
460 self.append_autoderefd_loan_path_to_string(&lp_base, out);
462 out.push_str(&info.as_str());
465 LpExtend(ref lp_base, _, LpInterior(_, InteriorElement)) => {
466 self.append_autoderefd_loan_path_to_string(&lp_base, out);
467 out.push_str("[..]");
470 LpExtend(ref lp_base, _, LpDeref(_)) => {
472 self.append_loan_path_to_string(&lp_base, out);
477 pub fn append_autoderefd_loan_path_to_string(&self,
478 loan_path: &LoanPath<'tcx>,
480 match loan_path.kind {
481 LpExtend(ref lp_base, _, LpDeref(_)) => {
482 // For a path like `(*x).f` or `(*x)[3]`, autoderef
483 // rules would normally allow users to omit the `*x`.
484 // So just serialize such paths to `x.f` or x[3]` respectively.
485 self.append_autoderefd_loan_path_to_string(&lp_base, out)
488 LpDowncast(ref lp_base, variant_def_id) => {
490 self.append_autoderefd_loan_path_to_string(&lp_base, out);
491 out.push_str(DOWNCAST_PRINTED_OPERATOR);
492 out.push_str(&self.tcx.def_path_str(variant_def_id));
496 LpVar(..) | LpUpvar(..) | LpExtend(.., LpInterior(..)) => {
497 self.append_loan_path_to_string(loan_path, out)
502 pub fn loan_path_to_string(&self, loan_path: &LoanPath<'tcx>) -> String {
503 let mut result = String::new();
504 self.append_loan_path_to_string(loan_path, &mut result);
508 pub fn cmt_to_cow_str(&self, cmt: &mc::cmt_<'tcx>) -> Cow<'static, str> {
509 cmt.descriptive_string(self.tcx)
512 pub fn cmt_to_path_or_string(&self, cmt: &mc::cmt_<'tcx>) -> String {
513 match opt_loan_path(cmt) {
514 Some(lp) => format!("`{}`", self.loan_path_to_string(&lp)),
515 None => self.cmt_to_cow_str(cmt).into_owned(),
520 impl BitwiseOperator for LoanDataFlowOperator {
522 fn join(&self, succ: usize, pred: usize) -> usize {
523 succ | pred // loans from both preds are in scope
527 impl DataFlowOperator for LoanDataFlowOperator {
529 fn initial_value(&self) -> bool {
530 false // no loans in scope by default
534 impl fmt::Debug for InteriorKind {
535 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
537 InteriorField(mc::FieldIndex(_, info)) => write!(f, "{}", info),
538 InteriorElement => write!(f, "[]"),
543 impl<'tcx> fmt::Debug for Loan<'tcx> {
544 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
545 write!(f, "Loan_{}({:?}, {:?}, {:?}-{:?}, {:?})",
551 self.restricted_paths)
555 impl<'tcx> fmt::Debug for LoanPath<'tcx> {
556 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
559 write!(f, "$({})", ty::tls::with(|tcx| tcx.hir().node_to_string(id)))
562 LpUpvar(ty::UpvarId{ var_path: ty::UpvarPath {hir_id: var_id}, closure_expr_id }) => {
563 let s = ty::tls::with(|tcx| {
564 tcx.hir().node_to_string(var_id)
566 write!(f, "$({} captured by id={:?})", s, closure_expr_id)
569 LpDowncast(ref lp, variant_def_id) => {
570 let variant_str = if variant_def_id.is_local() {
571 ty::tls::with(|tcx| tcx.def_path_str(variant_def_id))
573 format!("{:?}", variant_def_id)
575 write!(f, "({:?}{}{})", lp, DOWNCAST_PRINTED_OPERATOR, variant_str)
578 LpExtend(ref lp, _, LpDeref(_)) => {
579 write!(f, "{:?}.*", lp)
582 LpExtend(ref lp, _, LpInterior(_, ref interior)) => {
583 write!(f, "{:?}.{:?}", lp, interior)
589 impl<'tcx> fmt::Display for LoanPath<'tcx> {
590 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
593 write!(f, "$({})", ty::tls::with(|tcx| tcx.hir().hir_to_user_string(id)))
596 LpUpvar(ty::UpvarId{ var_path: ty::UpvarPath { hir_id }, closure_expr_id: _ }) => {
597 let s = ty::tls::with(|tcx| {
598 tcx.hir().node_to_string(hir_id)
600 write!(f, "$({} captured by closure)", s)
603 LpDowncast(ref lp, variant_def_id) => {
604 let variant_str = if variant_def_id.is_local() {
605 ty::tls::with(|tcx| tcx.def_path_str(variant_def_id))
607 format!("{:?}", variant_def_id)
609 write!(f, "({}{}{})", lp, DOWNCAST_PRINTED_OPERATOR, variant_str)
612 LpExtend(ref lp, _, LpDeref(_)) => {
613 write!(f, "{}.*", lp)
616 LpExtend(ref lp, _, LpInterior(_, ref interior)) => {
617 write!(f, "{}.{:?}", lp, interior)