rustc_borrowck/
lib.rs

1//! This query borrow-checks the MIR to (further) ensure it is not broken.
2
3// tidy-alphabetical-start
4#![allow(internal_features)]
5#![cfg_attr(doc, recursion_limit = "256")] // FIXME(nnethercote): will be removed by #124141
6#![doc(rust_logo)]
7#![feature(assert_matches)]
8#![feature(box_patterns)]
9#![feature(file_buffered)]
10#![feature(if_let_guard)]
11#![feature(let_chains)]
12#![feature(never_type)]
13#![feature(rustc_attrs)]
14#![feature(rustdoc_internals)]
15#![feature(stmt_expr_attributes)]
16#![feature(try_blocks)]
17// tidy-alphabetical-end
18
19use std::borrow::Cow;
20use std::cell::RefCell;
21use std::marker::PhantomData;
22use std::ops::{ControlFlow, Deref};
23
24use rustc_abi::FieldIdx;
25use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
26use rustc_data_structures::graph::dominators::Dominators;
27use rustc_errors::LintDiagnostic;
28use rustc_hir as hir;
29use rustc_hir::CRATE_HIR_ID;
30use rustc_hir::def_id::LocalDefId;
31use rustc_index::bit_set::{DenseBitSet, MixedBitSet};
32use rustc_index::{IndexSlice, IndexVec};
33use rustc_infer::infer::{
34    InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin, TyCtxtInferExt,
35};
36use rustc_middle::mir::*;
37use rustc_middle::query::Providers;
38use rustc_middle::ty::{self, ParamEnv, RegionVid, TyCtxt, TypingMode, fold_regions};
39use rustc_middle::{bug, span_bug};
40use rustc_mir_dataflow::impls::{
41    EverInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces,
42};
43use rustc_mir_dataflow::move_paths::{
44    InitIndex, InitLocation, LookupResult, MoveData, MovePathIndex,
45};
46use rustc_mir_dataflow::{Analysis, EntryStates, Results, ResultsVisitor, visit_results};
47use rustc_session::lint::builtin::{TAIL_EXPR_DROP_ORDER, UNUSED_MUT};
48use rustc_span::{Span, Symbol};
49use smallvec::SmallVec;
50use tracing::{debug, instrument};
51
52use crate::borrow_set::{BorrowData, BorrowSet};
53use crate::consumers::{BodyWithBorrowckFacts, ConsumerOptions};
54use crate::dataflow::{BorrowIndex, Borrowck, BorrowckDomain, Borrows};
55use crate::diagnostics::{
56    AccessKind, BorrowckDiagnosticsBuffer, IllegalMoveOriginKind, MoveError, RegionName,
57};
58use crate::path_utils::*;
59use crate::place_ext::PlaceExt;
60use crate::places_conflict::{PlaceConflictBias, places_conflict};
61use crate::polonius::PoloniusDiagnosticsContext;
62use crate::polonius::legacy::{PoloniusLocationTable, PoloniusOutput};
63use crate::prefixes::PrefixSet;
64use crate::region_infer::RegionInferenceContext;
65use crate::renumber::RegionCtxt;
66use crate::session_diagnostics::VarNeedNotMut;
67
68mod borrow_set;
69mod borrowck_errors;
70mod constraints;
71mod dataflow;
72mod def_use;
73mod diagnostics;
74mod member_constraints;
75mod nll;
76mod opaque_types;
77mod path_utils;
78mod place_ext;
79mod places_conflict;
80mod polonius;
81mod prefixes;
82mod region_infer;
83mod renumber;
84mod session_diagnostics;
85mod type_check;
86mod universal_regions;
87mod used_muts;
88
89/// A public API provided for the Rust compiler consumers.
90pub mod consumers;
91
92rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
93
94/// Associate some local constants with the `'tcx` lifetime
95struct TyCtxtConsts<'tcx>(PhantomData<&'tcx ()>);
96
97impl<'tcx> TyCtxtConsts<'tcx> {
98    const DEREF_PROJECTION: &'tcx [PlaceElem<'tcx>; 1] = &[ProjectionElem::Deref];
99}
100
101pub fn provide(providers: &mut Providers) {
102    *providers = Providers { mir_borrowck, ..*providers };
103}
104
105fn mir_borrowck(tcx: TyCtxt<'_>, def: LocalDefId) -> &BorrowCheckResult<'_> {
106    let (input_body, promoted) = tcx.mir_promoted(def);
107    debug!("run query mir_borrowck: {}", tcx.def_path_str(def));
108
109    let input_body: &Body<'_> = &input_body.borrow();
110
111    if input_body.should_skip() || input_body.tainted_by_errors.is_some() {
112        debug!("Skipping borrowck because of injected body or tainted body");
113        // Let's make up a borrowck result! Fun times!
114        let result = BorrowCheckResult {
115            concrete_opaque_types: FxIndexMap::default(),
116            closure_requirements: None,
117            used_mut_upvars: SmallVec::new(),
118            tainted_by_errors: input_body.tainted_by_errors,
119        };
120        return tcx.arena.alloc(result);
121    }
122
123    let borrowck_result = do_mir_borrowck(tcx, input_body, &*promoted.borrow(), None).0;
124    debug!("mir_borrowck done");
125
126    tcx.arena.alloc(borrowck_result)
127}
128
129/// Perform the actual borrow checking.
130///
131/// Use `consumer_options: None` for the default behavior of returning
132/// [`BorrowCheckResult`] only. Otherwise, return [`BodyWithBorrowckFacts`] according
133/// to the given [`ConsumerOptions`].
134#[instrument(skip(tcx, input_body, input_promoted), fields(id=?input_body.source.def_id()), level = "debug")]
135fn do_mir_borrowck<'tcx>(
136    tcx: TyCtxt<'tcx>,
137    input_body: &Body<'tcx>,
138    input_promoted: &IndexSlice<Promoted, Body<'tcx>>,
139    consumer_options: Option<ConsumerOptions>,
140) -> (BorrowCheckResult<'tcx>, Option<Box<BodyWithBorrowckFacts<'tcx>>>) {
141    let def = input_body.source.def_id().expect_local();
142    let infcx = BorrowckInferCtxt::new(tcx, def);
143    if let Some(e) = input_body.tainted_by_errors {
144        infcx.set_tainted_by_errors(e);
145    }
146
147    let mut local_names = IndexVec::from_elem(None, &input_body.local_decls);
148    for var_debug_info in &input_body.var_debug_info {
149        if let VarDebugInfoContents::Place(place) = var_debug_info.value {
150            if let Some(local) = place.as_local() {
151                if let Some(prev_name) = local_names[local]
152                    && var_debug_info.name != prev_name
153                {
154                    span_bug!(
155                        var_debug_info.source_info.span,
156                        "local {:?} has many names (`{}` vs `{}`)",
157                        local,
158                        prev_name,
159                        var_debug_info.name
160                    );
161                }
162                local_names[local] = Some(var_debug_info.name);
163            }
164        }
165    }
166
167    // Replace all regions with fresh inference variables. This
168    // requires first making our own copy of the MIR. This copy will
169    // be modified (in place) to contain non-lexical lifetimes. It
170    // will have a lifetime tied to the inference context.
171    let mut body_owned = input_body.clone();
172    let mut promoted = input_promoted.to_owned();
173    let free_regions = nll::replace_regions_in_mir(&infcx, &mut body_owned, &mut promoted);
174    let body = &body_owned; // no further changes
175
176    // FIXME(-Znext-solver): A bit dubious that we're only registering
177    // predefined opaques in the typeck root.
178    if infcx.next_trait_solver() && !infcx.tcx.is_typeck_child(body.source.def_id()) {
179        infcx.register_predefined_opaques_for_next_solver(def);
180    }
181
182    let location_table = PoloniusLocationTable::new(body);
183
184    let move_data = MoveData::gather_moves(body, tcx, |_| true);
185
186    let flow_inits = MaybeInitializedPlaces::new(tcx, body, &move_data)
187        .iterate_to_fixpoint(tcx, body, Some("borrowck"))
188        .into_results_cursor(body);
189
190    let locals_are_invalidated_at_exit = tcx.hir_body_owner_kind(def).is_fn_or_closure();
191    let borrow_set = BorrowSet::build(tcx, body, locals_are_invalidated_at_exit, &move_data);
192
193    // Compute non-lexical lifetimes.
194    let nll::NllOutput {
195        regioncx,
196        concrete_opaque_types,
197        polonius_input,
198        polonius_output,
199        opt_closure_req,
200        nll_errors,
201        polonius_diagnostics,
202    } = nll::compute_regions(
203        &infcx,
204        free_regions,
205        body,
206        &promoted,
207        &location_table,
208        flow_inits,
209        &move_data,
210        &borrow_set,
211        consumer_options,
212    );
213
214    // Dump MIR results into a file, if that is enabled. This lets us
215    // write unit-tests, as well as helping with debugging.
216    nll::dump_nll_mir(&infcx, body, &regioncx, &opt_closure_req, &borrow_set);
217
218    // We also have a `#[rustc_regions]` annotation that causes us to dump
219    // information.
220    let diags_buffer = &mut BorrowckDiagnosticsBuffer::default();
221    nll::dump_annotation(
222        &infcx,
223        body,
224        &regioncx,
225        &opt_closure_req,
226        &concrete_opaque_types,
227        diags_buffer,
228    );
229
230    let movable_coroutine =
231        // The first argument is the coroutine type passed by value
232        if let Some(local) = body.local_decls.raw.get(1)
233        // Get the interior types and args which typeck computed
234        && let ty::Coroutine(def_id, _) = *local.ty.kind()
235        && tcx.coroutine_movability(def_id) == hir::Movability::Movable
236    {
237        true
238    } else {
239        false
240    };
241
242    // While promoteds should mostly be correct by construction, we need to check them for
243    // invalid moves to detect moving out of arrays:`struct S; fn main() { &([S][0]); }`.
244    for promoted_body in &promoted {
245        use rustc_middle::mir::visit::Visitor;
246        // This assumes that we won't use some of the fields of the `promoted_mbcx`
247        // when detecting and reporting move errors. While it would be nice to move
248        // this check out of `MirBorrowckCtxt`, actually doing so is far from trivial.
249        let move_data = MoveData::gather_moves(promoted_body, tcx, |_| true);
250        let mut promoted_mbcx = MirBorrowckCtxt {
251            infcx: &infcx,
252            body: promoted_body,
253            move_data: &move_data,
254            // no need to create a real location table for the promoted, it is not used
255            location_table: &location_table,
256            movable_coroutine,
257            fn_self_span_reported: Default::default(),
258            locals_are_invalidated_at_exit,
259            access_place_error_reported: Default::default(),
260            reservation_error_reported: Default::default(),
261            uninitialized_error_reported: Default::default(),
262            regioncx: &regioncx,
263            used_mut: Default::default(),
264            used_mut_upvars: SmallVec::new(),
265            borrow_set: &borrow_set,
266            upvars: &[],
267            local_names: IndexVec::from_elem(None, &promoted_body.local_decls),
268            region_names: RefCell::default(),
269            next_region_name: RefCell::new(1),
270            polonius_output: None,
271            move_errors: Vec::new(),
272            diags_buffer,
273            polonius_diagnostics: polonius_diagnostics.as_ref(),
274        };
275        struct MoveVisitor<'a, 'b, 'infcx, 'tcx> {
276            ctxt: &'a mut MirBorrowckCtxt<'b, 'infcx, 'tcx>,
277        }
278
279        impl<'tcx> Visitor<'tcx> for MoveVisitor<'_, '_, '_, 'tcx> {
280            fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
281                if let Operand::Move(place) = operand {
282                    self.ctxt.check_movable_place(location, *place);
283                }
284            }
285        }
286        MoveVisitor { ctxt: &mut promoted_mbcx }.visit_body(promoted_body);
287        promoted_mbcx.report_move_errors();
288    }
289
290    let mut mbcx = MirBorrowckCtxt {
291        infcx: &infcx,
292        body,
293        move_data: &move_data,
294        location_table: &location_table,
295        movable_coroutine,
296        locals_are_invalidated_at_exit,
297        fn_self_span_reported: Default::default(),
298        access_place_error_reported: Default::default(),
299        reservation_error_reported: Default::default(),
300        uninitialized_error_reported: Default::default(),
301        regioncx: &regioncx,
302        used_mut: Default::default(),
303        used_mut_upvars: SmallVec::new(),
304        borrow_set: &borrow_set,
305        upvars: tcx.closure_captures(def),
306        local_names,
307        region_names: RefCell::default(),
308        next_region_name: RefCell::new(1),
309        polonius_output,
310        move_errors: Vec::new(),
311        diags_buffer,
312        polonius_diagnostics: polonius_diagnostics.as_ref(),
313    };
314
315    // Compute and report region errors, if any.
316    mbcx.report_region_errors(nll_errors);
317
318    let mut flow_results = get_flow_results(tcx, body, &move_data, &borrow_set, &regioncx);
319    visit_results(
320        body,
321        traversal::reverse_postorder(body).map(|(bb, _)| bb),
322        &mut flow_results,
323        &mut mbcx,
324    );
325
326    mbcx.report_move_errors();
327
328    // If requested, dump polonius MIR.
329    polonius::dump_polonius_mir(
330        &infcx,
331        body,
332        &regioncx,
333        &borrow_set,
334        polonius_diagnostics.as_ref(),
335        &opt_closure_req,
336    );
337
338    // For each non-user used mutable variable, check if it's been assigned from
339    // a user-declared local. If so, then put that local into the used_mut set.
340    // Note that this set is expected to be small - only upvars from closures
341    // would have a chance of erroneously adding non-user-defined mutable vars
342    // to the set.
343    let temporary_used_locals: FxIndexSet<Local> = mbcx
344        .used_mut
345        .iter()
346        .filter(|&local| !mbcx.body.local_decls[*local].is_user_variable())
347        .cloned()
348        .collect();
349    // For the remaining unused locals that are marked as mutable, we avoid linting any that
350    // were never initialized. These locals may have been removed as unreachable code; or will be
351    // linted as unused variables.
352    let unused_mut_locals =
353        mbcx.body.mut_vars_iter().filter(|local| !mbcx.used_mut.contains(local)).collect();
354    mbcx.gather_used_muts(temporary_used_locals, unused_mut_locals);
355
356    debug!("mbcx.used_mut: {:?}", mbcx.used_mut);
357    mbcx.lint_unused_mut();
358    let tainted_by_errors = mbcx.emit_errors();
359
360    let result = BorrowCheckResult {
361        concrete_opaque_types: concrete_opaque_types.into_inner(),
362        closure_requirements: opt_closure_req,
363        used_mut_upvars: mbcx.used_mut_upvars,
364        tainted_by_errors,
365    };
366
367    let body_with_facts = if consumer_options.is_some() {
368        let output_facts = mbcx.polonius_output;
369        Some(Box::new(BodyWithBorrowckFacts {
370            body: body_owned,
371            promoted,
372            borrow_set,
373            region_inference_context: regioncx,
374            location_table: polonius_input.as_ref().map(|_| location_table),
375            input_facts: polonius_input,
376            output_facts,
377        }))
378    } else {
379        None
380    };
381
382    debug!("do_mir_borrowck: result = {:#?}", result);
383
384    (result, body_with_facts)
385}
386
387fn get_flow_results<'a, 'tcx>(
388    tcx: TyCtxt<'tcx>,
389    body: &'a Body<'tcx>,
390    move_data: &'a MoveData<'tcx>,
391    borrow_set: &'a BorrowSet<'tcx>,
392    regioncx: &RegionInferenceContext<'tcx>,
393) -> Results<'tcx, Borrowck<'a, 'tcx>> {
394    // We compute these three analyses individually, but them combine them into
395    // a single results so that `mbcx` can visit them all together.
396    let borrows = Borrows::new(tcx, body, regioncx, borrow_set).iterate_to_fixpoint(
397        tcx,
398        body,
399        Some("borrowck"),
400    );
401    let uninits = MaybeUninitializedPlaces::new(tcx, body, move_data).iterate_to_fixpoint(
402        tcx,
403        body,
404        Some("borrowck"),
405    );
406    let ever_inits = EverInitializedPlaces::new(body, move_data).iterate_to_fixpoint(
407        tcx,
408        body,
409        Some("borrowck"),
410    );
411
412    let analysis = Borrowck {
413        borrows: borrows.analysis,
414        uninits: uninits.analysis,
415        ever_inits: ever_inits.analysis,
416    };
417
418    assert_eq!(borrows.entry_states.len(), uninits.entry_states.len());
419    assert_eq!(borrows.entry_states.len(), ever_inits.entry_states.len());
420    let entry_states: EntryStates<'_, Borrowck<'_, '_>> =
421        itertools::izip!(borrows.entry_states, uninits.entry_states, ever_inits.entry_states)
422            .map(|(borrows, uninits, ever_inits)| BorrowckDomain { borrows, uninits, ever_inits })
423            .collect();
424
425    Results { analysis, entry_states }
426}
427
428pub(crate) struct BorrowckInferCtxt<'tcx> {
429    pub(crate) infcx: InferCtxt<'tcx>,
430    pub(crate) reg_var_to_origin: RefCell<FxIndexMap<ty::RegionVid, RegionCtxt>>,
431    pub(crate) param_env: ParamEnv<'tcx>,
432}
433
434impl<'tcx> BorrowckInferCtxt<'tcx> {
435    pub(crate) fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
436        let infcx = tcx.infer_ctxt().build(TypingMode::analysis_in_body(tcx, def_id));
437        let param_env = tcx.param_env(def_id);
438        BorrowckInferCtxt { infcx, reg_var_to_origin: RefCell::new(Default::default()), param_env }
439    }
440
441    pub(crate) fn next_region_var<F>(
442        &self,
443        origin: RegionVariableOrigin,
444        get_ctxt_fn: F,
445    ) -> ty::Region<'tcx>
446    where
447        F: Fn() -> RegionCtxt,
448    {
449        let next_region = self.infcx.next_region_var(origin);
450        let vid = next_region.as_var();
451
452        if cfg!(debug_assertions) {
453            debug!("inserting vid {:?} with origin {:?} into var_to_origin", vid, origin);
454            let ctxt = get_ctxt_fn();
455            let mut var_to_origin = self.reg_var_to_origin.borrow_mut();
456            assert_eq!(var_to_origin.insert(vid, ctxt), None);
457        }
458
459        next_region
460    }
461
462    #[instrument(skip(self, get_ctxt_fn), level = "debug")]
463    pub(crate) fn next_nll_region_var<F>(
464        &self,
465        origin: NllRegionVariableOrigin,
466        get_ctxt_fn: F,
467    ) -> ty::Region<'tcx>
468    where
469        F: Fn() -> RegionCtxt,
470    {
471        let next_region = self.infcx.next_nll_region_var(origin);
472        let vid = next_region.as_var();
473
474        if cfg!(debug_assertions) {
475            debug!("inserting vid {:?} with origin {:?} into var_to_origin", vid, origin);
476            let ctxt = get_ctxt_fn();
477            let mut var_to_origin = self.reg_var_to_origin.borrow_mut();
478            assert_eq!(var_to_origin.insert(vid, ctxt), None);
479        }
480
481        next_region
482    }
483
484    /// With the new solver we prepopulate the opaque type storage during
485    /// MIR borrowck with the hidden types from HIR typeck. This is necessary
486    /// to avoid ambiguities as earlier goals can rely on the hidden type
487    /// of an opaque which is only constrained by a later goal.
488    fn register_predefined_opaques_for_next_solver(&self, def_id: LocalDefId) {
489        let tcx = self.tcx;
490        // OK to use the identity arguments for each opaque type key, since
491        // we remap opaques from HIR typeck back to their definition params.
492        for data in tcx.typeck(def_id).concrete_opaque_types.iter().map(|(k, v)| (*k, *v)) {
493            // HIR typeck did not infer the regions of the opaque, so we instantiate
494            // them with fresh inference variables.
495            let (key, hidden_ty) = fold_regions(tcx, data, |_, _| {
496                self.next_nll_region_var_in_universe(
497                    NllRegionVariableOrigin::Existential { from_forall: false },
498                    ty::UniverseIndex::ROOT,
499                )
500            });
501
502            self.inject_new_hidden_type_unchecked(key, hidden_ty);
503        }
504    }
505}
506
507impl<'tcx> Deref for BorrowckInferCtxt<'tcx> {
508    type Target = InferCtxt<'tcx>;
509
510    fn deref(&self) -> &Self::Target {
511        &self.infcx
512    }
513}
514
515struct MirBorrowckCtxt<'a, 'infcx, 'tcx> {
516    infcx: &'infcx BorrowckInferCtxt<'tcx>,
517    body: &'a Body<'tcx>,
518    move_data: &'a MoveData<'tcx>,
519
520    /// Map from MIR `Location` to `LocationIndex`; created
521    /// when MIR borrowck begins.
522    location_table: &'a PoloniusLocationTable,
523
524    movable_coroutine: bool,
525    /// This keeps track of whether local variables are free-ed when the function
526    /// exits even without a `StorageDead`, which appears to be the case for
527    /// constants.
528    ///
529    /// I'm not sure this is the right approach - @eddyb could you try and
530    /// figure this out?
531    locals_are_invalidated_at_exit: bool,
532    /// This field keeps track of when borrow errors are reported in the access_place function
533    /// so that there is no duplicate reporting. This field cannot also be used for the conflicting
534    /// borrow errors that is handled by the `reservation_error_reported` field as the inclusion
535    /// of the `Span` type (while required to mute some errors) stops the muting of the reservation
536    /// errors.
537    access_place_error_reported: FxIndexSet<(Place<'tcx>, Span)>,
538    /// This field keeps track of when borrow conflict errors are reported
539    /// for reservations, so that we don't report seemingly duplicate
540    /// errors for corresponding activations.
541    //
542    // FIXME: ideally this would be a set of `BorrowIndex`, not `Place`s,
543    // but it is currently inconvenient to track down the `BorrowIndex`
544    // at the time we detect and report a reservation error.
545    reservation_error_reported: FxIndexSet<Place<'tcx>>,
546    /// This fields keeps track of the `Span`s that we have
547    /// used to report extra information for `FnSelfUse`, to avoid
548    /// unnecessarily verbose errors.
549    fn_self_span_reported: FxIndexSet<Span>,
550    /// This field keeps track of errors reported in the checking of uninitialized variables,
551    /// so that we don't report seemingly duplicate errors.
552    uninitialized_error_reported: FxIndexSet<Local>,
553    /// This field keeps track of all the local variables that are declared mut and are mutated.
554    /// Used for the warning issued by an unused mutable local variable.
555    used_mut: FxIndexSet<Local>,
556    /// If the function we're checking is a closure, then we'll need to report back the list of
557    /// mutable upvars that have been used. This field keeps track of them.
558    used_mut_upvars: SmallVec<[FieldIdx; 8]>,
559    /// Region inference context. This contains the results from region inference and lets us e.g.
560    /// find out which CFG points are contained in each borrow region.
561    regioncx: &'a RegionInferenceContext<'tcx>,
562
563    /// The set of borrows extracted from the MIR
564    borrow_set: &'a BorrowSet<'tcx>,
565
566    /// Information about upvars not necessarily preserved in types or MIR
567    upvars: &'tcx [&'tcx ty::CapturedPlace<'tcx>],
568
569    /// Names of local (user) variables (extracted from `var_debug_info`).
570    local_names: IndexVec<Local, Option<Symbol>>,
571
572    /// Record the region names generated for each region in the given
573    /// MIR def so that we can reuse them later in help/error messages.
574    region_names: RefCell<FxIndexMap<RegionVid, RegionName>>,
575
576    /// The counter for generating new region names.
577    next_region_name: RefCell<usize>,
578
579    /// Results of Polonius analysis.
580    polonius_output: Option<Box<PoloniusOutput>>,
581
582    diags_buffer: &'a mut BorrowckDiagnosticsBuffer<'infcx, 'tcx>,
583    move_errors: Vec<MoveError<'tcx>>,
584
585    /// When using `-Zpolonius=next`: the data used to compute errors and diagnostics.
586    polonius_diagnostics: Option<&'a PoloniusDiagnosticsContext>,
587}
588
589// Check that:
590// 1. assignments are always made to mutable locations (FIXME: does that still really go here?)
591// 2. loans made in overlapping scopes do not conflict
592// 3. assignments do not affect things loaned out as immutable
593// 4. moves do not affect things loaned out in any way
594impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<'a, '_, 'tcx> {
595    fn visit_after_early_statement_effect(
596        &mut self,
597        _results: &mut Results<'tcx, Borrowck<'a, 'tcx>>,
598        state: &BorrowckDomain,
599        stmt: &'a Statement<'tcx>,
600        location: Location,
601    ) {
602        debug!("MirBorrowckCtxt::process_statement({:?}, {:?}): {:?}", location, stmt, state);
603        let span = stmt.source_info.span;
604
605        self.check_activations(location, span, state);
606
607        match &stmt.kind {
608            StatementKind::Assign(box (lhs, rhs)) => {
609                self.consume_rvalue(location, (rhs, span), state);
610
611                self.mutate_place(location, (*lhs, span), Shallow(None), state);
612            }
613            StatementKind::FakeRead(box (_, place)) => {
614                // Read for match doesn't access any memory and is used to
615                // assert that a place is safe and live. So we don't have to
616                // do any checks here.
617                //
618                // FIXME: Remove check that the place is initialized. This is
619                // needed for now because matches don't have never patterns yet.
620                // So this is the only place we prevent
621                //      let x: !;
622                //      match x {};
623                // from compiling.
624                self.check_if_path_or_subpath_is_moved(
625                    location,
626                    InitializationRequiringAction::Use,
627                    (place.as_ref(), span),
628                    state,
629                );
630            }
631            StatementKind::Intrinsic(box kind) => match kind {
632                NonDivergingIntrinsic::Assume(op) => {
633                    self.consume_operand(location, (op, span), state);
634                }
635                NonDivergingIntrinsic::CopyNonOverlapping(..) => span_bug!(
636                    span,
637                    "Unexpected CopyNonOverlapping, should only appear after lower_intrinsics",
638                )
639            }
640            // Only relevant for mir typeck
641            StatementKind::AscribeUserType(..)
642            // Only relevant for liveness and unsafeck
643            | StatementKind::PlaceMention(..)
644            // Doesn't have any language semantics
645            | StatementKind::Coverage(..)
646            // These do not actually affect borrowck
647            | StatementKind::ConstEvalCounter
648            | StatementKind::StorageLive(..) => {}
649            // This does not affect borrowck
650            StatementKind::BackwardIncompatibleDropHint { place, reason: BackwardIncompatibleDropReason::Edition2024 } => {
651                self.check_backward_incompatible_drop(location, **place, state);
652            }
653            StatementKind::StorageDead(local) => {
654                self.access_place(
655                    location,
656                    (Place::from(*local), span),
657                    (Shallow(None), Write(WriteKind::StorageDeadOrDrop)),
658                    LocalMutationIsAllowed::Yes,
659                    state,
660                );
661            }
662            StatementKind::Nop
663            | StatementKind::Retag { .. }
664            | StatementKind::Deinit(..)
665            | StatementKind::SetDiscriminant { .. } => {
666                bug!("Statement not allowed in this MIR phase")
667            }
668        }
669    }
670
671    fn visit_after_early_terminator_effect(
672        &mut self,
673        _results: &mut Results<'tcx, Borrowck<'a, 'tcx>>,
674        state: &BorrowckDomain,
675        term: &'a Terminator<'tcx>,
676        loc: Location,
677    ) {
678        debug!("MirBorrowckCtxt::process_terminator({:?}, {:?}): {:?}", loc, term, state);
679        let span = term.source_info.span;
680
681        self.check_activations(loc, span, state);
682
683        match &term.kind {
684            TerminatorKind::SwitchInt { discr, targets: _ } => {
685                self.consume_operand(loc, (discr, span), state);
686            }
687            TerminatorKind::Drop { place, target: _, unwind: _, replace } => {
688                debug!(
689                    "visit_terminator_drop \
690                     loc: {:?} term: {:?} place: {:?} span: {:?}",
691                    loc, term, place, span
692                );
693
694                let write_kind =
695                    if *replace { WriteKind::Replace } else { WriteKind::StorageDeadOrDrop };
696                self.access_place(
697                    loc,
698                    (*place, span),
699                    (AccessDepth::Drop, Write(write_kind)),
700                    LocalMutationIsAllowed::Yes,
701                    state,
702                );
703            }
704            TerminatorKind::Call {
705                func,
706                args,
707                destination,
708                target: _,
709                unwind: _,
710                call_source: _,
711                fn_span: _,
712            } => {
713                self.consume_operand(loc, (func, span), state);
714                for arg in args {
715                    self.consume_operand(loc, (&arg.node, arg.span), state);
716                }
717                self.mutate_place(loc, (*destination, span), Deep, state);
718            }
719            TerminatorKind::TailCall { func, args, fn_span: _ } => {
720                self.consume_operand(loc, (func, span), state);
721                for arg in args {
722                    self.consume_operand(loc, (&arg.node, arg.span), state);
723                }
724            }
725            TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => {
726                self.consume_operand(loc, (cond, span), state);
727                if let AssertKind::BoundsCheck { len, index } = &**msg {
728                    self.consume_operand(loc, (len, span), state);
729                    self.consume_operand(loc, (index, span), state);
730                }
731            }
732
733            TerminatorKind::Yield { value, resume: _, resume_arg, drop: _ } => {
734                self.consume_operand(loc, (value, span), state);
735                self.mutate_place(loc, (*resume_arg, span), Deep, state);
736            }
737
738            TerminatorKind::InlineAsm {
739                asm_macro: _,
740                template: _,
741                operands,
742                options: _,
743                line_spans: _,
744                targets: _,
745                unwind: _,
746            } => {
747                for op in operands {
748                    match op {
749                        InlineAsmOperand::In { reg: _, value } => {
750                            self.consume_operand(loc, (value, span), state);
751                        }
752                        InlineAsmOperand::Out { reg: _, late: _, place, .. } => {
753                            if let Some(place) = place {
754                                self.mutate_place(loc, (*place, span), Shallow(None), state);
755                            }
756                        }
757                        InlineAsmOperand::InOut { reg: _, late: _, in_value, out_place } => {
758                            self.consume_operand(loc, (in_value, span), state);
759                            if let &Some(out_place) = out_place {
760                                self.mutate_place(loc, (out_place, span), Shallow(None), state);
761                            }
762                        }
763                        InlineAsmOperand::Const { value: _ }
764                        | InlineAsmOperand::SymFn { value: _ }
765                        | InlineAsmOperand::SymStatic { def_id: _ }
766                        | InlineAsmOperand::Label { target_index: _ } => {}
767                    }
768                }
769            }
770
771            TerminatorKind::Goto { target: _ }
772            | TerminatorKind::UnwindTerminate(_)
773            | TerminatorKind::Unreachable
774            | TerminatorKind::UnwindResume
775            | TerminatorKind::Return
776            | TerminatorKind::CoroutineDrop
777            | TerminatorKind::FalseEdge { real_target: _, imaginary_target: _ }
778            | TerminatorKind::FalseUnwind { real_target: _, unwind: _ } => {
779                // no data used, thus irrelevant to borrowck
780            }
781        }
782    }
783
784    fn visit_after_primary_terminator_effect(
785        &mut self,
786        _results: &mut Results<'tcx, Borrowck<'a, 'tcx>>,
787        state: &BorrowckDomain,
788        term: &'a Terminator<'tcx>,
789        loc: Location,
790    ) {
791        let span = term.source_info.span;
792
793        match term.kind {
794            TerminatorKind::Yield { value: _, resume: _, resume_arg: _, drop: _ } => {
795                if self.movable_coroutine {
796                    // Look for any active borrows to locals
797                    for i in state.borrows.iter() {
798                        let borrow = &self.borrow_set[i];
799                        self.check_for_local_borrow(borrow, span);
800                    }
801                }
802            }
803
804            TerminatorKind::UnwindResume
805            | TerminatorKind::Return
806            | TerminatorKind::TailCall { .. }
807            | TerminatorKind::CoroutineDrop => {
808                // Returning from the function implicitly kills storage for all locals and statics.
809                // Often, the storage will already have been killed by an explicit
810                // StorageDead, but we don't always emit those (notably on unwind paths),
811                // so this "extra check" serves as a kind of backup.
812                for i in state.borrows.iter() {
813                    let borrow = &self.borrow_set[i];
814                    self.check_for_invalidation_at_exit(loc, borrow, span);
815                }
816            }
817
818            TerminatorKind::UnwindTerminate(_)
819            | TerminatorKind::Assert { .. }
820            | TerminatorKind::Call { .. }
821            | TerminatorKind::Drop { .. }
822            | TerminatorKind::FalseEdge { real_target: _, imaginary_target: _ }
823            | TerminatorKind::FalseUnwind { real_target: _, unwind: _ }
824            | TerminatorKind::Goto { .. }
825            | TerminatorKind::SwitchInt { .. }
826            | TerminatorKind::Unreachable
827            | TerminatorKind::InlineAsm { .. } => {}
828        }
829    }
830}
831
832use self::AccessDepth::{Deep, Shallow};
833use self::ReadOrWrite::{Activation, Read, Reservation, Write};
834
835#[derive(Copy, Clone, PartialEq, Eq, Debug)]
836enum ArtificialField {
837    ArrayLength,
838    FakeBorrow,
839}
840
841#[derive(Copy, Clone, PartialEq, Eq, Debug)]
842enum AccessDepth {
843    /// From the RFC: "A *shallow* access means that the immediate
844    /// fields reached at P are accessed, but references or pointers
845    /// found within are not dereferenced. Right now, the only access
846    /// that is shallow is an assignment like `x = ...;`, which would
847    /// be a *shallow write* of `x`."
848    Shallow(Option<ArtificialField>),
849
850    /// From the RFC: "A *deep* access means that all data reachable
851    /// through the given place may be invalidated or accesses by
852    /// this action."
853    Deep,
854
855    /// Access is Deep only when there is a Drop implementation that
856    /// can reach the data behind the reference.
857    Drop,
858}
859
860/// Kind of access to a value: read or write
861/// (For informational purposes only)
862#[derive(Copy, Clone, PartialEq, Eq, Debug)]
863enum ReadOrWrite {
864    /// From the RFC: "A *read* means that the existing data may be
865    /// read, but will not be changed."
866    Read(ReadKind),
867
868    /// From the RFC: "A *write* means that the data may be mutated to
869    /// new values or otherwise invalidated (for example, it could be
870    /// de-initialized, as in a move operation).
871    Write(WriteKind),
872
873    /// For two-phase borrows, we distinguish a reservation (which is treated
874    /// like a Read) from an activation (which is treated like a write), and
875    /// each of those is furthermore distinguished from Reads/Writes above.
876    Reservation(WriteKind),
877    Activation(WriteKind, BorrowIndex),
878}
879
880/// Kind of read access to a value
881/// (For informational purposes only)
882#[derive(Copy, Clone, PartialEq, Eq, Debug)]
883enum ReadKind {
884    Borrow(BorrowKind),
885    Copy,
886}
887
888/// Kind of write access to a value
889/// (For informational purposes only)
890#[derive(Copy, Clone, PartialEq, Eq, Debug)]
891enum WriteKind {
892    StorageDeadOrDrop,
893    Replace,
894    MutableBorrow(BorrowKind),
895    Mutate,
896    Move,
897}
898
899/// When checking permissions for a place access, this flag is used to indicate that an immutable
900/// local place can be mutated.
901//
902// FIXME: @nikomatsakis suggested that this flag could be removed with the following modifications:
903// - Split `is_mutable()` into `is_assignable()` (can be directly assigned) and
904//   `is_declared_mutable()`.
905// - Take flow state into consideration in `is_assignable()` for local variables.
906#[derive(Copy, Clone, PartialEq, Eq, Debug)]
907enum LocalMutationIsAllowed {
908    Yes,
909    /// We want use of immutable upvars to cause a "write to immutable upvar"
910    /// error, not an "reassignment" error.
911    ExceptUpvars,
912    No,
913}
914
915#[derive(Copy, Clone, Debug)]
916enum InitializationRequiringAction {
917    Borrow,
918    MatchOn,
919    Use,
920    Assignment,
921    PartialAssignment,
922}
923
924#[derive(Debug)]
925struct RootPlace<'tcx> {
926    place_local: Local,
927    place_projection: &'tcx [PlaceElem<'tcx>],
928    is_local_mutation_allowed: LocalMutationIsAllowed,
929}
930
931impl InitializationRequiringAction {
932    fn as_noun(self) -> &'static str {
933        match self {
934            InitializationRequiringAction::Borrow => "borrow",
935            InitializationRequiringAction::MatchOn => "use", // no good noun
936            InitializationRequiringAction::Use => "use",
937            InitializationRequiringAction::Assignment => "assign",
938            InitializationRequiringAction::PartialAssignment => "assign to part",
939        }
940    }
941
942    fn as_verb_in_past_tense(self) -> &'static str {
943        match self {
944            InitializationRequiringAction::Borrow => "borrowed",
945            InitializationRequiringAction::MatchOn => "matched on",
946            InitializationRequiringAction::Use => "used",
947            InitializationRequiringAction::Assignment => "assigned",
948            InitializationRequiringAction::PartialAssignment => "partially assigned",
949        }
950    }
951
952    fn as_general_verb_in_past_tense(self) -> &'static str {
953        match self {
954            InitializationRequiringAction::Borrow
955            | InitializationRequiringAction::MatchOn
956            | InitializationRequiringAction::Use => "used",
957            InitializationRequiringAction::Assignment => "assigned",
958            InitializationRequiringAction::PartialAssignment => "partially assigned",
959        }
960    }
961}
962
963impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
964    fn body(&self) -> &'a Body<'tcx> {
965        self.body
966    }
967
968    /// Checks an access to the given place to see if it is allowed. Examines the set of borrows
969    /// that are in scope, as well as which paths have been initialized, to ensure that (a) the
970    /// place is initialized and (b) it is not borrowed in some way that would prevent this
971    /// access.
972    ///
973    /// Returns `true` if an error is reported.
974    fn access_place(
975        &mut self,
976        location: Location,
977        place_span: (Place<'tcx>, Span),
978        kind: (AccessDepth, ReadOrWrite),
979        is_local_mutation_allowed: LocalMutationIsAllowed,
980        state: &BorrowckDomain,
981    ) {
982        let (sd, rw) = kind;
983
984        if let Activation(_, borrow_index) = rw {
985            if self.reservation_error_reported.contains(&place_span.0) {
986                debug!(
987                    "skipping access_place for activation of invalid reservation \
988                     place: {:?} borrow_index: {:?}",
989                    place_span.0, borrow_index
990                );
991                return;
992            }
993        }
994
995        // Check is_empty() first because it's the common case, and doing that
996        // way we avoid the clone() call.
997        if !self.access_place_error_reported.is_empty()
998            && self.access_place_error_reported.contains(&(place_span.0, place_span.1))
999        {
1000            debug!(
1001                "access_place: suppressing error place_span=`{:?}` kind=`{:?}`",
1002                place_span, kind
1003            );
1004            return;
1005        }
1006
1007        let mutability_error = self.check_access_permissions(
1008            place_span,
1009            rw,
1010            is_local_mutation_allowed,
1011            state,
1012            location,
1013        );
1014        let conflict_error = self.check_access_for_conflict(location, place_span, sd, rw, state);
1015
1016        if conflict_error || mutability_error {
1017            debug!("access_place: logging error place_span=`{:?}` kind=`{:?}`", place_span, kind);
1018            self.access_place_error_reported.insert((place_span.0, place_span.1));
1019        }
1020    }
1021
1022    fn borrows_in_scope<'s>(
1023        &self,
1024        location: Location,
1025        state: &'s BorrowckDomain,
1026    ) -> Cow<'s, DenseBitSet<BorrowIndex>> {
1027        if let Some(polonius) = &self.polonius_output {
1028            // Use polonius output if it has been enabled.
1029            let location = self.location_table.start_index(location);
1030            let mut polonius_output = DenseBitSet::new_empty(self.borrow_set.len());
1031            for &idx in polonius.errors_at(location) {
1032                polonius_output.insert(idx);
1033            }
1034            Cow::Owned(polonius_output)
1035        } else {
1036            Cow::Borrowed(&state.borrows)
1037        }
1038    }
1039
1040    #[instrument(level = "debug", skip(self, state))]
1041    fn check_access_for_conflict(
1042        &mut self,
1043        location: Location,
1044        place_span: (Place<'tcx>, Span),
1045        sd: AccessDepth,
1046        rw: ReadOrWrite,
1047        state: &BorrowckDomain,
1048    ) -> bool {
1049        let mut error_reported = false;
1050
1051        let borrows_in_scope = self.borrows_in_scope(location, state);
1052
1053        each_borrow_involving_path(
1054            self,
1055            self.infcx.tcx,
1056            self.body,
1057            (sd, place_span.0),
1058            self.borrow_set,
1059            |borrow_index| borrows_in_scope.contains(borrow_index),
1060            |this, borrow_index, borrow| match (rw, borrow.kind) {
1061                // Obviously an activation is compatible with its own
1062                // reservation (or even prior activating uses of same
1063                // borrow); so don't check if they interfere.
1064                //
1065                // NOTE: *reservations* do conflict with themselves;
1066                // thus aren't injecting unsoundness w/ this check.)
1067                (Activation(_, activating), _) if activating == borrow_index => {
1068                    debug!(
1069                        "check_access_for_conflict place_span: {:?} sd: {:?} rw: {:?} \
1070                         skipping {:?} b/c activation of same borrow_index",
1071                        place_span,
1072                        sd,
1073                        rw,
1074                        (borrow_index, borrow),
1075                    );
1076                    ControlFlow::Continue(())
1077                }
1078
1079                (Read(_), BorrowKind::Shared | BorrowKind::Fake(_))
1080                | (
1081                    Read(ReadKind::Borrow(BorrowKind::Fake(FakeBorrowKind::Shallow))),
1082                    BorrowKind::Mut { .. },
1083                ) => ControlFlow::Continue(()),
1084
1085                (Reservation(_), BorrowKind::Fake(_) | BorrowKind::Shared) => {
1086                    // This used to be a future compatibility warning (to be
1087                    // disallowed on NLL). See rust-lang/rust#56254
1088                    ControlFlow::Continue(())
1089                }
1090
1091                (Write(WriteKind::Move), BorrowKind::Fake(FakeBorrowKind::Shallow)) => {
1092                    // Handled by initialization checks.
1093                    ControlFlow::Continue(())
1094                }
1095
1096                (Read(kind), BorrowKind::Mut { .. }) => {
1097                    // Reading from mere reservations of mutable-borrows is OK.
1098                    if !is_active(this.dominators(), borrow, location) {
1099                        assert!(borrow.kind.allows_two_phase_borrow());
1100                        return ControlFlow::Continue(());
1101                    }
1102
1103                    error_reported = true;
1104                    match kind {
1105                        ReadKind::Copy => {
1106                            let err = this
1107                                .report_use_while_mutably_borrowed(location, place_span, borrow);
1108                            this.buffer_error(err);
1109                        }
1110                        ReadKind::Borrow(bk) => {
1111                            let err =
1112                                this.report_conflicting_borrow(location, place_span, bk, borrow);
1113                            this.buffer_error(err);
1114                        }
1115                    }
1116                    ControlFlow::Break(())
1117                }
1118
1119                (Reservation(kind) | Activation(kind, _) | Write(kind), _) => {
1120                    match rw {
1121                        Reservation(..) => {
1122                            debug!(
1123                                "recording invalid reservation of \
1124                                 place: {:?}",
1125                                place_span.0
1126                            );
1127                            this.reservation_error_reported.insert(place_span.0);
1128                        }
1129                        Activation(_, activating) => {
1130                            debug!(
1131                                "observing check_place for activation of \
1132                                 borrow_index: {:?}",
1133                                activating
1134                            );
1135                        }
1136                        Read(..) | Write(..) => {}
1137                    }
1138
1139                    error_reported = true;
1140                    match kind {
1141                        WriteKind::MutableBorrow(bk) => {
1142                            let err =
1143                                this.report_conflicting_borrow(location, place_span, bk, borrow);
1144                            this.buffer_error(err);
1145                        }
1146                        WriteKind::StorageDeadOrDrop => this
1147                            .report_borrowed_value_does_not_live_long_enough(
1148                                location,
1149                                borrow,
1150                                place_span,
1151                                Some(WriteKind::StorageDeadOrDrop),
1152                            ),
1153                        WriteKind::Mutate => {
1154                            this.report_illegal_mutation_of_borrowed(location, place_span, borrow)
1155                        }
1156                        WriteKind::Move => {
1157                            this.report_move_out_while_borrowed(location, place_span, borrow)
1158                        }
1159                        WriteKind::Replace => {
1160                            this.report_illegal_mutation_of_borrowed(location, place_span, borrow)
1161                        }
1162                    }
1163                    ControlFlow::Break(())
1164                }
1165            },
1166        );
1167
1168        error_reported
1169    }
1170
1171    /// Through #123739, backward incompatible drops (BIDs) are introduced.
1172    /// We would like to emit lints whether borrow checking fails at these future drop locations.
1173    #[instrument(level = "debug", skip(self, state))]
1174    fn check_backward_incompatible_drop(
1175        &mut self,
1176        location: Location,
1177        place: Place<'tcx>,
1178        state: &BorrowckDomain,
1179    ) {
1180        let tcx = self.infcx.tcx;
1181        // If this type does not need `Drop`, then treat it like a `StorageDead`.
1182        // This is needed because we track the borrows of refs to thread locals,
1183        // and we'll ICE because we don't track borrows behind shared references.
1184        let sd = if place.ty(self.body, tcx).ty.needs_drop(tcx, self.body.typing_env(tcx)) {
1185            AccessDepth::Drop
1186        } else {
1187            AccessDepth::Shallow(None)
1188        };
1189
1190        let borrows_in_scope = self.borrows_in_scope(location, state);
1191
1192        // This is a very simplified version of `Self::check_access_for_conflict`.
1193        // We are here checking on BIDs and specifically still-live borrows of data involving the BIDs.
1194        each_borrow_involving_path(
1195            self,
1196            self.infcx.tcx,
1197            self.body,
1198            (sd, place),
1199            self.borrow_set,
1200            |borrow_index| borrows_in_scope.contains(borrow_index),
1201            |this, _borrow_index, borrow| {
1202                if matches!(borrow.kind, BorrowKind::Fake(_)) {
1203                    return ControlFlow::Continue(());
1204                }
1205                let borrowed = this.retrieve_borrow_spans(borrow).var_or_use_path_span();
1206                let explain = this.explain_why_borrow_contains_point(
1207                    location,
1208                    borrow,
1209                    Some((WriteKind::StorageDeadOrDrop, place)),
1210                );
1211                this.infcx.tcx.node_span_lint(
1212                    TAIL_EXPR_DROP_ORDER,
1213                    CRATE_HIR_ID,
1214                    borrowed,
1215                    |diag| {
1216                        session_diagnostics::TailExprDropOrder { borrowed }.decorate_lint(diag);
1217                        explain.add_explanation_to_diagnostic(&this, diag, "", None, None);
1218                    },
1219                );
1220                // We may stop at the first case
1221                ControlFlow::Break(())
1222            },
1223        );
1224    }
1225
1226    fn mutate_place(
1227        &mut self,
1228        location: Location,
1229        place_span: (Place<'tcx>, Span),
1230        kind: AccessDepth,
1231        state: &BorrowckDomain,
1232    ) {
1233        // Write of P[i] or *P requires P init'd.
1234        self.check_if_assigned_path_is_moved(location, place_span, state);
1235
1236        self.access_place(
1237            location,
1238            place_span,
1239            (kind, Write(WriteKind::Mutate)),
1240            LocalMutationIsAllowed::No,
1241            state,
1242        );
1243    }
1244
1245    fn consume_rvalue(
1246        &mut self,
1247        location: Location,
1248        (rvalue, span): (&'a Rvalue<'tcx>, Span),
1249        state: &BorrowckDomain,
1250    ) {
1251        match rvalue {
1252            &Rvalue::Ref(_ /*rgn*/, bk, place) => {
1253                let access_kind = match bk {
1254                    BorrowKind::Fake(FakeBorrowKind::Shallow) => {
1255                        (Shallow(Some(ArtificialField::FakeBorrow)), Read(ReadKind::Borrow(bk)))
1256                    }
1257                    BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep) => {
1258                        (Deep, Read(ReadKind::Borrow(bk)))
1259                    }
1260                    BorrowKind::Mut { .. } => {
1261                        let wk = WriteKind::MutableBorrow(bk);
1262                        if bk.allows_two_phase_borrow() {
1263                            (Deep, Reservation(wk))
1264                        } else {
1265                            (Deep, Write(wk))
1266                        }
1267                    }
1268                };
1269
1270                self.access_place(
1271                    location,
1272                    (place, span),
1273                    access_kind,
1274                    LocalMutationIsAllowed::No,
1275                    state,
1276                );
1277
1278                let action = if bk == BorrowKind::Fake(FakeBorrowKind::Shallow) {
1279                    InitializationRequiringAction::MatchOn
1280                } else {
1281                    InitializationRequiringAction::Borrow
1282                };
1283
1284                self.check_if_path_or_subpath_is_moved(
1285                    location,
1286                    action,
1287                    (place.as_ref(), span),
1288                    state,
1289                );
1290            }
1291
1292            &Rvalue::RawPtr(kind, place) => {
1293                let access_kind = match kind {
1294                    RawPtrKind::Mut => (
1295                        Deep,
1296                        Write(WriteKind::MutableBorrow(BorrowKind::Mut {
1297                            kind: MutBorrowKind::Default,
1298                        })),
1299                    ),
1300                    RawPtrKind::Const => (Deep, Read(ReadKind::Borrow(BorrowKind::Shared))),
1301                    RawPtrKind::FakeForPtrMetadata => {
1302                        (Shallow(Some(ArtificialField::ArrayLength)), Read(ReadKind::Copy))
1303                    }
1304                };
1305
1306                self.access_place(
1307                    location,
1308                    (place, span),
1309                    access_kind,
1310                    LocalMutationIsAllowed::No,
1311                    state,
1312                );
1313
1314                self.check_if_path_or_subpath_is_moved(
1315                    location,
1316                    InitializationRequiringAction::Borrow,
1317                    (place.as_ref(), span),
1318                    state,
1319                );
1320            }
1321
1322            Rvalue::ThreadLocalRef(_) => {}
1323
1324            Rvalue::Use(operand)
1325            | Rvalue::Repeat(operand, _)
1326            | Rvalue::UnaryOp(_ /*un_op*/, operand)
1327            | Rvalue::Cast(_ /*cast_kind*/, operand, _ /*ty*/)
1328            | Rvalue::ShallowInitBox(operand, _ /*ty*/) => {
1329                self.consume_operand(location, (operand, span), state)
1330            }
1331
1332            &Rvalue::CopyForDeref(place) => {
1333                self.access_place(
1334                    location,
1335                    (place, span),
1336                    (Deep, Read(ReadKind::Copy)),
1337                    LocalMutationIsAllowed::No,
1338                    state,
1339                );
1340
1341                // Finally, check if path was already moved.
1342                self.check_if_path_or_subpath_is_moved(
1343                    location,
1344                    InitializationRequiringAction::Use,
1345                    (place.as_ref(), span),
1346                    state,
1347                );
1348            }
1349
1350            &(Rvalue::Len(place) | Rvalue::Discriminant(place)) => {
1351                let af = match *rvalue {
1352                    Rvalue::Len(..) => Some(ArtificialField::ArrayLength),
1353                    Rvalue::Discriminant(..) => None,
1354                    _ => unreachable!(),
1355                };
1356                self.access_place(
1357                    location,
1358                    (place, span),
1359                    (Shallow(af), Read(ReadKind::Copy)),
1360                    LocalMutationIsAllowed::No,
1361                    state,
1362                );
1363                self.check_if_path_or_subpath_is_moved(
1364                    location,
1365                    InitializationRequiringAction::Use,
1366                    (place.as_ref(), span),
1367                    state,
1368                );
1369            }
1370
1371            Rvalue::BinaryOp(_bin_op, box (operand1, operand2)) => {
1372                self.consume_operand(location, (operand1, span), state);
1373                self.consume_operand(location, (operand2, span), state);
1374            }
1375
1376            Rvalue::NullaryOp(_op, _ty) => {
1377                // nullary ops take no dynamic input; no borrowck effect.
1378            }
1379
1380            Rvalue::Aggregate(aggregate_kind, operands) => {
1381                // We need to report back the list of mutable upvars that were
1382                // moved into the closure and subsequently used by the closure,
1383                // in order to populate our used_mut set.
1384                match **aggregate_kind {
1385                    AggregateKind::Closure(def_id, _)
1386                    | AggregateKind::CoroutineClosure(def_id, _)
1387                    | AggregateKind::Coroutine(def_id, _) => {
1388                        let def_id = def_id.expect_local();
1389                        let BorrowCheckResult { used_mut_upvars, .. } =
1390                            self.infcx.tcx.mir_borrowck(def_id);
1391                        debug!("{:?} used_mut_upvars={:?}", def_id, used_mut_upvars);
1392                        for field in used_mut_upvars {
1393                            self.propagate_closure_used_mut_upvar(&operands[*field]);
1394                        }
1395                    }
1396                    AggregateKind::Adt(..)
1397                    | AggregateKind::Array(..)
1398                    | AggregateKind::Tuple { .. }
1399                    | AggregateKind::RawPtr(..) => (),
1400                }
1401
1402                for operand in operands {
1403                    self.consume_operand(location, (operand, span), state);
1404                }
1405            }
1406
1407            Rvalue::WrapUnsafeBinder(op, _) => {
1408                self.consume_operand(location, (op, span), state);
1409            }
1410        }
1411    }
1412
1413    fn propagate_closure_used_mut_upvar(&mut self, operand: &Operand<'tcx>) {
1414        let propagate_closure_used_mut_place = |this: &mut Self, place: Place<'tcx>| {
1415            // We have three possibilities here:
1416            // a. We are modifying something through a mut-ref
1417            // b. We are modifying something that is local to our parent
1418            // c. Current body is a nested closure, and we are modifying path starting from
1419            //    a Place captured by our parent closure.
1420
1421            // Handle (c), the path being modified is exactly the path captured by our parent
1422            if let Some(field) = this.is_upvar_field_projection(place.as_ref()) {
1423                this.used_mut_upvars.push(field);
1424                return;
1425            }
1426
1427            for (place_ref, proj) in place.iter_projections().rev() {
1428                // Handle (a)
1429                if proj == ProjectionElem::Deref {
1430                    match place_ref.ty(this.body(), this.infcx.tcx).ty.kind() {
1431                        // We aren't modifying a variable directly
1432                        ty::Ref(_, _, hir::Mutability::Mut) => return,
1433
1434                        _ => {}
1435                    }
1436                }
1437
1438                // Handle (c)
1439                if let Some(field) = this.is_upvar_field_projection(place_ref) {
1440                    this.used_mut_upvars.push(field);
1441                    return;
1442                }
1443            }
1444
1445            // Handle(b)
1446            this.used_mut.insert(place.local);
1447        };
1448
1449        // This relies on the current way that by-value
1450        // captures of a closure are copied/moved directly
1451        // when generating MIR.
1452        match *operand {
1453            Operand::Move(place) | Operand::Copy(place) => {
1454                match place.as_local() {
1455                    Some(local) if !self.body.local_decls[local].is_user_variable() => {
1456                        if self.body.local_decls[local].ty.is_mutable_ptr() {
1457                            // The variable will be marked as mutable by the borrow.
1458                            return;
1459                        }
1460                        // This is an edge case where we have a `move` closure
1461                        // inside a non-move closure, and the inner closure
1462                        // contains a mutation:
1463                        //
1464                        // let mut i = 0;
1465                        // || { move || { i += 1; }; };
1466                        //
1467                        // In this case our usual strategy of assuming that the
1468                        // variable will be captured by mutable reference is
1469                        // wrong, since `i` can be copied into the inner
1470                        // closure from a shared reference.
1471                        //
1472                        // As such we have to search for the local that this
1473                        // capture comes from and mark it as being used as mut.
1474
1475                        let Some(temp_mpi) = self.move_data.rev_lookup.find_local(local) else {
1476                            bug!("temporary should be tracked");
1477                        };
1478                        let init = if let [init_index] = *self.move_data.init_path_map[temp_mpi] {
1479                            &self.move_data.inits[init_index]
1480                        } else {
1481                            bug!("temporary should be initialized exactly once")
1482                        };
1483
1484                        let InitLocation::Statement(loc) = init.location else {
1485                            bug!("temporary initialized in arguments")
1486                        };
1487
1488                        let body = self.body;
1489                        let bbd = &body[loc.block];
1490                        let stmt = &bbd.statements[loc.statement_index];
1491                        debug!("temporary assigned in: stmt={:?}", stmt);
1492
1493                        match stmt.kind {
1494                            StatementKind::Assign(box (
1495                                _,
1496                                Rvalue::Ref(_, _, source)
1497                                | Rvalue::Use(Operand::Copy(source) | Operand::Move(source)),
1498                            )) => {
1499                                propagate_closure_used_mut_place(self, source);
1500                            }
1501                            _ => {
1502                                bug!(
1503                                    "closures should only capture user variables \
1504                                 or references to user variables"
1505                                );
1506                            }
1507                        }
1508                    }
1509                    _ => propagate_closure_used_mut_place(self, place),
1510                }
1511            }
1512            Operand::Constant(..) => {}
1513        }
1514    }
1515
1516    fn consume_operand(
1517        &mut self,
1518        location: Location,
1519        (operand, span): (&'a Operand<'tcx>, Span),
1520        state: &BorrowckDomain,
1521    ) {
1522        match *operand {
1523            Operand::Copy(place) => {
1524                // copy of place: check if this is "copy of frozen path"
1525                // (FIXME: see check_loans.rs)
1526                self.access_place(
1527                    location,
1528                    (place, span),
1529                    (Deep, Read(ReadKind::Copy)),
1530                    LocalMutationIsAllowed::No,
1531                    state,
1532                );
1533
1534                // Finally, check if path was already moved.
1535                self.check_if_path_or_subpath_is_moved(
1536                    location,
1537                    InitializationRequiringAction::Use,
1538                    (place.as_ref(), span),
1539                    state,
1540                );
1541            }
1542            Operand::Move(place) => {
1543                // Check if moving from this place makes sense.
1544                self.check_movable_place(location, place);
1545
1546                // move of place: check if this is move of already borrowed path
1547                self.access_place(
1548                    location,
1549                    (place, span),
1550                    (Deep, Write(WriteKind::Move)),
1551                    LocalMutationIsAllowed::Yes,
1552                    state,
1553                );
1554
1555                // Finally, check if path was already moved.
1556                self.check_if_path_or_subpath_is_moved(
1557                    location,
1558                    InitializationRequiringAction::Use,
1559                    (place.as_ref(), span),
1560                    state,
1561                );
1562            }
1563            Operand::Constant(_) => {}
1564        }
1565    }
1566
1567    /// Checks whether a borrow of this place is invalidated when the function
1568    /// exits
1569    #[instrument(level = "debug", skip(self))]
1570    fn check_for_invalidation_at_exit(
1571        &mut self,
1572        location: Location,
1573        borrow: &BorrowData<'tcx>,
1574        span: Span,
1575    ) {
1576        let place = borrow.borrowed_place;
1577        let mut root_place = PlaceRef { local: place.local, projection: &[] };
1578
1579        // FIXME(nll-rfc#40): do more precise destructor tracking here. For now
1580        // we just know that all locals are dropped at function exit (otherwise
1581        // we'll have a memory leak) and assume that all statics have a destructor.
1582        //
1583        // FIXME: allow thread-locals to borrow other thread locals?
1584
1585        let (might_be_alive, will_be_dropped) =
1586            if self.body.local_decls[root_place.local].is_ref_to_thread_local() {
1587                // Thread-locals might be dropped after the function exits
1588                // We have to dereference the outer reference because
1589                // borrows don't conflict behind shared references.
1590                root_place.projection = TyCtxtConsts::DEREF_PROJECTION;
1591                (true, true)
1592            } else {
1593                (false, self.locals_are_invalidated_at_exit)
1594            };
1595
1596        if !will_be_dropped {
1597            debug!("place_is_invalidated_at_exit({:?}) - won't be dropped", place);
1598            return;
1599        }
1600
1601        let sd = if might_be_alive { Deep } else { Shallow(None) };
1602
1603        if places_conflict::borrow_conflicts_with_place(
1604            self.infcx.tcx,
1605            self.body,
1606            place,
1607            borrow.kind,
1608            root_place,
1609            sd,
1610            places_conflict::PlaceConflictBias::Overlap,
1611        ) {
1612            debug!("check_for_invalidation_at_exit({:?}): INVALID", place);
1613            // FIXME: should be talking about the region lifetime instead
1614            // of just a span here.
1615            let span = self.infcx.tcx.sess.source_map().end_point(span);
1616            self.report_borrowed_value_does_not_live_long_enough(
1617                location,
1618                borrow,
1619                (place, span),
1620                None,
1621            )
1622        }
1623    }
1624
1625    /// Reports an error if this is a borrow of local data.
1626    /// This is called for all Yield expressions on movable coroutines
1627    fn check_for_local_borrow(&mut self, borrow: &BorrowData<'tcx>, yield_span: Span) {
1628        debug!("check_for_local_borrow({:?})", borrow);
1629
1630        if borrow_of_local_data(borrow.borrowed_place) {
1631            let err = self.cannot_borrow_across_coroutine_yield(
1632                self.retrieve_borrow_spans(borrow).var_or_use(),
1633                yield_span,
1634            );
1635
1636            self.buffer_error(err);
1637        }
1638    }
1639
1640    fn check_activations(&mut self, location: Location, span: Span, state: &BorrowckDomain) {
1641        // Two-phase borrow support: For each activation that is newly
1642        // generated at this statement, check if it interferes with
1643        // another borrow.
1644        for &borrow_index in self.borrow_set.activations_at_location(location) {
1645            let borrow = &self.borrow_set[borrow_index];
1646
1647            // only mutable borrows should be 2-phase
1648            assert!(match borrow.kind {
1649                BorrowKind::Shared | BorrowKind::Fake(_) => false,
1650                BorrowKind::Mut { .. } => true,
1651            });
1652
1653            self.access_place(
1654                location,
1655                (borrow.borrowed_place, span),
1656                (Deep, Activation(WriteKind::MutableBorrow(borrow.kind), borrow_index)),
1657                LocalMutationIsAllowed::No,
1658                state,
1659            );
1660            // We do not need to call `check_if_path_or_subpath_is_moved`
1661            // again, as we already called it when we made the
1662            // initial reservation.
1663        }
1664    }
1665
1666    fn check_movable_place(&mut self, location: Location, place: Place<'tcx>) {
1667        use IllegalMoveOriginKind::*;
1668
1669        let body = self.body;
1670        let tcx = self.infcx.tcx;
1671        let mut place_ty = PlaceTy::from_ty(body.local_decls[place.local].ty);
1672        for (place_ref, elem) in place.iter_projections() {
1673            match elem {
1674                ProjectionElem::Deref => match place_ty.ty.kind() {
1675                    ty::Ref(..) | ty::RawPtr(..) => {
1676                        self.move_errors.push(MoveError::new(
1677                            place,
1678                            location,
1679                            BorrowedContent {
1680                                target_place: place_ref.project_deeper(&[elem], tcx),
1681                            },
1682                        ));
1683                        return;
1684                    }
1685                    ty::Adt(adt, _) => {
1686                        if !adt.is_box() {
1687                            bug!("Adt should be a box type when Place is deref");
1688                        }
1689                    }
1690                    ty::Bool
1691                    | ty::Char
1692                    | ty::Int(_)
1693                    | ty::Uint(_)
1694                    | ty::Float(_)
1695                    | ty::Foreign(_)
1696                    | ty::Str
1697                    | ty::Array(_, _)
1698                    | ty::Pat(_, _)
1699                    | ty::Slice(_)
1700                    | ty::FnDef(_, _)
1701                    | ty::FnPtr(..)
1702                    | ty::Dynamic(_, _, _)
1703                    | ty::Closure(_, _)
1704                    | ty::CoroutineClosure(_, _)
1705                    | ty::Coroutine(_, _)
1706                    | ty::CoroutineWitness(..)
1707                    | ty::Never
1708                    | ty::Tuple(_)
1709                    | ty::UnsafeBinder(_)
1710                    | ty::Alias(_, _)
1711                    | ty::Param(_)
1712                    | ty::Bound(_, _)
1713                    | ty::Infer(_)
1714                    | ty::Error(_)
1715                    | ty::Placeholder(_) => {
1716                        bug!("When Place is Deref it's type shouldn't be {place_ty:#?}")
1717                    }
1718                },
1719                ProjectionElem::Field(_, _) => match place_ty.ty.kind() {
1720                    ty::Adt(adt, _) => {
1721                        if adt.has_dtor(tcx) {
1722                            self.move_errors.push(MoveError::new(
1723                                place,
1724                                location,
1725                                InteriorOfTypeWithDestructor { container_ty: place_ty.ty },
1726                            ));
1727                            return;
1728                        }
1729                    }
1730                    ty::Closure(..)
1731                    | ty::CoroutineClosure(..)
1732                    | ty::Coroutine(_, _)
1733                    | ty::Tuple(_) => (),
1734                    ty::Bool
1735                    | ty::Char
1736                    | ty::Int(_)
1737                    | ty::Uint(_)
1738                    | ty::Float(_)
1739                    | ty::Foreign(_)
1740                    | ty::Str
1741                    | ty::Array(_, _)
1742                    | ty::Pat(_, _)
1743                    | ty::Slice(_)
1744                    | ty::RawPtr(_, _)
1745                    | ty::Ref(_, _, _)
1746                    | ty::FnDef(_, _)
1747                    | ty::FnPtr(..)
1748                    | ty::Dynamic(_, _, _)
1749                    | ty::CoroutineWitness(..)
1750                    | ty::Never
1751                    | ty::UnsafeBinder(_)
1752                    | ty::Alias(_, _)
1753                    | ty::Param(_)
1754                    | ty::Bound(_, _)
1755                    | ty::Infer(_)
1756                    | ty::Error(_)
1757                    | ty::Placeholder(_) => bug!(
1758                        "When Place contains ProjectionElem::Field it's type shouldn't be {place_ty:#?}"
1759                    ),
1760                },
1761                ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
1762                    match place_ty.ty.kind() {
1763                        ty::Slice(_) => {
1764                            self.move_errors.push(MoveError::new(
1765                                place,
1766                                location,
1767                                InteriorOfSliceOrArray { ty: place_ty.ty, is_index: false },
1768                            ));
1769                            return;
1770                        }
1771                        ty::Array(_, _) => (),
1772                        _ => bug!("Unexpected type {:#?}", place_ty.ty),
1773                    }
1774                }
1775                ProjectionElem::Index(_) => match place_ty.ty.kind() {
1776                    ty::Array(..) | ty::Slice(..) => {
1777                        self.move_errors.push(MoveError::new(
1778                            place,
1779                            location,
1780                            InteriorOfSliceOrArray { ty: place_ty.ty, is_index: true },
1781                        ));
1782                        return;
1783                    }
1784                    _ => bug!("Unexpected type {place_ty:#?}"),
1785                },
1786                // `OpaqueCast`: only transmutes the type, so no moves there.
1787                // `Downcast`  : only changes information about a `Place` without moving.
1788                // `Subtype`   : only transmutes the type, so no moves.
1789                // So it's safe to skip these.
1790                ProjectionElem::OpaqueCast(_)
1791                | ProjectionElem::Subtype(_)
1792                | ProjectionElem::Downcast(_, _)
1793                | ProjectionElem::UnwrapUnsafeBinder(_) => (),
1794            }
1795
1796            place_ty = place_ty.projection_ty(tcx, elem);
1797        }
1798    }
1799
1800    fn check_if_full_path_is_moved(
1801        &mut self,
1802        location: Location,
1803        desired_action: InitializationRequiringAction,
1804        place_span: (PlaceRef<'tcx>, Span),
1805        state: &BorrowckDomain,
1806    ) {
1807        let maybe_uninits = &state.uninits;
1808
1809        // Bad scenarios:
1810        //
1811        // 1. Move of `a.b.c`, use of `a.b.c`
1812        // 2. Move of `a.b.c`, use of `a.b.c.d` (without first reinitializing `a.b.c.d`)
1813        // 3. Uninitialized `(a.b.c: &_)`, use of `*a.b.c`; note that with
1814        //    partial initialization support, one might have `a.x`
1815        //    initialized but not `a.b`.
1816        //
1817        // OK scenarios:
1818        //
1819        // 4. Move of `a.b.c`, use of `a.b.d`
1820        // 5. Uninitialized `a.x`, initialized `a.b`, use of `a.b`
1821        // 6. Copied `(a.b: &_)`, use of `*(a.b).c`; note that `a.b`
1822        //    must have been initialized for the use to be sound.
1823        // 7. Move of `a.b.c` then reinit of `a.b.c.d`, use of `a.b.c.d`
1824
1825        // The dataflow tracks shallow prefixes distinctly (that is,
1826        // field-accesses on P distinctly from P itself), in order to
1827        // track substructure initialization separately from the whole
1828        // structure.
1829        //
1830        // E.g., when looking at (*a.b.c).d, if the closest prefix for
1831        // which we have a MovePath is `a.b`, then that means that the
1832        // initialization state of `a.b` is all we need to inspect to
1833        // know if `a.b.c` is valid (and from that we infer that the
1834        // dereference and `.d` access is also valid, since we assume
1835        // `a.b.c` is assigned a reference to an initialized and
1836        // well-formed record structure.)
1837
1838        // Therefore, if we seek out the *closest* prefix for which we
1839        // have a MovePath, that should capture the initialization
1840        // state for the place scenario.
1841        //
1842        // This code covers scenarios 1, 2, and 3.
1843
1844        debug!("check_if_full_path_is_moved place: {:?}", place_span.0);
1845        let (prefix, mpi) = self.move_path_closest_to(place_span.0);
1846        if maybe_uninits.contains(mpi) {
1847            self.report_use_of_moved_or_uninitialized(
1848                location,
1849                desired_action,
1850                (prefix, place_span.0, place_span.1),
1851                mpi,
1852            );
1853        } // Only query longest prefix with a MovePath, not further
1854        // ancestors; dataflow recurs on children when parents
1855        // move (to support partial (re)inits).
1856        //
1857        // (I.e., querying parents breaks scenario 7; but may want
1858        // to do such a query based on partial-init feature-gate.)
1859    }
1860
1861    /// Subslices correspond to multiple move paths, so we iterate through the
1862    /// elements of the base array. For each element we check
1863    ///
1864    /// * Does this element overlap with our slice.
1865    /// * Is any part of it uninitialized.
1866    fn check_if_subslice_element_is_moved(
1867        &mut self,
1868        location: Location,
1869        desired_action: InitializationRequiringAction,
1870        place_span: (PlaceRef<'tcx>, Span),
1871        maybe_uninits: &MixedBitSet<MovePathIndex>,
1872        from: u64,
1873        to: u64,
1874    ) {
1875        if let Some(mpi) = self.move_path_for_place(place_span.0) {
1876            let move_paths = &self.move_data.move_paths;
1877
1878            let root_path = &move_paths[mpi];
1879            for (child_mpi, child_move_path) in root_path.children(move_paths) {
1880                let last_proj = child_move_path.place.projection.last().unwrap();
1881                if let ProjectionElem::ConstantIndex { offset, from_end, .. } = last_proj {
1882                    debug_assert!(!from_end, "Array constant indexing shouldn't be `from_end`.");
1883
1884                    if (from..to).contains(offset) {
1885                        let uninit_child =
1886                            self.move_data.find_in_move_path_or_its_descendants(child_mpi, |mpi| {
1887                                maybe_uninits.contains(mpi)
1888                            });
1889
1890                        if let Some(uninit_child) = uninit_child {
1891                            self.report_use_of_moved_or_uninitialized(
1892                                location,
1893                                desired_action,
1894                                (place_span.0, place_span.0, place_span.1),
1895                                uninit_child,
1896                            );
1897                            return; // don't bother finding other problems.
1898                        }
1899                    }
1900                }
1901            }
1902        }
1903    }
1904
1905    fn check_if_path_or_subpath_is_moved(
1906        &mut self,
1907        location: Location,
1908        desired_action: InitializationRequiringAction,
1909        place_span: (PlaceRef<'tcx>, Span),
1910        state: &BorrowckDomain,
1911    ) {
1912        let maybe_uninits = &state.uninits;
1913
1914        // Bad scenarios:
1915        //
1916        // 1. Move of `a.b.c`, use of `a` or `a.b`
1917        //    partial initialization support, one might have `a.x`
1918        //    initialized but not `a.b`.
1919        // 2. All bad scenarios from `check_if_full_path_is_moved`
1920        //
1921        // OK scenarios:
1922        //
1923        // 3. Move of `a.b.c`, use of `a.b.d`
1924        // 4. Uninitialized `a.x`, initialized `a.b`, use of `a.b`
1925        // 5. Copied `(a.b: &_)`, use of `*(a.b).c`; note that `a.b`
1926        //    must have been initialized for the use to be sound.
1927        // 6. Move of `a.b.c` then reinit of `a.b.c.d`, use of `a.b.c.d`
1928
1929        self.check_if_full_path_is_moved(location, desired_action, place_span, state);
1930
1931        if let Some((place_base, ProjectionElem::Subslice { from, to, from_end: false })) =
1932            place_span.0.last_projection()
1933        {
1934            let place_ty = place_base.ty(self.body(), self.infcx.tcx);
1935            if let ty::Array(..) = place_ty.ty.kind() {
1936                self.check_if_subslice_element_is_moved(
1937                    location,
1938                    desired_action,
1939                    (place_base, place_span.1),
1940                    maybe_uninits,
1941                    from,
1942                    to,
1943                );
1944                return;
1945            }
1946        }
1947
1948        // A move of any shallow suffix of `place` also interferes
1949        // with an attempt to use `place`. This is scenario 3 above.
1950        //
1951        // (Distinct from handling of scenarios 1+2+4 above because
1952        // `place` does not interfere with suffixes of its prefixes,
1953        // e.g., `a.b.c` does not interfere with `a.b.d`)
1954        //
1955        // This code covers scenario 1.
1956
1957        debug!("check_if_path_or_subpath_is_moved place: {:?}", place_span.0);
1958        if let Some(mpi) = self.move_path_for_place(place_span.0) {
1959            let uninit_mpi = self
1960                .move_data
1961                .find_in_move_path_or_its_descendants(mpi, |mpi| maybe_uninits.contains(mpi));
1962
1963            if let Some(uninit_mpi) = uninit_mpi {
1964                self.report_use_of_moved_or_uninitialized(
1965                    location,
1966                    desired_action,
1967                    (place_span.0, place_span.0, place_span.1),
1968                    uninit_mpi,
1969                );
1970                return; // don't bother finding other problems.
1971            }
1972        }
1973    }
1974
1975    /// Currently MoveData does not store entries for all places in
1976    /// the input MIR. For example it will currently filter out
1977    /// places that are Copy; thus we do not track places of shared
1978    /// reference type. This routine will walk up a place along its
1979    /// prefixes, searching for a foundational place that *is*
1980    /// tracked in the MoveData.
1981    ///
1982    /// An Err result includes a tag indicated why the search failed.
1983    /// Currently this can only occur if the place is built off of a
1984    /// static variable, as we do not track those in the MoveData.
1985    fn move_path_closest_to(&mut self, place: PlaceRef<'tcx>) -> (PlaceRef<'tcx>, MovePathIndex) {
1986        match self.move_data.rev_lookup.find(place) {
1987            LookupResult::Parent(Some(mpi)) | LookupResult::Exact(mpi) => {
1988                (self.move_data.move_paths[mpi].place.as_ref(), mpi)
1989            }
1990            LookupResult::Parent(None) => panic!("should have move path for every Local"),
1991        }
1992    }
1993
1994    fn move_path_for_place(&mut self, place: PlaceRef<'tcx>) -> Option<MovePathIndex> {
1995        // If returns None, then there is no move path corresponding
1996        // to a direct owner of `place` (which means there is nothing
1997        // that borrowck tracks for its analysis).
1998
1999        match self.move_data.rev_lookup.find(place) {
2000            LookupResult::Parent(_) => None,
2001            LookupResult::Exact(mpi) => Some(mpi),
2002        }
2003    }
2004
2005    fn check_if_assigned_path_is_moved(
2006        &mut self,
2007        location: Location,
2008        (place, span): (Place<'tcx>, Span),
2009        state: &BorrowckDomain,
2010    ) {
2011        debug!("check_if_assigned_path_is_moved place: {:?}", place);
2012
2013        // None case => assigning to `x` does not require `x` be initialized.
2014        for (place_base, elem) in place.iter_projections().rev() {
2015            match elem {
2016                ProjectionElem::Index(_/*operand*/) |
2017                ProjectionElem::Subtype(_) |
2018                ProjectionElem::OpaqueCast(_) |
2019                ProjectionElem::ConstantIndex { .. } |
2020                // assigning to P[i] requires P to be valid.
2021                ProjectionElem::Downcast(_/*adt_def*/, _/*variant_idx*/) =>
2022                // assigning to (P->variant) is okay if assigning to `P` is okay
2023                //
2024                // FIXME: is this true even if P is an adt with a dtor?
2025                { }
2026
2027                ProjectionElem::UnwrapUnsafeBinder(_) => {
2028                    check_parent_of_field(self, location, place_base, span, state);
2029                }
2030
2031                // assigning to (*P) requires P to be initialized
2032                ProjectionElem::Deref => {
2033                    self.check_if_full_path_is_moved(
2034                        location, InitializationRequiringAction::Use,
2035                        (place_base, span), state);
2036                    // (base initialized; no need to
2037                    // recur further)
2038                    break;
2039                }
2040
2041                ProjectionElem::Subslice { .. } => {
2042                    panic!("we don't allow assignments to subslices, location: {location:?}");
2043                }
2044
2045                ProjectionElem::Field(..) => {
2046                    // if type of `P` has a dtor, then
2047                    // assigning to `P.f` requires `P` itself
2048                    // be already initialized
2049                    let tcx = self.infcx.tcx;
2050                    let base_ty = place_base.ty(self.body(), tcx).ty;
2051                    match base_ty.kind() {
2052                        ty::Adt(def, _) if def.has_dtor(tcx) => {
2053                            self.check_if_path_or_subpath_is_moved(
2054                                location, InitializationRequiringAction::Assignment,
2055                                (place_base, span), state);
2056
2057                            // (base initialized; no need to
2058                            // recur further)
2059                            break;
2060                        }
2061
2062                        // Once `let s; s.x = V; read(s.x);`,
2063                        // is allowed, remove this match arm.
2064                        ty::Adt(..) | ty::Tuple(..) => {
2065                            check_parent_of_field(self, location, place_base, span, state);
2066                        }
2067
2068                        _ => {}
2069                    }
2070                }
2071            }
2072        }
2073
2074        fn check_parent_of_field<'a, 'tcx>(
2075            this: &mut MirBorrowckCtxt<'a, '_, 'tcx>,
2076            location: Location,
2077            base: PlaceRef<'tcx>,
2078            span: Span,
2079            state: &BorrowckDomain,
2080        ) {
2081            // rust-lang/rust#21232: Until Rust allows reads from the
2082            // initialized parts of partially initialized structs, we
2083            // will, starting with the 2018 edition, reject attempts
2084            // to write to structs that are not fully initialized.
2085            //
2086            // In other words, *until* we allow this:
2087            //
2088            // 1. `let mut s; s.x = Val; read(s.x);`
2089            //
2090            // we will for now disallow this:
2091            //
2092            // 2. `let mut s; s.x = Val;`
2093            //
2094            // and also this:
2095            //
2096            // 3. `let mut s = ...; drop(s); s.x=Val;`
2097            //
2098            // This does not use check_if_path_or_subpath_is_moved,
2099            // because we want to *allow* reinitializations of fields:
2100            // e.g., want to allow
2101            //
2102            // `let mut s = ...; drop(s.x); s.x=Val;`
2103            //
2104            // This does not use check_if_full_path_is_moved on
2105            // `base`, because that would report an error about the
2106            // `base` as a whole, but in this scenario we *really*
2107            // want to report an error about the actual thing that was
2108            // moved, which may be some prefix of `base`.
2109
2110            // Shallow so that we'll stop at any dereference; we'll
2111            // report errors about issues with such bases elsewhere.
2112            let maybe_uninits = &state.uninits;
2113
2114            // Find the shortest uninitialized prefix you can reach
2115            // without going over a Deref.
2116            let mut shortest_uninit_seen = None;
2117            for prefix in this.prefixes(base, PrefixSet::Shallow) {
2118                let Some(mpi) = this.move_path_for_place(prefix) else { continue };
2119
2120                if maybe_uninits.contains(mpi) {
2121                    debug!(
2122                        "check_parent_of_field updating shortest_uninit_seen from {:?} to {:?}",
2123                        shortest_uninit_seen,
2124                        Some((prefix, mpi))
2125                    );
2126                    shortest_uninit_seen = Some((prefix, mpi));
2127                } else {
2128                    debug!("check_parent_of_field {:?} is definitely initialized", (prefix, mpi));
2129                }
2130            }
2131
2132            if let Some((prefix, mpi)) = shortest_uninit_seen {
2133                // Check for a reassignment into an uninitialized field of a union (for example,
2134                // after a move out). In this case, do not report an error here. There is an
2135                // exception, if this is the first assignment into the union (that is, there is
2136                // no move out from an earlier location) then this is an attempt at initialization
2137                // of the union - we should error in that case.
2138                let tcx = this.infcx.tcx;
2139                if base.ty(this.body(), tcx).ty.is_union()
2140                    && this.move_data.path_map[mpi].iter().any(|moi| {
2141                        this.move_data.moves[*moi].source.is_predecessor_of(location, this.body)
2142                    })
2143                {
2144                    return;
2145                }
2146
2147                this.report_use_of_moved_or_uninitialized(
2148                    location,
2149                    InitializationRequiringAction::PartialAssignment,
2150                    (prefix, base, span),
2151                    mpi,
2152                );
2153
2154                // rust-lang/rust#21232, #54499, #54986: during period where we reject
2155                // partial initialization, do not complain about unnecessary `mut` on
2156                // an attempt to do a partial initialization.
2157                this.used_mut.insert(base.local);
2158            }
2159        }
2160    }
2161
2162    /// Checks the permissions for the given place and read or write kind
2163    ///
2164    /// Returns `true` if an error is reported.
2165    fn check_access_permissions(
2166        &mut self,
2167        (place, span): (Place<'tcx>, Span),
2168        kind: ReadOrWrite,
2169        is_local_mutation_allowed: LocalMutationIsAllowed,
2170        state: &BorrowckDomain,
2171        location: Location,
2172    ) -> bool {
2173        debug!(
2174            "check_access_permissions({:?}, {:?}, is_local_mutation_allowed: {:?})",
2175            place, kind, is_local_mutation_allowed
2176        );
2177
2178        let error_access;
2179        let the_place_err;
2180
2181        match kind {
2182            Reservation(WriteKind::MutableBorrow(BorrowKind::Mut { kind: mut_borrow_kind }))
2183            | Write(WriteKind::MutableBorrow(BorrowKind::Mut { kind: mut_borrow_kind })) => {
2184                let is_local_mutation_allowed = match mut_borrow_kind {
2185                    // `ClosureCapture` is used for mutable variable with an immutable binding.
2186                    // This is only behaviour difference between `ClosureCapture` and mutable
2187                    // borrows.
2188                    MutBorrowKind::ClosureCapture => LocalMutationIsAllowed::Yes,
2189                    MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow => {
2190                        is_local_mutation_allowed
2191                    }
2192                };
2193                match self.is_mutable(place.as_ref(), is_local_mutation_allowed) {
2194                    Ok(root_place) => {
2195                        self.add_used_mut(root_place, state);
2196                        return false;
2197                    }
2198                    Err(place_err) => {
2199                        error_access = AccessKind::MutableBorrow;
2200                        the_place_err = place_err;
2201                    }
2202                }
2203            }
2204            Reservation(WriteKind::Mutate) | Write(WriteKind::Mutate) => {
2205                match self.is_mutable(place.as_ref(), is_local_mutation_allowed) {
2206                    Ok(root_place) => {
2207                        self.add_used_mut(root_place, state);
2208                        return false;
2209                    }
2210                    Err(place_err) => {
2211                        error_access = AccessKind::Mutate;
2212                        the_place_err = place_err;
2213                    }
2214                }
2215            }
2216
2217            Reservation(
2218                WriteKind::Move
2219                | WriteKind::Replace
2220                | WriteKind::StorageDeadOrDrop
2221                | WriteKind::MutableBorrow(BorrowKind::Shared)
2222                | WriteKind::MutableBorrow(BorrowKind::Fake(_)),
2223            )
2224            | Write(
2225                WriteKind::Move
2226                | WriteKind::Replace
2227                | WriteKind::StorageDeadOrDrop
2228                | WriteKind::MutableBorrow(BorrowKind::Shared)
2229                | WriteKind::MutableBorrow(BorrowKind::Fake(_)),
2230            ) => {
2231                if self.is_mutable(place.as_ref(), is_local_mutation_allowed).is_err()
2232                    && !self.has_buffered_diags()
2233                {
2234                    // rust-lang/rust#46908: In pure NLL mode this code path should be
2235                    // unreachable, but we use `span_delayed_bug` because we can hit this when
2236                    // dereferencing a non-Copy raw pointer *and* have `-Ztreat-err-as-bug`
2237                    // enabled. We don't want to ICE for that case, as other errors will have
2238                    // been emitted (#52262).
2239                    self.dcx().span_delayed_bug(
2240                        span,
2241                        format!(
2242                            "Accessing `{place:?}` with the kind `{kind:?}` shouldn't be possible",
2243                        ),
2244                    );
2245                }
2246                return false;
2247            }
2248            Activation(..) => {
2249                // permission checks are done at Reservation point.
2250                return false;
2251            }
2252            Read(
2253                ReadKind::Borrow(BorrowKind::Mut { .. } | BorrowKind::Shared | BorrowKind::Fake(_))
2254                | ReadKind::Copy,
2255            ) => {
2256                // Access authorized
2257                return false;
2258            }
2259        }
2260
2261        // rust-lang/rust#21232, #54986: during period where we reject
2262        // partial initialization, do not complain about mutability
2263        // errors except for actual mutation (as opposed to an attempt
2264        // to do a partial initialization).
2265        let previously_initialized = self.is_local_ever_initialized(place.local, state);
2266
2267        // at this point, we have set up the error reporting state.
2268        if let Some(init_index) = previously_initialized {
2269            if let (AccessKind::Mutate, Some(_)) = (error_access, place.as_local()) {
2270                // If this is a mutate access to an immutable local variable with no projections
2271                // report the error as an illegal reassignment
2272                let init = &self.move_data.inits[init_index];
2273                let assigned_span = init.span(self.body);
2274                self.report_illegal_reassignment((place, span), assigned_span, place);
2275            } else {
2276                self.report_mutability_error(place, span, the_place_err, error_access, location)
2277            }
2278            true
2279        } else {
2280            false
2281        }
2282    }
2283
2284    fn is_local_ever_initialized(&self, local: Local, state: &BorrowckDomain) -> Option<InitIndex> {
2285        let mpi = self.move_data.rev_lookup.find_local(local)?;
2286        let ii = &self.move_data.init_path_map[mpi];
2287        ii.into_iter().find(|&&index| state.ever_inits.contains(index)).copied()
2288    }
2289
2290    /// Adds the place into the used mutable variables set
2291    fn add_used_mut(&mut self, root_place: RootPlace<'tcx>, state: &BorrowckDomain) {
2292        match root_place {
2293            RootPlace { place_local: local, place_projection: [], is_local_mutation_allowed } => {
2294                // If the local may have been initialized, and it is now currently being
2295                // mutated, then it is justified to be annotated with the `mut`
2296                // keyword, since the mutation may be a possible reassignment.
2297                if is_local_mutation_allowed != LocalMutationIsAllowed::Yes
2298                    && self.is_local_ever_initialized(local, state).is_some()
2299                {
2300                    self.used_mut.insert(local);
2301                }
2302            }
2303            RootPlace {
2304                place_local: _,
2305                place_projection: _,
2306                is_local_mutation_allowed: LocalMutationIsAllowed::Yes,
2307            } => {}
2308            RootPlace {
2309                place_local,
2310                place_projection: place_projection @ [.., _],
2311                is_local_mutation_allowed: _,
2312            } => {
2313                if let Some(field) = self.is_upvar_field_projection(PlaceRef {
2314                    local: place_local,
2315                    projection: place_projection,
2316                }) {
2317                    self.used_mut_upvars.push(field);
2318                }
2319            }
2320        }
2321    }
2322
2323    /// Whether this value can be written or borrowed mutably.
2324    /// Returns the root place if the place passed in is a projection.
2325    fn is_mutable(
2326        &self,
2327        place: PlaceRef<'tcx>,
2328        is_local_mutation_allowed: LocalMutationIsAllowed,
2329    ) -> Result<RootPlace<'tcx>, PlaceRef<'tcx>> {
2330        debug!("is_mutable: place={:?}, is_local...={:?}", place, is_local_mutation_allowed);
2331        match place.last_projection() {
2332            None => {
2333                let local = &self.body.local_decls[place.local];
2334                match local.mutability {
2335                    Mutability::Not => match is_local_mutation_allowed {
2336                        LocalMutationIsAllowed::Yes => Ok(RootPlace {
2337                            place_local: place.local,
2338                            place_projection: place.projection,
2339                            is_local_mutation_allowed: LocalMutationIsAllowed::Yes,
2340                        }),
2341                        LocalMutationIsAllowed::ExceptUpvars => Ok(RootPlace {
2342                            place_local: place.local,
2343                            place_projection: place.projection,
2344                            is_local_mutation_allowed: LocalMutationIsAllowed::ExceptUpvars,
2345                        }),
2346                        LocalMutationIsAllowed::No => Err(place),
2347                    },
2348                    Mutability::Mut => Ok(RootPlace {
2349                        place_local: place.local,
2350                        place_projection: place.projection,
2351                        is_local_mutation_allowed,
2352                    }),
2353                }
2354            }
2355            Some((place_base, elem)) => {
2356                match elem {
2357                    ProjectionElem::Deref => {
2358                        let base_ty = place_base.ty(self.body(), self.infcx.tcx).ty;
2359
2360                        // Check the kind of deref to decide
2361                        match base_ty.kind() {
2362                            ty::Ref(_, _, mutbl) => {
2363                                match mutbl {
2364                                    // Shared borrowed data is never mutable
2365                                    hir::Mutability::Not => Err(place),
2366                                    // Mutably borrowed data is mutable, but only if we have a
2367                                    // unique path to the `&mut`
2368                                    hir::Mutability::Mut => {
2369                                        let mode = match self.is_upvar_field_projection(place) {
2370                                            Some(field)
2371                                                if self.upvars[field.index()].is_by_ref() =>
2372                                            {
2373                                                is_local_mutation_allowed
2374                                            }
2375                                            _ => LocalMutationIsAllowed::Yes,
2376                                        };
2377
2378                                        self.is_mutable(place_base, mode)
2379                                    }
2380                                }
2381                            }
2382                            ty::RawPtr(_, mutbl) => {
2383                                match mutbl {
2384                                    // `*const` raw pointers are not mutable
2385                                    hir::Mutability::Not => Err(place),
2386                                    // `*mut` raw pointers are always mutable, regardless of
2387                                    // context. The users have to check by themselves.
2388                                    hir::Mutability::Mut => Ok(RootPlace {
2389                                        place_local: place.local,
2390                                        place_projection: place.projection,
2391                                        is_local_mutation_allowed,
2392                                    }),
2393                                }
2394                            }
2395                            // `Box<T>` owns its content, so mutable if its location is mutable
2396                            _ if base_ty.is_box() => {
2397                                self.is_mutable(place_base, is_local_mutation_allowed)
2398                            }
2399                            // Deref should only be for reference, pointers or boxes
2400                            _ => bug!("Deref of unexpected type: {:?}", base_ty),
2401                        }
2402                    }
2403                    // All other projections are owned by their base path, so mutable if
2404                    // base path is mutable
2405                    ProjectionElem::Field(..)
2406                    | ProjectionElem::Index(..)
2407                    | ProjectionElem::ConstantIndex { .. }
2408                    | ProjectionElem::Subslice { .. }
2409                    | ProjectionElem::Subtype(..)
2410                    | ProjectionElem::OpaqueCast { .. }
2411                    | ProjectionElem::Downcast(..)
2412                    | ProjectionElem::UnwrapUnsafeBinder(_) => {
2413                        let upvar_field_projection = self.is_upvar_field_projection(place);
2414                        if let Some(field) = upvar_field_projection {
2415                            let upvar = &self.upvars[field.index()];
2416                            debug!(
2417                                "is_mutable: upvar.mutability={:?} local_mutation_is_allowed={:?} \
2418                                 place={:?}, place_base={:?}",
2419                                upvar, is_local_mutation_allowed, place, place_base
2420                            );
2421                            match (upvar.mutability, is_local_mutation_allowed) {
2422                                (
2423                                    Mutability::Not,
2424                                    LocalMutationIsAllowed::No
2425                                    | LocalMutationIsAllowed::ExceptUpvars,
2426                                ) => Err(place),
2427                                (Mutability::Not, LocalMutationIsAllowed::Yes)
2428                                | (Mutability::Mut, _) => {
2429                                    // Subtle: this is an upvar reference, so it looks like
2430                                    // `self.foo` -- we want to double check that the location
2431                                    // `*self` is mutable (i.e., this is not a `Fn` closure). But
2432                                    // if that check succeeds, we want to *blame* the mutability on
2433                                    // `place` (that is, `self.foo`). This is used to propagate the
2434                                    // info about whether mutability declarations are used
2435                                    // outwards, so that we register the outer variable as mutable.
2436                                    // Otherwise a test like this fails to record the `mut` as
2437                                    // needed:
2438                                    // ```
2439                                    // fn foo<F: FnOnce()>(_f: F) { }
2440                                    // fn main() {
2441                                    //     let var = Vec::new();
2442                                    //     foo(move || {
2443                                    //         var.push(1);
2444                                    //     });
2445                                    // }
2446                                    // ```
2447                                    let _ =
2448                                        self.is_mutable(place_base, is_local_mutation_allowed)?;
2449                                    Ok(RootPlace {
2450                                        place_local: place.local,
2451                                        place_projection: place.projection,
2452                                        is_local_mutation_allowed,
2453                                    })
2454                                }
2455                            }
2456                        } else {
2457                            self.is_mutable(place_base, is_local_mutation_allowed)
2458                        }
2459                    }
2460                }
2461            }
2462        }
2463    }
2464
2465    /// If `place` is a field projection, and the field is being projected from a closure type,
2466    /// then returns the index of the field being projected. Note that this closure will always
2467    /// be `self` in the current MIR, because that is the only time we directly access the fields
2468    /// of a closure type.
2469    fn is_upvar_field_projection(&self, place_ref: PlaceRef<'tcx>) -> Option<FieldIdx> {
2470        path_utils::is_upvar_field_projection(self.infcx.tcx, &self.upvars, place_ref, self.body())
2471    }
2472
2473    fn dominators(&self) -> &Dominators<BasicBlock> {
2474        // `BasicBlocks` computes dominators on-demand and caches them.
2475        self.body.basic_blocks.dominators()
2476    }
2477
2478    fn lint_unused_mut(&self) {
2479        let tcx = self.infcx.tcx;
2480        let body = self.body;
2481        for local in body.mut_vars_and_args_iter().filter(|local| !self.used_mut.contains(local)) {
2482            let local_decl = &body.local_decls[local];
2483            let ClearCrossCrate::Set(SourceScopeLocalData { lint_root, .. }) =
2484                body.source_scopes[local_decl.source_info.scope].local_data
2485            else {
2486                continue;
2487            };
2488
2489            // Skip over locals that begin with an underscore or have no name
2490            if self.local_names[local].is_none_or(|name| name.as_str().starts_with('_')) {
2491                continue;
2492            }
2493
2494            let span = local_decl.source_info.span;
2495            if span.desugaring_kind().is_some() {
2496                // If the `mut` arises as part of a desugaring, we should ignore it.
2497                continue;
2498            }
2499
2500            let mut_span = tcx.sess.source_map().span_until_non_whitespace(span);
2501
2502            tcx.emit_node_span_lint(UNUSED_MUT, lint_root, span, VarNeedNotMut { span: mut_span })
2503        }
2504    }
2505}
2506
2507/// The degree of overlap between 2 places for borrow-checking.
2508enum Overlap {
2509    /// The places might partially overlap - in this case, we give
2510    /// up and say that they might conflict. This occurs when
2511    /// different fields of a union are borrowed. For example,
2512    /// if `u` is a union, we have no way of telling how disjoint
2513    /// `u.a.x` and `a.b.y` are.
2514    Arbitrary,
2515    /// The places have the same type, and are either completely disjoint
2516    /// or equal - i.e., they can't "partially" overlap as can occur with
2517    /// unions. This is the "base case" on which we recur for extensions
2518    /// of the place.
2519    EqualOrDisjoint,
2520    /// The places are disjoint, so we know all extensions of them
2521    /// will also be disjoint.
2522    Disjoint,
2523}