1 //! This query borrow-checks the MIR to (further) ensure it is not broken.
3 #![allow(rustc::potential_query_instability)]
4 #![feature(box_patterns)]
5 #![feature(let_chains)]
6 #![feature(min_specialization)]
7 #![feature(never_type)]
9 #![feature(rustc_attrs)]
10 #![feature(stmt_expr_attributes)]
11 #![feature(trusted_step)]
12 #![feature(try_blocks)]
13 #![recursion_limit = "256"]
16 extern crate rustc_middle
;
20 use rustc_data_structures
::fx
::{FxHashMap, FxHashSet}
;
21 use rustc_data_structures
::graph
::dominators
::Dominators
;
22 use rustc_data_structures
::vec_map
::VecMap
;
23 use rustc_errors
::{Diagnostic, DiagnosticBuilder}
;
25 use rustc_hir
::def_id
::LocalDefId
;
26 use rustc_index
::bit_set
::ChunkedBitSet
;
27 use rustc_index
::vec
::IndexVec
;
28 use rustc_infer
::infer
::{DefiningAnchor, InferCtxt, TyCtxtInferExt}
;
29 use rustc_middle
::mir
::{
30 traversal
, Body
, ClearCrossCrate
, Local
, Location
, Mutability
, NonDivergingIntrinsic
, Operand
,
31 Place
, PlaceElem
, PlaceRef
, VarDebugInfoContents
,
33 use rustc_middle
::mir
::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind}
;
34 use rustc_middle
::mir
::{Field, ProjectionElem, Promoted, Rvalue, Statement, StatementKind}
;
35 use rustc_middle
::mir
::{InlineAsmOperand, Terminator, TerminatorKind}
;
36 use rustc_middle
::ty
::query
::Providers
;
37 use rustc_middle
::ty
::{self, CapturedPlace, ParamEnv, RegionVid, TyCtxt}
;
38 use rustc_session
::lint
::builtin
::UNUSED_MUT
;
39 use rustc_span
::{Span, Symbol}
;
42 use smallvec
::SmallVec
;
43 use std
::cell
::OnceCell
;
44 use std
::cell
::RefCell
;
45 use std
::collections
::BTreeMap
;
48 use rustc_mir_dataflow
::impls
::{
49 EverInitializedPlaces
, MaybeInitializedPlaces
, MaybeUninitializedPlaces
,
51 use rustc_mir_dataflow
::move_paths
::{InitIndex, MoveOutIndex, MovePathIndex}
;
52 use rustc_mir_dataflow
::move_paths
::{InitLocation, LookupResult, MoveData, MoveError}
;
53 use rustc_mir_dataflow
::Analysis
;
54 use rustc_mir_dataflow
::MoveDataParamEnv
;
56 use crate::session_diagnostics
::VarNeedNotMut
;
58 use self::diagnostics
::{AccessKind, RegionName}
;
59 use self::location
::LocationTable
;
60 use self::prefixes
::PrefixSet
;
63 use self::path_utils
::*;
67 mod constraint_generation
;
75 mod member_constraints
;
83 mod session_diagnostics
;
85 mod universal_regions
;
88 /// A public API provided for the Rust compiler consumers.
91 use borrow_set
::{BorrowData, BorrowSet}
;
92 use dataflow
::{BorrowIndex, BorrowckFlowState as Flows, BorrowckResults, Borrows}
;
93 use nll
::{PoloniusOutput, ToRegionVid}
;
94 use place_ext
::PlaceExt
;
95 use places_conflict
::{places_conflict, PlaceConflictBias}
;
96 use region_infer
::RegionInferenceContext
;
98 // FIXME(eddyb) perhaps move this somewhere more centrally.
101 place
: CapturedPlace
<'tcx
>,
103 /// If true, the capture is behind a reference.
107 /// Associate some local constants with the `'tcx` lifetime
108 struct TyCtxtConsts
<'tcx
>(TyCtxt
<'tcx
>);
109 impl<'tcx
> TyCtxtConsts
<'tcx
> {
110 const DEREF_PROJECTION
: &'tcx
[PlaceElem
<'tcx
>; 1] = &[ProjectionElem
::Deref
];
113 pub fn provide(providers
: &mut Providers
) {
114 *providers
= Providers
{
115 mir_borrowck
: |tcx
, did
| {
116 if let Some(def
) = ty
::WithOptConstParam
::try_lookup(did
, tcx
) {
117 tcx
.mir_borrowck_const_arg(def
)
119 mir_borrowck(tcx
, ty
::WithOptConstParam
::unknown(did
))
122 mir_borrowck_const_arg
: |tcx
, (did
, param_did
)| {
123 mir_borrowck(tcx
, ty
::WithOptConstParam { did, const_param_did: Some(param_did) }
)
129 fn mir_borrowck(tcx
: TyCtxt
<'_
>, def
: ty
::WithOptConstParam
<LocalDefId
>) -> &BorrowCheckResult
<'_
> {
130 let (input_body
, promoted
) = tcx
.mir_promoted(def
);
131 debug
!("run query mir_borrowck: {}", tcx
.def_path_str(def
.did
.to_def_id()));
133 if input_body
.borrow().should_skip() {
134 debug
!("Skipping borrowck because of injected body");
135 // Let's make up a borrowck result! Fun times!
136 let result
= BorrowCheckResult
{
137 concrete_opaque_types
: VecMap
::new(),
138 closure_requirements
: None
,
139 used_mut_upvars
: SmallVec
::new(),
140 tainted_by_errors
: None
,
142 return tcx
.arena
.alloc(result
);
145 let hir_owner
= tcx
.hir().local_def_id_to_hir_id(def
.did
).owner
;
148 tcx
.infer_ctxt().with_opaque_type_inference(DefiningAnchor
::Bind(hir_owner
.def_id
)).build();
149 let input_body
: &Body
<'_
> = &input_body
.borrow();
150 let promoted
: &IndexVec
<_
, _
> = &promoted
.borrow();
151 let opt_closure_req
= do_mir_borrowck(&infcx
, input_body
, promoted
, false).0;
152 debug
!("mir_borrowck done");
154 tcx
.arena
.alloc(opt_closure_req
)
157 /// Perform the actual borrow checking.
159 /// If `return_body_with_facts` is true, then return the body with non-erased
160 /// region ids on which the borrow checking was performed together with Polonius
162 #[instrument(skip(infcx, input_body, input_promoted), fields(id=?input_body.source.with_opt_param().as_local().unwrap()), level = "debug")]
163 fn do_mir_borrowck
<'tcx
>(
164 infcx
: &InferCtxt
<'tcx
>,
165 input_body
: &Body
<'tcx
>,
166 input_promoted
: &IndexVec
<Promoted
, Body
<'tcx
>>,
167 return_body_with_facts
: bool
,
168 ) -> (BorrowCheckResult
<'tcx
>, Option
<Box
<BodyWithBorrowckFacts
<'tcx
>>>) {
169 let def
= input_body
.source
.with_opt_param().as_local().unwrap();
174 let param_env
= tcx
.param_env(def
.did
);
176 let mut local_names
= IndexVec
::from_elem(None
, &input_body
.local_decls
);
177 for var_debug_info
in &input_body
.var_debug_info
{
178 if let VarDebugInfoContents
::Place(place
) = var_debug_info
.value
{
179 if let Some(local
) = place
.as_local() {
180 if let Some(prev_name
) = local_names
[local
] && var_debug_info
.name
!= prev_name
{
182 var_debug_info
.source_info
.span
,
183 "local {:?} has many names (`{}` vs `{}`)",
189 local_names
[local
] = Some(var_debug_info
.name
);
194 let mut errors
= error
::BorrowckErrors
::new(infcx
.tcx
);
196 // Gather the upvars of a closure, if any.
197 let tables
= tcx
.typeck_opt_const_arg(def
);
198 if let Some(e
) = tables
.tainted_by_errors
{
199 infcx
.set_tainted_by_errors(e
);
200 errors
.set_tainted_by_errors(e
);
202 let upvars
: Vec
<_
> = tables
203 .closure_min_captures_flattened(def
.did
)
204 .map(|captured_place
| {
205 let capture
= captured_place
.info
.capture_kind
;
206 let by_ref
= match capture
{
207 ty
::UpvarCapture
::ByValue
=> false,
208 ty
::UpvarCapture
::ByRef(..) => true,
210 Upvar { place: captured_place.clone(), by_ref }
214 // Replace all regions with fresh inference variables. This
215 // requires first making our own copy of the MIR. This copy will
216 // be modified (in place) to contain non-lexical lifetimes. It
217 // will have a lifetime tied to the inference context.
218 let mut body_owned
= input_body
.clone();
219 let mut promoted
= input_promoted
.clone();
221 nll
::replace_regions_in_mir(infcx
, param_env
, &mut body_owned
, &mut promoted
);
222 let body
= &body_owned
; // no further changes
224 let location_table_owned
= LocationTable
::new(body
);
225 let location_table
= &location_table_owned
;
227 let (move_data
, move_errors
): (MoveData
<'tcx
>, Vec
<(Place
<'tcx
>, MoveError
<'tcx
>)>) =
228 match MoveData
::gather_moves(&body
, tcx
, param_env
) {
229 Ok((_
, move_data
)) => (move_data
, Vec
::new()),
230 Err((move_data
, move_errors
)) => (move_data
, move_errors
),
232 let promoted_errors
= promoted
234 .map(|(idx
, body
)| (idx
, MoveData
::gather_moves(&body
, tcx
, param_env
)));
236 let mdpe
= MoveDataParamEnv { move_data, param_env }
;
238 let mut flow_inits
= MaybeInitializedPlaces
::new(tcx
, &body
, &mdpe
)
239 .into_engine(tcx
, &body
)
240 .pass_name("borrowck")
241 .iterate_to_fixpoint()
242 .into_results_cursor(&body
);
244 let locals_are_invalidated_at_exit
= tcx
.hir().body_owner_kind(def
.did
).is_fn_or_closure();
246 Rc
::new(BorrowSet
::build(tcx
, body
, locals_are_invalidated_at_exit
, &mdpe
.move_data
));
248 let use_polonius
= return_body_with_facts
|| infcx
.tcx
.sess
.opts
.unstable_opts
.polonius
;
250 // Compute non-lexical lifetimes.
258 } = nll
::compute_regions(
272 // Dump MIR results into a file, if that is enabled. This let us
273 // write unit-tests, as well as helping with debugging.
274 nll
::dump_mir_results(infcx
, &body
, ®ioncx
, &opt_closure_req
);
276 // We also have a `#[rustc_regions]` annotation that causes us to dump
278 nll
::dump_annotation(
287 // The various `flow_*` structures can be large. We drop `flow_inits` here
288 // so it doesn't overlap with the others below. This reduces peak memory
289 // usage significantly on some benchmarks.
292 let regioncx
= Rc
::new(regioncx
);
294 let flow_borrows
= Borrows
::new(tcx
, body
, ®ioncx
, &borrow_set
)
295 .into_engine(tcx
, body
)
296 .pass_name("borrowck")
297 .iterate_to_fixpoint();
298 let flow_uninits
= MaybeUninitializedPlaces
::new(tcx
, body
, &mdpe
)
299 .into_engine(tcx
, body
)
300 .pass_name("borrowck")
301 .iterate_to_fixpoint();
302 let flow_ever_inits
= EverInitializedPlaces
::new(tcx
, body
, &mdpe
)
303 .into_engine(tcx
, body
)
304 .pass_name("borrowck")
305 .iterate_to_fixpoint();
307 let movable_generator
=
308 // The first argument is the generator type passed by value
309 if let Some(local
) = body
.local_decls
.raw
.get(1)
310 // Get the interior types and substs which typeck computed
311 && let ty
::Generator(_
, _
, hir
::Movability
::Static
) = local
.ty
.kind()
318 for (idx
, move_data_results
) in promoted_errors
{
319 let promoted_body
= &promoted
[idx
];
321 if let Err((move_data
, move_errors
)) = move_data_results
{
322 let mut promoted_mbcx
= MirBorrowckCtxt
{
326 move_data
: &move_data
,
327 location_table
, // no need to create a real one for the promoted, it is not used
329 fn_self_span_reported
: Default
::default(),
330 locals_are_invalidated_at_exit
,
331 access_place_error_reported
: Default
::default(),
332 reservation_error_reported
: Default
::default(),
333 uninitialized_error_reported
: Default
::default(),
334 regioncx
: regioncx
.clone(),
335 used_mut
: Default
::default(),
336 used_mut_upvars
: SmallVec
::new(),
337 borrow_set
: Rc
::clone(&borrow_set
),
338 dominators
: Default
::default(),
340 local_names
: IndexVec
::from_elem(None
, &promoted_body
.local_decls
),
341 region_names
: RefCell
::default(),
342 next_region_name
: RefCell
::new(1),
343 polonius_output
: None
,
346 promoted_mbcx
.report_move_errors(move_errors
);
347 errors
= promoted_mbcx
.errors
;
351 let mut mbcx
= MirBorrowckCtxt
{
355 move_data
: &mdpe
.move_data
,
358 locals_are_invalidated_at_exit
,
359 fn_self_span_reported
: Default
::default(),
360 access_place_error_reported
: Default
::default(),
361 reservation_error_reported
: Default
::default(),
362 uninitialized_error_reported
: Default
::default(),
363 regioncx
: Rc
::clone(®ioncx
),
364 used_mut
: Default
::default(),
365 used_mut_upvars
: SmallVec
::new(),
366 borrow_set
: Rc
::clone(&borrow_set
),
367 dominators
: Default
::default(),
370 region_names
: RefCell
::default(),
371 next_region_name
: RefCell
::new(1),
376 // Compute and report region errors, if any.
377 mbcx
.report_region_errors(nll_errors
);
379 let results
= BorrowckResults
{
380 ever_inits
: flow_ever_inits
,
381 uninits
: flow_uninits
,
382 borrows
: flow_borrows
,
385 mbcx
.report_move_errors(move_errors
);
387 rustc_mir_dataflow
::visit_results(
389 traversal
::reverse_postorder(body
).map(|(bb
, _
)| bb
),
394 // For each non-user used mutable variable, check if it's been assigned from
395 // a user-declared local. If so, then put that local into the used_mut set.
396 // Note that this set is expected to be small - only upvars from closures
397 // would have a chance of erroneously adding non-user-defined mutable vars
399 let temporary_used_locals
: FxHashSet
<Local
> = mbcx
402 .filter(|&local
| !mbcx
.body
.local_decls
[*local
].is_user_variable())
405 // For the remaining unused locals that are marked as mutable, we avoid linting any that
406 // were never initialized. These locals may have been removed as unreachable code; or will be
407 // linted as unused variables.
408 let unused_mut_locals
=
409 mbcx
.body
.mut_vars_iter().filter(|local
| !mbcx
.used_mut
.contains(local
)).collect();
410 mbcx
.gather_used_muts(temporary_used_locals
, unused_mut_locals
);
412 debug
!("mbcx.used_mut: {:?}", mbcx
.used_mut
);
413 let used_mut
= std
::mem
::take(&mut mbcx
.used_mut
);
414 for local
in mbcx
.body
.mut_vars_and_args_iter().filter(|local
| !used_mut
.contains(local
)) {
415 let local_decl
= &mbcx
.body
.local_decls
[local
];
416 let lint_root
= match &mbcx
.body
.source_scopes
[local_decl
.source_info
.scope
].local_data
{
417 ClearCrossCrate
::Set(data
) => data
.lint_root
,
421 // Skip over locals that begin with an underscore or have no name
422 match mbcx
.local_names
[local
] {
424 if name
.as_str().starts_with('_'
) {
431 let span
= local_decl
.source_info
.span
;
432 if span
.desugaring_kind().is_some() {
433 // If the `mut` arises as part of a desugaring, we should ignore it.
437 let mut_span
= tcx
.sess
.source_map().span_until_non_whitespace(span
);
439 tcx
.emit_spanned_lint(UNUSED_MUT
, lint_root
, span
, VarNeedNotMut { span: mut_span }
)
442 let tainted_by_errors
= mbcx
.emit_errors();
444 let result
= BorrowCheckResult
{
445 concrete_opaque_types
: opaque_type_values
,
446 closure_requirements
: opt_closure_req
,
447 used_mut_upvars
: mbcx
.used_mut_upvars
,
451 let body_with_facts
= if return_body_with_facts
{
452 let output_facts
= mbcx
.polonius_output
.expect("Polonius output was not computed");
453 Some(Box
::new(BodyWithBorrowckFacts
{
455 input_facts
: *polonius_input
.expect("Polonius input facts were not generated"),
457 location_table
: location_table_owned
,
463 debug
!("do_mir_borrowck: result = {:#?}", result
);
465 (result
, body_with_facts
)
468 /// A `Body` with information computed by the borrow checker. This struct is
469 /// intended to be consumed by compiler consumers.
471 /// We need to include the MIR body here because the region identifiers must
472 /// match the ones in the Polonius facts.
473 pub struct BodyWithBorrowckFacts
<'tcx
> {
474 /// A mir body that contains region identifiers.
475 pub body
: Body
<'tcx
>,
476 /// Polonius input facts.
477 pub input_facts
: AllFacts
,
478 /// Polonius output facts.
479 pub output_facts
: Rc
<self::nll
::PoloniusOutput
>,
480 /// The table that maps Polonius points to locations in the table.
481 pub location_table
: LocationTable
,
484 struct MirBorrowckCtxt
<'cx
, 'tcx
> {
485 infcx
: &'cx InferCtxt
<'tcx
>,
486 param_env
: ParamEnv
<'tcx
>,
487 body
: &'cx Body
<'tcx
>,
488 move_data
: &'cx MoveData
<'tcx
>,
490 /// Map from MIR `Location` to `LocationIndex`; created
491 /// when MIR borrowck begins.
492 location_table
: &'cx LocationTable
,
494 movable_generator
: bool
,
495 /// This keeps track of whether local variables are free-ed when the function
496 /// exits even without a `StorageDead`, which appears to be the case for
499 /// I'm not sure this is the right approach - @eddyb could you try and
501 locals_are_invalidated_at_exit
: bool
,
502 /// This field keeps track of when borrow errors are reported in the access_place function
503 /// so that there is no duplicate reporting. This field cannot also be used for the conflicting
504 /// borrow errors that is handled by the `reservation_error_reported` field as the inclusion
505 /// of the `Span` type (while required to mute some errors) stops the muting of the reservation
507 access_place_error_reported
: FxHashSet
<(Place
<'tcx
>, Span
)>,
508 /// This field keeps track of when borrow conflict errors are reported
509 /// for reservations, so that we don't report seemingly duplicate
510 /// errors for corresponding activations.
512 // FIXME: ideally this would be a set of `BorrowIndex`, not `Place`s,
513 // but it is currently inconvenient to track down the `BorrowIndex`
514 // at the time we detect and report a reservation error.
515 reservation_error_reported
: FxHashSet
<Place
<'tcx
>>,
516 /// This fields keeps track of the `Span`s that we have
517 /// used to report extra information for `FnSelfUse`, to avoid
518 /// unnecessarily verbose errors.
519 fn_self_span_reported
: FxHashSet
<Span
>,
520 /// This field keeps track of errors reported in the checking of uninitialized variables,
521 /// so that we don't report seemingly duplicate errors.
522 uninitialized_error_reported
: FxHashSet
<PlaceRef
<'tcx
>>,
523 /// This field keeps track of all the local variables that are declared mut and are mutated.
524 /// Used for the warning issued by an unused mutable local variable.
525 used_mut
: FxHashSet
<Local
>,
526 /// If the function we're checking is a closure, then we'll need to report back the list of
527 /// mutable upvars that have been used. This field keeps track of them.
528 used_mut_upvars
: SmallVec
<[Field
; 8]>,
529 /// Region inference context. This contains the results from region inference and lets us e.g.
530 /// find out which CFG points are contained in each borrow region.
531 regioncx
: Rc
<RegionInferenceContext
<'tcx
>>,
533 /// The set of borrows extracted from the MIR
534 borrow_set
: Rc
<BorrowSet
<'tcx
>>,
536 /// Dominators for MIR
537 dominators
: OnceCell
<Dominators
<BasicBlock
>>,
539 /// Information about upvars not necessarily preserved in types or MIR
540 upvars
: Vec
<Upvar
<'tcx
>>,
542 /// Names of local (user) variables (extracted from `var_debug_info`).
543 local_names
: IndexVec
<Local
, Option
<Symbol
>>,
545 /// Record the region names generated for each region in the given
546 /// MIR def so that we can reuse them later in help/error messages.
547 region_names
: RefCell
<FxHashMap
<RegionVid
, RegionName
>>,
549 /// The counter for generating new region names.
550 next_region_name
: RefCell
<usize>,
552 /// Results of Polonius analysis.
553 polonius_output
: Option
<Rc
<PoloniusOutput
>>,
555 errors
: error
::BorrowckErrors
<'tcx
>,
559 // 1. assignments are always made to mutable locations (FIXME: does that still really go here?)
560 // 2. loans made in overlapping scopes do not conflict
561 // 3. assignments do not affect things loaned out as immutable
562 // 4. moves do not affect things loaned out in any way
563 impl<'cx
, 'tcx
> rustc_mir_dataflow
::ResultsVisitor
<'cx
, 'tcx
> for MirBorrowckCtxt
<'cx
, 'tcx
> {
564 type FlowState
= Flows
<'cx
, 'tcx
>;
566 fn visit_statement_before_primary_effect(
568 flow_state
: &Flows
<'cx
, 'tcx
>,
569 stmt
: &'cx Statement
<'tcx
>,
572 debug
!("MirBorrowckCtxt::process_statement({:?}, {:?}): {:?}", location
, stmt
, flow_state
);
573 let span
= stmt
.source_info
.span
;
575 self.check_activations(location
, span
, flow_state
);
578 StatementKind
::Assign(box (lhs
, rhs
)) => {
579 self.consume_rvalue(location
, (rhs
, span
), flow_state
);
581 self.mutate_place(location
, (*lhs
, span
), Shallow(None
), flow_state
);
583 StatementKind
::FakeRead(box (_
, place
)) => {
584 // Read for match doesn't access any memory and is used to
585 // assert that a place is safe and live. So we don't have to
586 // do any checks here.
588 // FIXME: Remove check that the place is initialized. This is
589 // needed for now because matches don't have never patterns yet.
590 // So this is the only place we prevent
594 self.check_if_path_or_subpath_is_moved(
596 InitializationRequiringAction
::Use
,
597 (place
.as_ref(), span
),
601 StatementKind
::Intrinsic(box kind
) => match kind
{
602 NonDivergingIntrinsic
::Assume(op
) => self.consume_operand(location
, (op
, span
), flow_state
),
603 NonDivergingIntrinsic
::CopyNonOverlapping(..) => span_bug
!(
605 "Unexpected CopyNonOverlapping, should only appear after lower_intrinsics",
608 // Only relevant for mir typeck
609 StatementKind
::AscribeUserType(..)
610 // Doesn't have any language semantics
611 | StatementKind
::Coverage(..)
612 // Does not actually affect borrowck
613 | StatementKind
::StorageLive(..) => {}
614 StatementKind
::StorageDead(local
) => {
617 (Place
::from(*local
), span
),
618 (Shallow(None
), Write(WriteKind
::StorageDeadOrDrop
)),
619 LocalMutationIsAllowed
::Yes
,
624 | StatementKind
::Retag { .. }
625 | StatementKind
::Deinit(..)
626 | StatementKind
::SetDiscriminant { .. }
=> {
627 bug
!("Statement not allowed in this MIR phase")
632 fn visit_terminator_before_primary_effect(
634 flow_state
: &Flows
<'cx
, 'tcx
>,
635 term
: &'cx Terminator
<'tcx
>,
638 debug
!("MirBorrowckCtxt::process_terminator({:?}, {:?}): {:?}", loc
, term
, flow_state
);
639 let span
= term
.source_info
.span
;
641 self.check_activations(loc
, span
, flow_state
);
644 TerminatorKind
::SwitchInt { discr, targets: _ }
=> {
645 self.consume_operand(loc
, (discr
, span
), flow_state
);
647 TerminatorKind
::Drop { place, target: _, unwind: _ }
=> {
649 "visit_terminator_drop \
650 loc: {:?} term: {:?} place: {:?} span: {:?}",
651 loc
, term
, place
, span
657 (AccessDepth
::Drop
, Write(WriteKind
::StorageDeadOrDrop
)),
658 LocalMutationIsAllowed
::Yes
,
662 TerminatorKind
::DropAndReplace
{
668 self.mutate_place(loc
, (*drop_place
, span
), Deep
, flow_state
);
669 self.consume_operand(loc
, (new_value
, span
), flow_state
);
671 TerminatorKind
::Call
{
680 self.consume_operand(loc
, (func
, span
), flow_state
);
682 self.consume_operand(loc
, (arg
, span
), flow_state
);
684 self.mutate_place(loc
, (*destination
, span
), Deep
, flow_state
);
686 TerminatorKind
::Assert { cond, expected: _, msg, target: _, cleanup: _ }
=> {
687 self.consume_operand(loc
, (cond
, span
), flow_state
);
688 use rustc_middle
::mir
::AssertKind
;
689 if let AssertKind
::BoundsCheck { len, index }
= msg
{
690 self.consume_operand(loc
, (len
, span
), flow_state
);
691 self.consume_operand(loc
, (index
, span
), flow_state
);
695 TerminatorKind
::Yield { value, resume: _, resume_arg, drop: _ }
=> {
696 self.consume_operand(loc
, (value
, span
), flow_state
);
697 self.mutate_place(loc
, (*resume_arg
, span
), Deep
, flow_state
);
700 TerminatorKind
::InlineAsm
{
710 InlineAsmOperand
::In { reg: _, value }
=> {
711 self.consume_operand(loc
, (value
, span
), flow_state
);
713 InlineAsmOperand
::Out { reg: _, late: _, place, .. }
=> {
714 if let Some(place
) = place
{
715 self.mutate_place(loc
, (*place
, span
), Shallow(None
), flow_state
);
718 InlineAsmOperand
::InOut { reg: _, late: _, in_value, out_place }
=> {
719 self.consume_operand(loc
, (in_value
, span
), flow_state
);
720 if let &Some(out_place
) = out_place
{
729 InlineAsmOperand
::Const { value: _ }
730 | InlineAsmOperand
::SymFn { value: _ }
731 | InlineAsmOperand
::SymStatic { def_id: _ }
=> {}
736 TerminatorKind
::Goto { target: _ }
737 | TerminatorKind
::Abort
738 | TerminatorKind
::Unreachable
739 | TerminatorKind
::Resume
740 | TerminatorKind
::Return
741 | TerminatorKind
::GeneratorDrop
742 | TerminatorKind
::FalseEdge { real_target: _, imaginary_target: _ }
743 | TerminatorKind
::FalseUnwind { real_target: _, unwind: _ }
=> {
744 // no data used, thus irrelevant to borrowck
749 fn visit_terminator_after_primary_effect(
751 flow_state
: &Flows
<'cx
, 'tcx
>,
752 term
: &'cx Terminator
<'tcx
>,
755 let span
= term
.source_info
.span
;
758 TerminatorKind
::Yield { value: _, resume: _, resume_arg: _, drop: _ }
=> {
759 if self.movable_generator
{
760 // Look for any active borrows to locals
761 let borrow_set
= self.borrow_set
.clone();
762 for i
in flow_state
.borrows
.iter() {
763 let borrow
= &borrow_set
[i
];
764 self.check_for_local_borrow(borrow
, span
);
769 TerminatorKind
::Resume
| TerminatorKind
::Return
| TerminatorKind
::GeneratorDrop
=> {
770 // Returning from the function implicitly kills storage for all locals and statics.
771 // Often, the storage will already have been killed by an explicit
772 // StorageDead, but we don't always emit those (notably on unwind paths),
773 // so this "extra check" serves as a kind of backup.
774 let borrow_set
= self.borrow_set
.clone();
775 for i
in flow_state
.borrows
.iter() {
776 let borrow
= &borrow_set
[i
];
777 self.check_for_invalidation_at_exit(loc
, borrow
, span
);
781 TerminatorKind
::Abort
782 | TerminatorKind
::Assert { .. }
783 | TerminatorKind
::Call { .. }
784 | TerminatorKind
::Drop { .. }
785 | TerminatorKind
::DropAndReplace { .. }
786 | TerminatorKind
::FalseEdge { real_target: _, imaginary_target: _ }
787 | TerminatorKind
::FalseUnwind { real_target: _, unwind: _ }
788 | TerminatorKind
::Goto { .. }
789 | TerminatorKind
::SwitchInt { .. }
790 | TerminatorKind
::Unreachable
791 | TerminatorKind
::InlineAsm { .. }
=> {}
796 use self::AccessDepth
::{Deep, Shallow}
;
797 use self::ReadOrWrite
::{Activation, Read, Reservation, Write}
;
799 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
800 enum ArtificialField
{
805 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
807 /// From the RFC: "A *shallow* access means that the immediate
808 /// fields reached at P are accessed, but references or pointers
809 /// found within are not dereferenced. Right now, the only access
810 /// that is shallow is an assignment like `x = ...;`, which would
811 /// be a *shallow write* of `x`."
812 Shallow(Option
<ArtificialField
>),
814 /// From the RFC: "A *deep* access means that all data reachable
815 /// through the given place may be invalidated or accesses by
819 /// Access is Deep only when there is a Drop implementation that
820 /// can reach the data behind the reference.
824 /// Kind of access to a value: read or write
825 /// (For informational purposes only)
826 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
828 /// From the RFC: "A *read* means that the existing data may be
829 /// read, but will not be changed."
832 /// From the RFC: "A *write* means that the data may be mutated to
833 /// new values or otherwise invalidated (for example, it could be
834 /// de-initialized, as in a move operation).
837 /// For two-phase borrows, we distinguish a reservation (which is treated
838 /// like a Read) from an activation (which is treated like a write), and
839 /// each of those is furthermore distinguished from Reads/Writes above.
840 Reservation(WriteKind
),
841 Activation(WriteKind
, BorrowIndex
),
844 /// Kind of read access to a value
845 /// (For informational purposes only)
846 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
852 /// Kind of write access to a value
853 /// (For informational purposes only)
854 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
857 MutableBorrow(BorrowKind
),
862 /// When checking permissions for a place access, this flag is used to indicate that an immutable
863 /// local place can be mutated.
865 // FIXME: @nikomatsakis suggested that this flag could be removed with the following modifications:
866 // - Split `is_mutable()` into `is_assignable()` (can be directly assigned) and
867 // `is_declared_mutable()`.
868 // - Take flow state into consideration in `is_assignable()` for local variables.
869 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
870 enum LocalMutationIsAllowed
{
872 /// We want use of immutable upvars to cause a "write to immutable upvar"
873 /// error, not an "reassignment" error.
878 #[derive(Copy, Clone, Debug)]
879 enum InitializationRequiringAction
{
887 struct RootPlace
<'tcx
> {
889 place_projection
: &'tcx
[PlaceElem
<'tcx
>],
890 is_local_mutation_allowed
: LocalMutationIsAllowed
,
893 impl InitializationRequiringAction
{
894 fn as_noun(self) -> &'
static str {
896 InitializationRequiringAction
::Borrow
=> "borrow",
897 InitializationRequiringAction
::MatchOn
=> "use", // no good noun
898 InitializationRequiringAction
::Use
=> "use",
899 InitializationRequiringAction
::Assignment
=> "assign",
900 InitializationRequiringAction
::PartialAssignment
=> "assign to part",
904 fn as_verb_in_past_tense(self) -> &'
static str {
906 InitializationRequiringAction
::Borrow
=> "borrowed",
907 InitializationRequiringAction
::MatchOn
=> "matched on",
908 InitializationRequiringAction
::Use
=> "used",
909 InitializationRequiringAction
::Assignment
=> "assigned",
910 InitializationRequiringAction
::PartialAssignment
=> "partially assigned",
914 fn as_general_verb_in_past_tense(self) -> &'
static str {
916 InitializationRequiringAction
::Borrow
917 | InitializationRequiringAction
::MatchOn
918 | InitializationRequiringAction
::Use
=> "used",
919 InitializationRequiringAction
::Assignment
=> "assigned",
920 InitializationRequiringAction
::PartialAssignment
=> "partially assigned",
925 impl<'cx
, 'tcx
> MirBorrowckCtxt
<'cx
, 'tcx
> {
926 fn body(&self) -> &'cx Body
<'tcx
> {
930 /// Checks an access to the given place to see if it is allowed. Examines the set of borrows
931 /// that are in scope, as well as which paths have been initialized, to ensure that (a) the
932 /// place is initialized and (b) it is not borrowed in some way that would prevent this
935 /// Returns `true` if an error is reported.
939 place_span
: (Place
<'tcx
>, Span
),
940 kind
: (AccessDepth
, ReadOrWrite
),
941 is_local_mutation_allowed
: LocalMutationIsAllowed
,
942 flow_state
: &Flows
<'cx
, 'tcx
>,
946 if let Activation(_
, borrow_index
) = rw
{
947 if self.reservation_error_reported
.contains(&place_span
.0) {
949 "skipping access_place for activation of invalid reservation \
950 place: {:?} borrow_index: {:?}",
951 place_span
.0, borrow_index
957 // Check is_empty() first because it's the common case, and doing that
958 // way we avoid the clone() call.
959 if !self.access_place_error_reported
.is_empty()
960 && self.access_place_error_reported
.contains(&(place_span
.0, place_span
.1))
963 "access_place: suppressing error place_span=`{:?}` kind=`{:?}`",
969 let mutability_error
= self.check_access_permissions(
972 is_local_mutation_allowed
,
977 self.check_access_for_conflict(location
, place_span
, sd
, rw
, flow_state
);
979 if conflict_error
|| mutability_error
{
980 debug
!("access_place: logging error place_span=`{:?}` kind=`{:?}`", place_span
, kind
);
981 self.access_place_error_reported
.insert((place_span
.0, place_span
.1));
985 #[instrument(level = "debug", skip(self, flow_state))]
986 fn check_access_for_conflict(
989 place_span
: (Place
<'tcx
>, Span
),
992 flow_state
: &Flows
<'cx
, 'tcx
>,
994 let mut error_reported
= false;
995 let tcx
= self.infcx
.tcx
;
996 let body
= self.body
;
997 let borrow_set
= self.borrow_set
.clone();
999 // Use polonius output if it has been enabled.
1000 let polonius_output
= self.polonius_output
.clone();
1001 let borrows_in_scope
= if let Some(polonius
) = &polonius_output
{
1002 let location
= self.location_table
.start_index(location
);
1003 Either
::Left(polonius
.errors_at(location
).iter().copied())
1005 Either
::Right(flow_state
.borrows
.iter())
1008 each_borrow_involving_path(
1016 |this
, borrow_index
, borrow
| match (rw
, borrow
.kind
) {
1017 // Obviously an activation is compatible with its own
1018 // reservation (or even prior activating uses of same
1019 // borrow); so don't check if they interfere.
1021 // NOTE: *reservations* do conflict with themselves;
1022 // thus aren't injecting unsoundness w/ this check.)
1023 (Activation(_
, activating
), _
) if activating
== borrow_index
=> {
1025 "check_access_for_conflict place_span: {:?} sd: {:?} rw: {:?} \
1026 skipping {:?} b/c activation of same borrow_index",
1030 (borrow_index
, borrow
),
1035 (Read(_
), BorrowKind
::Shared
| BorrowKind
::Shallow
)
1037 Read(ReadKind
::Borrow(BorrowKind
::Shallow
)),
1038 BorrowKind
::Unique
| BorrowKind
::Mut { .. }
,
1039 ) => Control
::Continue
,
1041 (Reservation(_
), BorrowKind
::Shallow
| BorrowKind
::Shared
) => {
1042 // This used to be a future compatibility warning (to be
1043 // disallowed on NLL). See rust-lang/rust#56254
1047 (Write(WriteKind
::Move
), BorrowKind
::Shallow
) => {
1048 // Handled by initialization checks.
1052 (Read(kind
), BorrowKind
::Unique
| BorrowKind
::Mut { .. }
) => {
1053 // Reading from mere reservations of mutable-borrows is OK.
1054 if !is_active(this
.dominators(), borrow
, location
) {
1055 assert
!(allow_two_phase_borrow(borrow
.kind
));
1056 return Control
::Continue
;
1059 error_reported
= true;
1063 .report_use_while_mutably_borrowed(location
, place_span
, borrow
);
1064 this
.buffer_error(err
);
1066 ReadKind
::Borrow(bk
) => {
1068 this
.report_conflicting_borrow(location
, place_span
, bk
, borrow
);
1069 this
.buffer_error(err
);
1075 (Reservation(kind
) | Activation(kind
, _
) | Write(kind
), _
) => {
1077 Reservation(..) => {
1079 "recording invalid reservation of \
1083 this
.reservation_error_reported
.insert(place_span
.0);
1085 Activation(_
, activating
) => {
1087 "observing check_place for activation of \
1088 borrow_index: {:?}",
1092 Read(..) | Write(..) => {}
1095 error_reported
= true;
1097 WriteKind
::MutableBorrow(bk
) => {
1099 this
.report_conflicting_borrow(location
, place_span
, bk
, borrow
);
1100 this
.buffer_error(err
);
1102 WriteKind
::StorageDeadOrDrop
=> this
1103 .report_borrowed_value_does_not_live_long_enough(
1109 WriteKind
::Mutate
=> {
1110 this
.report_illegal_mutation_of_borrowed(location
, place_span
, borrow
)
1112 WriteKind
::Move
=> {
1113 this
.report_move_out_while_borrowed(location
, place_span
, borrow
)
1127 place_span
: (Place
<'tcx
>, Span
),
1129 flow_state
: &Flows
<'cx
, 'tcx
>,
1131 // Write of P[i] or *P requires P init'd.
1132 self.check_if_assigned_path_is_moved(location
, place_span
, flow_state
);
1137 (kind
, Write(WriteKind
::Mutate
)),
1138 LocalMutationIsAllowed
::No
,
1146 (rvalue
, span
): (&'cx Rvalue
<'tcx
>, Span
),
1147 flow_state
: &Flows
<'cx
, 'tcx
>,
1150 &Rvalue
::Ref(_
/*rgn*/, bk
, place
) => {
1151 let access_kind
= match bk
{
1152 BorrowKind
::Shallow
=> {
1153 (Shallow(Some(ArtificialField
::ShallowBorrow
)), Read(ReadKind
::Borrow(bk
)))
1155 BorrowKind
::Shared
=> (Deep
, Read(ReadKind
::Borrow(bk
))),
1156 BorrowKind
::Unique
| BorrowKind
::Mut { .. }
=> {
1157 let wk
= WriteKind
::MutableBorrow(bk
);
1158 if allow_two_phase_borrow(bk
) {
1159 (Deep
, Reservation(wk
))
1170 LocalMutationIsAllowed
::No
,
1174 let action
= if bk
== BorrowKind
::Shallow
{
1175 InitializationRequiringAction
::MatchOn
1177 InitializationRequiringAction
::Borrow
1180 self.check_if_path_or_subpath_is_moved(
1183 (place
.as_ref(), span
),
1188 &Rvalue
::AddressOf(mutability
, place
) => {
1189 let access_kind
= match mutability
{
1190 Mutability
::Mut
=> (
1192 Write(WriteKind
::MutableBorrow(BorrowKind
::Mut
{
1193 allow_two_phase_borrow
: false,
1196 Mutability
::Not
=> (Deep
, Read(ReadKind
::Borrow(BorrowKind
::Shared
))),
1203 LocalMutationIsAllowed
::No
,
1207 self.check_if_path_or_subpath_is_moved(
1209 InitializationRequiringAction
::Borrow
,
1210 (place
.as_ref(), span
),
1215 Rvalue
::ThreadLocalRef(_
) => {}
1217 Rvalue
::Use(operand
)
1218 | Rvalue
::Repeat(operand
, _
)
1219 | Rvalue
::UnaryOp(_
/*un_op*/, operand
)
1220 | Rvalue
::Cast(_
/*cast_kind*/, operand
, _
/*ty*/)
1221 | Rvalue
::ShallowInitBox(operand
, _
/*ty*/) => {
1222 self.consume_operand(location
, (operand
, span
), flow_state
)
1225 &Rvalue
::CopyForDeref(place
) => {
1229 (Deep
, Read(ReadKind
::Copy
)),
1230 LocalMutationIsAllowed
::No
,
1234 // Finally, check if path was already moved.
1235 self.check_if_path_or_subpath_is_moved(
1237 InitializationRequiringAction
::Use
,
1238 (place
.as_ref(), span
),
1243 &(Rvalue
::Len(place
) | Rvalue
::Discriminant(place
)) => {
1244 let af
= match *rvalue
{
1245 Rvalue
::Len(..) => Some(ArtificialField
::ArrayLength
),
1246 Rvalue
::Discriminant(..) => None
,
1247 _
=> unreachable
!(),
1252 (Shallow(af
), Read(ReadKind
::Copy
)),
1253 LocalMutationIsAllowed
::No
,
1256 self.check_if_path_or_subpath_is_moved(
1258 InitializationRequiringAction
::Use
,
1259 (place
.as_ref(), span
),
1264 Rvalue
::BinaryOp(_bin_op
, box (operand1
, operand2
))
1265 | Rvalue
::CheckedBinaryOp(_bin_op
, box (operand1
, operand2
)) => {
1266 self.consume_operand(location
, (operand1
, span
), flow_state
);
1267 self.consume_operand(location
, (operand2
, span
), flow_state
);
1270 Rvalue
::NullaryOp(_op
, _ty
) => {
1271 // nullary ops take no dynamic input; no borrowck effect.
1274 Rvalue
::Aggregate(aggregate_kind
, operands
) => {
1275 // We need to report back the list of mutable upvars that were
1276 // moved into the closure and subsequently used by the closure,
1277 // in order to populate our used_mut set.
1278 match **aggregate_kind
{
1279 AggregateKind
::Closure(def_id
, _
) | AggregateKind
::Generator(def_id
, _
, _
) => {
1280 let BorrowCheckResult { used_mut_upvars, .. }
=
1281 self.infcx
.tcx
.mir_borrowck(def_id
);
1282 debug
!("{:?} used_mut_upvars={:?}", def_id
, used_mut_upvars
);
1283 for field
in used_mut_upvars
{
1284 self.propagate_closure_used_mut_upvar(&operands
[field
.index()]);
1287 AggregateKind
::Adt(..)
1288 | AggregateKind
::Array(..)
1289 | AggregateKind
::Tuple { .. }
=> (),
1292 for operand
in operands
{
1293 self.consume_operand(location
, (operand
, span
), flow_state
);
1299 fn propagate_closure_used_mut_upvar(&mut self, operand
: &Operand
<'tcx
>) {
1300 let propagate_closure_used_mut_place
= |this
: &mut Self, place
: Place
<'tcx
>| {
1301 // We have three possibilities here:
1302 // a. We are modifying something through a mut-ref
1303 // b. We are modifying something that is local to our parent
1304 // c. Current body is a nested closure, and we are modifying path starting from
1305 // a Place captured by our parent closure.
1307 // Handle (c), the path being modified is exactly the path captured by our parent
1308 if let Some(field
) = this
.is_upvar_field_projection(place
.as_ref()) {
1309 this
.used_mut_upvars
.push(field
);
1313 for (place_ref
, proj
) in place
.iter_projections().rev() {
1315 if proj
== ProjectionElem
::Deref
{
1316 match place_ref
.ty(this
.body(), this
.infcx
.tcx
).ty
.kind() {
1317 // We aren't modifying a variable directly
1318 ty
::Ref(_
, _
, hir
::Mutability
::Mut
) => return,
1325 if let Some(field
) = this
.is_upvar_field_projection(place_ref
) {
1326 this
.used_mut_upvars
.push(field
);
1332 this
.used_mut
.insert(place
.local
);
1335 // This relies on the current way that by-value
1336 // captures of a closure are copied/moved directly
1337 // when generating MIR.
1339 Operand
::Move(place
) | Operand
::Copy(place
) => {
1340 match place
.as_local() {
1341 Some(local
) if !self.body
.local_decls
[local
].is_user_variable() => {
1342 if self.body
.local_decls
[local
].ty
.is_mutable_ptr() {
1343 // The variable will be marked as mutable by the borrow.
1346 // This is an edge case where we have a `move` closure
1347 // inside a non-move closure, and the inner closure
1348 // contains a mutation:
1351 // || { move || { i += 1; }; };
1353 // In this case our usual strategy of assuming that the
1354 // variable will be captured by mutable reference is
1355 // wrong, since `i` can be copied into the inner
1356 // closure from a shared reference.
1358 // As such we have to search for the local that this
1359 // capture comes from and mark it as being used as mut.
1361 let temp_mpi
= self.move_data
.rev_lookup
.find_local(local
);
1362 let init
= if let [init_index
] = *self.move_data
.init_path_map
[temp_mpi
] {
1363 &self.move_data
.inits
[init_index
]
1365 bug
!("temporary should be initialized exactly once")
1368 let InitLocation
::Statement(loc
) = init
.location
else {
1369 bug
!("temporary initialized in arguments")
1372 let body
= self.body
;
1373 let bbd
= &body
[loc
.block
];
1374 let stmt
= &bbd
.statements
[loc
.statement_index
];
1375 debug
!("temporary assigned in: stmt={:?}", stmt
);
1377 if let StatementKind
::Assign(box (_
, Rvalue
::Ref(_
, _
, source
))) = stmt
.kind
1379 propagate_closure_used_mut_place(self, source
);
1382 "closures should only capture user variables \
1383 or references to user variables"
1387 _
=> propagate_closure_used_mut_place(self, place
),
1390 Operand
::Constant(..) => {}
1397 (operand
, span
): (&'cx Operand
<'tcx
>, Span
),
1398 flow_state
: &Flows
<'cx
, 'tcx
>,
1401 Operand
::Copy(place
) => {
1402 // copy of place: check if this is "copy of frozen path"
1403 // (FIXME: see check_loans.rs)
1407 (Deep
, Read(ReadKind
::Copy
)),
1408 LocalMutationIsAllowed
::No
,
1412 // Finally, check if path was already moved.
1413 self.check_if_path_or_subpath_is_moved(
1415 InitializationRequiringAction
::Use
,
1416 (place
.as_ref(), span
),
1420 Operand
::Move(place
) => {
1421 // move of place: check if this is move of already borrowed path
1425 (Deep
, Write(WriteKind
::Move
)),
1426 LocalMutationIsAllowed
::Yes
,
1430 // Finally, check if path was already moved.
1431 self.check_if_path_or_subpath_is_moved(
1433 InitializationRequiringAction
::Use
,
1434 (place
.as_ref(), span
),
1438 Operand
::Constant(_
) => {}
1442 /// Checks whether a borrow of this place is invalidated when the function
1444 #[instrument(level = "debug", skip(self))]
1445 fn check_for_invalidation_at_exit(
1448 borrow
: &BorrowData
<'tcx
>,
1451 let place
= borrow
.borrowed_place
;
1452 let mut root_place
= PlaceRef { local: place.local, projection: &[] }
;
1454 // FIXME(nll-rfc#40): do more precise destructor tracking here. For now
1455 // we just know that all locals are dropped at function exit (otherwise
1456 // we'll have a memory leak) and assume that all statics have a destructor.
1458 // FIXME: allow thread-locals to borrow other thread locals?
1460 let (might_be_alive
, will_be_dropped
) =
1461 if self.body
.local_decls
[root_place
.local
].is_ref_to_thread_local() {
1462 // Thread-locals might be dropped after the function exits
1463 // We have to dereference the outer reference because
1464 // borrows don't conflict behind shared references.
1465 root_place
.projection
= TyCtxtConsts
::DEREF_PROJECTION
;
1468 (false, self.locals_are_invalidated_at_exit
)
1471 if !will_be_dropped
{
1472 debug
!("place_is_invalidated_at_exit({:?}) - won't be dropped", place
);
1476 let sd
= if might_be_alive { Deep }
else { Shallow(None) }
;
1478 if places_conflict
::borrow_conflicts_with_place(
1485 places_conflict
::PlaceConflictBias
::Overlap
,
1487 debug
!("check_for_invalidation_at_exit({:?}): INVALID", place
);
1488 // FIXME: should be talking about the region lifetime instead
1489 // of just a span here.
1490 let span
= self.infcx
.tcx
.sess
.source_map().end_point(span
);
1491 self.report_borrowed_value_does_not_live_long_enough(
1500 /// Reports an error if this is a borrow of local data.
1501 /// This is called for all Yield expressions on movable generators
1502 fn check_for_local_borrow(&mut self, borrow
: &BorrowData
<'tcx
>, yield_span
: Span
) {
1503 debug
!("check_for_local_borrow({:?})", borrow
);
1505 if borrow_of_local_data(borrow
.borrowed_place
) {
1506 let err
= self.cannot_borrow_across_generator_yield(
1507 self.retrieve_borrow_spans(borrow
).var_or_use(),
1511 self.buffer_error(err
);
1515 fn check_activations(&mut self, location
: Location
, span
: Span
, flow_state
: &Flows
<'cx
, 'tcx
>) {
1516 // Two-phase borrow support: For each activation that is newly
1517 // generated at this statement, check if it interferes with
1519 let borrow_set
= self.borrow_set
.clone();
1520 for &borrow_index
in borrow_set
.activations_at_location(location
) {
1521 let borrow
= &borrow_set
[borrow_index
];
1523 // only mutable borrows should be 2-phase
1524 assert
!(match borrow
.kind
{
1525 BorrowKind
::Shared
| BorrowKind
::Shallow
=> false,
1526 BorrowKind
::Unique
| BorrowKind
::Mut { .. }
=> true,
1531 (borrow
.borrowed_place
, span
),
1532 (Deep
, Activation(WriteKind
::MutableBorrow(borrow
.kind
), borrow_index
)),
1533 LocalMutationIsAllowed
::No
,
1536 // We do not need to call `check_if_path_or_subpath_is_moved`
1537 // again, as we already called it when we made the
1538 // initial reservation.
1542 fn check_if_full_path_is_moved(
1545 desired_action
: InitializationRequiringAction
,
1546 place_span
: (PlaceRef
<'tcx
>, Span
),
1547 flow_state
: &Flows
<'cx
, 'tcx
>,
1549 let maybe_uninits
= &flow_state
.uninits
;
1553 // 1. Move of `a.b.c`, use of `a.b.c`
1554 // 2. Move of `a.b.c`, use of `a.b.c.d` (without first reinitializing `a.b.c.d`)
1555 // 3. Uninitialized `(a.b.c: &_)`, use of `*a.b.c`; note that with
1556 // partial initialization support, one might have `a.x`
1557 // initialized but not `a.b`.
1561 // 4. Move of `a.b.c`, use of `a.b.d`
1562 // 5. Uninitialized `a.x`, initialized `a.b`, use of `a.b`
1563 // 6. Copied `(a.b: &_)`, use of `*(a.b).c`; note that `a.b`
1564 // must have been initialized for the use to be sound.
1565 // 7. Move of `a.b.c` then reinit of `a.b.c.d`, use of `a.b.c.d`
1567 // The dataflow tracks shallow prefixes distinctly (that is,
1568 // field-accesses on P distinctly from P itself), in order to
1569 // track substructure initialization separately from the whole
1572 // E.g., when looking at (*a.b.c).d, if the closest prefix for
1573 // which we have a MovePath is `a.b`, then that means that the
1574 // initialization state of `a.b` is all we need to inspect to
1575 // know if `a.b.c` is valid (and from that we infer that the
1576 // dereference and `.d` access is also valid, since we assume
1577 // `a.b.c` is assigned a reference to an initialized and
1578 // well-formed record structure.)
1580 // Therefore, if we seek out the *closest* prefix for which we
1581 // have a MovePath, that should capture the initialization
1582 // state for the place scenario.
1584 // This code covers scenarios 1, 2, and 3.
1586 debug
!("check_if_full_path_is_moved place: {:?}", place_span
.0);
1587 let (prefix
, mpi
) = self.move_path_closest_to(place_span
.0);
1588 if maybe_uninits
.contains(mpi
) {
1589 self.report_use_of_moved_or_uninitialized(
1592 (prefix
, place_span
.0, place_span
.1),
1595 } // Only query longest prefix with a MovePath, not further
1596 // ancestors; dataflow recurs on children when parents
1597 // move (to support partial (re)inits).
1599 // (I.e., querying parents breaks scenario 7; but may want
1600 // to do such a query based on partial-init feature-gate.)
1603 /// Subslices correspond to multiple move paths, so we iterate through the
1604 /// elements of the base array. For each element we check
1606 /// * Does this element overlap with our slice.
1607 /// * Is any part of it uninitialized.
1608 fn check_if_subslice_element_is_moved(
1611 desired_action
: InitializationRequiringAction
,
1612 place_span
: (PlaceRef
<'tcx
>, Span
),
1613 maybe_uninits
: &ChunkedBitSet
<MovePathIndex
>,
1617 if let Some(mpi
) = self.move_path_for_place(place_span
.0) {
1618 let move_paths
= &self.move_data
.move_paths
;
1620 let root_path
= &move_paths
[mpi
];
1621 for (child_mpi
, child_move_path
) in root_path
.children(move_paths
) {
1622 let last_proj
= child_move_path
.place
.projection
.last().unwrap();
1623 if let ProjectionElem
::ConstantIndex { offset, from_end, .. }
= last_proj
{
1624 debug_assert
!(!from_end
, "Array constant indexing shouldn't be `from_end`.");
1626 if (from
..to
).contains(offset
) {
1628 self.move_data
.find_in_move_path_or_its_descendants(child_mpi
, |mpi
| {
1629 maybe_uninits
.contains(mpi
)
1632 if let Some(uninit_child
) = uninit_child
{
1633 self.report_use_of_moved_or_uninitialized(
1636 (place_span
.0, place_span
.0, place_span
.1),
1639 return; // don't bother finding other problems.
1647 fn check_if_path_or_subpath_is_moved(
1650 desired_action
: InitializationRequiringAction
,
1651 place_span
: (PlaceRef
<'tcx
>, Span
),
1652 flow_state
: &Flows
<'cx
, 'tcx
>,
1654 let maybe_uninits
= &flow_state
.uninits
;
1658 // 1. Move of `a.b.c`, use of `a` or `a.b`
1659 // partial initialization support, one might have `a.x`
1660 // initialized but not `a.b`.
1661 // 2. All bad scenarios from `check_if_full_path_is_moved`
1665 // 3. Move of `a.b.c`, use of `a.b.d`
1666 // 4. Uninitialized `a.x`, initialized `a.b`, use of `a.b`
1667 // 5. Copied `(a.b: &_)`, use of `*(a.b).c`; note that `a.b`
1668 // must have been initialized for the use to be sound.
1669 // 6. Move of `a.b.c` then reinit of `a.b.c.d`, use of `a.b.c.d`
1671 self.check_if_full_path_is_moved(location
, desired_action
, place_span
, flow_state
);
1673 if let Some((place_base
, ProjectionElem
::Subslice { from, to, from_end: false }
)) =
1674 place_span
.0.last_projection()
1676 let place_ty
= place_base
.ty(self.body(), self.infcx
.tcx
);
1677 if let ty
::Array(..) = place_ty
.ty
.kind() {
1678 self.check_if_subslice_element_is_moved(
1681 (place_base
, place_span
.1),
1690 // A move of any shallow suffix of `place` also interferes
1691 // with an attempt to use `place`. This is scenario 3 above.
1693 // (Distinct from handling of scenarios 1+2+4 above because
1694 // `place` does not interfere with suffixes of its prefixes,
1695 // e.g., `a.b.c` does not interfere with `a.b.d`)
1697 // This code covers scenario 1.
1699 debug
!("check_if_path_or_subpath_is_moved place: {:?}", place_span
.0);
1700 if let Some(mpi
) = self.move_path_for_place(place_span
.0) {
1701 let uninit_mpi
= self
1703 .find_in_move_path_or_its_descendants(mpi
, |mpi
| maybe_uninits
.contains(mpi
));
1705 if let Some(uninit_mpi
) = uninit_mpi
{
1706 self.report_use_of_moved_or_uninitialized(
1709 (place_span
.0, place_span
.0, place_span
.1),
1712 return; // don't bother finding other problems.
1717 /// Currently MoveData does not store entries for all places in
1718 /// the input MIR. For example it will currently filter out
1719 /// places that are Copy; thus we do not track places of shared
1720 /// reference type. This routine will walk up a place along its
1721 /// prefixes, searching for a foundational place that *is*
1722 /// tracked in the MoveData.
1724 /// An Err result includes a tag indicated why the search failed.
1725 /// Currently this can only occur if the place is built off of a
1726 /// static variable, as we do not track those in the MoveData.
1727 fn move_path_closest_to(&mut self, place
: PlaceRef
<'tcx
>) -> (PlaceRef
<'tcx
>, MovePathIndex
) {
1728 match self.move_data
.rev_lookup
.find(place
) {
1729 LookupResult
::Parent(Some(mpi
)) | LookupResult
::Exact(mpi
) => {
1730 (self.move_data
.move_paths
[mpi
].place
.as_ref(), mpi
)
1732 LookupResult
::Parent(None
) => panic
!("should have move path for every Local"),
1736 fn move_path_for_place(&mut self, place
: PlaceRef
<'tcx
>) -> Option
<MovePathIndex
> {
1737 // If returns None, then there is no move path corresponding
1738 // to a direct owner of `place` (which means there is nothing
1739 // that borrowck tracks for its analysis).
1741 match self.move_data
.rev_lookup
.find(place
) {
1742 LookupResult
::Parent(_
) => None
,
1743 LookupResult
::Exact(mpi
) => Some(mpi
),
1747 fn check_if_assigned_path_is_moved(
1750 (place
, span
): (Place
<'tcx
>, Span
),
1751 flow_state
: &Flows
<'cx
, 'tcx
>,
1753 debug
!("check_if_assigned_path_is_moved place: {:?}", place
);
1755 // None case => assigning to `x` does not require `x` be initialized.
1756 for (place_base
, elem
) in place
.iter_projections().rev() {
1758 ProjectionElem
::Index(_
/*operand*/) |
1759 ProjectionElem
::OpaqueCast(_
) |
1760 ProjectionElem
::ConstantIndex { .. }
|
1761 // assigning to P[i] requires P to be valid.
1762 ProjectionElem
::Downcast(_
/*adt_def*/, _
/*variant_idx*/) =>
1763 // assigning to (P->variant) is okay if assigning to `P` is okay
1765 // FIXME: is this true even if P is an adt with a dtor?
1768 // assigning to (*P) requires P to be initialized
1769 ProjectionElem
::Deref
=> {
1770 self.check_if_full_path_is_moved(
1771 location
, InitializationRequiringAction
::Use
,
1772 (place_base
, span
), flow_state
);
1773 // (base initialized; no need to
1778 ProjectionElem
::Subslice { .. }
=> {
1779 panic
!("we don't allow assignments to subslices, location: {:?}",
1783 ProjectionElem
::Field(..) => {
1784 // if type of `P` has a dtor, then
1785 // assigning to `P.f` requires `P` itself
1786 // be already initialized
1787 let tcx
= self.infcx
.tcx
;
1788 let base_ty
= place_base
.ty(self.body(), tcx
).ty
;
1789 match base_ty
.kind() {
1790 ty
::Adt(def
, _
) if def
.has_dtor(tcx
) => {
1791 self.check_if_path_or_subpath_is_moved(
1792 location
, InitializationRequiringAction
::Assignment
,
1793 (place_base
, span
), flow_state
);
1795 // (base initialized; no need to
1800 // Once `let s; s.x = V; read(s.x);`,
1801 // is allowed, remove this match arm.
1802 ty
::Adt(..) | ty
::Tuple(..) => {
1803 check_parent_of_field(self, location
, place_base
, span
, flow_state
);
1805 // rust-lang/rust#21232, #54499, #54986: during period where we reject
1806 // partial initialization, do not complain about unnecessary `mut` on
1807 // an attempt to do a partial initialization.
1808 self.used_mut
.insert(place
.local
);
1817 fn check_parent_of_field
<'cx
, 'tcx
>(
1818 this
: &mut MirBorrowckCtxt
<'cx
, 'tcx
>,
1820 base
: PlaceRef
<'tcx
>,
1822 flow_state
: &Flows
<'cx
, 'tcx
>,
1824 // rust-lang/rust#21232: Until Rust allows reads from the
1825 // initialized parts of partially initialized structs, we
1826 // will, starting with the 2018 edition, reject attempts
1827 // to write to structs that are not fully initialized.
1829 // In other words, *until* we allow this:
1831 // 1. `let mut s; s.x = Val; read(s.x);`
1833 // we will for now disallow this:
1835 // 2. `let mut s; s.x = Val;`
1839 // 3. `let mut s = ...; drop(s); s.x=Val;`
1841 // This does not use check_if_path_or_subpath_is_moved,
1842 // because we want to *allow* reinitializations of fields:
1843 // e.g., want to allow
1845 // `let mut s = ...; drop(s.x); s.x=Val;`
1847 // This does not use check_if_full_path_is_moved on
1848 // `base`, because that would report an error about the
1849 // `base` as a whole, but in this scenario we *really*
1850 // want to report an error about the actual thing that was
1851 // moved, which may be some prefix of `base`.
1853 // Shallow so that we'll stop at any dereference; we'll
1854 // report errors about issues with such bases elsewhere.
1855 let maybe_uninits
= &flow_state
.uninits
;
1857 // Find the shortest uninitialized prefix you can reach
1858 // without going over a Deref.
1859 let mut shortest_uninit_seen
= None
;
1860 for prefix
in this
.prefixes(base
, PrefixSet
::Shallow
) {
1861 let Some(mpi
) = this
.move_path_for_place(prefix
) else { continue }
;
1863 if maybe_uninits
.contains(mpi
) {
1865 "check_parent_of_field updating shortest_uninit_seen from {:?} to {:?}",
1866 shortest_uninit_seen
,
1869 shortest_uninit_seen
= Some((prefix
, mpi
));
1871 debug
!("check_parent_of_field {:?} is definitely initialized", (prefix
, mpi
));
1875 if let Some((prefix
, mpi
)) = shortest_uninit_seen
{
1876 // Check for a reassignment into an uninitialized field of a union (for example,
1877 // after a move out). In this case, do not report an error here. There is an
1878 // exception, if this is the first assignment into the union (that is, there is
1879 // no move out from an earlier location) then this is an attempt at initialization
1880 // of the union - we should error in that case.
1881 let tcx
= this
.infcx
.tcx
;
1882 if base
.ty(this
.body(), tcx
).ty
.is_union() {
1883 if this
.move_data
.path_map
[mpi
].iter().any(|moi
| {
1884 this
.move_data
.moves
[*moi
].source
.is_predecessor_of(location
, this
.body
)
1890 this
.report_use_of_moved_or_uninitialized(
1892 InitializationRequiringAction
::PartialAssignment
,
1893 (prefix
, base
, span
),
1900 /// Checks the permissions for the given place and read or write kind
1902 /// Returns `true` if an error is reported.
1903 fn check_access_permissions(
1905 (place
, span
): (Place
<'tcx
>, Span
),
1907 is_local_mutation_allowed
: LocalMutationIsAllowed
,
1908 flow_state
: &Flows
<'cx
, 'tcx
>,
1912 "check_access_permissions({:?}, {:?}, is_local_mutation_allowed: {:?})",
1913 place
, kind
, is_local_mutation_allowed
1920 Reservation(WriteKind
::MutableBorrow(
1921 borrow_kind @
(BorrowKind
::Unique
| BorrowKind
::Mut { .. }
),
1923 | Write(WriteKind
::MutableBorrow(
1924 borrow_kind @
(BorrowKind
::Unique
| BorrowKind
::Mut { .. }
),
1926 let is_local_mutation_allowed
= match borrow_kind
{
1927 BorrowKind
::Unique
=> LocalMutationIsAllowed
::Yes
,
1928 BorrowKind
::Mut { .. }
=> is_local_mutation_allowed
,
1929 BorrowKind
::Shared
| BorrowKind
::Shallow
=> unreachable
!(),
1931 match self.is_mutable(place
.as_ref(), is_local_mutation_allowed
) {
1933 self.add_used_mut(root_place
, flow_state
);
1937 error_access
= AccessKind
::MutableBorrow
;
1938 the_place_err
= place_err
;
1942 Reservation(WriteKind
::Mutate
) | Write(WriteKind
::Mutate
) => {
1943 match self.is_mutable(place
.as_ref(), is_local_mutation_allowed
) {
1945 self.add_used_mut(root_place
, flow_state
);
1949 error_access
= AccessKind
::Mutate
;
1950 the_place_err
= place_err
;
1957 | WriteKind
::StorageDeadOrDrop
1958 | WriteKind
::MutableBorrow(BorrowKind
::Shared
)
1959 | WriteKind
::MutableBorrow(BorrowKind
::Shallow
),
1963 | WriteKind
::StorageDeadOrDrop
1964 | WriteKind
::MutableBorrow(BorrowKind
::Shared
)
1965 | WriteKind
::MutableBorrow(BorrowKind
::Shallow
),
1967 if self.is_mutable(place
.as_ref(), is_local_mutation_allowed
).is_err()
1968 && !self.has_buffered_errors()
1970 // rust-lang/rust#46908: In pure NLL mode this code path should be
1971 // unreachable, but we use `delay_span_bug` because we can hit this when
1972 // dereferencing a non-Copy raw pointer *and* have `-Ztreat-err-as-bug`
1973 // enabled. We don't want to ICE for that case, as other errors will have
1974 // been emitted (#52262).
1975 self.infcx
.tcx
.sess
.delay_span_bug(
1978 "Accessing `{:?}` with the kind `{:?}` shouldn't be possible",
1986 // permission checks are done at Reservation point.
1992 | BorrowKind
::Mut { .. }
1993 | BorrowKind
::Shared
1994 | BorrowKind
::Shallow
,
1998 // Access authorized
2003 // rust-lang/rust#21232, #54986: during period where we reject
2004 // partial initialization, do not complain about mutability
2005 // errors except for actual mutation (as opposed to an attempt
2006 // to do a partial initialization).
2007 let previously_initialized
= self.is_local_ever_initialized(place
.local
, flow_state
);
2009 // at this point, we have set up the error reporting state.
2010 if let Some(init_index
) = previously_initialized
{
2011 if let (AccessKind
::Mutate
, Some(_
)) = (error_access
, place
.as_local()) {
2012 // If this is a mutate access to an immutable local variable with no projections
2013 // report the error as an illegal reassignment
2014 let init
= &self.move_data
.inits
[init_index
];
2015 let assigned_span
= init
.span(&self.body
);
2016 self.report_illegal_reassignment(location
, (place
, span
), assigned_span
, place
);
2018 self.report_mutability_error(place
, span
, the_place_err
, error_access
, location
)
2026 fn is_local_ever_initialized(
2029 flow_state
: &Flows
<'cx
, 'tcx
>,
2030 ) -> Option
<InitIndex
> {
2031 let mpi
= self.move_data
.rev_lookup
.find_local(local
);
2032 let ii
= &self.move_data
.init_path_map
[mpi
];
2033 ii
.into_iter().find(|&&index
| flow_state
.ever_inits
.contains(index
)).copied()
2036 /// Adds the place into the used mutable variables set
2037 fn add_used_mut(&mut self, root_place
: RootPlace
<'tcx
>, flow_state
: &Flows
<'cx
, 'tcx
>) {
2039 RootPlace { place_local: local, place_projection: [], is_local_mutation_allowed }
=> {
2040 // If the local may have been initialized, and it is now currently being
2041 // mutated, then it is justified to be annotated with the `mut`
2042 // keyword, since the mutation may be a possible reassignment.
2043 if is_local_mutation_allowed
!= LocalMutationIsAllowed
::Yes
2044 && self.is_local_ever_initialized(local
, flow_state
).is_some()
2046 self.used_mut
.insert(local
);
2051 place_projection
: _
,
2052 is_local_mutation_allowed
: LocalMutationIsAllowed
::Yes
,
2056 place_projection
: place_projection @
[.., _
],
2057 is_local_mutation_allowed
: _
,
2059 if let Some(field
) = self.is_upvar_field_projection(PlaceRef
{
2061 projection
: place_projection
,
2063 self.used_mut_upvars
.push(field
);
2069 /// Whether this value can be written or borrowed mutably.
2070 /// Returns the root place if the place passed in is a projection.
2073 place
: PlaceRef
<'tcx
>,
2074 is_local_mutation_allowed
: LocalMutationIsAllowed
,
2075 ) -> Result
<RootPlace
<'tcx
>, PlaceRef
<'tcx
>> {
2076 debug
!("is_mutable: place={:?}, is_local...={:?}", place
, is_local_mutation_allowed
);
2077 match place
.last_projection() {
2079 let local
= &self.body
.local_decls
[place
.local
];
2080 match local
.mutability
{
2081 Mutability
::Not
=> match is_local_mutation_allowed
{
2082 LocalMutationIsAllowed
::Yes
=> Ok(RootPlace
{
2083 place_local
: place
.local
,
2084 place_projection
: place
.projection
,
2085 is_local_mutation_allowed
: LocalMutationIsAllowed
::Yes
,
2087 LocalMutationIsAllowed
::ExceptUpvars
=> Ok(RootPlace
{
2088 place_local
: place
.local
,
2089 place_projection
: place
.projection
,
2090 is_local_mutation_allowed
: LocalMutationIsAllowed
::ExceptUpvars
,
2092 LocalMutationIsAllowed
::No
=> Err(place
),
2094 Mutability
::Mut
=> Ok(RootPlace
{
2095 place_local
: place
.local
,
2096 place_projection
: place
.projection
,
2097 is_local_mutation_allowed
,
2101 Some((place_base
, elem
)) => {
2103 ProjectionElem
::Deref
=> {
2104 let base_ty
= place_base
.ty(self.body(), self.infcx
.tcx
).ty
;
2106 // Check the kind of deref to decide
2107 match base_ty
.kind() {
2108 ty
::Ref(_
, _
, mutbl
) => {
2110 // Shared borrowed data is never mutable
2111 hir
::Mutability
::Not
=> Err(place
),
2112 // Mutably borrowed data is mutable, but only if we have a
2113 // unique path to the `&mut`
2114 hir
::Mutability
::Mut
=> {
2115 let mode
= match self.is_upvar_field_projection(place
) {
2116 Some(field
) if self.upvars
[field
.index()].by_ref
=> {
2117 is_local_mutation_allowed
2119 _
=> LocalMutationIsAllowed
::Yes
,
2122 self.is_mutable(place_base
, mode
)
2126 ty
::RawPtr(tnm
) => {
2128 // `*const` raw pointers are not mutable
2129 hir
::Mutability
::Not
=> Err(place
),
2130 // `*mut` raw pointers are always mutable, regardless of
2131 // context. The users have to check by themselves.
2132 hir
::Mutability
::Mut
=> Ok(RootPlace
{
2133 place_local
: place
.local
,
2134 place_projection
: place
.projection
,
2135 is_local_mutation_allowed
,
2139 // `Box<T>` owns its content, so mutable if its location is mutable
2140 _
if base_ty
.is_box() => {
2141 self.is_mutable(place_base
, is_local_mutation_allowed
)
2143 // Deref should only be for reference, pointers or boxes
2144 _
=> bug
!("Deref of unexpected type: {:?}", base_ty
),
2147 // All other projections are owned by their base path, so mutable if
2148 // base path is mutable
2149 ProjectionElem
::Field(..)
2150 | ProjectionElem
::Index(..)
2151 | ProjectionElem
::ConstantIndex { .. }
2152 | ProjectionElem
::Subslice { .. }
2153 | ProjectionElem
::OpaqueCast { .. }
2154 | ProjectionElem
::Downcast(..) => {
2155 let upvar_field_projection
= self.is_upvar_field_projection(place
);
2156 if let Some(field
) = upvar_field_projection
{
2157 let upvar
= &self.upvars
[field
.index()];
2159 "is_mutable: upvar.mutability={:?} local_mutation_is_allowed={:?} \
2160 place={:?}, place_base={:?}",
2161 upvar
, is_local_mutation_allowed
, place
, place_base
2163 match (upvar
.place
.mutability
, is_local_mutation_allowed
) {
2166 LocalMutationIsAllowed
::No
2167 | LocalMutationIsAllowed
::ExceptUpvars
,
2169 (Mutability
::Not
, LocalMutationIsAllowed
::Yes
)
2170 | (Mutability
::Mut
, _
) => {
2171 // Subtle: this is an upvar
2172 // reference, so it looks like
2173 // `self.foo` -- we want to double
2174 // check that the location `*self`
2175 // is mutable (i.e., this is not a
2176 // `Fn` closure). But if that
2177 // check succeeds, we want to
2178 // *blame* the mutability on
2179 // `place` (that is,
2180 // `self.foo`). This is used to
2181 // propagate the info about
2182 // whether mutability declarations
2183 // are used outwards, so that we register
2184 // the outer variable as mutable. Otherwise a
2185 // test like this fails to record the `mut`
2189 // fn foo<F: FnOnce()>(_f: F) { }
2191 // let var = Vec::new();
2198 self.is_mutable(place_base
, is_local_mutation_allowed
)?
;
2200 place_local
: place
.local
,
2201 place_projection
: place
.projection
,
2202 is_local_mutation_allowed
,
2207 self.is_mutable(place_base
, is_local_mutation_allowed
)
2215 /// If `place` is a field projection, and the field is being projected from a closure type,
2216 /// then returns the index of the field being projected. Note that this closure will always
2217 /// be `self` in the current MIR, because that is the only time we directly access the fields
2218 /// of a closure type.
2219 fn is_upvar_field_projection(&self, place_ref
: PlaceRef
<'tcx
>) -> Option
<Field
> {
2220 path_utils
::is_upvar_field_projection(self.infcx
.tcx
, &self.upvars
, place_ref
, self.body())
2223 fn dominators(&self) -> &Dominators
<BasicBlock
> {
2224 self.dominators
.get_or_init(|| self.body
.basic_blocks
.dominators())
2229 use rustc_errors
::ErrorGuaranteed
;
2233 pub struct BorrowckErrors
<'tcx
> {
2235 /// This field keeps track of move errors that are to be reported for given move indices.
2237 /// There are situations where many errors can be reported for a single move out (see #53807)
2238 /// and we want only the best of those errors.
2240 /// The `report_use_of_moved_or_uninitialized` function checks this map and replaces the
2241 /// diagnostic (if there is one) if the `Place` of the error being reported is a prefix of the
2242 /// `Place` of the previous most diagnostic. This happens instead of buffering the error. Once
2243 /// all move errors have been reported, any diagnostics in this map are added to the buffer
2246 /// `BTreeMap` is used to preserve the order of insertions when iterating. This is necessary
2247 /// when errors in the map are being re-added to the error buffer so that errors with the
2248 /// same primary span come out in a consistent order.
2249 buffered_move_errors
:
2250 BTreeMap
<Vec
<MoveOutIndex
>, (PlaceRef
<'tcx
>, DiagnosticBuilder
<'tcx
, ErrorGuaranteed
>)>,
2251 buffered_mut_errors
: FxHashMap
<Span
, (DiagnosticBuilder
<'tcx
, ErrorGuaranteed
>, usize)>,
2252 /// Diagnostics to be reported buffer.
2253 buffered
: Vec
<Diagnostic
>,
2254 /// Set to Some if we emit an error during borrowck
2255 tainted_by_errors
: Option
<ErrorGuaranteed
>,
2258 impl<'tcx
> BorrowckErrors
<'tcx
> {
2259 pub fn new(tcx
: TyCtxt
<'tcx
>) -> Self {
2262 buffered_move_errors
: BTreeMap
::new(),
2263 buffered_mut_errors
: Default
::default(),
2264 buffered
: Default
::default(),
2265 tainted_by_errors
: None
,
2269 pub fn buffer_error(&mut self, t
: DiagnosticBuilder
<'_
, ErrorGuaranteed
>) {
2270 if let None
= self.tainted_by_errors
{
2271 self.tainted_by_errors
= Some(
2274 .delay_span_bug(t
.span
.clone(), "diagnostic buffered but not emitted"),
2277 t
.buffer(&mut self.buffered
);
2280 pub fn buffer_non_error_diag(&mut self, t
: DiagnosticBuilder
<'_
, ()>) {
2281 t
.buffer(&mut self.buffered
);
2284 pub fn set_tainted_by_errors(&mut self, e
: ErrorGuaranteed
) {
2285 self.tainted_by_errors
= Some(e
);
2289 impl<'cx
, 'tcx
> MirBorrowckCtxt
<'cx
, 'tcx
> {
2290 pub fn buffer_error(&mut self, t
: DiagnosticBuilder
<'_
, ErrorGuaranteed
>) {
2291 self.errors
.buffer_error(t
);
2294 pub fn buffer_non_error_diag(&mut self, t
: DiagnosticBuilder
<'_
, ()>) {
2295 self.errors
.buffer_non_error_diag(t
);
2298 pub fn buffer_move_error(
2300 move_out_indices
: Vec
<MoveOutIndex
>,
2301 place_and_err
: (PlaceRef
<'tcx
>, DiagnosticBuilder
<'tcx
, ErrorGuaranteed
>),
2303 if let Some((_
, diag
)) =
2304 self.errors
.buffered_move_errors
.insert(move_out_indices
, place_and_err
)
2306 // Cancel the old diagnostic so we don't ICE
2314 pub fn get_buffered_mut_error(
2317 ) -> Option
<(DiagnosticBuilder
<'tcx
, ErrorGuaranteed
>, usize)> {
2318 self.errors
.buffered_mut_errors
.remove(&span
)
2321 pub fn buffer_mut_error(
2324 t
: DiagnosticBuilder
<'tcx
, ErrorGuaranteed
>,
2327 self.errors
.buffered_mut_errors
.insert(span
, (t
, count
));
2330 pub fn emit_errors(&mut self) -> Option
<ErrorGuaranteed
> {
2331 // Buffer any move errors that we collected and de-duplicated.
2332 for (_
, (_
, diag
)) in std
::mem
::take(&mut self.errors
.buffered_move_errors
) {
2333 // We have already set tainted for this error, so just buffer it.
2334 diag
.buffer(&mut self.errors
.buffered
);
2336 for (_
, (mut diag
, count
)) in std
::mem
::take(&mut self.errors
.buffered_mut_errors
) {
2338 diag
.note(&format
!("...and {} other attempted mutable borrows", count
- 10));
2340 diag
.buffer(&mut self.errors
.buffered
);
2343 if !self.errors
.buffered
.is_empty() {
2344 self.errors
.buffered
.sort_by_key(|diag
| diag
.sort_span
);
2346 for mut diag
in self.errors
.buffered
.drain(..) {
2347 self.infcx
.tcx
.sess
.diagnostic().emit_diagnostic(&mut diag
);
2351 self.errors
.tainted_by_errors
2354 pub fn has_buffered_errors(&self) -> bool
{
2355 self.errors
.buffered
.is_empty()
2358 pub fn has_move_error(
2360 move_out_indices
: &[MoveOutIndex
],
2361 ) -> Option
<&(PlaceRef
<'tcx
>, DiagnosticBuilder
<'cx
, ErrorGuaranteed
>)> {
2362 self.errors
.buffered_move_errors
.get(move_out_indices
)
2367 /// The degree of overlap between 2 places for borrow-checking.
2369 /// The places might partially overlap - in this case, we give
2370 /// up and say that they might conflict. This occurs when
2371 /// different fields of a union are borrowed. For example,
2372 /// if `u` is a union, we have no way of telling how disjoint
2373 /// `u.a.x` and `a.b.y` are.
2375 /// The places have the same type, and are either completely disjoint
2376 /// or equal - i.e., they can't "partially" overlap as can occur with
2377 /// unions. This is the "base case" on which we recur for extensions
2380 /// The places are disjoint, so we know all extensions of them
2381 /// will also be disjoint.