summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_borrowck
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_borrowck')
-rw-r--r--compiler/rustc_borrowck/Cargo.toml2
-rw-r--r--compiler/rustc_borrowck/messages.ftl291
-rw-r--r--compiler/rustc_borrowck/src/borrow_set.rs4
-rw-r--r--compiler/rustc_borrowck/src/borrowck_errors.rs1
-rw-r--r--compiler/rustc_borrowck/src/constraints/graph.rs2
-rw-r--r--compiler/rustc_borrowck/src/constraints/mod.rs2
-rw-r--r--compiler/rustc_borrowck/src/consumers.rs99
-rw-r--r--compiler/rustc_borrowck/src/dataflow.rs132
-rw-r--r--compiler/rustc_borrowck/src/def_use.rs8
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs2
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs437
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs17
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mod.rs298
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/move_errors.rs55
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs555
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs13
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_errors.rs22
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_name.rs2
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/var_name.rs4
-rw-r--r--compiler/rustc_borrowck/src/facts.rs5
-rw-r--r--compiler/rustc_borrowck/src/invalidation.rs12
-rw-r--r--compiler/rustc_borrowck/src/lib.rs140
-rw-r--r--compiler/rustc_borrowck/src/location.rs8
-rw-r--r--compiler/rustc_borrowck/src/member_constraints.rs4
-rw-r--r--compiler/rustc_borrowck/src/nll.rs35
-rw-r--r--compiler/rustc_borrowck/src/place_ext.rs2
-rw-r--r--compiler/rustc_borrowck/src/places_conflict.rs4
-rw-r--r--compiler/rustc_borrowck/src/region_infer/mod.rs31
-rw-r--r--compiler/rustc_borrowck/src/region_infer/opaque_types.rs11
-rw-r--r--compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs13
-rw-r--r--compiler/rustc_borrowck/src/region_infer/values.rs22
-rw-r--r--compiler/rustc_borrowck/src/renumber.rs10
-rw-r--r--compiler/rustc_borrowck/src/session_diagnostics.rs215
-rw-r--r--compiler/rustc_borrowck/src/type_check/canonical.rs68
-rw-r--r--compiler/rustc_borrowck/src/type_check/free_region_relations.rs21
-rw-r--r--compiler/rustc_borrowck/src/type_check/input_output.rs5
-rw-r--r--compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs2
-rw-r--r--compiler/rustc_borrowck/src/type_check/liveness/trace.rs18
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs223
-rw-r--r--compiler/rustc_borrowck/src/type_check/relate_tys.rs42
-rw-r--r--compiler/rustc_borrowck/src/universal_regions.rs66
-rw-r--r--compiler/rustc_borrowck/src/util/collect_writes.rs36
-rw-r--r--compiler/rustc_borrowck/src/util/mod.rs3
43 files changed, 1825 insertions, 1117 deletions
diff --git a/compiler/rustc_borrowck/Cargo.toml b/compiler/rustc_borrowck/Cargo.toml
index 87c113f3e..56a9deb6a 100644
--- a/compiler/rustc_borrowck/Cargo.toml
+++ b/compiler/rustc_borrowck/Cargo.toml
@@ -15,12 +15,12 @@ rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
rustc_graphviz = { path = "../rustc_graphviz" }
rustc_hir = { path = "../rustc_hir" }
+rustc_fluent_macro = { path = "../rustc_fluent_macro" }
rustc_index = { path = "../rustc_index" }
rustc_infer = { path = "../rustc_infer" }
rustc_lexer = { path = "../rustc_lexer" }
rustc_macros = { path = "../rustc_macros" }
rustc_middle = { path = "../rustc_middle" }
-rustc_const_eval = { path = "../rustc_const_eval" }
rustc_mir_dataflow = { path = "../rustc_mir_dataflow" }
rustc_serialize = { path = "../rustc_serialize" }
rustc_session = { path = "../rustc_session" }
diff --git a/compiler/rustc_borrowck/messages.ftl b/compiler/rustc_borrowck/messages.ftl
index a3b6b5e81..67fdb6717 100644
--- a/compiler/rustc_borrowck/messages.ftl
+++ b/compiler/rustc_borrowck/messages.ftl
@@ -1,129 +1,264 @@
-borrowck_move_unsized =
- cannot move a value of type `{$ty}`
- .label = the size of `{$ty}` cannot be statically determined
+borrowck_assign_due_to_use_closure =
+ assignment occurs due to use in closure
-borrowck_higher_ranked_lifetime_error =
- higher-ranked lifetime error
+borrowck_assign_due_to_use_generator =
+ assign occurs due to use in generator
-borrowck_could_not_prove =
- could not prove `{$predicate}`
+borrowck_assign_part_due_to_use_closure =
+ assignment to part occurs due to use in closure
+
+borrowck_assign_part_due_to_use_generator =
+ assign to part occurs due to use in generator
+
+borrowck_borrow_due_to_use_closure =
+ borrow occurs due to use in closure
+
+borrowck_borrow_due_to_use_generator =
+ borrow occurs due to use in generator
+
+borrowck_calling_operator_moves_lhs =
+ calling this operator moves the left-hand side
+
+borrowck_cannot_move_when_borrowed =
+ cannot move out of {$place ->
+ [value] value
+ *[other] {$place}
+ } because it is borrowed
+ .label = borrow of {$borrow_place ->
+ [value] value
+ *[other] {$borrow_place}
+ } occurs here
+ .move_label = move out of {$value_place ->
+ [value] value
+ *[other] {$value_place}
+ } occurs here
+
+borrowck_capture_immute =
+ capture is immutable because of use here
+
+borrowck_capture_move =
+ capture is moved because of use here
+
+borrowck_capture_mut =
+ capture is mutable because of use here
+
+borrowck_closure_inferred_mut = inferred to be a `FnMut` closure
+
+borrowck_closure_invoked_twice =
+ closure cannot be invoked more than once because it moves the variable `{$place_name}` out of its environment
+
+borrowck_closure_moved_twice =
+ closure cannot be moved more than once as it is not `Copy` due to moving the variable `{$place_name}` out of its environment
+
+borrowck_consider_borrow_type_contents =
+ help: consider calling `.as_ref()` or `.as_mut()` to borrow the type's contents
borrowck_could_not_normalize =
could not normalize `{$value}`
-borrowck_higher_ranked_subtype_error =
- higher-ranked subtype error
+borrowck_could_not_prove =
+ could not prove `{$predicate}`
+
+borrowck_func_take_self_moved_place =
+ `{$func}` takes ownership of the receiver `self`, which moves {$place_name}
borrowck_generic_does_not_live_long_enough =
`{$kind}` does not live long enough
+borrowck_higher_ranked_lifetime_error =
+ higher-ranked lifetime error
+
+borrowck_higher_ranked_subtype_error =
+ higher-ranked subtype error
+
+borrowck_lifetime_constraints_error =
+ lifetime may not live long enough
+
borrowck_move_borrowed =
cannot move out of `{$desc}` because it is borrowed
-borrowck_var_does_not_need_mut =
- variable does not need to be mutable
- .suggestion = remove this `mut`
+borrowck_move_out_place_here =
+ {$place} is moved here
-borrowck_var_cannot_escape_closure =
- captured variable cannot escape `FnMut` closure body
- .note = `FnMut` closures only have access to their captured variables while they are executing...
- .cannot_escape = ...therefore, they cannot allow references to captured variables to escape
+borrowck_move_unsized =
+ cannot move a value of type `{$ty}`
+ .label = the size of `{$ty}` cannot be statically determined
-borrowck_var_here_defined = variable defined here
+borrowck_moved_a_fn_once_in_call =
+ this value implements `FnOnce`, which causes it to be moved when called
-borrowck_var_here_captured = variable captured here
+borrowck_moved_due_to_await =
+ {$place_name} {$is_partial ->
+ [true] partially moved
+ *[false] moved
+ } due to this {$is_loop_message ->
+ [true] await, in previous iteration of loop
+ *[false] await
+ }
-borrowck_closure_inferred_mut = inferred to be a `FnMut` closure
+borrowck_moved_due_to_call =
+ {$place_name} {$is_partial ->
+ [true] partially moved
+ *[false] moved
+ } due to this {$is_loop_message ->
+ [true] call, in previous iteration of loop
+ *[false] call
+ }
-borrowck_returned_closure_escaped =
- returns a closure that contains a reference to a captured variable, which then escapes the closure body
+borrowck_moved_due_to_implicit_into_iter_call =
+ {$place_name} {$is_partial ->
+ [true] partially moved
+ *[false] moved
+ } due to this implicit call to {$is_loop_message ->
+ [true] `.into_iter()`, in previous iteration of loop
+ *[false] `.into_iter()`
+ }
+
+borrowck_moved_due_to_method_call =
+ {$place_name} {$is_partial ->
+ [true] partially moved
+ *[false] moved
+ } due to this method {$is_loop_message ->
+ [true] call, in previous iteration of loop
+ *[false] call
+ }
+
+borrowck_moved_due_to_usage_in_operator =
+ {$place_name} {$is_partial ->
+ [true] partially moved
+ *[false] moved
+ } due to usage in {$is_loop_message ->
+ [true] operator, in previous iteration of loop
+ *[false] operator
+ }
+
+borrowck_opaque_type_non_generic_param =
+ expected generic {$kind} parameter, found `{$ty}`
+ .label = {STREQ($ty, "'static") ->
+ [true] cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type
+ *[other] this generic parameter must be used with a generic {$kind} parameter
+ }
+
+borrowck_partial_var_move_by_use_in_closure =
+ variable {$is_partial ->
+ [true] partially moved
+ *[false] moved
+ } due to use in closure
+
+borrowck_partial_var_move_by_use_in_generator =
+ variable {$is_partial ->
+ [true] partially moved
+ *[false] moved
+ } due to use in generator
borrowck_returned_async_block_escaped =
returns an `async` block that contains a reference to a captured variable, which then escapes the closure body
-borrowck_returned_ref_escaped =
- returns a reference to a captured variable which escapes the closure body
+borrowck_returned_closure_escaped =
+ returns a closure that contains a reference to a captured variable, which then escapes the closure body
-borrowck_lifetime_constraints_error =
- lifetime may not live long enough
+borrowck_returned_lifetime_short =
+ {$category_desc}requires that `{$free_region_name}` must outlive `{$outlived_fr_name}`
borrowck_returned_lifetime_wrong =
{$mir_def_name} was supposed to return data with lifetime `{$outlived_fr_name}` but it is returning data with lifetime `{$fr_name}`
-borrowck_returned_lifetime_short =
- {$category_desc}requires that `{$free_region_name}` must outlive `{$outlived_fr_name}`
+borrowck_returned_ref_escaped =
+ returns a reference to a captured variable which escapes the closure body
+
+borrowck_suggest_create_freash_reborrow =
+ consider reborrowing the `Pin` instead of moving it
+
+borrowck_suggest_iterate_over_slice =
+ consider iterating over a slice of the `{$ty}`'s content to avoid moving into the `for` loop
+
+borrowck_ty_no_impl_copy =
+ {$is_partial_move ->
+ [true] partial move
+ *[false] move
+ } occurs because {$place} has type `{$ty}`, which does not implement the `Copy` trait
+
+borrowck_use_due_to_use_closure =
+ use occurs due to use in closure
+
+borrowck_use_due_to_use_generator =
+ use occurs due to use in generator
borrowck_used_impl_require_static =
the used `impl` has a `'static` requirement
-borrowck_capture_kind_label =
- capture is {$kind_desc} because of use here
-
-borrowck_var_borrow_by_use_place_in_generator =
- borrow occurs due to use of {$place} in closure in generator
+borrowck_value_capture_here =
+ value captured {$is_within ->
+ [true] here by generator
+ *[false] here
+ }
-borrowck_var_borrow_by_use_place_in_closure =
- borrow occurs due to use of {$place} in closure
+borrowck_value_moved_here =
+ value {$is_partial ->
+ [true] partially moved
+ *[false] moved
+ } {$is_move_msg ->
+ [true] into closure here
+ *[false] here
+ }{$is_loop_message ->
+ [true] , in previous iteration of loop
+ *[false] {""}
+ }
-borrowck_var_borrow_by_use_place =
- borrow occurs due to use of {$place}
+borrowck_var_borrow_by_use_in_closure =
+ borrow occurs due to use in closure
-borrowck_borrow_due_to_use_generator =
+borrowck_var_borrow_by_use_in_generator =
borrow occurs due to use in generator
-borrowck_use_due_to_use_generator =
- use occurs due to use in generator
+borrowck_var_borrow_by_use_place_in_closure =
+ {$is_single_var ->
+ *[true] borrow occurs
+ [false] borrows occur
+ } due to use of {$place} in closure
-borrowck_assign_due_to_use_generator =
- assign occurs due to use in generator
+borrowck_var_borrow_by_use_place_in_generator =
+ {$is_single_var ->
+ *[true] borrow occurs
+ [false] borrows occur
+ } due to use of {$place} in generator
-borrowck_assign_part_due_to_use_generator =
- assign to part occurs due to use in generator
+borrowck_var_cannot_escape_closure =
+ captured variable cannot escape `FnMut` closure body
+ .note = `FnMut` closures only have access to their captured variables while they are executing...
+ .cannot_escape = ...therefore, they cannot allow references to captured variables to escape
-borrowck_borrow_due_to_use_closure =
- borrow occurs due to use in closure
+borrowck_var_does_not_need_mut =
+ variable does not need to be mutable
+ .suggestion = remove this `mut`
-borrowck_use_due_to_use_closure =
- use occurs due to use in closure
+borrowck_var_first_borrow_by_use_place_in_closure =
+ first borrow occurs due to use of {$place} in closure
-borrowck_assign_due_to_use_closure =
- assignment occurs due to use in closure
+borrowck_var_first_borrow_by_use_place_in_generator =
+ first borrow occurs due to use of {$place} in generator
-borrowck_assign_part_due_to_use_closure =
- assignment to part occurs due to use in closure
+borrowck_var_here_captured = variable captured here
-borrowck_capture_immute =
- capture is immutable because of use here
+borrowck_var_here_defined = variable defined here
-borrowck_capture_mut =
- capture is mutable because of use here
+borrowck_var_move_by_use_in_closure =
+ move occurs due to use in closure
-borrowck_capture_move =
- capture is moved because of use here
+borrowck_var_move_by_use_in_generator =
+ move occurs due to use in generator
+
+borrowck_var_move_by_use_place_in_closure =
+ move occurs due to use of {$place} in closure
borrowck_var_move_by_use_place_in_generator =
move occurs due to use of {$place} in generator
-borrowck_var_move_by_use_place_in_closure =
- move occurs due to use of {$place} in closure
+borrowck_var_mutable_borrow_by_use_place_in_closure =
+ mutable borrow occurs due to use of {$place} in closure
-borrowck_cannot_move_when_borrowed =
- cannot move out of {$place ->
- [value] value
- *[other] {$place}
- } because it is borrowed
- .label = borrow of {$borrow_place ->
- [value] value
- *[other] {$borrow_place}
- } occurs here
- .move_label = move out of {$value_place ->
- [value] value
- *[other] {$value_place}
- } occurs here
+borrowck_var_second_borrow_by_use_place_in_closure =
+ second borrow occurs due to use of {$place} in closure
-borrowck_opaque_type_non_generic_param =
- expected generic {$kind} parameter, found `{$ty}`
- .label = {STREQ($ty, "'static") ->
- [true] cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type
- *[other] this generic parameter must be used with a generic {$kind} parameter
- }
+borrowck_var_second_borrow_by_use_place_in_generator =
+ second borrow occurs due to use of {$place} in generator
diff --git a/compiler/rustc_borrowck/src/borrow_set.rs b/compiler/rustc_borrowck/src/borrow_set.rs
index 4824f6346..6be20b097 100644
--- a/compiler/rustc_borrowck/src/borrow_set.rs
+++ b/compiler/rustc_borrowck/src/borrow_set.rs
@@ -30,7 +30,7 @@ pub struct BorrowSet<'tcx> {
/// Map from local to all the borrows on that local.
pub local_map: FxIndexMap<mir::Local, FxIndexSet<BorrowIndex>>,
- pub(crate) locals_state_at_exit: LocalsStateAtExit,
+ pub locals_state_at_exit: LocalsStateAtExit,
}
impl<'tcx> Index<BorrowIndex> for BorrowSet<'tcx> {
@@ -153,7 +153,7 @@ impl<'tcx> BorrowSet<'tcx> {
self.activation_map.get(&location).map_or(&[], |activations| &activations[..])
}
- pub(crate) fn len(&self) -> usize {
+ pub fn len(&self) -> usize {
self.location_map.len()
}
diff --git a/compiler/rustc_borrowck/src/borrowck_errors.rs b/compiler/rustc_borrowck/src/borrowck_errors.rs
index 2bbb9618d..acca1a147 100644
--- a/compiler/rustc_borrowck/src/borrowck_errors.rs
+++ b/compiler/rustc_borrowck/src/borrowck_errors.rs
@@ -469,6 +469,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
}
#[rustc_lint_diagnostics]
+ #[track_caller]
pub(crate) fn struct_span_err_with_code<S: Into<MultiSpan>>(
&self,
sp: S,
diff --git a/compiler/rustc_borrowck/src/constraints/graph.rs b/compiler/rustc_borrowck/src/constraints/graph.rs
index f5a34cb05..8b7d9ec2c 100644
--- a/compiler/rustc_borrowck/src/constraints/graph.rs
+++ b/compiler/rustc_borrowck/src/constraints/graph.rs
@@ -1,5 +1,5 @@
use rustc_data_structures::graph;
-use rustc_index::vec::IndexVec;
+use rustc_index::IndexVec;
use rustc_middle::mir::ConstraintCategory;
use rustc_middle::ty::{RegionVid, VarianceDiagInfo};
use rustc_span::DUMMY_SP;
diff --git a/compiler/rustc_borrowck/src/constraints/mod.rs b/compiler/rustc_borrowck/src/constraints/mod.rs
index d2d9779db..315886bbe 100644
--- a/compiler/rustc_borrowck/src/constraints/mod.rs
+++ b/compiler/rustc_borrowck/src/constraints/mod.rs
@@ -2,7 +2,7 @@
#![deny(rustc::diagnostic_outside_of_impl)]
use rustc_data_structures::graph::scc::Sccs;
-use rustc_index::vec::{IndexSlice, IndexVec};
+use rustc_index::{IndexSlice, IndexVec};
use rustc_middle::mir::ConstraintCategory;
use rustc_middle::ty::{RegionVid, VarianceDiagInfo};
use rustc_span::Span;
diff --git a/compiler/rustc_borrowck/src/consumers.rs b/compiler/rustc_borrowck/src/consumers.rs
index cb1a65222..d25714537 100644
--- a/compiler/rustc_borrowck/src/consumers.rs
+++ b/compiler/rustc_borrowck/src/consumers.rs
@@ -3,22 +3,96 @@
//! This file provides API for compiler consumers.
use rustc_hir::def_id::LocalDefId;
-use rustc_index::vec::IndexSlice;
-use rustc_infer::infer::{DefiningAnchor, TyCtxtInferExt};
-use rustc_middle::mir::Body;
-use rustc_middle::ty::{self, TyCtxt};
+use rustc_index::{IndexSlice, IndexVec};
+use rustc_infer::infer::TyCtxtInferExt;
+use rustc_middle::mir::{Body, Promoted};
+use rustc_middle::traits::DefiningAnchor;
+use rustc_middle::ty::TyCtxt;
+use std::rc::Rc;
+
+use crate::borrow_set::BorrowSet;
pub use super::{
+ constraints::OutlivesConstraint,
+ dataflow::{calculate_borrows_out_of_scope_at_location, BorrowIndex, Borrows},
facts::{AllFacts as PoloniusInput, RustcFacts},
location::{LocationTable, RichLocation},
nll::PoloniusOutput,
- BodyWithBorrowckFacts,
+ place_ext::PlaceExt,
+ places_conflict::{places_conflict, PlaceConflictBias},
+ region_infer::RegionInferenceContext,
};
-/// This function computes Polonius facts for the given body. It makes a copy of
-/// the body because it needs to regenerate the region identifiers. This function
-/// should never be invoked during a typical compilation session due to performance
-/// issues with Polonius.
+/// Options determining the output behavior of [`get_body_with_borrowck_facts`].
+///
+/// If executing under `-Z polonius` the choice here has no effect, and everything as if
+/// [`PoloniusOutputFacts`](ConsumerOptions::PoloniusOutputFacts) had been selected
+/// will be retrieved.
+#[derive(Debug, Copy, Clone)]
+pub enum ConsumerOptions {
+ /// Retrieve the [`Body`] along with the [`BorrowSet`](super::borrow_set::BorrowSet)
+ /// and [`RegionInferenceContext`]. If you would like the body only, use
+ /// [`TyCtxt::mir_promoted`].
+ ///
+ /// These can be used in conjunction with [`calculate_borrows_out_of_scope_at_location`].
+ RegionInferenceContext,
+ /// The recommended option. Retrieves the maximal amount of information
+ /// without significant slowdowns.
+ ///
+ /// Implies [`RegionInferenceContext`](ConsumerOptions::RegionInferenceContext),
+ /// and additionally retrieve the [`LocationTable`] and [`PoloniusInput`] that
+ /// would be given to Polonius. Critically, this does not run Polonius, which
+ /// one may want to avoid due to performance issues on large bodies.
+ PoloniusInputFacts,
+ /// Implies [`PoloniusInputFacts`](ConsumerOptions::PoloniusInputFacts),
+ /// and additionally runs Polonius to calculate the [`PoloniusOutput`].
+ PoloniusOutputFacts,
+}
+
+impl ConsumerOptions {
+ /// Should the Polonius input facts be computed?
+ pub(crate) fn polonius_input(&self) -> bool {
+ matches!(self, Self::PoloniusInputFacts | Self::PoloniusOutputFacts)
+ }
+ /// Should we run Polonius and collect the output facts?
+ pub(crate) fn polonius_output(&self) -> bool {
+ matches!(self, Self::PoloniusOutputFacts)
+ }
+}
+
+/// A `Body` with information computed by the borrow checker. This struct is
+/// intended to be consumed by compiler consumers.
+///
+/// We need to include the MIR body here because the region identifiers must
+/// match the ones in the Polonius facts.
+pub struct BodyWithBorrowckFacts<'tcx> {
+ /// A mir body that contains region identifiers.
+ pub body: Body<'tcx>,
+ /// The mir bodies of promoteds.
+ pub promoted: IndexVec<Promoted, Body<'tcx>>,
+ /// The set of borrows occurring in `body` with data about them.
+ pub borrow_set: Rc<BorrowSet<'tcx>>,
+ /// Context generated during borrowck, intended to be passed to
+ /// [`calculate_borrows_out_of_scope_at_location`].
+ pub region_inference_context: Rc<RegionInferenceContext<'tcx>>,
+ /// The table that maps Polonius points to locations in the table.
+ /// Populated when using [`ConsumerOptions::PoloniusInputFacts`]
+ /// or [`ConsumerOptions::PoloniusOutputFacts`].
+ pub location_table: Option<LocationTable>,
+ /// Polonius input facts.
+ /// Populated when using [`ConsumerOptions::PoloniusInputFacts`]
+ /// or [`ConsumerOptions::PoloniusOutputFacts`].
+ pub input_facts: Option<Box<PoloniusInput>>,
+ /// Polonius output facts. Populated when using
+ /// [`ConsumerOptions::PoloniusOutputFacts`].
+ pub output_facts: Option<Rc<PoloniusOutput>>,
+}
+
+/// This function computes borrowck facts for the given body. The [`ConsumerOptions`]
+/// determine which facts are returned. This function makes a copy of the body because
+/// it needs to regenerate the region identifiers. It should never be invoked during a
+/// typical compilation session due to the unnecessary overhead of returning
+/// [`BodyWithBorrowckFacts`].
///
/// Note:
/// * This function will panic if the required body was already stolen. This
@@ -30,11 +104,12 @@ pub use super::{
/// * Polonius is highly unstable, so expect regular changes in its signature or other details.
pub fn get_body_with_borrowck_facts(
tcx: TyCtxt<'_>,
- def: ty::WithOptConstParam<LocalDefId>,
+ def: LocalDefId,
+ options: ConsumerOptions,
) -> BodyWithBorrowckFacts<'_> {
let (input_body, promoted) = tcx.mir_promoted(def);
- let infcx = tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bind(def.did)).build();
+ let infcx = tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bind(def)).build();
let input_body: &Body<'_> = &input_body.borrow();
let promoted: &IndexSlice<_, _> = &promoted.borrow();
- *super::do_mir_borrowck(&infcx, input_body, promoted, true).1.unwrap()
+ *super::do_mir_borrowck(&infcx, input_body, promoted, Some(options)).1.unwrap()
}
diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs
index 94939c7e4..2daa82aef 100644
--- a/compiler/rustc_borrowck/src/dataflow.rs
+++ b/compiler/rustc_borrowck/src/dataflow.rs
@@ -156,10 +156,10 @@ impl<'tcx> OutOfScopePrecomputer<'_, 'tcx> {
&mut self,
borrow_index: BorrowIndex,
borrow_region: RegionVid,
- location: Location,
+ first_location: Location,
) {
// We visit one BB at a time. The complication is that we may start in the
- // middle of the first BB visited (the one containing `location`), in which
+ // middle of the first BB visited (the one containing `first_location`), in which
// case we may have to later on process the first part of that BB if there
// is a path back to its start.
@@ -168,61 +168,58 @@ impl<'tcx> OutOfScopePrecomputer<'_, 'tcx> {
// `visited` once they are added to `stack`, before they are actually
// processed, because this avoids the need to look them up again on
// completion.
- self.visited.insert(location.block);
+ self.visited.insert(first_location.block);
- let mut first_lo = location.statement_index;
- let first_hi = self.body[location.block].statements.len();
+ let first_block = first_location.block;
+ let mut first_lo = first_location.statement_index;
+ let first_hi = self.body[first_block].statements.len();
- self.visit_stack.push(StackEntry { bb: location.block, lo: first_lo, hi: first_hi });
+ self.visit_stack.push(StackEntry { bb: first_block, lo: first_lo, hi: first_hi });
- while let Some(StackEntry { bb, lo, hi }) = self.visit_stack.pop() {
- // If we process the first part of the first basic block (i.e. we encounter that block
- // for the second time), we no longer have to visit its successors again.
- let mut finished_early = bb == location.block && hi != first_hi;
- for i in lo..=hi {
- let location = Location { block: bb, statement_index: i };
+ 'preorder: while let Some(StackEntry { bb, lo, hi }) = self.visit_stack.pop() {
+ if let Some(kill_stmt) =
+ self.regioncx.first_non_contained_inclusive(borrow_region, bb, lo, hi)
+ {
+ let kill_location = Location { block: bb, statement_index: kill_stmt };
// If region does not contain a point at the location, then add to list and skip
// successor locations.
- if !self.regioncx.region_contains(borrow_region, location) {
- debug!("borrow {:?} gets killed at {:?}", borrow_index, location);
- self.borrows_out_of_scope_at_location
- .entry(location)
- .or_default()
- .push(borrow_index);
- finished_early = true;
- break;
- }
+ debug!("borrow {:?} gets killed at {:?}", borrow_index, kill_location);
+ self.borrows_out_of_scope_at_location
+ .entry(kill_location)
+ .or_default()
+ .push(borrow_index);
+ continue 'preorder;
}
- if !finished_early {
- // Add successor BBs to the work list, if necessary.
- let bb_data = &self.body[bb];
- debug_assert!(hi == bb_data.statements.len());
- for succ_bb in bb_data.terminator().successors() {
- if !self.visited.insert(succ_bb) {
- if succ_bb == location.block && first_lo > 0 {
- // `succ_bb` has been seen before. If it wasn't
- // fully processed, add its first part to `stack`
- // for processing.
- self.visit_stack.push(StackEntry {
- bb: succ_bb,
- lo: 0,
- hi: first_lo - 1,
- });
-
- // And update this entry with 0, to represent the
- // whole BB being processed.
- first_lo = 0;
- }
- } else {
- // succ_bb hasn't been seen before. Add it to
- // `stack` for processing.
- self.visit_stack.push(StackEntry {
- bb: succ_bb,
- lo: 0,
- hi: self.body[succ_bb].statements.len(),
- });
+ // If we process the first part of the first basic block (i.e. we encounter that block
+ // for the second time), we no longer have to visit its successors again.
+ if bb == first_block && hi != first_hi {
+ continue;
+ }
+
+ // Add successor BBs to the work list, if necessary.
+ let bb_data = &self.body[bb];
+ debug_assert!(hi == bb_data.statements.len());
+ for succ_bb in bb_data.terminator().successors() {
+ if !self.visited.insert(succ_bb) {
+ if succ_bb == first_block && first_lo > 0 {
+ // `succ_bb` has been seen before. If it wasn't
+ // fully processed, add its first part to `stack`
+ // for processing.
+ self.visit_stack.push(StackEntry { bb: succ_bb, lo: 0, hi: first_lo - 1 });
+
+ // And update this entry with 0, to represent the
+ // whole BB being processed.
+ first_lo = 0;
}
+ } else {
+ // succ_bb hasn't been seen before. Add it to
+ // `stack` for processing.
+ self.visit_stack.push(StackEntry {
+ bb: succ_bb,
+ lo: 0,
+ hi: self.body[succ_bb].statements.len(),
+ });
}
}
}
@@ -231,27 +228,32 @@ impl<'tcx> OutOfScopePrecomputer<'_, 'tcx> {
}
}
+pub fn calculate_borrows_out_of_scope_at_location<'tcx>(
+ body: &Body<'tcx>,
+ regioncx: &RegionInferenceContext<'tcx>,
+ borrow_set: &BorrowSet<'tcx>,
+) -> FxIndexMap<Location, Vec<BorrowIndex>> {
+ let mut prec = OutOfScopePrecomputer::new(body, regioncx);
+ for (borrow_index, borrow_data) in borrow_set.iter_enumerated() {
+ let borrow_region = borrow_data.region;
+ let location = borrow_data.reserve_location;
+
+ prec.precompute_borrows_out_of_scope(borrow_index, borrow_region, location);
+ }
+
+ prec.borrows_out_of_scope_at_location
+}
+
impl<'a, 'tcx> Borrows<'a, 'tcx> {
- pub(crate) fn new(
+ pub fn new(
tcx: TyCtxt<'tcx>,
body: &'a Body<'tcx>,
nonlexical_regioncx: &'a RegionInferenceContext<'tcx>,
borrow_set: &'a BorrowSet<'tcx>,
) -> Self {
- let mut prec = OutOfScopePrecomputer::new(body, nonlexical_regioncx);
- for (borrow_index, borrow_data) in borrow_set.iter_enumerated() {
- let borrow_region = borrow_data.region;
- let location = borrow_data.reserve_location;
-
- prec.precompute_borrows_out_of_scope(borrow_index, borrow_region, location);
- }
-
- Borrows {
- tcx,
- body,
- borrow_set,
- borrows_out_of_scope_at_location: prec.borrows_out_of_scope_at_location,
- }
+ let borrows_out_of_scope_at_location =
+ calculate_borrows_out_of_scope_at_location(body, nonlexical_regioncx, borrow_set);
+ Borrows { tcx, body, borrow_set, borrows_out_of_scope_at_location }
}
pub fn location(&self, idx: BorrowIndex) -> &Location {
@@ -328,7 +330,7 @@ impl<'tcx> rustc_mir_dataflow::AnalysisDomain<'tcx> for Borrows<'_, 'tcx> {
fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
// bottom = nothing is reserved or activated yet;
- BitSet::new_empty(self.borrow_set.len() * 2)
+ BitSet::new_empty(self.borrow_set.len())
}
fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) {
diff --git a/compiler/rustc_borrowck/src/def_use.rs b/compiler/rustc_borrowck/src/def_use.rs
index 6deb4e361..b719a610e 100644
--- a/compiler/rustc_borrowck/src/def_use.rs
+++ b/compiler/rustc_borrowck/src/def_use.rs
@@ -51,12 +51,16 @@ pub fn categorize(context: PlaceContext) -> Option<DefUse> {
PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow) |
PlaceContext::NonMutatingUse(NonMutatingUseContext::ShallowBorrow) |
+ // `PlaceMention` and `AscribeUserType` both evaluate the place, which must not
+ // contain dangling references.
+ PlaceContext::NonMutatingUse(NonMutatingUseContext::PlaceMention) |
+ PlaceContext::NonUse(NonUseContext::AscribeUserTy(_)) |
+
PlaceContext::MutatingUse(MutatingUseContext::AddressOf) |
PlaceContext::NonMutatingUse(NonMutatingUseContext::AddressOf) |
PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect) |
PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) |
PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) |
- PlaceContext::NonUse(NonUseContext::AscribeUserTy) |
PlaceContext::MutatingUse(MutatingUseContext::Retag) =>
Some(DefUse::Use),
@@ -71,8 +75,6 @@ pub fn categorize(context: PlaceContext) -> Option<DefUse> {
PlaceContext::MutatingUse(MutatingUseContext::Drop) =>
Some(DefUse::Drop),
- // This statement exists to help unsafeck. It does not require the place to be live.
- PlaceContext::NonUse(NonUseContext::PlaceMention) => None,
// Debug info is neither def nor use.
PlaceContext::NonUse(NonUseContext::VarDebugInfo) => None,
diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
index 84f75caa6..f41795d60 100644
--- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
@@ -128,7 +128,7 @@ impl<'tcx> ToUniverseInfo<'tcx>
}
}
-impl<'tcx, F, G> ToUniverseInfo<'tcx> for Canonical<'tcx, type_op::custom::CustomTypeOp<F, G>> {
+impl<'tcx, F> ToUniverseInfo<'tcx> for Canonical<'tcx, type_op::custom::CustomTypeOp<F>> {
fn to_universe_info(self, _base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> {
// We can't rerun custom type ops.
UniverseInfo::other()
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 75a3dd0c0..15d73ed73 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -1,5 +1,6 @@
+use std::iter;
+
use either::Either;
-use rustc_const_eval::util::CallKind;
use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::FxIndexSet;
use rustc_errors::{
@@ -9,8 +10,8 @@ use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit::{walk_block, walk_expr, Visitor};
use rustc_hir::{AsyncGeneratorKind, GeneratorKind, LangItem};
-use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::ObligationCause;
+use rustc_middle::hir::nested_filter::OnlyBodies;
use rustc_middle::mir::tcx::PlaceTy;
use rustc_middle::mir::{
self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory,
@@ -18,20 +19,19 @@ use rustc_middle::mir::{
ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm,
};
use rustc_middle::ty::{self, suggest_constraining_type_params, PredicateKind, Ty};
+use rustc_middle::util::CallKind;
use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex};
use rustc_span::def_id::LocalDefId;
use rustc_span::hygiene::DesugaringKind;
-use rustc_span::symbol::{kw, sym};
+use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::{BytePos, Span, Symbol};
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::ObligationCtxt;
use crate::borrow_set::TwoPhaseActivation;
use crate::borrowck_errors;
-
use crate::diagnostics::conflict_errors::StorageDeadOrDrop::LocalStorageDead;
-use crate::diagnostics::find_all_local_uses;
-use crate::diagnostics::mutability_errors::mut_borrow_of_mutable_ref;
+use crate::diagnostics::{find_all_local_uses, CapturedMessageOpt};
use crate::{
borrow_set::BorrowData, diagnostics::Instance, prefixes::IsPrefixOf,
InitializationRequiringAction, MirBorrowckCtxt, PrefixSet, WriteKind,
@@ -158,7 +158,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
} else if reinits > 1 {
err.span_note(
MultiSpan::from_spans(reinit_spans),
- &if reinits <= 3 {
+ if reinits <= 3 {
format!("these {reinits} reinitializations might get skipped")
} else {
format!(
@@ -183,13 +183,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let move_spans = self.move_spans(moved_place.as_ref(), move_out.source);
let move_span = move_spans.args_or_use();
- let move_msg = if move_spans.for_closure() { " into closure" } else { "" };
+ let is_move_msg = move_spans.for_closure();
- let loop_message = if location == move_out.source || move_site.traversed_back_edge {
- ", in previous iteration of loop"
- } else {
- ""
- };
+ let is_loop_message = location == move_out.source || move_site.traversed_back_edge;
if location == move_out.source {
is_loop_move = true;
@@ -206,17 +202,21 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
);
}
+ let msg_opt = CapturedMessageOpt {
+ is_partial_move,
+ is_loop_message,
+ is_move_msg,
+ is_loop_move,
+ maybe_reinitialized_locations_is_empty: maybe_reinitialized_locations
+ .is_empty(),
+ };
self.explain_captures(
&mut err,
span,
move_span,
move_spans,
*moved_place,
- partially_str,
- loop_message,
- move_msg,
- is_loop_move,
- maybe_reinitialized_locations.is_empty(),
+ msg_opt,
);
}
seen_spans.insert(move_span);
@@ -253,7 +253,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
// We have a `&mut` ref, we need to reborrow on each iteration (#62112).
err.span_suggestion_verbose(
span.shrink_to_lo(),
- &format!(
+ format!(
"consider creating a fresh reborrow of {} here",
self.describe_place(moved_place)
.map(|n| format!("`{n}`"))
@@ -282,12 +282,21 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}
if needs_note {
- let span = if let Some(local) = place.as_local() {
- Some(self.body.local_decls[local].source_info.span)
+ if let Some(local) = place.as_local() {
+ let span = self.body.local_decls[local].source_info.span;
+ err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
+ is_partial_move,
+ ty,
+ place: &note_msg,
+ span,
+ });
} else {
- None
+ err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Note {
+ is_partial_move,
+ ty,
+ place: &note_msg,
+ });
};
- self.note_type_does_not_implement_copy(&mut err, &note_msg, ty, span, partial_str);
}
if let UseSpans::FnSelfUse {
@@ -295,7 +304,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
..
} = use_spans
{
- err.note(&format!(
+ err.note(format!(
"{} occurs due to deref coercion to `{deref_target_ty}`",
desired_action.as_noun(),
));
@@ -577,7 +586,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
// _ => {} // We don't want to point to this.
// };
// ```
- err.span_label(sp, &label);
+ err.span_label(sp, label);
shown = true;
}
}
@@ -633,11 +642,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let Some(default_trait) = tcx.get_diagnostic_item(sym::Default) else {
return false;
};
- // Regions are already solved, so we must use a fresh InferCtxt,
- // but the type has region variables, so erase those.
- tcx.infer_ctxt()
- .build()
- .type_implements_trait(default_trait, [tcx.erase_regions(ty)], param_env)
+ self.infcx
+ .type_implements_trait(default_trait, [ty], param_env)
.must_apply_modulo_regions()
};
@@ -696,7 +702,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
.copied()
.find_map(find_fn_kind_from_did),
ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => tcx
- .bound_explicit_item_bounds(def_id)
+ .explicit_item_bounds(def_id)
.subst_iter_copied(tcx, substs)
.find_map(find_fn_kind_from_did),
ty::Closure(_, substs) => match substs.as_closure().kind() {
@@ -730,13 +736,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
fn suggest_cloning(&self, err: &mut Diagnostic, ty: Ty<'tcx>, span: Span) {
let tcx = self.infcx.tcx;
// Try to find predicates on *generic params* that would allow copying `ty`
- let infcx = tcx.infer_ctxt().build();
-
if let Some(clone_trait_def) = tcx.lang_items().clone_trait()
- && infcx
+ && self.infcx
.type_implements_trait(
clone_trait_def,
- [tcx.erase_regions(ty)],
+ [ty],
self.param_env,
)
.must_apply_modulo_regions()
@@ -760,12 +764,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
.and_then(|def_id| tcx.hir().get_generics(def_id))
else { return; };
// Try to find predicates on *generic params* that would allow copying `ty`
- let infcx = tcx.infer_ctxt().build();
- let ocx = ObligationCtxt::new(&infcx);
+ let ocx = ObligationCtxt::new(&self.infcx);
let copy_did = tcx.require_lang_item(LangItem::Copy, Some(span));
let cause = ObligationCause::misc(span, self.mir_def_id());
- ocx.register_bound(cause, self.param_env, infcx.tcx.erase_regions(ty), copy_did);
+ ocx.register_bound(cause, self.param_env, ty, copy_did);
let errors = ocx.select_all_or_error();
// Only emit suggestion if all required predicates are on generic
@@ -827,11 +830,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
borrow_spans.var_path_only_subdiag(&mut err, crate::InitializationRequiringAction::Borrow);
- move_spans.var_span_label(
- &mut err,
- format!("move occurs due to use{}", move_spans.describe()),
- "moved",
- );
+ move_spans.var_subdiag(None, &mut err, None, |kind, var_span| {
+ use crate::session_diagnostics::CaptureVarCause::*;
+ match kind {
+ Some(_) => MoveUseInGenerator { var_span },
+ None => MoveUseInClosure { var_span },
+ }
+ });
self.explain_why_borrow_contains_point(location, borrow, None)
.add_explanation_to_diagnostic(
@@ -868,13 +873,15 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
borrow_span,
&self.describe_any_place(borrow.borrowed_place.as_ref()),
);
- borrow_spans.var_subdiag(&mut err, Some(borrow.kind), |kind, var_span| {
+ borrow_spans.var_subdiag(None, &mut err, Some(borrow.kind), |kind, var_span| {
use crate::session_diagnostics::CaptureVarCause::*;
let place = &borrow.borrowed_place;
let desc_place = self.describe_any_place(place.as_ref());
match kind {
- Some(_) => BorrowUsePlaceGenerator { place: desc_place, var_span },
- None => BorrowUsePlaceClosure { place: desc_place, var_span },
+ Some(_) => {
+ BorrowUsePlaceGenerator { place: desc_place, var_span, is_single_var: true }
+ }
+ None => BorrowUsePlaceClosure { place: desc_place, var_span, is_single_var: true },
}
});
@@ -946,7 +953,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
&msg_borrow,
None,
);
- self.suggest_binding_for_closure_capture_self(
+ self.suggest_binding_for_closure_capture_self(&mut err, &issued_spans);
+ self.suggest_using_closure_argument_instead_of_capture(
&mut err,
issued_borrow.borrowed_place,
&issued_spans,
@@ -969,6 +977,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
place,
issued_borrow.borrowed_place,
);
+ self.suggest_using_closure_argument_instead_of_capture(
+ &mut err,
+ issued_borrow.borrowed_place,
+ &issued_spans,
+ );
err
}
@@ -988,16 +1001,26 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
immutable_section_description,
"mutably borrow",
);
- borrow_spans.var_span_label(
+ borrow_spans.var_subdiag(
+ None,
&mut err,
- format!(
- "borrow occurs due to use of {}{}",
- desc_place,
- borrow_spans.describe(),
- ),
- "immutable",
+ Some(BorrowKind::Unique),
+ |kind, var_span| {
+ use crate::session_diagnostics::CaptureVarCause::*;
+ match kind {
+ Some(_) => BorrowUsePlaceGenerator {
+ place: desc_place,
+ var_span,
+ is_single_var: true,
+ },
+ None => BorrowUsePlaceClosure {
+ place: desc_place,
+ var_span,
+ is_single_var: true,
+ },
+ }
+ },
);
-
return err;
} else {
first_borrow_desc = "immutable ";
@@ -1070,37 +1093,53 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
};
if issued_spans == borrow_spans {
- borrow_spans.var_span_label(
- &mut err,
- format!("borrows occur due to use of {}{}", desc_place, borrow_spans.describe(),),
- gen_borrow_kind.describe_mutability(),
- );
+ borrow_spans.var_subdiag(None, &mut err, Some(gen_borrow_kind), |kind, var_span| {
+ use crate::session_diagnostics::CaptureVarCause::*;
+ match kind {
+ Some(_) => BorrowUsePlaceGenerator {
+ place: desc_place,
+ var_span,
+ is_single_var: false,
+ },
+ None => {
+ BorrowUsePlaceClosure { place: desc_place, var_span, is_single_var: false }
+ }
+ }
+ });
} else {
- let borrow_place = &issued_borrow.borrowed_place;
- let borrow_place_desc = self.describe_any_place(borrow_place.as_ref());
- issued_spans.var_span_label(
+ issued_spans.var_subdiag(
+ Some(&self.infcx.tcx.sess.parse_sess.span_diagnostic),
&mut err,
- format!(
- "first borrow occurs due to use of {}{}",
- borrow_place_desc,
- issued_spans.describe(),
- ),
- issued_borrow.kind.describe_mutability(),
+ Some(issued_borrow.kind),
+ |kind, var_span| {
+ use crate::session_diagnostics::CaptureVarCause::*;
+ let borrow_place = &issued_borrow.borrowed_place;
+ let borrow_place_desc = self.describe_any_place(borrow_place.as_ref());
+ match kind {
+ Some(_) => {
+ FirstBorrowUsePlaceGenerator { place: borrow_place_desc, var_span }
+ }
+ None => FirstBorrowUsePlaceClosure { place: borrow_place_desc, var_span },
+ }
+ },
);
- borrow_spans.var_span_label(
+ borrow_spans.var_subdiag(
+ Some(&self.infcx.tcx.sess.parse_sess.span_diagnostic),
&mut err,
- format!(
- "second borrow occurs due to use of {}{}",
- desc_place,
- borrow_spans.describe(),
- ),
- gen_borrow_kind.describe_mutability(),
+ Some(gen_borrow_kind),
+ |kind, var_span| {
+ use crate::session_diagnostics::CaptureVarCause::*;
+ match kind {
+ Some(_) => SecondBorrowUsePlaceGenerator { place: desc_place, var_span },
+ None => SecondBorrowUsePlaceClosure { place: desc_place, var_span },
+ }
+ },
);
}
if union_type_name != "" {
- err.note(&format!(
+ err.note(format!(
"{} is a field of the union `{}`, so it overlaps the field {}",
msg_place, union_type_name, msg_borrow,
));
@@ -1199,14 +1238,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}
err.span_help(
inner_call_span,
- &format!(
+ format!(
"try adding a local storing this{}...",
if use_span.is_some() { "" } else { " argument" }
),
);
err.span_help(
outer_call_span,
- &format!(
+ format!(
"...and then using that local {}",
if use_span.is_some() { "here" } else { "as the argument to this call" }
),
@@ -1229,22 +1268,160 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}
}
- fn suggest_binding_for_closure_capture_self(
+ /// Suggest using closure argument instead of capture.
+ ///
+ /// For example:
+ /// ```ignore (illustrative)
+ /// struct S;
+ ///
+ /// impl S {
+ /// fn call(&mut self, f: impl Fn(&mut Self)) { /* ... */ }
+ /// fn x(&self) {}
+ /// }
+ ///
+ /// let mut v = S;
+ /// v.call(|this: &mut S| v.x());
+ /// // ^\ ^-- help: try using the closure argument: `this`
+ /// // *-- error: cannot borrow `v` as mutable because it is also borrowed as immutable
+ /// ```
+ fn suggest_using_closure_argument_instead_of_capture(
&self,
err: &mut Diagnostic,
borrowed_place: Place<'tcx>,
issued_spans: &UseSpans<'tcx>,
) {
- let UseSpans::ClosureUse { capture_kind_span, .. } = issued_spans else { return };
- let hir = self.infcx.tcx.hir();
+ let &UseSpans::ClosureUse { capture_kind_span, .. } = issued_spans else { return };
+ let tcx = self.infcx.tcx;
+ let hir = tcx.hir();
- // check whether the borrowed place is capturing `self` by mut reference
+ // Get the type of the local that we are trying to borrow
let local = borrowed_place.local;
- let Some(_) = self
- .body
- .local_decls
- .get(local)
- .map(|l| mut_borrow_of_mutable_ref(l, self.local_names[local])) else { return };
+ let local_ty = self.body.local_decls[local].ty;
+
+ // Get the body the error happens in
+ let Some(body_id) = hir.get(self.mir_hir_id()).body_id() else { return };
+
+ let body_expr = hir.body(body_id).value;
+
+ struct ClosureFinder<'hir> {
+ hir: rustc_middle::hir::map::Map<'hir>,
+ borrow_span: Span,
+ res: Option<(&'hir hir::Expr<'hir>, &'hir hir::Closure<'hir>)>,
+ /// The path expression with the `borrow_span` span
+ error_path: Option<(&'hir hir::Expr<'hir>, &'hir hir::QPath<'hir>)>,
+ }
+ impl<'hir> Visitor<'hir> for ClosureFinder<'hir> {
+ type NestedFilter = OnlyBodies;
+
+ fn nested_visit_map(&mut self) -> Self::Map {
+ self.hir
+ }
+
+ fn visit_expr(&mut self, ex: &'hir hir::Expr<'hir>) {
+ if let hir::ExprKind::Path(qpath) = &ex.kind
+ && ex.span == self.borrow_span
+ {
+ self.error_path = Some((ex, qpath));
+ }
+
+ if let hir::ExprKind::Closure(closure) = ex.kind
+ && ex.span.contains(self.borrow_span)
+ // To support cases like `|| { v.call(|this| v.get()) }`
+ // FIXME: actually support such cases (need to figure out how to move from the capture place to original local)
+ && self.res.as_ref().map_or(true, |(prev_res, _)| prev_res.span.contains(ex.span))
+ {
+ self.res = Some((ex, closure));
+ }
+
+ hir::intravisit::walk_expr(self, ex);
+ }
+ }
+
+ // Find the closure that most tightly wraps `capture_kind_span`
+ let mut finder =
+ ClosureFinder { hir, borrow_span: capture_kind_span, res: None, error_path: None };
+ finder.visit_expr(body_expr);
+ let Some((closure_expr, closure)) = finder.res else { return };
+
+ let typeck_results = tcx.typeck(self.mir_def_id());
+
+ // Check that the parent of the closure is a method call,
+ // with receiver matching with local's type (modulo refs)
+ let parent = hir.parent_id(closure_expr.hir_id);
+ if let hir::Node::Expr(parent) = hir.get(parent) {
+ if let hir::ExprKind::MethodCall(_, recv, ..) = parent.kind {
+ let recv_ty = typeck_results.expr_ty(recv);
+
+ if recv_ty.peel_refs() != local_ty {
+ return;
+ }
+ }
+ }
+
+ // Get closure's arguments
+ let ty::Closure(_, substs) = typeck_results.expr_ty(closure_expr).kind() else { /* hir::Closure can be a generator too */ return };
+ let sig = substs.as_closure().sig();
+ let tupled_params =
+ tcx.erase_late_bound_regions(sig.inputs().iter().next().unwrap().map_bound(|&b| b));
+ let ty::Tuple(params) = tupled_params.kind() else { return };
+
+ // Find the first argument with a matching type, get its name
+ let Some((_, this_name)) = params
+ .iter()
+ .zip(hir.body_param_names(closure.body))
+ .find(|(param_ty, name)|{
+ // FIXME: also support deref for stuff like `Rc` arguments
+ param_ty.peel_refs() == local_ty && name != &Ident::empty()
+ })
+ else { return };
+
+ let spans;
+ if let Some((_path_expr, qpath)) = finder.error_path
+ && let hir::QPath::Resolved(_, path) = qpath
+ && let hir::def::Res::Local(local_id) = path.res
+ {
+ // Find all references to the problematic variable in this closure body
+
+ struct VariableUseFinder {
+ local_id: hir::HirId,
+ spans: Vec<Span>,
+ }
+ impl<'hir> Visitor<'hir> for VariableUseFinder {
+ fn visit_expr(&mut self, ex: &'hir hir::Expr<'hir>) {
+ if let hir::ExprKind::Path(qpath) = &ex.kind
+ && let hir::QPath::Resolved(_, path) = qpath
+ && let hir::def::Res::Local(local_id) = path.res
+ && local_id == self.local_id
+ {
+ self.spans.push(ex.span);
+ }
+
+ hir::intravisit::walk_expr(self, ex);
+ }
+ }
+
+ let mut finder = VariableUseFinder { local_id, spans: Vec::new() };
+ finder.visit_expr(hir.body(closure.body).value);
+
+ spans = finder.spans;
+ } else {
+ spans = vec![capture_kind_span];
+ }
+
+ err.multipart_suggestion(
+ "try using the closure argument",
+ iter::zip(spans, iter::repeat(this_name.to_string())).collect(),
+ Applicability::MaybeIncorrect,
+ );
+ }
+
+ fn suggest_binding_for_closure_capture_self(
+ &self,
+ err: &mut Diagnostic,
+ issued_spans: &UseSpans<'tcx>,
+ ) {
+ let UseSpans::ClosureUse { capture_kind_span, .. } = issued_spans else { return };
+ let hir = self.infcx.tcx.hir();
struct ExpressionFinder<'hir> {
capture_span: Span,
@@ -1458,34 +1635,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
})
}
- /// Reports StorageDeadOrDrop of `place` conflicts with `borrow`.
- ///
- /// Depending on the origin of the StorageDeadOrDrop, this may be
- /// reported as either a drop or an illegal mutation of a borrowed value.
- /// The latter is preferred when the this is a drop triggered by a
- /// reassignment, as it's more user friendly to report a problem with the
- /// explicit assignment than the implicit drop.
- #[instrument(level = "debug", skip(self))]
- pub(crate) fn report_storage_dead_or_drop_of_borrowed(
- &mut self,
- location: Location,
- place_span: (Place<'tcx>, Span),
- borrow: &BorrowData<'tcx>,
- ) {
- // It's sufficient to check the last desugaring as Replace is the last
- // one to be applied.
- if let Some(DesugaringKind::Replace) = place_span.1.desugaring_kind() {
- self.report_illegal_mutation_of_borrowed(location, place_span, borrow)
- } else {
- self.report_borrowed_value_does_not_live_long_enough(
- location,
- borrow,
- place_span,
- Some(WriteKind::StorageDeadOrDrop),
- )
- }
- }
-
/// This means that some data referenced by `borrow` needs to live
/// past the point where the StorageDeadOrDrop of `place` occurs.
/// This is usually interpreted as meaning that `place` has too
@@ -1731,9 +1880,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
err.span_label(borrow_span, "borrowed value does not live long enough");
err.span_label(drop_span, format!("`{}` dropped here while still borrowed", name));
- let within = if borrow_spans.for_generator() { " by generator" } else { "" };
-
- borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
+ borrow_spans.args_subdiag(&mut err, |args_span| {
+ crate::session_diagnostics::CaptureArgLabel::Capture {
+ is_within: borrow_spans.for_generator(),
+ args_span,
+ }
+ });
explanation.add_explanation_to_diagnostic(
self.infcx.tcx,
@@ -1947,9 +2099,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
None,
);
- let within = if borrow_spans.for_generator() { " by generator" } else { "" };
-
- borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
+ borrow_spans.args_subdiag(&mut err, |args_span| {
+ crate::session_diagnostics::CaptureArgLabel::Capture {
+ is_within: borrow_spans.for_generator(),
+ args_span,
+ }
+ });
err
}
@@ -2029,7 +2184,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let tcx = self.infcx.tcx;
let return_ty = self.regioncx.universal_regions().unnormalized_output_ty;
- let return_ty = tcx.erase_regions(return_ty);
// to avoid panics
if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator)
@@ -2099,7 +2253,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
);
err.span_suggestion_verbose(
sugg_span,
- &format!(
+ format!(
"to force the {} to take ownership of {} (and any \
other referenced variables), use the `move` keyword",
kind, captured_var
@@ -2111,7 +2265,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
match category {
ConstraintCategory::Return(_) | ConstraintCategory::OpaqueType => {
let msg = format!("{} is returned here", kind);
- err.span_note(constraint_span, &msg);
+ err.span_note(constraint_span, msg);
}
ConstraintCategory::CallArgument(_) => {
fr_name.highlight_region_name(&mut err);
@@ -2122,7 +2276,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
);
} else {
let msg = format!("{scope} requires argument type to outlive `{fr_name}`");
- err.span_note(constraint_span, &msg);
+ err.span_note(constraint_span, msg);
}
}
_ => bug!(
@@ -2382,11 +2536,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
section,
"assign",
);
- loan_spans.var_span_label(
- &mut err,
- format!("borrow occurs due to use{}", loan_spans.describe()),
- loan.kind.describe_mutability(),
- );
+
+ loan_spans.var_subdiag(None, &mut err, Some(loan.kind), |kind, var_span| {
+ use crate::session_diagnostics::CaptureVarCause::*;
+ match kind {
+ Some(_) => BorrowUseInGenerator { var_span },
+ None => BorrowUseInClosure { var_span },
+ }
+ });
self.buffer_error(err);
@@ -2396,11 +2553,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let mut err = self.cannot_assign_to_borrowed(span, loan_span, &descr_place);
- loan_spans.var_span_label(
- &mut err,
- format!("borrow occurs due to use{}", loan_spans.describe()),
- loan.kind.describe_mutability(),
- );
+ loan_spans.var_subdiag(None, &mut err, Some(loan.kind), |kind, var_span| {
+ use crate::session_diagnostics::CaptureVarCause::*;
+ match kind {
+ Some(_) => BorrowUseInGenerator { var_span },
+ None => BorrowUseInClosure { var_span },
+ }
+ });
self.explain_why_borrow_contains_point(location, loan, None).add_explanation_to_diagnostic(
self.infcx.tcx,
@@ -2424,7 +2583,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
Some((method_did, method_substs)),
) = (
&self.body[loan.reserve_location.block].terminator,
- rustc_const_eval::util::find_self_call(
+ rustc_middle::util::find_self_call(
tcx,
self.body,
loan.assigned_place.local,
@@ -2439,7 +2598,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
});
if let Some(Ok(instance)) = deref_target {
let deref_target_ty = instance.ty(tcx, self.param_env);
- err.note(&format!(
+ err.note(format!(
"borrow occurs due to deref coercion to `{}`",
deref_target_ty
));
@@ -2993,7 +3152,7 @@ impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
diag.span_label(*return_span, format!("also has lifetime `{}`", region_name,));
- diag.help(&format!(
+ diag.help(format!(
"use data from the highlighted arguments which match the `{}` lifetime of \
the return type",
region_name,
diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
index 8860395e7..1d430a93a 100644
--- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
@@ -3,7 +3,7 @@
use rustc_errors::{Applicability, Diagnostic};
use rustc_hir as hir;
use rustc_hir::intravisit::Visitor;
-use rustc_index::vec::IndexSlice;
+use rustc_index::IndexSlice;
use rustc_infer::infer::NllRegionVariableOrigin;
use rustc_middle::mir::{
Body, CastKind, ConstraintCategory, FakeReadCause, Local, LocalInfo, Location, Operand, Place,
@@ -90,7 +90,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
{
err.span_label(
pat.span,
- &format!("binding `{ident}` declared here"),
+ format!("binding `{ident}` declared here"),
);
}
}
@@ -118,7 +118,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
let path_span = path_span.unwrap();
// path_span is only present in the case of closure capture
assert!(matches!(later_use_kind, LaterUseKind::ClosureCapture));
- if !borrow_span.map_or(false, |sp| sp.overlaps(var_or_use_span)) {
+ if !borrow_span.is_some_and(|sp| sp.overlaps(var_or_use_span)) {
let path_label = "used here by closure";
let capture_kind_label = message;
err.span_label(
@@ -224,12 +224,9 @@ impl<'tcx> BorrowExplanation<'tcx> {
if info.tail_result_is_ignored {
// #85581: If the first mutable borrow's scope contains
// the second borrow, this suggestion isn't helpful.
- if !multiple_borrow_span
- .map(|(old, new)| {
- old.to(info.span.shrink_to_hi()).contains(new)
- })
- .unwrap_or(false)
- {
+ if !multiple_borrow_span.is_some_and(|(old, new)| {
+ old.to(info.span.shrink_to_hi()).contains(new)
+ }) {
err.span_suggestion_verbose(
info.span.shrink_to_hi(),
"consider adding semicolon after the expression so its \
@@ -323,7 +320,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
err.span_suggestion_verbose(
span.shrink_to_hi(),
- &msg,
+ msg,
format!(" + {suggestable_name}"),
Applicability::Unspecified,
);
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs
index 110354a20..20370e4c6 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mod.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -1,13 +1,16 @@
//! Borrow checker diagnostics.
+use crate::session_diagnostics::{
+ CaptureArgLabel, CaptureReasonLabel, CaptureReasonNote, CaptureReasonSuggest, CaptureVarCause,
+ CaptureVarKind, CaptureVarPathUseCause, OnClosureNote,
+};
use itertools::Itertools;
-use rustc_const_eval::util::{call_kind, CallDesugaringKind};
use rustc_errors::{Applicability, Diagnostic};
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, Namespace};
use rustc_hir::GeneratorKind;
-use rustc_index::vec::IndexSlice;
-use rustc_infer::infer::{LateBoundRegionConversionTime, TyCtxtInferExt};
+use rustc_index::IndexSlice;
+use rustc_infer::infer::LateBoundRegionConversionTime;
use rustc_middle::mir::tcx::PlaceTy;
use rustc_middle::mir::{
AggregateKind, Constant, FakeReadCause, Local, LocalInfo, LocalKind, Location, Operand, Place,
@@ -15,6 +18,7 @@ use rustc_middle::mir::{
};
use rustc_middle::ty::print::Print;
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
+use rustc_middle::util::{call_kind, CallDesugaringKind};
use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult};
use rustc_span::def_id::LocalDefId;
use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP};
@@ -45,7 +49,7 @@ pub(crate) use mutability_errors::AccessKind;
pub(crate) use outlives_suggestion::OutlivesSuggestionBuilder;
pub(crate) use region_errors::{ErrorConstraintInfo, RegionErrorKind, RegionErrors};
pub(crate) use region_name::{RegionName, RegionNameSource};
-pub(crate) use rustc_const_eval::util::CallKind;
+pub(crate) use rustc_middle::util::CallKind;
pub(super) struct DescribePlaceOpt {
pub including_downcast: bool,
@@ -117,13 +121,15 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
if let ty::Closure(did, _) = self.body.local_decls[closure].ty.kind() {
let did = did.expect_local();
if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) {
- diag.span_note(
- *span,
- &format!(
- "closure cannot be invoked more than once because it moves the \
- variable `{}` out of its environment",
- ty::place_to_string_for_capture(self.infcx.tcx, hir_place)
- ),
+ diag.eager_subdiagnostic(
+ &self.infcx.tcx.sess.parse_sess.span_diagnostic,
+ OnClosureNote::InvokedTwice {
+ place_name: &ty::place_to_string_for_capture(
+ self.infcx.tcx,
+ hir_place,
+ ),
+ span: *span,
+ },
);
return true;
}
@@ -137,13 +143,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
if let ty::Closure(did, _) = self.body.local_decls[target].ty.kind() {
let did = did.expect_local();
if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) {
- diag.span_note(
- *span,
- &format!(
- "closure cannot be moved more than once as it is not `Copy` due to \
- moving the variable `{}` out of its environment",
- ty::place_to_string_for_capture(self.infcx.tcx, hir_place)
- ),
+ diag.eager_subdiagnostic(
+ &self.infcx.tcx.sess.parse_sess.span_diagnostic,
+ OnClosureNote::MovedTwice {
+ place_name: &ty::place_to_string_for_capture(self.infcx.tcx, hir_place),
+ span: *span,
+ },
);
return true;
}
@@ -380,25 +385,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}
}
- /// Add a note that a type does not implement `Copy`
- pub(super) fn note_type_does_not_implement_copy(
- &self,
- err: &mut Diagnostic,
- place_desc: &str,
- ty: Ty<'tcx>,
- span: Option<Span>,
- move_prefix: &str,
- ) {
- let message = format!(
- "{move_prefix}move occurs because {place_desc} has type `{ty}`, which does not implement the `Copy` trait",
- );
- if let Some(span) = span {
- err.span_label(span, message);
- } else {
- err.note(&message);
- }
- }
-
pub(super) fn borrowed_content_source(
&self,
deref_base: PlaceRef<'tcx>,
@@ -582,9 +568,13 @@ impl UseSpans<'_> {
}
/// Add a span label to the arguments of the closure, if it exists.
- pub(super) fn args_span_label(self, err: &mut Diagnostic, message: impl Into<String>) {
+ pub(super) fn args_subdiag(
+ self,
+ err: &mut Diagnostic,
+ f: impl FnOnce(Span) -> CaptureArgLabel,
+ ) {
if let UseSpans::ClosureUse { args_span, .. } = self {
- err.span_label(args_span, message);
+ err.subdiagnostic(f(args_span));
}
}
@@ -595,8 +585,8 @@ impl UseSpans<'_> {
err: &mut Diagnostic,
action: crate::InitializationRequiringAction,
) {
- use crate::session_diagnostics::CaptureVarPathUseCause::*;
use crate::InitializationRequiringAction::*;
+ use CaptureVarPathUseCause::*;
if let UseSpans::ClosureUse { generator_kind, path_span, .. } = self {
match generator_kind {
Some(_) => {
@@ -619,34 +609,14 @@ impl UseSpans<'_> {
}
}
- /// Add a span label to the use of the captured variable, if it exists.
- pub(super) fn var_span_label(
- self,
- err: &mut Diagnostic,
- message: impl Into<String>,
- kind_desc: impl Into<String>,
- ) {
- if let UseSpans::ClosureUse { capture_kind_span, path_span, .. } = self {
- if capture_kind_span == path_span {
- err.span_label(capture_kind_span, message);
- } else {
- let capture_kind_label =
- format!("capture is {} because of use here", kind_desc.into());
- let path_label = message;
- err.span_label(capture_kind_span, capture_kind_label);
- err.span_label(path_span, path_label);
- }
- }
- }
-
/// Add a subdiagnostic to the use of the captured variable, if it exists.
pub(super) fn var_subdiag(
self,
+ handler: Option<&rustc_errors::Handler>,
err: &mut Diagnostic,
kind: Option<rustc_middle::mir::BorrowKind>,
- f: impl Fn(Option<GeneratorKind>, Span) -> crate::session_diagnostics::CaptureVarCause,
+ f: impl FnOnce(Option<GeneratorKind>, Span) -> CaptureVarCause,
) {
- use crate::session_diagnostics::CaptureVarKind::*;
if let UseSpans::ClosureUse { generator_kind, capture_kind_span, path_span, .. } = self {
if capture_kind_span != path_span {
err.subdiagnostic(match kind {
@@ -654,17 +624,21 @@ impl UseSpans<'_> {
rustc_middle::mir::BorrowKind::Shared
| rustc_middle::mir::BorrowKind::Shallow
| rustc_middle::mir::BorrowKind::Unique => {
- Immute { kind_span: capture_kind_span }
+ CaptureVarKind::Immut { kind_span: capture_kind_span }
}
rustc_middle::mir::BorrowKind::Mut { .. } => {
- Mut { kind_span: capture_kind_span }
+ CaptureVarKind::Mut { kind_span: capture_kind_span }
}
},
- None => Move { kind_span: capture_kind_span },
+ None => CaptureVarKind::Move { kind_span: capture_kind_span },
});
};
- err.subdiagnostic(f(generator_kind, path_span));
+ let diag = f(generator_kind, path_span);
+ match handler {
+ Some(hd) => err.eager_subdiagnostic(hd, diag),
+ None => err.subdiagnostic(diag),
+ };
}
}
@@ -684,20 +658,6 @@ impl UseSpans<'_> {
}
}
- /// Describe the span associated with a use of a place.
- pub(super) fn describe(&self) -> &str {
- match *self {
- UseSpans::ClosureUse { generator_kind, .. } => {
- if generator_kind.is_some() {
- " in generator"
- } else {
- " in closure"
- }
- }
- _ => "",
- }
- }
-
pub(super) fn or_else<F>(self, if_other: F) -> Self
where
F: FnOnce() -> Self,
@@ -788,6 +748,15 @@ impl<'tcx> BorrowedContentSource<'tcx> {
}
}
+///helper struct for explain_captures()
+struct CapturedMessageOpt {
+ is_partial_move: bool,
+ is_loop_message: bool,
+ is_move_msg: bool,
+ is_loop_move: bool,
+ maybe_reinitialized_locations_is_empty: bool,
+}
+
impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
/// Finds the spans associated to a move or copy of move_place at location.
pub(super) fn move_spans(
@@ -874,7 +843,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}) = &self.body[location.block].terminator
{
let Some((method_did, method_substs)) =
- rustc_const_eval::util::find_self_call(
+ rustc_middle::util::find_self_call(
self.infcx.tcx,
&self.body,
target_temp,
@@ -1027,12 +996,15 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
move_span: Span,
move_spans: UseSpans<'tcx>,
moved_place: Place<'tcx>,
- partially_str: &str,
- loop_message: &str,
- move_msg: &str,
- is_loop_move: bool,
- maybe_reinitialized_locations_is_empty: bool,
+ msg_opt: CapturedMessageOpt,
) {
+ let CapturedMessageOpt {
+ is_partial_move: is_partial,
+ is_loop_message,
+ is_move_msg,
+ is_loop_move,
+ maybe_reinitialized_locations_is_empty,
+ } = msg_opt;
if let UseSpans::FnSelfUse { var_span, fn_call_span, fn_span, kind } = move_spans {
let place_name = self
.describe_place(moved_place.as_ref())
@@ -1042,30 +1014,26 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
CallKind::FnCall { fn_trait_id, .. }
if Some(fn_trait_id) == self.infcx.tcx.lang_items().fn_once_trait() =>
{
- err.span_label(
+ err.subdiagnostic(CaptureReasonLabel::Call {
fn_call_span,
- &format!(
- "{place_name} {partially_str}moved due to this call{loop_message}",
- ),
- );
- err.span_note(
- var_span,
- "this value implements `FnOnce`, which causes it to be moved when called",
- );
+ place_name: &place_name,
+ is_partial,
+ is_loop_message,
+ });
+ err.subdiagnostic(CaptureReasonNote::FnOnceMoveInCall { var_span });
}
CallKind::Operator { self_arg, .. } => {
let self_arg = self_arg.unwrap();
- err.span_label(
+ err.subdiagnostic(CaptureReasonLabel::OperatorUse {
fn_call_span,
- &format!(
- "{place_name} {partially_str}moved due to usage in operator{loop_message}",
- ),
- );
+ place_name: &place_name,
+ is_partial,
+ is_loop_message,
+ });
if self.fn_self_span_reported.insert(fn_span) {
- err.span_note(
- self_arg.span,
- "calling this operator moves the left-hand side",
- );
+ err.subdiagnostic(CaptureReasonNote::LhsMoveByOperator {
+ span: self_arg.span,
+ });
}
}
CallKind::Normal { self_arg, desugaring, method_did, method_substs } => {
@@ -1074,35 +1042,27 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
if let Some((CallDesugaringKind::ForLoopIntoIter, _)) = desugaring {
let ty = moved_place.ty(self.body, tcx).ty;
let suggest = match tcx.get_diagnostic_item(sym::IntoIterator) {
- Some(def_id) => {
- let infcx = self.infcx.tcx.infer_ctxt().build();
- type_known_to_meet_bound_modulo_regions(
- &infcx,
- self.param_env,
- tcx.mk_imm_ref(tcx.lifetimes.re_erased, tcx.erase_regions(ty)),
- def_id,
- )
- }
+ Some(def_id) => type_known_to_meet_bound_modulo_regions(
+ &self.infcx,
+ self.param_env,
+ tcx.mk_imm_ref(tcx.lifetimes.re_erased, ty),
+ def_id,
+ ),
_ => false,
};
if suggest {
- err.span_suggestion_verbose(
- move_span.shrink_to_lo(),
- &format!(
- "consider iterating over a slice of the `{ty}`'s content to \
- avoid moving into the `for` loop",
- ),
- "&",
- Applicability::MaybeIncorrect,
- );
+ err.subdiagnostic(CaptureReasonSuggest::IterateSlice {
+ ty,
+ span: move_span.shrink_to_lo(),
+ });
}
- err.span_label(
+ err.subdiagnostic(CaptureReasonLabel::ImplicitCall {
fn_call_span,
- &format!(
- "{place_name} {partially_str}moved due to this implicit call to `.into_iter()`{loop_message}",
- ),
- );
+ place_name: &place_name,
+ is_partial,
+ is_loop_message,
+ });
// If the moved place was a `&mut` ref, then we can
// suggest to reborrow it where it was moved, so it
// will still be valid by the time we get to the usage.
@@ -1113,7 +1073,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
if !is_loop_move {
err.span_suggestion_verbose(
move_span.shrink_to_lo(),
- &format!(
+ format!(
"consider creating a fresh reborrow of {} here",
self.describe_place(moved_place.as_ref())
.map(|n| format!("`{n}`"))
@@ -1125,44 +1085,49 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}
}
} else {
- err.span_label(
- fn_call_span,
- &format!(
- "{place_name} {partially_str}moved due to this method call{loop_message}",
- ),
- );
-
- let infcx = tcx.infer_ctxt().build();
+ if let Some((CallDesugaringKind::Await, _)) = desugaring {
+ err.subdiagnostic(CaptureReasonLabel::Await {
+ fn_call_span,
+ place_name: &place_name,
+ is_partial,
+ is_loop_message,
+ });
+ } else {
+ err.subdiagnostic(CaptureReasonLabel::MethodCall {
+ fn_call_span,
+ place_name: &place_name,
+ is_partial,
+ is_loop_message,
+ });
+ }
// Erase and shadow everything that could be passed to the new infcx.
- let ty = tcx.erase_regions(moved_place.ty(self.body, tcx).ty);
- let method_substs = tcx.erase_regions(method_substs);
+ let ty = moved_place.ty(self.body, tcx).ty;
if let ty::Adt(def, substs) = ty.kind()
&& Some(def.did()) == tcx.lang_items().pin_type()
&& let ty::Ref(_, _, hir::Mutability::Mut) = substs.type_at(0).kind()
- && let self_ty = infcx.instantiate_binder_with_fresh_vars(
+ && let self_ty = self.infcx.instantiate_binder_with_fresh_vars(
fn_call_span,
LateBoundRegionConversionTime::FnCall,
tcx.fn_sig(method_did).subst(tcx, method_substs).input(0),
)
- && infcx.can_eq(self.param_env, ty, self_ty)
+ && self.infcx.can_eq(self.param_env, ty, self_ty)
{
- err.span_suggestion_verbose(
- fn_call_span.shrink_to_lo(),
- "consider reborrowing the `Pin` instead of moving it",
- "as_mut().".to_string(),
- Applicability::MaybeIncorrect,
- );
+ err.eager_subdiagnostic(
+ &self.infcx.tcx.sess.parse_sess.span_diagnostic,
+ CaptureReasonSuggest::FreshReborrow {
+ span: fn_call_span.shrink_to_lo(),
+ });
}
if let Some(clone_trait) = tcx.lang_items().clone_trait()
- && let trait_ref = tcx.mk_trait_ref(clone_trait, [ty])
+ && let trait_ref = ty::TraitRef::new(tcx, clone_trait, [ty])
&& let o = Obligation::new(
tcx,
ObligationCause::dummy(),
self.param_env,
ty::Binder::dummy(trait_ref),
)
- && infcx.predicate_must_hold_modulo_regions(&o)
+ && self.infcx.predicate_must_hold_modulo_regions(&o)
{
err.span_suggestion_verbose(
fn_call_span.shrink_to_lo(),
@@ -1177,10 +1142,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
// error messages.
if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span) {
let func = tcx.def_path_str(method_did);
- err.span_note(
- self_arg.span,
- &format!("`{func}` takes ownership of the receiver `self`, which moves {place_name}")
- );
+ err.subdiagnostic(CaptureReasonNote::FuncTakeSelf {
+ func,
+ place_name,
+ span: self_arg.span,
+ });
}
let parent_did = tcx.parent(method_did);
let parent_self_ty =
@@ -1190,34 +1156,32 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
ty::Adt(def, ..) => Some(def.did()),
_ => None,
});
- let is_option_or_result = parent_self_ty.map_or(false, |def_id| {
+ let is_option_or_result = parent_self_ty.is_some_and(|def_id| {
matches!(tcx.get_diagnostic_name(def_id), Some(sym::Option | sym::Result))
});
if is_option_or_result && maybe_reinitialized_locations_is_empty {
- err.span_label(
- var_span,
- "help: consider calling `.as_ref()` or `.as_mut()` to borrow the type's contents",
- );
+ err.subdiagnostic(CaptureReasonLabel::BorrowContent { var_span });
}
}
// Other desugarings takes &self, which cannot cause a move
_ => {}
}
} else {
- if move_span != span || !loop_message.is_empty() {
- err.span_label(
+ if move_span != span || is_loop_message {
+ err.subdiagnostic(CaptureReasonLabel::MovedHere {
move_span,
- format!("value {partially_str}moved{move_msg} here{loop_message}"),
- );
+ is_partial,
+ is_move_msg,
+ is_loop_message,
+ });
}
// If the move error occurs due to a loop, don't show
// another message for the same span
- if loop_message.is_empty() {
- move_spans.var_span_label(
- err,
- format!("variable {partially_str}moved due to use{}", move_spans.describe()),
- "moved",
- );
+ if !is_loop_message {
+ move_spans.var_subdiag(None, err, None, |kind, var_span| match kind {
+ Some(_) => CaptureVarCause::PartialMoveUseInGenerator { var_span, is_partial },
+ None => CaptureVarCause::PartialMoveUseInClosure { var_span, is_partial },
+ })
}
}
}
diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
index 3662bec0c..8b77477a3 100644
--- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
@@ -6,6 +6,7 @@ use rustc_mir_dataflow::move_paths::{
};
use rustc_span::{BytePos, Span};
+use crate::diagnostics::CapturedMessageOpt;
use crate::diagnostics::{DescribePlaceOpt, UseSpans};
use crate::prefixes::PrefixSet;
use crate::MirBorrowckCtxt;
@@ -397,10 +398,15 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
}
}
};
+ let msg_opt = CapturedMessageOpt {
+ is_partial_move: false,
+ is_loop_message: false,
+ is_move_msg: false,
+ is_loop_move: false,
+ maybe_reinitialized_locations_is_empty: true,
+ };
if let Some(use_spans) = use_spans {
- self.explain_captures(
- &mut err, span, span, use_spans, move_place, "", "", "", false, true,
- );
+ self.explain_captures(&mut err, span, span, use_spans, move_place, msg_opt);
}
err
}
@@ -416,13 +422,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
None => "value".to_string(),
};
- self.note_type_does_not_implement_copy(
- err,
- &place_desc,
- place_ty,
- Some(span),
- "",
- );
+ err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
+ is_partial_move: false,
+ ty: place_ty,
+ place: &place_desc,
+ span,
+ });
} else {
binds_to.sort();
binds_to.dedup();
@@ -444,9 +449,19 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
Some(desc) => format!("`{desc}`"),
None => "value".to_string(),
};
- self.note_type_does_not_implement_copy(err, &place_desc, place_ty, Some(span), "");
+ err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
+ is_partial_move: false,
+ ty: place_ty,
+ place: &place_desc,
+ span,
+ });
- use_spans.args_span_label(err, format!("{place_desc} is moved here"));
+ use_spans.args_subdiag(err, |args_span| {
+ crate::session_diagnostics::CaptureArgLabel::MoveOutPlace {
+ place: place_desc,
+ args_span,
+ }
+ });
}
}
}
@@ -518,7 +533,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
suggestions.sort_unstable_by_key(|&(span, _, _)| span);
suggestions.dedup_by_key(|&mut (span, _, _)| span);
for (span, msg, suggestion) in suggestions {
- err.span_suggestion_verbose(span, &msg, suggestion, Applicability::MachineApplicable);
+ err.span_suggestion_verbose(span, msg, suggestion, Applicability::MachineApplicable);
}
}
@@ -534,13 +549,13 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
}
if binds_to.len() == 1 {
- self.note_type_does_not_implement_copy(
- err,
- &format!("`{}`", self.local_names[*local].unwrap()),
- bind_to.ty,
- Some(binding_span),
- "",
- );
+ let place_desc = &format!("`{}`", self.local_names[*local].unwrap());
+ err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
+ is_partial_move: false,
+ ty: bind_to.ty,
+ place: &place_desc,
+ span: binding_span,
+ });
}
}
diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
index 9d9040096..d0e17bf5a 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
@@ -1,4 +1,4 @@
-use rustc_errors::{Applicability, Diagnostic};
+use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
use rustc_hir as hir;
use rustc_hir::intravisit::Visitor;
use rustc_hir::Node;
@@ -15,8 +15,8 @@ use rustc_span::{sym, BytePos, Span};
use rustc_target::abi::FieldIdx;
use crate::diagnostics::BorrowedContentSource;
+use crate::util::FindAssignments;
use crate::MirBorrowckCtxt;
-use rustc_const_eval::util::collect_writes::FindAssignments;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub(crate) enum AccessKind {
@@ -231,14 +231,18 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
}
}
if suggest {
- borrow_spans.var_span_label(
- &mut err,
- format!(
- "mutable borrow occurs due to use of {} in closure",
- self.describe_any_place(access_place.as_ref()),
- ),
- "mutable",
- );
+ borrow_spans.var_subdiag(
+ None,
+ &mut err,
+ Some(mir::BorrowKind::Mut { allow_two_phase_borrow: false }),
+ |_kind, var_span| {
+ let place = self.describe_any_place(access_place.as_ref());
+ crate::session_diagnostics::CaptureVarCause::MutableBorrowUsePlaceClosure {
+ place,
+ var_span,
+ }
+ },
+ );
}
borrow_span
}
@@ -285,8 +289,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
.body
.local_decls
.get(local)
- .map(|l| mut_borrow_of_mutable_ref(l, self.local_names[local]))
- .unwrap_or(false) =>
+ .is_some_and(|l| mut_borrow_of_mutable_ref(l, self.local_names[local])) =>
{
let decl = &self.body.local_decls[local];
err.span_label(span, format!("cannot {act}"));
@@ -439,7 +442,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
.sess
.source_map()
.span_to_snippet(span)
- .map_or(false, |snippet| snippet.starts_with("&mut ")) =>
+ .is_ok_and(|snippet| snippet.starts_with("&mut ")) =>
{
err.span_label(span, format!("cannot {act}"));
err.span_suggestion(
@@ -474,179 +477,6 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
match self.local_names[local] {
Some(name) if !local_decl.from_compiler_desugaring() => {
- let label = match *local_decl.local_info() {
- LocalInfo::User(mir::BindingForm::ImplicitSelf(_)) => {
- let (span, suggestion) =
- suggest_ampmut_self(self.infcx.tcx, local_decl);
- Some((true, span, suggestion))
- }
-
- LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
- binding_mode: ty::BindingMode::BindByValue(_),
- opt_ty_info,
- ..
- })) => {
- // check if the RHS is from desugaring
- let opt_assignment_rhs_span =
- self.body.find_assignments(local).first().map(|&location| {
- if let Some(mir::Statement {
- source_info: _,
- kind:
- mir::StatementKind::Assign(box (
- _,
- mir::Rvalue::Use(mir::Operand::Copy(place)),
- )),
- }) = self.body[location.block]
- .statements
- .get(location.statement_index)
- {
- self.body.local_decls[place.local].source_info.span
- } else {
- self.body.source_info(location).span
- }
- });
- match opt_assignment_rhs_span.and_then(|s| s.desugaring_kind()) {
- // on for loops, RHS points to the iterator part
- Some(DesugaringKind::ForLoop) => {
- self.suggest_similar_mut_method_for_for_loop(&mut err);
- err.span_label(opt_assignment_rhs_span.unwrap(), format!(
- "this iterator yields `{pointer_sigil}` {pointer_desc}s",
- ));
- None
- }
- // don't create labels for compiler-generated spans
- Some(_) => None,
- None => {
- let label = if name != kw::SelfLower {
- suggest_ampmut(
- self.infcx.tcx,
- local_decl,
- opt_assignment_rhs_span,
- opt_ty_info,
- )
- } else {
- match local_decl.local_info() {
- LocalInfo::User(mir::BindingForm::Var(
- mir::VarBindingForm {
- opt_ty_info: None, ..
- },
- )) => {
- let (span, sugg) = suggest_ampmut_self(
- self.infcx.tcx,
- local_decl,
- );
- (true, span, sugg)
- }
- // explicit self (eg `self: &'a Self`)
- _ => suggest_ampmut(
- self.infcx.tcx,
- local_decl,
- opt_assignment_rhs_span,
- opt_ty_info,
- ),
- }
- };
- Some(label)
- }
- }
- }
-
- LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
- binding_mode: ty::BindingMode::BindByReference(_),
- ..
- })) => {
- let pattern_span = local_decl.source_info.span;
- suggest_ref_mut(self.infcx.tcx, pattern_span)
- .map(|replacement| (true, pattern_span, replacement))
- }
-
- _ => unreachable!(),
- };
-
- match label {
- Some((true, err_help_span, suggested_code)) => {
- let (is_trait_sig, local_trait) = self.is_error_in_trait(local);
- if !is_trait_sig {
- err.span_suggestion_verbose(
- err_help_span,
- &format!(
- "consider changing this to be a mutable {pointer_desc}"
- ),
- suggested_code,
- Applicability::MachineApplicable,
- );
- } else if let Some(x) = local_trait {
- err.span_suggestion_verbose(
- x,
- &format!(
- "consider changing that to be a mutable {pointer_desc}"
- ),
- suggested_code,
- Applicability::MachineApplicable,
- );
- }
- }
- Some((false, err_label_span, message)) => {
- struct BindingFinder {
- span: Span,
- hir_id: Option<hir::HirId>,
- }
-
- impl<'tcx> Visitor<'tcx> for BindingFinder {
- fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'tcx>) {
- if let hir::StmtKind::Local(local) = s.kind {
- if local.pat.span == self.span {
- self.hir_id = Some(local.hir_id);
- }
- }
- hir::intravisit::walk_stmt(self, s);
- }
- }
- let hir_map = self.infcx.tcx.hir();
- let def_id = self.body.source.def_id();
- let hir_id = hir_map.local_def_id_to_hir_id(def_id.expect_local());
- let node = hir_map.find(hir_id);
- let hir_id = if let Some(hir::Node::Item(item)) = node
- && let hir::ItemKind::Fn(.., body_id) = item.kind
- {
- let body = hir_map.body(body_id);
- let mut v = BindingFinder {
- span: err_label_span,
- hir_id: None,
- };
- v.visit_body(body);
- v.hir_id
- } else {
- None
- };
- if let Some(hir_id) = hir_id
- && let Some(hir::Node::Local(local)) = hir_map.find(hir_id)
- {
- let (changing, span, sugg) = match local.ty {
- Some(ty) => ("changing", ty.span, message),
- None => (
- "specifying",
- local.pat.span.shrink_to_hi(),
- format!(": {message}"),
- ),
- };
- err.span_suggestion_verbose(
- span,
- &format!("consider {changing} this binding's type"),
- sugg,
- Applicability::HasPlaceholders,
- );
- } else {
- err.span_label(
- err_label_span,
- &format!(
- "consider changing this binding's type to be: `{message}`"
- ),
- );
- }
- }
- None => {}
- }
err.span_label(
span,
format!(
@@ -654,6 +484,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
so the data it refers to cannot be {acted_on}",
),
);
+
+ self.suggest_make_local_mut(&mut err, local, name);
}
_ => {
err.span_label(
@@ -675,13 +507,13 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
match opt_source {
Some(BorrowedContentSource::OverloadedDeref(ty)) => {
- err.help(&format!(
+ err.help(format!(
"trait `DerefMut` is required to modify through a dereference, \
but it is not implemented for `{ty}`",
));
}
Some(BorrowedContentSource::OverloadedIndex(ty)) => {
- err.help(&format!(
+ err.help(format!(
"trait `IndexMut` is required to modify indexed content, \
but it is not implemented for `{ty}`",
));
@@ -732,7 +564,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
// val[index] = rv;
// ---------- place
self.err.multipart_suggestions(
- &format!(
+ format!(
"to modify a `{}`, use `.get_mut()`, `.insert()` or the entry API",
self.ty,
),
@@ -784,7 +616,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
{
// val[index].path(args..);
self.err.multipart_suggestion(
- &format!("to modify a `{}` use `.get_mut()`", self.ty),
+ format!("to modify a `{}` use `.get_mut()`", self.ty),
vec![
(
val.span.shrink_to_hi().with_hi(index.span.lo()),
@@ -809,16 +641,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
let Some(hir::Node::Item(item)) = node else { return; };
let hir::ItemKind::Fn(.., body_id) = item.kind else { return; };
let body = self.infcx.tcx.hir().body(body_id);
- let mut assign_span = span;
- // Drop desugaring is done at MIR build so it's not in the HIR
- if let Some(DesugaringKind::Replace) = span.desugaring_kind() {
- assign_span.remove_mark();
- }
- let mut v = V { assign_span, err, ty, suggested: false };
+ let mut v = V { assign_span: span, err, ty, suggested: false };
v.visit_body(body);
if !v.suggested {
- err.help(&format!(
+ err.help(format!(
"to modify a `{ty}`, use `.get_mut()`, `.insert()` or the entry API",
));
}
@@ -1127,6 +954,184 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
}
}
}
+
+ fn suggest_make_local_mut(
+ &self,
+ err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
+ local: Local,
+ name: Symbol,
+ ) {
+ let local_decl = &self.body.local_decls[local];
+
+ let (pointer_sigil, pointer_desc) =
+ if local_decl.ty.is_ref() { ("&", "reference") } else { ("*const", "pointer") };
+
+ let (is_trait_sig, local_trait) = self.is_error_in_trait(local);
+ if is_trait_sig && local_trait.is_none() {
+ return;
+ }
+
+ let decl_span = match local_trait {
+ Some(span) => span,
+ None => local_decl.source_info.span,
+ };
+
+ let label = match *local_decl.local_info() {
+ LocalInfo::User(mir::BindingForm::ImplicitSelf(_)) => {
+ let suggestion = suggest_ampmut_self(self.infcx.tcx, decl_span);
+ Some((true, decl_span, suggestion))
+ }
+
+ LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
+ binding_mode: ty::BindingMode::BindByValue(_),
+ opt_ty_info,
+ ..
+ })) => {
+ // check if the RHS is from desugaring
+ let opt_assignment_rhs_span =
+ self.body.find_assignments(local).first().map(|&location| {
+ if let Some(mir::Statement {
+ source_info: _,
+ kind:
+ mir::StatementKind::Assign(box (
+ _,
+ mir::Rvalue::Use(mir::Operand::Copy(place)),
+ )),
+ }) = self.body[location.block].statements.get(location.statement_index)
+ {
+ self.body.local_decls[place.local].source_info.span
+ } else {
+ self.body.source_info(location).span
+ }
+ });
+ match opt_assignment_rhs_span.and_then(|s| s.desugaring_kind()) {
+ // on for loops, RHS points to the iterator part
+ Some(DesugaringKind::ForLoop) => {
+ self.suggest_similar_mut_method_for_for_loop(err);
+ err.span_label(
+ opt_assignment_rhs_span.unwrap(),
+ format!("this iterator yields `{pointer_sigil}` {pointer_desc}s",),
+ );
+ None
+ }
+ // don't create labels for compiler-generated spans
+ Some(_) => None,
+ None => {
+ let label = if name != kw::SelfLower {
+ suggest_ampmut(
+ self.infcx.tcx,
+ local_decl.ty,
+ decl_span,
+ opt_assignment_rhs_span,
+ opt_ty_info,
+ )
+ } else {
+ match local_decl.local_info() {
+ LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
+ opt_ty_info: None,
+ ..
+ })) => {
+ let sugg = suggest_ampmut_self(self.infcx.tcx, decl_span);
+ (true, decl_span, sugg)
+ }
+ // explicit self (eg `self: &'a Self`)
+ _ => suggest_ampmut(
+ self.infcx.tcx,
+ local_decl.ty,
+ decl_span,
+ opt_assignment_rhs_span,
+ opt_ty_info,
+ ),
+ }
+ };
+ Some(label)
+ }
+ }
+ }
+
+ LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
+ binding_mode: ty::BindingMode::BindByReference(_),
+ ..
+ })) => {
+ let pattern_span: Span = local_decl.source_info.span;
+ suggest_ref_mut(self.infcx.tcx, pattern_span)
+ .map(|span| (true, span, "mut ".to_owned()))
+ }
+
+ _ => unreachable!(),
+ };
+
+ match label {
+ Some((true, err_help_span, suggested_code)) => {
+ err.span_suggestion_verbose(
+ err_help_span,
+ format!("consider changing this to be a mutable {pointer_desc}"),
+ suggested_code,
+ Applicability::MachineApplicable,
+ );
+ }
+ Some((false, err_label_span, message)) => {
+ struct BindingFinder {
+ span: Span,
+ hir_id: Option<hir::HirId>,
+ }
+
+ impl<'tcx> Visitor<'tcx> for BindingFinder {
+ fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'tcx>) {
+ if let hir::StmtKind::Local(local) = s.kind {
+ if local.pat.span == self.span {
+ self.hir_id = Some(local.hir_id);
+ }
+ }
+ hir::intravisit::walk_stmt(self, s);
+ }
+ }
+ let hir_map = self.infcx.tcx.hir();
+ let def_id = self.body.source.def_id();
+ let hir_id = hir_map.local_def_id_to_hir_id(def_id.expect_local());
+ let node = hir_map.find(hir_id);
+ let hir_id = if let Some(hir::Node::Item(item)) = node
+ && let hir::ItemKind::Fn(.., body_id) = item.kind
+ {
+ let body = hir_map.body(body_id);
+ let mut v = BindingFinder {
+ span: err_label_span,
+ hir_id: None,
+ };
+ v.visit_body(body);
+ v.hir_id
+ } else {
+ None
+ };
+ if let Some(hir_id) = hir_id
+ && let Some(hir::Node::Local(local)) = hir_map.find(hir_id)
+ {
+ let (changing, span, sugg) = match local.ty {
+ Some(ty) => ("changing", ty.span, message),
+ None => (
+ "specifying",
+ local.pat.span.shrink_to_hi(),
+ format!(": {message}"),
+ ),
+ };
+ err.span_suggestion_verbose(
+ span,
+ format!("consider {changing} this binding's type"),
+ sugg,
+ Applicability::HasPlaceholders,
+ );
+ } else {
+ err.span_label(
+ err_label_span,
+ format!(
+ "consider changing this binding's type to be: `{message}`"
+ ),
+ );
+ }
+ }
+ None => {}
+ }
+ }
}
pub fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<Symbol>) -> bool {
@@ -1143,7 +1148,7 @@ pub fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<
// suggest removing the `&mut`.
//
// Deliberately fall into this case for all implicit self types,
- // so that we don't fall in to the next case with them.
+ // so that we don't fall into the next case with them.
kind == hir::ImplicitSelfKind::MutRef
}
_ if Some(kw::SelfLower) == local_name => {
@@ -1156,25 +1161,18 @@ pub fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<
}
}
-fn suggest_ampmut_self<'tcx>(
- tcx: TyCtxt<'tcx>,
- local_decl: &mir::LocalDecl<'tcx>,
-) -> (Span, String) {
- let sp = local_decl.source_info.span;
- (
- sp,
- match tcx.sess.source_map().span_to_snippet(sp) {
- Ok(snippet) => {
- let lt_pos = snippet.find('\'');
- if let Some(lt_pos) = lt_pos {
- format!("&{}mut self", &snippet[lt_pos..snippet.len() - 4])
- } else {
- "&mut self".to_string()
- }
+fn suggest_ampmut_self<'tcx>(tcx: TyCtxt<'tcx>, span: Span) -> String {
+ match tcx.sess.source_map().span_to_snippet(span) {
+ Ok(snippet) => {
+ let lt_pos = snippet.find('\'');
+ if let Some(lt_pos) = lt_pos {
+ format!("&{}mut self", &snippet[lt_pos..snippet.len() - 4])
+ } else {
+ "&mut self".to_string()
}
- _ => "&mut self".to_string(),
- },
- )
+ }
+ _ => "&mut self".to_string(),
+ }
}
// When we want to suggest a user change a local variable to be a `&mut`, there
@@ -1194,72 +1192,89 @@ fn suggest_ampmut_self<'tcx>(
// by trying (3.), then (2.) and finally falling back on (1.).
fn suggest_ampmut<'tcx>(
tcx: TyCtxt<'tcx>,
- local_decl: &mir::LocalDecl<'tcx>,
+ decl_ty: Ty<'tcx>,
+ decl_span: Span,
opt_assignment_rhs_span: Option<Span>,
opt_ty_info: Option<Span>,
) -> (bool, Span, String) {
+ // if there is a RHS and it starts with a `&` from it, then check if it is
+ // mutable, and if not, put suggest putting `mut ` to make it mutable.
+ // we don't have to worry about lifetime annotations here because they are
+ // not valid when taking a reference. For example, the following is not valid Rust:
+ //
+ // let x: &i32 = &'a 5;
+ // ^^ lifetime annotation not allowed
+ //
if let Some(assignment_rhs_span) = opt_assignment_rhs_span
&& let Ok(src) = tcx.sess.source_map().span_to_snippet(assignment_rhs_span)
+ && let Some(stripped) = src.strip_prefix('&')
{
- let is_mutbl = |ty: &str| -> bool {
- if let Some(rest) = ty.strip_prefix("mut") {
- match rest.chars().next() {
- // e.g. `&mut x`
- Some(c) if c.is_whitespace() => true,
- // e.g. `&mut(x)`
- Some('(') => true,
- // e.g. `&mut{x}`
- Some('{') => true,
- // e.g. `&mutablevar`
- _ => false,
- }
- } else {
- false
+ let is_mut = if let Some(rest) = stripped.trim_start().strip_prefix("mut") {
+ match rest.chars().next() {
+ // e.g. `&mut x`
+ Some(c) if c.is_whitespace() => true,
+ // e.g. `&mut(x)`
+ Some('(') => true,
+ // e.g. `&mut{x}`
+ Some('{') => true,
+ // e.g. `&mutablevar`
+ _ => false,
}
+ } else {
+ false
};
- if let (true, Some(ws_pos)) = (src.starts_with("&'"), src.find(char::is_whitespace)) {
- let lt_name = &src[1..ws_pos];
- let ty = src[ws_pos..].trim_start();
- if !is_mutbl(ty) {
- return (true, assignment_rhs_span, format!("&{lt_name} mut {ty}"));
- }
- } else if let Some(stripped) = src.strip_prefix('&') {
- let stripped = stripped.trim_start();
- if !is_mutbl(stripped) {
- return (true, assignment_rhs_span, format!("&mut {stripped}"));
- }
+ // if the reference is already mutable then there is nothing we can do
+ // here.
+ if !is_mut {
+ let span = assignment_rhs_span;
+ // shrink the span to just after the `&` in `&variable`
+ let span = span.with_lo(span.lo() + BytePos(1)).shrink_to_lo();
+
+ // FIXME(Ezrashaw): returning is bad because we still might want to
+ // update the annotated type, see #106857.
+ return (true, span, "mut ".to_owned());
}
}
- let (suggestability, highlight_span) = match opt_ty_info {
+ let (binding_exists, span) = match opt_ty_info {
// if this is a variable binding with an explicit type,
- // try to highlight that for the suggestion.
+ // then we will suggest changing it to be mutable.
+ // this is `Applicability::MachineApplicable`.
Some(ty_span) => (true, ty_span),
- // otherwise, just highlight the span associated with
- // the (MIR) LocalDecl.
- None => (false, local_decl.source_info.span),
+ // otherwise, we'll suggest *adding* an annotated type, we'll suggest
+ // the RHS's type for that.
+ // this is `Applicability::HasPlaceholders`.
+ None => (false, decl_span),
};
- if let Ok(src) = tcx.sess.source_map().span_to_snippet(highlight_span)
- && let (true, Some(ws_pos)) = (src.starts_with("&'"), src.find(char::is_whitespace))
+ // if the binding already exists and is a reference with a explicit
+ // lifetime, then we can suggest adding ` mut`. this is special-cased from
+ // the path without a explicit lifetime.
+ if let Ok(src) = tcx.sess.source_map().span_to_snippet(span)
+ && src.starts_with("&'")
+ // note that `& 'a T` is invalid so this is correct.
+ && let Some(ws_pos) = src.find(char::is_whitespace)
{
- let lt_name = &src[1..ws_pos];
- let ty = &src[ws_pos..];
- return (true, highlight_span, format!("&{lt_name} mut{ty}"));
- }
+ let span = span.with_lo(span.lo() + BytePos(ws_pos as u32)).shrink_to_lo();
+ (true, span, " mut".to_owned())
+ // if there is already a binding, we modify it to be `mut`
+ } else if binding_exists {
+ // shrink the span to just after the `&` in `&variable`
+ let span = span.with_lo(span.lo() + BytePos(1)).shrink_to_lo();
+ (true, span, "mut ".to_owned())
+ } else {
+ // otherwise, suggest that the user annotates the binding; we provide the
+ // type of the local.
+ let ty_mut = decl_ty.builtin_deref(true).unwrap();
+ assert_eq!(ty_mut.mutbl, hir::Mutability::Not);
- let ty_mut = local_decl.ty.builtin_deref(true).unwrap();
- assert_eq!(ty_mut.mutbl, hir::Mutability::Not);
- (
- suggestability,
- highlight_span,
- if local_decl.ty.is_ref() {
- format!("&mut {}", ty_mut.ty)
- } else {
- format!("*mut {}", ty_mut.ty)
- },
- )
+ (
+ false,
+ span,
+ format!("{}mut {}", if decl_ty.is_ref() {"&"} else {"*"}, ty_mut.ty)
+ )
+ }
}
fn is_closure_or_generator(ty: Ty<'_>) -> bool {
@@ -1296,11 +1311,13 @@ fn get_mut_span_in_struct_field<'tcx>(
}
/// If possible, suggest replacing `ref` with `ref mut`.
-fn suggest_ref_mut(tcx: TyCtxt<'_>, binding_span: Span) -> Option<String> {
- let hi_src = tcx.sess.source_map().span_to_snippet(binding_span).ok()?;
- if hi_src.starts_with("ref") && hi_src["ref".len()..].starts_with(rustc_lexer::is_whitespace) {
- let replacement = format!("ref mut{}", &hi_src["ref".len()..]);
- Some(replacement)
+fn suggest_ref_mut(tcx: TyCtxt<'_>, span: Span) -> Option<Span> {
+ let pattern_str = tcx.sess.source_map().span_to_snippet(span).ok()?;
+ if pattern_str.starts_with("ref")
+ && pattern_str["ref".len()..].starts_with(rustc_lexer::is_whitespace)
+ {
+ let span = span.with_lo(span.lo() + BytePos(4)).shrink_to_lo();
+ Some(span)
} else {
None
}
diff --git a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs
index d5ece5743..b6eb9ae98 100644
--- a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs
@@ -125,8 +125,7 @@ impl OutlivesSuggestionBuilder {
|(r, _)| {
self.constraints_to_add
.get(r)
- .map(|r_outlived| r_outlived.as_slice().contains(fr))
- .unwrap_or(false)
+ .is_some_and(|r_outlived| r_outlived.as_slice().contains(fr))
},
);
@@ -171,7 +170,7 @@ impl OutlivesSuggestionBuilder {
if let (Some(fr_name), Some(outlived_fr_name)) = (fr_name, outlived_fr_name)
&& !matches!(outlived_fr_name.source, RegionNameSource::Static)
{
- diag.help(&format!(
+ diag.help(format!(
"consider adding the following bound: `{fr_name}: {outlived_fr_name}`",
));
}
@@ -207,7 +206,7 @@ impl OutlivesSuggestionBuilder {
// If there is exactly one suggestable constraints, then just suggest it. Otherwise, emit a
// list of diagnostics.
let mut diag = if suggested.len() == 1 {
- mbcx.infcx.tcx.sess.diagnostic().struct_help(&match suggested.last().unwrap() {
+ mbcx.infcx.tcx.sess.diagnostic().struct_help(match suggested.last().unwrap() {
SuggestedConstraint::Outlives(a, bs) => {
let bs: SmallVec<[String; 2]> = bs.iter().map(|r| r.to_string()).collect();
format!("add bound `{a}: {}`", bs.join(" + "))
@@ -232,15 +231,15 @@ impl OutlivesSuggestionBuilder {
match constraint {
SuggestedConstraint::Outlives(a, bs) => {
let bs: SmallVec<[String; 2]> = bs.iter().map(|r| r.to_string()).collect();
- diag.help(&format!("add bound `{a}: {}`", bs.join(" + ")));
+ diag.help(format!("add bound `{a}: {}`", bs.join(" + ")));
}
SuggestedConstraint::Equal(a, b) => {
- diag.help(&format!(
+ diag.help(format!(
"`{a}` and `{b}` must be the same: replace one with the other",
));
}
SuggestedConstraint::Static(a) => {
- diag.help(&format!("replace `{a}` with `'static`"));
+ diag.help(format!("replace `{a}` with `'static`"));
}
}
}
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
index 9fcebeb0a..8ec872e20 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
@@ -533,8 +533,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
}
_ => panic!("Unexpected type {ty:?}"),
};
- diag.note(&format!("requirement occurs because of {desc}",));
- diag.note(&note);
+ diag.note(format!("requirement occurs because of {desc}",));
+ diag.note(note);
diag.help("see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance");
}
}
@@ -845,7 +845,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
return;
}
- let Some((alias_tys, alias_span)) = self
+ let Some((alias_tys, alias_span, lt_addition_span)) = self
.infcx
.tcx
.return_type_impl_or_dyn_traits_with_type_alias(suitable_region.def_id) else { return; };
@@ -858,12 +858,22 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
()
}
if let TyKind::TraitObject(_, lt, _) = alias_ty.kind {
- spans_suggs.push((lt.ident.span.shrink_to_hi(), " + 'a".to_string()));
+ if lt.ident.name == kw::Empty {
+ spans_suggs.push((lt.ident.span.shrink_to_hi(), " + 'a".to_string()));
+ } else {
+ spans_suggs.push((lt.ident.span, "'a".to_string()));
+ }
}
}
- spans_suggs.push((alias_span.shrink_to_hi(), "<'a>".to_string()));
+
+ if let Some(lt_addition_span) = lt_addition_span {
+ spans_suggs.push((lt_addition_span, "'a, ".to_string()));
+ } else {
+ spans_suggs.push((alias_span.shrink_to_hi(), "<'a>".to_string()));
+ }
+
diag.multipart_suggestion_verbose(
- &format!(
+ format!(
"to declare that the trait object {captures}, you can add a lifetime parameter `'a` in the type alias"
),
spans_suggs,
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
index f69c4829a..f38e1605f 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
@@ -622,7 +622,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
// programs, so we need to use delay_span_bug here. See #82126.
self.infcx.tcx.sess.delay_span_bug(
hir_arg.span(),
- &format!("unmatched subst and hir arg: found {kind:?} vs {hir_arg:?}"),
+ format!("unmatched subst and hir arg: found {kind:?} vs {hir_arg:?}"),
);
}
}
diff --git a/compiler/rustc_borrowck/src/diagnostics/var_name.rs b/compiler/rustc_borrowck/src/diagnostics/var_name.rs
index 376415e3d..98418e237 100644
--- a/compiler/rustc_borrowck/src/diagnostics/var_name.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/var_name.rs
@@ -3,7 +3,7 @@
use crate::region_infer::RegionInferenceContext;
use crate::Upvar;
-use rustc_index::vec::{Idx, IndexSlice};
+use rustc_index::IndexSlice;
use rustc_middle::mir::{Body, Local};
use rustc_middle::ty::{RegionVid, TyCtxt};
use rustc_span::source_map::Span;
@@ -117,7 +117,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
argument_index: usize,
) -> (Option<Symbol>, Span) {
let implicit_inputs = self.universal_regions().defining_ty.implicit_inputs();
- let argument_local = Local::new(implicit_inputs + argument_index + 1);
+ let argument_local = Local::from_usize(implicit_inputs + argument_index + 1);
debug!("get_argument_name_and_span_for_region: argument_local={argument_local:?}");
let argument_name = local_names[argument_local];
diff --git a/compiler/rustc_borrowck/src/facts.rs b/compiler/rustc_borrowck/src/facts.rs
index 02ffb51fb..87fad9a35 100644
--- a/compiler/rustc_borrowck/src/facts.rs
+++ b/compiler/rustc_borrowck/src/facts.rs
@@ -4,7 +4,6 @@ use crate::location::{LocationIndex, LocationTable};
use crate::BorrowIndex;
use polonius_engine::AllFacts as PoloniusFacts;
use polonius_engine::Atom;
-use rustc_index::vec::Idx;
use rustc_middle::mir::Local;
use rustc_middle::ty::{RegionVid, TyCtxt};
use rustc_mir_dataflow::move_paths::MovePathIndex;
@@ -93,13 +92,13 @@ impl AllFactsExt for AllFacts {
impl Atom for BorrowIndex {
fn index(self) -> usize {
- Idx::index(self)
+ self.as_usize()
}
}
impl Atom for LocationIndex {
fn index(self) -> usize {
- Idx::index(self)
+ self.as_usize()
}
}
diff --git a/compiler/rustc_borrowck/src/invalidation.rs b/compiler/rustc_borrowck/src/invalidation.rs
index 498d254da..b2ff25ecb 100644
--- a/compiler/rustc_borrowck/src/invalidation.rs
+++ b/compiler/rustc_borrowck/src/invalidation.rs
@@ -46,7 +46,7 @@ struct InvalidationGenerator<'cx, 'tcx> {
all_facts: &'cx mut AllFacts,
location_table: &'cx LocationTable,
body: &'cx Body<'tcx>,
- dominators: Dominators<BasicBlock>,
+ dominators: &'cx Dominators<BasicBlock>,
borrow_set: &'cx BorrowSet<'tcx>,
}
@@ -79,7 +79,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> {
}
// Only relevant for mir typeck
StatementKind::AscribeUserType(..)
- // Only relevant for unsafeck
+ // Only relevant for liveness and unsafeck
| StatementKind::PlaceMention(..)
// Doesn't have any language semantics
| StatementKind::Coverage(..)
@@ -112,11 +112,13 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> {
TerminatorKind::SwitchInt { discr, targets: _ } => {
self.consume_operand(location, discr);
}
- TerminatorKind::Drop { place: drop_place, target: _, unwind: _ } => {
+ TerminatorKind::Drop { place: drop_place, target: _, unwind: _, replace } => {
+ let write_kind =
+ if *replace { WriteKind::Replace } else { WriteKind::StorageDeadOrDrop };
self.access_place(
location,
*drop_place,
- (AccessDepth::Drop, Write(WriteKind::StorageDeadOrDrop)),
+ (AccessDepth::Drop, Write(write_kind)),
LocalMutationIsAllowed::Yes,
);
}
@@ -138,7 +140,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> {
TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => {
self.consume_operand(location, cond);
use rustc_middle::mir::AssertKind;
- if let AssertKind::BoundsCheck { len, index } = msg {
+ if let AssertKind::BoundsCheck { len, index } = &**msg {
self.consume_operand(location, len);
self.consume_operand(location, index);
}
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index a4b285a34..a53ea100c 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -20,14 +20,14 @@ extern crate tracing;
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_data_structures::graph::dominators::Dominators;
use rustc_errors::{Diagnostic, DiagnosticBuilder, DiagnosticMessage, SubdiagnosticMessage};
+use rustc_fluent_macro::fluent_messages;
use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
use rustc_index::bit_set::ChunkedBitSet;
-use rustc_index::vec::{IndexSlice, IndexVec};
+use rustc_index::{IndexSlice, IndexVec};
use rustc_infer::infer::{
- DefiningAnchor, InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin, TyCtxtInferExt,
+ InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin, TyCtxtInferExt,
};
-use rustc_macros::fluent_messages;
use rustc_middle::mir::{
traversal, Body, ClearCrossCrate, Local, Location, Mutability, NonDivergingIntrinsic, Operand,
Place, PlaceElem, PlaceRef, VarDebugInfoContents,
@@ -35,7 +35,8 @@ use rustc_middle::mir::{
use rustc_middle::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind};
use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind};
use rustc_middle::mir::{ProjectionElem, Promoted, Rvalue, Statement, StatementKind};
-use rustc_middle::ty::query::Providers;
+use rustc_middle::query::Providers;
+use rustc_middle::traits::DefiningAnchor;
use rustc_middle::ty::{self, CapturedPlace, ParamEnv, RegionVid, TyCtxt};
use rustc_session::lint::builtin::UNUSED_MUT;
use rustc_span::{Span, Symbol};
@@ -43,7 +44,6 @@ use rustc_target::abi::FieldIdx;
use either::Either;
use smallvec::SmallVec;
-use std::cell::OnceCell;
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::ops::Deref;
@@ -62,7 +62,7 @@ use crate::session_diagnostics::VarNeedNotMut;
use self::diagnostics::{AccessKind, RegionName};
use self::location::LocationTable;
use self::prefixes::PrefixSet;
-use facts::AllFacts;
+use consumers::{BodyWithBorrowckFacts, ConsumerOptions};
use self::path_utils::*;
@@ -88,6 +88,7 @@ mod session_diagnostics;
mod type_check;
mod universal_regions;
mod used_muts;
+mod util;
/// A public API provided for the Rust compiler consumers.
pub mod consumers;
@@ -118,24 +119,12 @@ impl<'tcx> TyCtxtConsts<'tcx> {
}
pub fn provide(providers: &mut Providers) {
- *providers = Providers {
- mir_borrowck: |tcx, did| {
- if let Some(def) = ty::WithOptConstParam::try_lookup(did, tcx) {
- tcx.mir_borrowck_const_arg(def)
- } else {
- mir_borrowck(tcx, ty::WithOptConstParam::unknown(did))
- }
- },
- mir_borrowck_const_arg: |tcx, (did, param_did)| {
- mir_borrowck(tcx, ty::WithOptConstParam { did, const_param_did: Some(param_did) })
- },
- ..*providers
- };
+ *providers = Providers { mir_borrowck, ..*providers };
}
-fn mir_borrowck(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -> &BorrowCheckResult<'_> {
+fn mir_borrowck(tcx: TyCtxt<'_>, def: LocalDefId) -> &BorrowCheckResult<'_> {
let (input_body, promoted) = tcx.mir_promoted(def);
- debug!("run query mir_borrowck: {}", tcx.def_path_str(def.did.to_def_id()));
+ debug!("run query mir_borrowck: {}", tcx.def_path_str(def));
if input_body.borrow().should_skip() {
debug!("Skipping borrowck because of injected body");
@@ -149,13 +138,13 @@ fn mir_borrowck(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -> &Bor
return tcx.arena.alloc(result);
}
- let hir_owner = tcx.hir().local_def_id_to_hir_id(def.did).owner;
+ let hir_owner = tcx.hir().local_def_id_to_hir_id(def).owner;
let infcx =
tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bind(hir_owner.def_id)).build();
let input_body: &Body<'_> = &input_body.borrow();
let promoted: &IndexSlice<_, _> = &promoted.borrow();
- let opt_closure_req = do_mir_borrowck(&infcx, input_body, promoted, false).0;
+ let opt_closure_req = do_mir_borrowck(&infcx, input_body, promoted, None).0;
debug!("mir_borrowck done");
tcx.arena.alloc(opt_closure_req)
@@ -163,22 +152,22 @@ fn mir_borrowck(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -> &Bor
/// Perform the actual borrow checking.
///
-/// If `return_body_with_facts` is true, then return the body with non-erased
-/// region ids on which the borrow checking was performed together with Polonius
-/// facts.
-#[instrument(skip(infcx, input_body, input_promoted), fields(id=?input_body.source.with_opt_param().as_local().unwrap()), level = "debug")]
+/// Use `consumer_options: None` for the default behavior of returning
+/// [`BorrowCheckResult`] only. Otherwise, return [`BodyWithBorrowckFacts`] according
+/// to the given [`ConsumerOptions`].
+#[instrument(skip(infcx, input_body, input_promoted), fields(id=?input_body.source.def_id()), level = "debug")]
fn do_mir_borrowck<'tcx>(
infcx: &InferCtxt<'tcx>,
input_body: &Body<'tcx>,
input_promoted: &IndexSlice<Promoted, Body<'tcx>>,
- return_body_with_facts: bool,
+ consumer_options: Option<ConsumerOptions>,
) -> (BorrowCheckResult<'tcx>, Option<Box<BodyWithBorrowckFacts<'tcx>>>) {
- let def = input_body.source.with_opt_param().as_local().unwrap();
+ let def = input_body.source.def_id().expect_local();
debug!(?def);
let tcx = infcx.tcx;
let infcx = BorrowckInferCtxt::new(infcx);
- let param_env = tcx.param_env(def.did);
+ let param_env = tcx.param_env(def);
let mut local_names = IndexVec::from_elem(None, &input_body.local_decls);
for var_debug_info in &input_body.var_debug_info {
@@ -206,7 +195,7 @@ fn do_mir_borrowck<'tcx>(
errors.set_tainted_by_errors(e);
}
let upvars: Vec<_> = tcx
- .closure_captures(def.did)
+ .closure_captures(def)
.iter()
.map(|&captured_place| {
let capture = captured_place.info.capture_kind;
@@ -248,12 +237,10 @@ fn do_mir_borrowck<'tcx>(
.iterate_to_fixpoint()
.into_results_cursor(&body);
- let locals_are_invalidated_at_exit = tcx.hir().body_owner_kind(def.did).is_fn_or_closure();
+ let locals_are_invalidated_at_exit = tcx.hir().body_owner_kind(def).is_fn_or_closure();
let borrow_set =
Rc::new(BorrowSet::build(tcx, body, locals_are_invalidated_at_exit, &mdpe.move_data));
- let use_polonius = return_body_with_facts || infcx.tcx.sess.opts.unstable_opts.polonius;
-
// Compute non-lexical lifetimes.
let nll::NllOutput {
regioncx,
@@ -273,7 +260,7 @@ fn do_mir_borrowck<'tcx>(
&mdpe.move_data,
&borrow_set,
&upvars,
- use_polonius,
+ consumer_options,
);
// Dump MIR results into a file, if that is enabled. This let us
@@ -342,7 +329,6 @@ fn do_mir_borrowck<'tcx>(
used_mut: Default::default(),
used_mut_upvars: SmallVec::new(),
borrow_set: Rc::clone(&borrow_set),
- dominators: Default::default(),
upvars: Vec::new(),
local_names: IndexVec::from_elem(None, &promoted_body.local_decls),
region_names: RefCell::default(),
@@ -371,7 +357,6 @@ fn do_mir_borrowck<'tcx>(
used_mut: Default::default(),
used_mut_upvars: SmallVec::new(),
borrow_set: Rc::clone(&borrow_set),
- dominators: Default::default(),
upvars,
local_names,
region_names: RefCell::default(),
@@ -455,13 +440,16 @@ fn do_mir_borrowck<'tcx>(
tainted_by_errors,
};
- let body_with_facts = if return_body_with_facts {
- let output_facts = mbcx.polonius_output.expect("Polonius output was not computed");
+ let body_with_facts = if consumer_options.is_some() {
+ let output_facts = mbcx.polonius_output;
Some(Box::new(BodyWithBorrowckFacts {
body: body_owned,
- input_facts: *polonius_input.expect("Polonius input facts were not generated"),
+ promoted,
+ borrow_set,
+ region_inference_context: regioncx,
+ location_table: polonius_input.as_ref().map(|_| location_table_owned),
+ input_facts: polonius_input,
output_facts,
- location_table: location_table_owned,
}))
} else {
None
@@ -472,22 +460,6 @@ fn do_mir_borrowck<'tcx>(
(result, body_with_facts)
}
-/// A `Body` with information computed by the borrow checker. This struct is
-/// intended to be consumed by compiler consumers.
-///
-/// We need to include the MIR body here because the region identifiers must
-/// match the ones in the Polonius facts.
-pub struct BodyWithBorrowckFacts<'tcx> {
- /// A mir body that contains region identifiers.
- pub body: Body<'tcx>,
- /// Polonius input facts.
- pub input_facts: AllFacts,
- /// Polonius output facts.
- pub output_facts: Rc<self::nll::PoloniusOutput>,
- /// The table that maps Polonius points to locations in the table.
- pub location_table: LocationTable,
-}
-
pub struct BorrowckInferCtxt<'cx, 'tcx> {
pub(crate) infcx: &'cx InferCtxt<'tcx>,
pub(crate) reg_var_to_origin: RefCell<FxIndexMap<ty::RegionVid, RegionCtxt>>,
@@ -509,11 +481,11 @@ impl<'cx, 'tcx> BorrowckInferCtxt<'cx, 'tcx> {
let next_region = self.infcx.next_region_var(origin);
let vid = next_region.as_var();
- if cfg!(debug_assertions) && !self.inside_canonicalization_ctxt() {
+ if cfg!(debug_assertions) {
debug!("inserting vid {:?} with origin {:?} into var_to_origin", vid, origin);
let ctxt = get_ctxt_fn();
let mut var_to_origin = self.reg_var_to_origin.borrow_mut();
- var_to_origin.insert(vid, ctxt);
+ assert_eq!(var_to_origin.insert(vid, ctxt), None);
}
next_region
@@ -528,14 +500,14 @@ impl<'cx, 'tcx> BorrowckInferCtxt<'cx, 'tcx> {
where
F: Fn() -> RegionCtxt,
{
- let next_region = self.infcx.next_nll_region_var(origin.clone());
+ let next_region = self.infcx.next_nll_region_var(origin);
let vid = next_region.as_var();
- if cfg!(debug_assertions) && !self.inside_canonicalization_ctxt() {
+ if cfg!(debug_assertions) {
debug!("inserting vid {:?} with origin {:?} into var_to_origin", vid, origin);
let ctxt = get_ctxt_fn();
let mut var_to_origin = self.reg_var_to_origin.borrow_mut();
- var_to_origin.insert(vid, ctxt);
+ assert_eq!(var_to_origin.insert(vid, ctxt), None);
}
next_region
@@ -602,9 +574,6 @@ struct MirBorrowckCtxt<'cx, 'tcx> {
/// The set of borrows extracted from the MIR
borrow_set: Rc<BorrowSet<'tcx>>,
- /// Dominators for MIR
- dominators: OnceCell<Dominators<BasicBlock>>,
-
/// Information about upvars not necessarily preserved in types or MIR
upvars: Vec<Upvar<'tcx>>,
@@ -676,7 +645,7 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx
}
// Only relevant for mir typeck
StatementKind::AscribeUserType(..)
- // Only relevant for unsafeck
+ // Only relevant for liveness and unsafeck
| StatementKind::PlaceMention(..)
// Doesn't have any language semantics
| StatementKind::Coverage(..)
@@ -716,17 +685,19 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx
TerminatorKind::SwitchInt { discr, targets: _ } => {
self.consume_operand(loc, (discr, span), flow_state);
}
- TerminatorKind::Drop { place, target: _, unwind: _ } => {
+ TerminatorKind::Drop { place, target: _, unwind: _, replace } => {
debug!(
"visit_terminator_drop \
loc: {:?} term: {:?} place: {:?} span: {:?}",
loc, term, place, span
);
+ let write_kind =
+ if *replace { WriteKind::Replace } else { WriteKind::StorageDeadOrDrop };
self.access_place(
loc,
(*place, span),
- (AccessDepth::Drop, Write(WriteKind::StorageDeadOrDrop)),
+ (AccessDepth::Drop, Write(write_kind)),
LocalMutationIsAllowed::Yes,
flow_state,
);
@@ -749,7 +720,7 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx
TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => {
self.consume_operand(loc, (cond, span), flow_state);
use rustc_middle::mir::AssertKind;
- if let AssertKind::BoundsCheck { len, index } = msg {
+ if let AssertKind::BoundsCheck { len, index } = &**msg {
self.consume_operand(loc, (len, span), flow_state);
self.consume_operand(loc, (index, span), flow_state);
}
@@ -916,6 +887,7 @@ enum ReadKind {
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum WriteKind {
StorageDeadOrDrop,
+ Replace,
MutableBorrow(BorrowKind),
Mutate,
Move,
@@ -946,6 +918,7 @@ enum InitializationRequiringAction {
PartialAssignment,
}
+#[derive(Debug)]
struct RootPlace<'tcx> {
place_local: Local,
place_projection: &'tcx [PlaceElem<'tcx>],
@@ -1162,13 +1135,21 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
this.buffer_error(err);
}
WriteKind::StorageDeadOrDrop => this
- .report_storage_dead_or_drop_of_borrowed(location, place_span, borrow),
+ .report_borrowed_value_does_not_live_long_enough(
+ location,
+ borrow,
+ place_span,
+ Some(WriteKind::StorageDeadOrDrop),
+ ),
WriteKind::Mutate => {
this.report_illegal_mutation_of_borrowed(location, place_span, borrow)
}
WriteKind::Move => {
this.report_move_out_while_borrowed(location, place_span, borrow)
}
+ WriteKind::Replace => {
+ this.report_illegal_mutation_of_borrowed(location, place_span, borrow)
+ }
}
Control::Break
}
@@ -1859,11 +1840,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
// is allowed, remove this match arm.
ty::Adt(..) | ty::Tuple(..) => {
check_parent_of_field(self, location, place_base, span, flow_state);
-
- // rust-lang/rust#21232, #54499, #54986: during period where we reject
- // partial initialization, do not complain about unnecessary `mut` on
- // an attempt to do a partial initialization.
- self.used_mut.insert(place.local);
}
_ => {}
@@ -1951,6 +1927,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
(prefix, base, span),
mpi,
);
+
+ // rust-lang/rust#21232, #54499, #54986: during period where we reject
+ // partial initialization, do not complain about unnecessary `mut` on
+ // an attempt to do a partial initialization.
+ this.used_mut.insert(base.local);
}
}
}
@@ -2012,12 +1993,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
Reservation(
WriteKind::Move
+ | WriteKind::Replace
| WriteKind::StorageDeadOrDrop
| WriteKind::MutableBorrow(BorrowKind::Shared)
| WriteKind::MutableBorrow(BorrowKind::Shallow),
)
| Write(
WriteKind::Move
+ | WriteKind::Replace
| WriteKind::StorageDeadOrDrop
| WriteKind::MutableBorrow(BorrowKind::Shared)
| WriteKind::MutableBorrow(BorrowKind::Shallow),
@@ -2032,7 +2015,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
// been emitted (#52262).
self.infcx.tcx.sess.delay_span_bug(
span,
- &format!(
+ format!(
"Accessing `{:?}` with the kind `{:?}` shouldn't be possible",
place, kind,
),
@@ -2279,7 +2262,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}
fn dominators(&self) -> &Dominators<BasicBlock> {
- self.dominators.get_or_init(|| self.body.basic_blocks.dominators())
+ // `BasicBlocks` computes dominators on-demand and caches them.
+ self.body.basic_blocks.dominators()
}
}
@@ -2393,7 +2377,7 @@ mod error {
}
for (_, (mut diag, count)) in std::mem::take(&mut self.errors.buffered_mut_errors) {
if count > 10 {
- diag.note(&format!("...and {} other attempted mutable borrows", count - 10));
+ diag.note(format!("...and {} other attempted mutable borrows", count - 10));
}
diag.buffer(&mut self.errors.buffered);
}
diff --git a/compiler/rustc_borrowck/src/location.rs b/compiler/rustc_borrowck/src/location.rs
index 288b7d85b..0e669abfd 100644
--- a/compiler/rustc_borrowck/src/location.rs
+++ b/compiler/rustc_borrowck/src/location.rs
@@ -1,6 +1,6 @@
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
-use rustc_index::vec::{Idx, IndexVec};
+use rustc_index::IndexVec;
use rustc_middle::mir::{BasicBlock, Body, Location};
/// Maps between a MIR Location, which identifies a particular
@@ -50,19 +50,19 @@ impl LocationTable {
}
pub fn all_points(&self) -> impl Iterator<Item = LocationIndex> {
- (0..self.num_points).map(LocationIndex::new)
+ (0..self.num_points).map(LocationIndex::from_usize)
}
pub fn start_index(&self, location: Location) -> LocationIndex {
let Location { block, statement_index } = location;
let start_index = self.statements_before_block[block];
- LocationIndex::new(start_index + statement_index * 2)
+ LocationIndex::from_usize(start_index + statement_index * 2)
}
pub fn mid_index(&self, location: Location) -> LocationIndex {
let Location { block, statement_index } = location;
let start_index = self.statements_before_block[block];
- LocationIndex::new(start_index + statement_index * 2 + 1)
+ LocationIndex::from_usize(start_index + statement_index * 2 + 1)
}
pub fn to_location(&self, index: LocationIndex) -> RichLocation {
diff --git a/compiler/rustc_borrowck/src/member_constraints.rs b/compiler/rustc_borrowck/src/member_constraints.rs
index f637e6a95..842e90080 100644
--- a/compiler/rustc_borrowck/src/member_constraints.rs
+++ b/compiler/rustc_borrowck/src/member_constraints.rs
@@ -2,7 +2,7 @@
#![deny(rustc::diagnostic_outside_of_impl)]
use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::FxIndexMap;
-use rustc_index::vec::{IndexSlice, IndexVec};
+use rustc_index::{IndexSlice, IndexVec};
use rustc_middle::infer::MemberConstraint;
use rustc_middle::ty::{self, Ty};
use rustc_span::Span;
@@ -221,7 +221,7 @@ fn append_list(
) {
let mut p = target_list;
loop {
- let mut r = &mut constraints[p];
+ let r = &mut constraints[p];
match r.next_constraint {
Some(q) => p = q,
None => {
diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs
index 59a3ab318..889acb3ac 100644
--- a/compiler/rustc_borrowck/src/nll.rs
+++ b/compiler/rustc_borrowck/src/nll.rs
@@ -4,11 +4,11 @@
use rustc_data_structures::fx::FxIndexMap;
use rustc_hir::def_id::LocalDefId;
-use rustc_index::vec::IndexSlice;
+use rustc_index::IndexSlice;
use rustc_middle::mir::{create_dump_file, dump_enabled, dump_mir, PassWhere};
use rustc_middle::mir::{
- BasicBlock, Body, ClosureOutlivesSubject, ClosureRegionRequirements, LocalKind, Location,
- Promoted,
+ Body, ClosureOutlivesSubject, ClosureRegionRequirements, LocalKind, Location, Promoted,
+ START_BLOCK,
};
use rustc_middle::ty::{self, OpaqueHiddenType, TyCtxt};
use rustc_span::symbol::sym;
@@ -27,6 +27,7 @@ use rustc_mir_dataflow::ResultsCursor;
use crate::{
borrow_set::BorrowSet,
constraint_generation,
+ consumers::ConsumerOptions,
diagnostics::RegionErrors,
facts::{AllFacts, AllFactsExt, RustcFacts},
invalidation,
@@ -61,7 +62,7 @@ pub(crate) fn replace_regions_in_mir<'tcx>(
body: &mut Body<'tcx>,
promoted: &mut IndexSlice<Promoted, Body<'tcx>>,
) -> UniversalRegions<'tcx> {
- let def = body.source.with_opt_param().as_local().unwrap();
+ let def = body.source.def_id().expect_local();
debug!(?def);
@@ -94,8 +95,8 @@ fn populate_polonius_move_facts(
}
}
- let fn_entry_start = location_table
- .start_index(Location { block: BasicBlock::from_u32(0u32), statement_index: 0 });
+ let fn_entry_start =
+ location_table.start_index(Location { block: START_BLOCK, statement_index: 0 });
// initialized_at
for init in move_data.inits.iter() {
@@ -165,10 +166,14 @@ pub(crate) fn compute_regions<'cx, 'tcx>(
move_data: &MoveData<'tcx>,
borrow_set: &BorrowSet<'tcx>,
upvars: &[Upvar<'tcx>],
- use_polonius: bool,
+ consumer_options: Option<ConsumerOptions>,
) -> NllOutput<'tcx> {
+ let polonius_input = consumer_options.map(|c| c.polonius_input()).unwrap_or_default()
+ || infcx.tcx.sess.opts.unstable_opts.polonius;
+ let polonius_output = consumer_options.map(|c| c.polonius_output()).unwrap_or_default()
+ || infcx.tcx.sess.opts.unstable_opts.polonius;
let mut all_facts =
- (use_polonius || AllFacts::enabled(infcx.tcx)).then_some(AllFacts::default());
+ (polonius_input || AllFacts::enabled(infcx.tcx)).then_some(AllFacts::default());
let universal_regions = Rc::new(universal_regions);
@@ -189,7 +194,7 @@ pub(crate) fn compute_regions<'cx, 'tcx>(
move_data,
elements,
upvars,
- use_polonius,
+ polonius_input,
);
if let Some(all_facts) = &mut all_facts {
@@ -235,7 +240,7 @@ pub(crate) fn compute_regions<'cx, 'tcx>(
// Create the region inference context, taking ownership of the
// region inference data that was contained in `infcx`, and the
// base constraints generated by the type-check.
- let var_origins = infcx.take_region_var_origins();
+ let var_origins = infcx.get_region_var_origins();
let MirTypeckRegionConstraints {
placeholder_indices,
placeholder_index_to_region: _,
@@ -284,7 +289,7 @@ pub(crate) fn compute_regions<'cx, 'tcx>(
all_facts.write_to_dir(dir_path, location_table).unwrap();
}
- if use_polonius {
+ if polonius_output {
let algorithm =
env::var("POLONIUS_ALGORITHM").unwrap_or_else(|_| String::from("Hybrid"));
let algorithm = Algorithm::from_str(&algorithm).unwrap();
@@ -399,7 +404,7 @@ pub(super) fn dump_annotation<'tcx>(
regioncx.annotate(tcx, &mut err);
- err.note(&format!(
+ err.note(format!(
"number of external vids: {}",
closure_region_requirements.num_external_vids
));
@@ -421,7 +426,7 @@ pub(super) fn dump_annotation<'tcx>(
};
if !opaque_type_values.is_empty() {
- err.note(&format!("Inferred opaque type values:\n{:#?}", opaque_type_values));
+ err.note(format!("Inferred opaque type values:\n{:#?}", opaque_type_values));
}
errors.buffer_non_error_diag(err);
@@ -430,7 +435,7 @@ pub(super) fn dump_annotation<'tcx>(
fn for_each_region_constraint<'tcx>(
tcx: TyCtxt<'tcx>,
closure_region_requirements: &ClosureRegionRequirements<'tcx>,
- with_msg: &mut dyn FnMut(&str) -> io::Result<()>,
+ with_msg: &mut dyn FnMut(String) -> io::Result<()>,
) -> io::Result<()> {
for req in &closure_region_requirements.outlives_requirements {
let subject = match req.subject {
@@ -439,7 +444,7 @@ fn for_each_region_constraint<'tcx>(
format!("{:?}", ty.instantiate(tcx, |vid| tcx.mk_re_var(vid)))
}
};
- with_msg(&format!("where {}: {:?}", subject, req.outlived_free_region,))?;
+ with_msg(format!("where {}: {:?}", subject, req.outlived_free_region,))?;
}
Ok(())
}
diff --git a/compiler/rustc_borrowck/src/place_ext.rs b/compiler/rustc_borrowck/src/place_ext.rs
index 85d207b2f..d521d0db2 100644
--- a/compiler/rustc_borrowck/src/place_ext.rs
+++ b/compiler/rustc_borrowck/src/place_ext.rs
@@ -7,7 +7,7 @@ use rustc_middle::mir::{Body, Mutability, Place};
use rustc_middle::ty::{self, TyCtxt};
/// Extension methods for the `Place` type.
-pub(crate) trait PlaceExt<'tcx> {
+pub trait PlaceExt<'tcx> {
/// Returns `true` if we can safely ignore borrows of this place.
/// This is true whenever there is no action that the user can do
/// to the place `self` that would invalidate the borrow. This is true
diff --git a/compiler/rustc_borrowck/src/places_conflict.rs b/compiler/rustc_borrowck/src/places_conflict.rs
index 918fb2d69..25c485b81 100644
--- a/compiler/rustc_borrowck/src/places_conflict.rs
+++ b/compiler/rustc_borrowck/src/places_conflict.rs
@@ -16,7 +16,7 @@ use std::iter;
/// being run in the calling context, the conservative choice is to assume the compared indices
/// are disjoint (and therefore, do not overlap).
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
-pub(crate) enum PlaceConflictBias {
+pub enum PlaceConflictBias {
Overlap,
NoOverlap,
}
@@ -24,7 +24,7 @@ pub(crate) enum PlaceConflictBias {
/// Helper function for checking if places conflict with a mutable borrow and deep access depth.
/// This is used to check for places conflicting outside of the borrow checking code (such as in
/// dataflow).
-pub(crate) fn places_conflict<'tcx>(
+pub fn places_conflict<'tcx>(
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
borrow_place: Place<'tcx>,
diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs
index 729f3dbff..50b246b14 100644
--- a/compiler/rustc_borrowck/src/region_infer/mod.rs
+++ b/compiler/rustc_borrowck/src/region_infer/mod.rs
@@ -7,12 +7,12 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_data_structures::graph::scc::Sccs;
use rustc_errors::Diagnostic;
use rustc_hir::def_id::CRATE_DEF_ID;
-use rustc_index::vec::{IndexSlice, IndexVec};
+use rustc_index::{IndexSlice, IndexVec};
use rustc_infer::infer::outlives::test_type_match;
use rustc_infer::infer::region_constraints::{GenericKind, VarInfos, VerifyBound, VerifyIfEq};
use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin};
use rustc_middle::mir::{
- Body, ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureOutlivesSubjectTy,
+ BasicBlock, Body, ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureOutlivesSubjectTy,
ClosureRegionRequirements, ConstraintCategory, Local, Location, ReturnConstraint,
TerminatorKind,
};
@@ -76,7 +76,7 @@ pub struct RegionInferenceContext<'tcx> {
/// Reverse of the SCC constraint graph -- i.e., an edge `A -> B` exists if
/// `B: A`. This is used to compute the universal regions that are required
/// to outlive a given SCC. Computed lazily.
- rev_scc_graph: Option<Rc<ReverseSccGraph>>,
+ rev_scc_graph: Option<ReverseSccGraph>,
/// The "R0 member of [R1..Rn]" constraints, indexed by SCC.
member_constraints: Rc<MemberConstraintSet<'tcx, ConstraintSccIndex>>,
@@ -585,6 +585,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
self.universal_regions.to_region_vid(r)
}
+ /// Returns an iterator over all the outlives constraints.
+ pub fn outlives_constraints(&self) -> impl Iterator<Item = OutlivesConstraint<'tcx>> + '_ {
+ self.constraints.outlives().iter().copied()
+ }
+
/// Adds annotations for `#[rustc_regions]`; see `UniversalRegions::annotate`.
pub(crate) fn annotate(&self, tcx: TyCtxt<'tcx>, err: &mut Diagnostic) {
self.universal_regions.annotate(tcx, err)
@@ -598,6 +603,20 @@ impl<'tcx> RegionInferenceContext<'tcx> {
self.scc_values.contains(scc, p)
}
+ /// Returns the lowest statement index in `start..=end` which is not contained by `r`.
+ ///
+ /// Panics if called before `solve()` executes.
+ pub(crate) fn first_non_contained_inclusive(
+ &self,
+ r: RegionVid,
+ block: BasicBlock,
+ start: usize,
+ end: usize,
+ ) -> Option<usize> {
+ let scc = self.constraint_sccs.scc(r);
+ self.scc_values.first_non_contained_inclusive(scc, block, start, end)
+ }
+
/// Returns access to the value of `r` for debugging purposes.
pub(crate) fn region_value_str(&self, r: RegionVid) -> String {
let scc = self.constraint_sccs.scc(r);
@@ -698,7 +717,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
#[instrument(skip(self, _body), level = "debug")]
fn propagate_constraints(&mut self, _body: &Body<'tcx>) {
debug!("constraints={:#?}", {
- let mut constraints: Vec<_> = self.constraints.outlives().iter().collect();
+ let mut constraints: Vec<_> = self.outlives_constraints().collect();
constraints.sort_by_key(|c| (c.sup, c.sub));
constraints
.into_iter()
@@ -813,9 +832,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// free region that must outlive the member region `R0` (`UB:
// R0`). Therefore, we need only keep an option `O` if `UB: O`
// for all UB.
- let rev_scc_graph = self.reverse_scc_graph();
+ self.compute_reverse_scc_graph();
let universal_region_relations = &self.universal_region_relations;
- for ub in rev_scc_graph.upper_bounds(scc) {
+ for ub in self.rev_scc_graph.as_ref().unwrap().upper_bounds(scc) {
debug!(?ub);
choice_regions.retain(|&o_r| universal_region_relations.outlives(ub, o_r));
}
diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
index 2b16655cf..7fc89e89a 100644
--- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
+++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
@@ -2,9 +2,10 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_errors::ErrorGuaranteed;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::OpaqueTyOrigin;
+use rustc_infer::infer::InferCtxt;
use rustc_infer::infer::TyCtxtInferExt as _;
-use rustc_infer::infer::{DefiningAnchor, InferCtxt};
use rustc_infer::traits::{Obligation, ObligationCause};
+use rustc_middle::traits::DefiningAnchor;
use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts};
use rustc_middle::ty::visit::TypeVisitableExt;
use rustc_middle::ty::{self, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable};
@@ -152,8 +153,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
let guar = ty.error_reported().err().unwrap_or_else(|| {
prev.report_mismatch(
&OpaqueHiddenType { ty, span: concrete_type.span },
+ opaque_type_key.def_id,
infcx.tcx,
)
+ .emit()
});
prev.ty = infcx.tcx.ty_error(guar);
}
@@ -265,7 +268,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
// Only check this for TAIT. RPIT already supports `tests/ui/impl-trait/nested-return-type2.rs`
// on stable and we'd break that.
- let OpaqueTyOrigin::TyAlias = origin else {
+ let OpaqueTyOrigin::TyAlias { .. } = origin else {
return definition_ty;
};
let def_id = opaque_type_key.def_id;
@@ -360,7 +363,7 @@ fn check_opaque_type_parameter_valid(
// which would error here on all of the `'static` args.
OpaqueTyOrigin::FnReturn(..) | OpaqueTyOrigin::AsyncFn(..) => return Ok(()),
// Check these
- OpaqueTyOrigin::TyAlias => {}
+ OpaqueTyOrigin::TyAlias { .. } => {}
}
let opaque_generics = tcx.generics_of(opaque_type_key.def_id);
let mut seen_params: FxIndexMap<_, Vec<_>> = FxIndexMap::default();
@@ -399,7 +402,7 @@ fn check_opaque_type_parameter_valid(
return Err(tcx
.sess
.struct_span_err(span, "non-defining opaque type use in defining scope")
- .span_note(spans, &format!("{} used multiple times", descr))
+ .span_note(spans, format!("{} used multiple times", descr))
.emit());
}
}
diff --git a/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs b/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs
index 23a59c128..fe56bd54a 100644
--- a/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs
+++ b/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs
@@ -8,7 +8,6 @@ use rustc_data_structures::graph::vec_graph::VecGraph;
use rustc_data_structures::graph::WithSuccessors;
use rustc_middle::ty::RegionVid;
use std::ops::Range;
-use std::rc::Rc;
pub(crate) struct ReverseSccGraph {
graph: VecGraph<ConstraintSccIndex>,
@@ -40,10 +39,10 @@ impl ReverseSccGraph {
}
impl RegionInferenceContext<'_> {
- /// Compute and return the reverse SCC-based constraint graph (lazily).
- pub(super) fn reverse_scc_graph(&mut self) -> Rc<ReverseSccGraph> {
- if let Some(g) = &self.rev_scc_graph {
- return g.clone();
+ /// Compute the reverse SCC-based constraint graph (lazily).
+ pub(super) fn compute_reverse_scc_graph(&mut self) {
+ if self.rev_scc_graph.is_some() {
+ return;
}
let graph = self.constraint_sccs.reverse();
@@ -63,8 +62,6 @@ impl RegionInferenceContext<'_> {
start += group_size;
}
- let rev_graph = Rc::new(ReverseSccGraph { graph, scc_regions, universal_regions });
- self.rev_scc_graph = Some(rev_graph.clone());
- rev_graph
+ self.rev_scc_graph = Some(ReverseSccGraph { graph, scc_regions, universal_regions });
}
}
diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs
index 8132800f1..9290e7479 100644
--- a/compiler/rustc_borrowck/src/region_infer/values.rs
+++ b/compiler/rustc_borrowck/src/region_infer/values.rs
@@ -4,8 +4,8 @@ use rustc_data_structures::fx::FxIndexSet;
use rustc_index::bit_set::SparseBitMatrix;
use rustc_index::interval::IntervalSet;
use rustc_index::interval::SparseIntervalMatrix;
-use rustc_index::vec::Idx;
-use rustc_index::vec::IndexVec;
+use rustc_index::Idx;
+use rustc_index::IndexVec;
use rustc_middle::mir::{BasicBlock, Body, Location};
use rustc_middle::ty::{self, RegionVid};
use std::fmt::Debug;
@@ -159,7 +159,7 @@ impl<N: Idx> LivenessValues<N> {
/// Returns `true` if the region `r` contains the given element.
pub(crate) fn contains(&self, row: N, location: Location) -> bool {
let index = self.elements.point_from_location(location);
- self.points.row(row).map_or(false, |r| r.contains(index))
+ self.points.row(row).is_some_and(|r| r.contains(index))
}
/// Returns an iterator of all the elements contained by the region `r`
@@ -283,6 +283,22 @@ impl<N: Idx> RegionValues<N> {
elem.contained_in_row(self, r)
}
+ /// Returns the lowest statement index in `start..=end` which is not contained by `r`.
+ pub(crate) fn first_non_contained_inclusive(
+ &self,
+ r: N,
+ block: BasicBlock,
+ start: usize,
+ end: usize,
+ ) -> Option<usize> {
+ let row = self.points.row(r)?;
+ let block = self.elements.entry_point(block);
+ let start = block.plus(start);
+ let end = block.plus(end);
+ let first_unset = row.first_unset_in(start..=end)?;
+ Some(first_unset.index() - block.index())
+ }
+
/// `self[to] |= values[from]`, essentially: that is, take all the
/// elements for the region `from` from `values` and add them to
/// the region `to` in `self`.
diff --git a/compiler/rustc_borrowck/src/renumber.rs b/compiler/rustc_borrowck/src/renumber.rs
index 94ce29dfe..4389d2b60 100644
--- a/compiler/rustc_borrowck/src/renumber.rs
+++ b/compiler/rustc_borrowck/src/renumber.rs
@@ -1,7 +1,7 @@
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
use crate::BorrowckInferCtxt;
-use rustc_index::vec::IndexSlice;
+use rustc_index::IndexSlice;
use rustc_infer::infer::NllRegionVariableOrigin;
use rustc_middle::mir::visit::{MutVisitor, TyContext};
use rustc_middle::mir::Constant;
@@ -109,6 +109,14 @@ impl<'a, 'tcx> MutVisitor<'tcx> for RegionRenumberer<'a, 'tcx> {
}
#[instrument(skip(self), level = "debug")]
+ fn visit_ty_const(&mut self, ct: &mut ty::Const<'tcx>, location: Location) {
+ let old_ct = *ct;
+ *ct = self.renumber_regions(old_ct, || RegionCtxt::Location(location));
+
+ debug!(?ct);
+ }
+
+ #[instrument(skip(self), level = "debug")]
fn visit_constant(&mut self, constant: &mut Constant<'tcx>, location: Location) {
let literal = constant.literal;
constant.literal = self.renumber_regions(literal, || RegionCtxt::Location(location));
diff --git a/compiler/rustc_borrowck/src/session_diagnostics.rs b/compiler/rustc_borrowck/src/session_diagnostics.rs
index a36789290..fceae5bb3 100644
--- a/compiler/rustc_borrowck/src/session_diagnostics.rs
+++ b/compiler/rustc_borrowck/src/session_diagnostics.rs
@@ -184,7 +184,7 @@ pub(crate) enum CaptureVarPathUseCause {
#[derive(Subdiagnostic)]
pub(crate) enum CaptureVarKind {
#[label(borrowck_capture_immute)]
- Immute {
+ Immut {
#[primary_span]
kind_span: Span,
},
@@ -204,16 +204,80 @@ pub(crate) enum CaptureVarKind {
pub(crate) enum CaptureVarCause {
#[label(borrowck_var_borrow_by_use_place_in_generator)]
BorrowUsePlaceGenerator {
+ is_single_var: bool,
place: String,
#[primary_span]
var_span: Span,
},
#[label(borrowck_var_borrow_by_use_place_in_closure)]
BorrowUsePlaceClosure {
+ is_single_var: bool,
place: String,
#[primary_span]
var_span: Span,
},
+ #[label(borrowck_var_borrow_by_use_in_generator)]
+ BorrowUseInGenerator {
+ #[primary_span]
+ var_span: Span,
+ },
+ #[label(borrowck_var_borrow_by_use_in_closure)]
+ BorrowUseInClosure {
+ #[primary_span]
+ var_span: Span,
+ },
+ #[label(borrowck_var_move_by_use_in_generator)]
+ MoveUseInGenerator {
+ #[primary_span]
+ var_span: Span,
+ },
+ #[label(borrowck_var_move_by_use_in_closure)]
+ MoveUseInClosure {
+ #[primary_span]
+ var_span: Span,
+ },
+ #[label(borrowck_var_first_borrow_by_use_place_in_generator)]
+ FirstBorrowUsePlaceGenerator {
+ place: String,
+ #[primary_span]
+ var_span: Span,
+ },
+ #[label(borrowck_var_first_borrow_by_use_place_in_closure)]
+ FirstBorrowUsePlaceClosure {
+ place: String,
+ #[primary_span]
+ var_span: Span,
+ },
+ #[label(borrowck_var_second_borrow_by_use_place_in_generator)]
+ SecondBorrowUsePlaceGenerator {
+ place: String,
+ #[primary_span]
+ var_span: Span,
+ },
+ #[label(borrowck_var_second_borrow_by_use_place_in_closure)]
+ SecondBorrowUsePlaceClosure {
+ place: String,
+ #[primary_span]
+ var_span: Span,
+ },
+ #[label(borrowck_var_mutable_borrow_by_use_place_in_closure)]
+ MutableBorrowUsePlaceClosure {
+ place: String,
+ #[primary_span]
+ var_span: Span,
+ },
+ #[label(borrowck_partial_var_move_by_use_in_generator)]
+ PartialMoveUseInGenerator {
+ #[primary_span]
+ var_span: Span,
+ is_partial: bool,
+ },
+ #[label(borrowck_partial_var_move_by_use_in_closure)]
+ PartialMoveUseInClosure {
+ #[primary_span]
+ var_span: Span,
+ is_partial: bool,
+ },
}
#[derive(Diagnostic)]
@@ -239,3 +303,152 @@ pub(crate) struct NonGenericOpaqueTypeParam<'a, 'tcx> {
#[label]
pub param_span: Span,
}
+
+#[derive(Subdiagnostic)]
+pub(crate) enum CaptureReasonLabel<'a> {
+ #[label(borrowck_moved_due_to_call)]
+ Call {
+ #[primary_span]
+ fn_call_span: Span,
+ place_name: &'a str,
+ is_partial: bool,
+ is_loop_message: bool,
+ },
+ #[label(borrowck_moved_due_to_usage_in_operator)]
+ OperatorUse {
+ #[primary_span]
+ fn_call_span: Span,
+ place_name: &'a str,
+ is_partial: bool,
+ is_loop_message: bool,
+ },
+ #[label(borrowck_moved_due_to_implicit_into_iter_call)]
+ ImplicitCall {
+ #[primary_span]
+ fn_call_span: Span,
+ place_name: &'a str,
+ is_partial: bool,
+ is_loop_message: bool,
+ },
+ #[label(borrowck_moved_due_to_method_call)]
+ MethodCall {
+ #[primary_span]
+ fn_call_span: Span,
+ place_name: &'a str,
+ is_partial: bool,
+ is_loop_message: bool,
+ },
+ #[label(borrowck_moved_due_to_await)]
+ Await {
+ #[primary_span]
+ fn_call_span: Span,
+ place_name: &'a str,
+ is_partial: bool,
+ is_loop_message: bool,
+ },
+ #[label(borrowck_value_moved_here)]
+ MovedHere {
+ #[primary_span]
+ move_span: Span,
+ is_partial: bool,
+ is_move_msg: bool,
+ is_loop_message: bool,
+ },
+ #[label(borrowck_consider_borrow_type_contents)]
+ BorrowContent {
+ #[primary_span]
+ var_span: Span,
+ },
+}
+
+#[derive(Subdiagnostic)]
+pub(crate) enum CaptureReasonNote {
+ #[note(borrowck_moved_a_fn_once_in_call)]
+ FnOnceMoveInCall {
+ #[primary_span]
+ var_span: Span,
+ },
+ #[note(borrowck_calling_operator_moves_lhs)]
+ LhsMoveByOperator {
+ #[primary_span]
+ span: Span,
+ },
+ #[note(borrowck_func_take_self_moved_place)]
+ FuncTakeSelf {
+ func: String,
+ place_name: String,
+ #[primary_span]
+ span: Span,
+ },
+}
+
+#[derive(Subdiagnostic)]
+pub(crate) enum CaptureReasonSuggest<'tcx> {
+ #[suggestion(
+ borrowck_suggest_iterate_over_slice,
+ applicability = "maybe-incorrect",
+ code = "&",
+ style = "verbose"
+ )]
+ IterateSlice {
+ ty: Ty<'tcx>,
+ #[primary_span]
+ span: Span,
+ },
+ #[suggestion(
+ borrowck_suggest_create_freash_reborrow,
+ applicability = "maybe-incorrect",
+ code = "as_mut().",
+ style = "verbose"
+ )]
+ FreshReborrow {
+ #[primary_span]
+ span: Span,
+ },
+}
+
+#[derive(Subdiagnostic)]
+pub(crate) enum CaptureArgLabel {
+ #[label(borrowck_value_capture_here)]
+ Capture {
+ is_within: bool,
+ #[primary_span]
+ args_span: Span,
+ },
+ #[label(borrowck_move_out_place_here)]
+ MoveOutPlace {
+ place: String,
+ #[primary_span]
+ args_span: Span,
+ },
+}
+
+#[derive(Subdiagnostic)]
+pub(crate) enum OnClosureNote<'a> {
+ #[note(borrowck_closure_invoked_twice)]
+ InvokedTwice {
+ place_name: &'a str,
+ #[primary_span]
+ span: Span,
+ },
+ #[note(borrowck_closure_moved_twice)]
+ MovedTwice {
+ place_name: &'a str,
+ #[primary_span]
+ span: Span,
+ },
+}
+
+#[derive(Subdiagnostic)]
+pub(crate) enum TypeNoCopy<'a, 'tcx> {
+ #[label(borrowck_ty_no_impl_copy)]
+ Label {
+ is_partial_move: bool,
+ ty: Ty<'tcx>,
+ place: &'a str,
+ #[primary_span]
+ span: Span,
+ },
+ #[note(borrowck_ty_no_impl_copy)]
+ Note { is_partial_move: bool, ty: Ty<'tcx>, place: &'a str },
+}
diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs
index b27d5d205..f527eee7b 100644
--- a/compiler/rustc_borrowck/src/type_check/canonical.rs
+++ b/compiler/rustc_borrowck/src/type_check/canonical.rs
@@ -1,13 +1,13 @@
use std::fmt;
-use rustc_infer::infer::{canonical::Canonical, InferOk};
+use rustc_errors::ErrorGuaranteed;
+use rustc_infer::infer::canonical::Canonical;
use rustc_middle::mir::ConstraintCategory;
use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable};
use rustc_span::def_id::DefId;
use rustc_span::Span;
use rustc_trait_selection::traits::query::type_op::{self, TypeOpOutput};
-use rustc_trait_selection::traits::query::{Fallible, NoSolution};
-use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt};
+use rustc_trait_selection::traits::ObligationCause;
use crate::diagnostics::{ToUniverseInfo, UniverseInfo};
@@ -30,14 +30,15 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
locations: Locations,
category: ConstraintCategory<'tcx>,
op: Op,
- ) -> Fallible<R>
+ ) -> Result<R, ErrorGuaranteed>
where
Op: type_op::TypeOp<'tcx, Output = R>,
Op::ErrorInfo: ToUniverseInfo<'tcx>,
{
let old_universe = self.infcx.universe();
- let TypeOpOutput { output, constraints, error_info } = op.fully_perform(self.infcx)?;
+ let TypeOpOutput { output, constraints, error_info } =
+ op.fully_perform(self.infcx, locations.span(self.body))?;
debug!(?output, ?constraints);
@@ -135,14 +136,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
) {
let param_env = self.param_env;
let predicate = predicate.to_predicate(self.tcx());
- self.fully_perform_op(
+ let _: Result<_, ErrorGuaranteed> = self.fully_perform_op(
locations,
category,
param_env.and(type_op::prove_predicate::ProvePredicate::new(predicate)),
- )
- .unwrap_or_else(|NoSolution| {
- span_mirbug!(self, NoSolution, "could not prove {:?}", predicate);
- })
+ );
}
pub(super) fn normalize<T>(&mut self, value: T, location: impl NormalizeLocation) -> T
@@ -163,15 +161,12 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
T: type_op::normalize::Normalizable<'tcx> + fmt::Display + Copy + 'tcx,
{
let param_env = self.param_env;
- self.fully_perform_op(
+ let result: Result<_, ErrorGuaranteed> = self.fully_perform_op(
location.to_locations(),
category,
param_env.and(type_op::normalize::Normalize::new(value)),
- )
- .unwrap_or_else(|NoSolution| {
- span_mirbug!(self, NoSolution, "failed to normalize `{:?}`", value);
- value
- })
+ );
+ result.unwrap_or(value)
}
#[instrument(skip(self), level = "debug")]
@@ -181,18 +176,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
user_ty: ty::UserType<'tcx>,
span: Span,
) {
- self.fully_perform_op(
+ let _: Result<_, ErrorGuaranteed> = self.fully_perform_op(
Locations::All(span),
ConstraintCategory::Boring,
self.param_env.and(type_op::ascribe_user_type::AscribeUserType::new(mir_ty, user_ty)),
- )
- .unwrap_or_else(|err| {
- span_mirbug!(
- self,
- span,
- "ascribe_user_type `{mir_ty:?}=={user_ty:?}` failed with `{err:?}`",
- );
- });
+ );
}
/// *Incorrectly* skips the WF checks we normally do in `ascribe_user_type`.
@@ -219,27 +207,17 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
let cause = ObligationCause::dummy_with_span(span);
let param_env = self.param_env;
- let op = |infcx: &'_ _| {
- let ocx = ObligationCtxt::new_in_snapshot(infcx);
- let user_ty = ocx.normalize(&cause, param_env, user_ty);
- ocx.eq(&cause, param_env, user_ty, mir_ty)?;
- if !ocx.select_all_or_error().is_empty() {
- return Err(NoSolution);
- }
- Ok(InferOk { value: (), obligations: vec![] })
- };
-
- self.fully_perform_op(
+ let _: Result<_, ErrorGuaranteed> = self.fully_perform_op(
Locations::All(span),
ConstraintCategory::Boring,
- type_op::custom::CustomTypeOp::new(op, || "ascribe_user_type_skip_wf".to_string()),
- )
- .unwrap_or_else(|err| {
- span_mirbug!(
- self,
- span,
- "ascribe_user_type_skip_wf `{mir_ty:?}=={user_ty:?}` failed with `{err:?}`",
- );
- });
+ type_op::custom::CustomTypeOp::new(
+ |ocx| {
+ let user_ty = ocx.normalize(&cause, param_env, user_ty);
+ ocx.eq(&cause, param_env, user_ty, mir_ty)?;
+ Ok(())
+ },
+ "ascribe_user_type_skip_wf",
+ ),
+ );
}
}
diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
index 4004966c4..c8ec1257d 100644
--- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
+++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
@@ -8,7 +8,7 @@ use rustc_infer::infer::InferCtxt;
use rustc_middle::mir::ConstraintCategory;
use rustc_middle::traits::query::OutlivesBound;
use rustc_middle::ty::{self, RegionVid, Ty};
-use rustc_span::Span;
+use rustc_span::{Span, DUMMY_SP};
use rustc_trait_selection::traits::query::type_op::{self, TypeOp};
use std::rc::Rc;
use type_op::TypeOpOutput;
@@ -243,18 +243,11 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
let TypeOpOutput { output: norm_ty, constraints: constraints_normalize, .. } = self
.param_env
.and(type_op::normalize::Normalize::new(ty))
- .fully_perform(self.infcx)
- .unwrap_or_else(|_| {
- let guar = self
- .infcx
- .tcx
- .sess
- .delay_span_bug(span, &format!("failed to normalize {:?}", ty));
- TypeOpOutput {
- output: self.infcx.tcx.ty_error(guar),
- constraints: None,
- error_info: None,
- }
+ .fully_perform(self.infcx, span)
+ .unwrap_or_else(|guar| TypeOpOutput {
+ output: self.infcx.tcx.ty_error(guar),
+ constraints: None,
+ error_info: None,
});
if let Some(c) = constraints_normalize {
constraints.push(c)
@@ -324,7 +317,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
let TypeOpOutput { output: bounds, constraints, .. } = self
.param_env
.and(type_op::implied_outlives_bounds::ImpliedOutlivesBounds { ty })
- .fully_perform(self.infcx)
+ .fully_perform(self.infcx, DUMMY_SP)
.unwrap_or_else(|_| bug!("failed to compute implied bounds {:?}", ty));
debug!(?bounds, ?constraints);
self.add_outlives_bounds(bounds);
diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs
index 17e702eb8..a06d4bcc6 100644
--- a/compiler/rustc_borrowck/src/type_check/input_output.rs
+++ b/compiler/rustc_borrowck/src/type_check/input_output.rs
@@ -7,7 +7,6 @@
//! `RETURN_PLACE` the MIR arguments) are always fully normalized (and
//! contain revealed `impl Trait` values).
-use rustc_index::vec::Idx;
use rustc_infer::infer::LateBoundRegionConversionTime;
use rustc_middle::mir::*;
use rustc_middle::ty::{self, Ty};
@@ -83,7 +82,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
}
// In MIR, argument N is stored in local N+1.
- let local = Local::new(argument_index + 1);
+ let local = Local::from_usize(argument_index + 1);
let mir_input_ty = body.local_decls[local].ty;
@@ -107,7 +106,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
if body.yield_ty().is_some() != universal_regions.yield_ty.is_some() {
self.tcx().sess.delay_span_bug(
body.span,
- &format!(
+ format!(
"Expected body to have yield_ty ({:?}) iff we have a UR yield_ty ({:?})",
body.yield_ty(),
universal_regions.yield_ty,
diff --git a/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs b/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs
index 2c387edfe..a9ca94567 100644
--- a/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs
+++ b/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs
@@ -1,5 +1,5 @@
use rustc_data_structures::vec_linked_list as vll;
-use rustc_index::vec::IndexVec;
+use rustc_index::IndexVec;
use rustc_middle::mir::visit::{PlaceContext, Visitor};
use rustc_middle::mir::{Body, Local, Location};
diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
index 9731b10aa..eb02604b9 100644
--- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
+++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
@@ -3,8 +3,9 @@ use rustc_index::bit_set::HybridBitSet;
use rustc_index::interval::IntervalSet;
use rustc_infer::infer::canonical::QueryRegionConstraints;
use rustc_middle::mir::{BasicBlock, Body, ConstraintCategory, Local, Location};
+use rustc_middle::traits::query::DropckOutlivesResult;
use rustc_middle::ty::{Ty, TyCtxt, TypeVisitable, TypeVisitableExt};
-use rustc_trait_selection::traits::query::dropck_outlives::DropckOutlivesResult;
+use rustc_span::DUMMY_SP;
use rustc_trait_selection::traits::query::type_op::outlives::DropckOutlives;
use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};
use std::rc::Rc;
@@ -568,10 +569,15 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
) -> DropData<'tcx> {
debug!("compute_drop_data(dropped_ty={:?})", dropped_ty,);
- let param_env = typeck.param_env;
- let TypeOpOutput { output, constraints, .. } =
- param_env.and(DropckOutlives::new(dropped_ty)).fully_perform(typeck.infcx).unwrap();
-
- DropData { dropck_result: output, region_constraint_data: constraints }
+ match typeck
+ .param_env
+ .and(DropckOutlives::new(dropped_ty))
+ .fully_perform(typeck.infcx, DUMMY_SP)
+ {
+ Ok(TypeOpOutput { output, constraints, .. }) => {
+ DropData { dropck_result: output, region_constraint_data: constraints }
+ }
+ Err(_) => DropData { dropck_result: Default::default(), region_constraint_data: None },
+ }
}
}
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 2f10e30be..dc5121e1a 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -10,22 +10,24 @@ use either::Either;
use hir::OpaqueTyOrigin;
use rustc_data_structures::frozen::Frozen;
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
+use rustc_errors::ErrorGuaranteed;
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::lang_items::LangItem;
-use rustc_index::vec::{IndexSlice, IndexVec};
+use rustc_index::{IndexSlice, IndexVec};
use rustc_infer::infer::canonical::QueryRegionConstraints;
use rustc_infer::infer::outlives::env::RegionBoundPairs;
use rustc_infer::infer::region_constraints::RegionConstraintData;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::infer::{
- InferCtxt, InferOk, LateBoundRegion, LateBoundRegionConversionTime, NllRegionVariableOrigin,
+ InferCtxt, LateBoundRegion, LateBoundRegionConversionTime, NllRegionVariableOrigin,
};
use rustc_middle::mir::tcx::PlaceTy;
use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::AssertKind;
use rustc_middle::mir::*;
+use rustc_middle::traits::query::NoSolution;
use rustc_middle::ty::adjustment::PointerCast;
use rustc_middle::ty::cast::CastTy;
use rustc_middle::ty::subst::{SubstsRef, UserSubsts};
@@ -41,13 +43,14 @@ use rustc_target::abi::{FieldIdx, FIRST_VARIANT};
use rustc_trait_selection::traits::query::type_op::custom::scrape_region_constraints;
use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp;
use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};
-use rustc_trait_selection::traits::query::Fallible;
+
use rustc_trait_selection::traits::PredicateObligation;
use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
use rustc_mir_dataflow::move_paths::MoveData;
use rustc_mir_dataflow::ResultsCursor;
+use crate::renumber::RegionCtxt;
use crate::session_diagnostics::MoveUnsized;
use crate::{
borrow_set::BorrowSet,
@@ -71,7 +74,7 @@ macro_rules! span_mirbug {
$crate::type_check::mirbug(
$context.tcx(),
$context.last_span,
- &format!(
+ format!(
"broken MIR in {:?} ({:?}): {}",
$context.body().source.def_id(),
$elem,
@@ -183,17 +186,19 @@ pub(crate) fn type_check<'mir, 'tcx>(
&mut borrowck_context,
);
- let errors_reported = {
- let mut verifier = TypeVerifier::new(&mut checker, promoted);
- verifier.visit_body(&body);
- verifier.errors_reported
- };
-
- if !errors_reported {
- // if verifier failed, don't do further checks to avoid ICEs
- checker.typeck_mir(body);
+ // FIXME(-Ztrait-solver=next): A bit dubious that we're only registering
+ // predefined opaques in the typeck root.
+ // FIXME(-Ztrait-solver=next): This is also totally wrong for TAITs, since
+ // the HIR typeck map defining usages back to their definition params,
+ // they won't actually match up with the usages in this body...
+ if infcx.tcx.trait_solver_next() && !infcx.tcx.is_typeck_child(body.source.def_id()) {
+ checker.register_predefined_opaques_in_new_solver();
}
+ let mut verifier = TypeVerifier::new(&mut checker, promoted);
+ verifier.visit_body(&body);
+
+ checker.typeck_mir(body);
checker.equate_inputs_and_outputs(&body, universal_regions, &normalized_inputs_and_output);
checker.check_signature_annotation(&body);
@@ -213,30 +218,28 @@ pub(crate) fn type_check<'mir, 'tcx>(
let opaque_type_values = opaque_type_values
.into_iter()
.map(|(opaque_type_key, decl)| {
- checker
- .fully_perform_op(
- Locations::All(body.span),
- ConstraintCategory::OpaqueType,
- CustomTypeOp::new(
- |infcx| {
- infcx.register_member_constraints(
- param_env,
- opaque_type_key,
- decl.hidden_type.ty,
- decl.hidden_type.span,
- );
- Ok(InferOk { value: (), obligations: vec![] })
- },
- || "opaque_type_map".to_string(),
- ),
- )
- .unwrap();
+ let _: Result<_, ErrorGuaranteed> = checker.fully_perform_op(
+ Locations::All(body.span),
+ ConstraintCategory::OpaqueType,
+ CustomTypeOp::new(
+ |ocx| {
+ ocx.infcx.register_member_constraints(
+ param_env,
+ opaque_type_key,
+ decl.hidden_type.ty,
+ decl.hidden_type.span,
+ );
+ Ok(())
+ },
+ "opaque_type_map",
+ ),
+ );
let mut hidden_type = infcx.resolve_vars_if_possible(decl.hidden_type);
trace!("finalized opaque type {:?} to {:#?}", opaque_type_key, hidden_type.ty.kind());
if hidden_type.has_non_region_infer() {
let reported = infcx.tcx.sess.delay_span_bug(
decl.hidden_type.span,
- &format!("could not resolve {:#?}", hidden_type.ty.kind()),
+ format!("could not resolve {:#?}", hidden_type.ty.kind()),
);
hidden_type.ty = infcx.tcx.ty_error(reported);
}
@@ -274,7 +277,7 @@ fn translate_outlives_facts(typeck: &mut TypeChecker<'_, '_>) {
}
#[track_caller]
-fn mirbug(tcx: TyCtxt<'_>, span: Span, msg: &str) {
+fn mirbug(tcx: TyCtxt<'_>, span: Span, msg: String) {
// We sometimes see MIR failures (notably predicate failures) due to
// the fact that we check rvalue sized predicates here. So use `delay_span_bug`
// to avoid reporting bugs in those cases.
@@ -294,7 +297,6 @@ struct TypeVerifier<'a, 'b, 'tcx> {
cx: &'a mut TypeChecker<'b, 'tcx>,
promoted: &'b IndexSlice<Promoted, Body<'tcx>>,
last_span: Span,
- errors_reported: bool,
}
impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
@@ -383,18 +385,16 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
};
};
- if !self.errors_reported {
- let promoted_body = &self.promoted[promoted];
- self.sanitize_promoted(promoted_body, location);
+ let promoted_body = &self.promoted[promoted];
+ self.sanitize_promoted(promoted_body, location);
- let promoted_ty = promoted_body.return_ty();
- check_err(self, promoted_body, ty, promoted_ty);
- }
+ let promoted_ty = promoted_body.return_ty();
+ check_err(self, promoted_body, ty, promoted_ty);
} else {
self.cx.ascribe_user_type(
constant.literal.ty(),
UserType::TypeOf(
- uv.def.did,
+ uv.def,
UserSubsts { substs: uv.substs, user_self_ty: None },
),
locations.span(&self.cx.body),
@@ -483,9 +483,6 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
for local_decl in &body.local_decls {
self.sanitize_type(local_decl, local_decl.ty);
}
- if self.errors_reported {
- return;
- }
self.super_body(body);
}
}
@@ -495,7 +492,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
cx: &'a mut TypeChecker<'b, 'tcx>,
promoted: &'b IndexSlice<Promoted, Body<'tcx>>,
) -> Self {
- TypeVerifier { promoted, last_span: cx.body.span, cx, errors_reported: false }
+ TypeVerifier { promoted, last_span: cx.body.span, cx }
}
fn body(&self) -> &Body<'tcx> {
@@ -529,7 +526,6 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
for elem in place.projection.iter() {
if place_ty.variant_index.is_none() {
if let Err(guar) = place_ty.ty.error_reported() {
- assert!(self.errors_reported);
return PlaceTy::from_ty(self.tcx().ty_error(guar));
}
}
@@ -538,7 +534,8 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
if let PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) = context {
let tcx = self.tcx();
- let trait_ref = tcx.at(self.last_span).mk_trait_ref(LangItem::Copy, [place_ty.ty]);
+ let trait_ref =
+ ty::TraitRef::from_lang_item(tcx, LangItem::Copy, self.last_span, [place_ty.ty]);
// To have a `Copy` operand, the type `T` of the
// value must be `Copy`. Note that we prove that `T: Copy`,
@@ -592,10 +589,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
self.visit_body(&promoted_body);
- if !self.errors_reported {
- // if verifier failed, don't do further checks to avoid ICEs
- self.cx.typeck_mir(promoted_body);
- }
+ self.cx.typeck_mir(promoted_body);
self.cx.body = parent_body;
// Merge the outlives constraints back in, at the given location.
@@ -761,7 +755,6 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
}
fn error(&mut self) -> Ty<'tcx> {
- self.errors_reported = true;
self.tcx().ty_error_misc()
}
@@ -771,15 +764,12 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
match context {
PlaceContext::MutatingUse(_) => ty::Invariant,
- PlaceContext::NonUse(StorageDead | StorageLive | PlaceMention | VarDebugInfo) => {
- ty::Invariant
- }
+ PlaceContext::NonUse(StorageDead | StorageLive | VarDebugInfo) => ty::Invariant,
PlaceContext::NonMutatingUse(
- Inspect | Copy | Move | SharedBorrow | ShallowBorrow | AddressOf | Projection
- ) => {
- ty::Covariant
- },
- PlaceContext::NonUse(AscribeUserTy) => ty::Covariant,
+ Inspect | Copy | Move | PlaceMention | SharedBorrow | ShallowBorrow | AddressOf
+ | Projection,
+ ) => ty::Covariant,
+ PlaceContext::NonUse(AscribeUserTy(variance)) => variance,
}
}
@@ -1043,6 +1033,57 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
checker
}
+ pub(super) fn register_predefined_opaques_in_new_solver(&mut self) {
+ // OK to use the identity substitutions for each opaque type key, since
+ // we remap opaques from HIR typeck back to their definition params.
+ let opaques: Vec<_> = self
+ .infcx
+ .tcx
+ .typeck(self.body.source.def_id().expect_local())
+ .concrete_opaque_types
+ .iter()
+ .map(|(&def_id, &hidden_ty)| {
+ let substs = ty::InternalSubsts::identity_for_item(self.infcx.tcx, def_id);
+ (ty::OpaqueTypeKey { def_id, substs }, hidden_ty)
+ })
+ .collect();
+
+ let renumbered_opaques = self.infcx.tcx.fold_regions(opaques, |_, _| {
+ self.infcx.next_nll_region_var(
+ NllRegionVariableOrigin::Existential { from_forall: false },
+ || RegionCtxt::Unknown,
+ )
+ });
+
+ let param_env = self.param_env;
+ let result = self.fully_perform_op(
+ Locations::All(self.body.span),
+ ConstraintCategory::OpaqueType,
+ CustomTypeOp::new(
+ |ocx| {
+ for (key, hidden_ty) in renumbered_opaques {
+ ocx.register_infer_ok_obligations(
+ ocx.infcx.register_hidden_type_in_new_solver(
+ key,
+ param_env,
+ hidden_ty.ty,
+ )?,
+ );
+ }
+ Ok(())
+ },
+ "register pre-defined opaques",
+ ),
+ );
+
+ if result.is_err() {
+ self.infcx.tcx.sess.delay_span_bug(
+ self.body.span,
+ "failed re-defining predefined opaques in mir typeck",
+ );
+ }
+ }
+
fn body(&self) -> &Body<'tcx> {
self.body
}
@@ -1093,7 +1134,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
sup: Ty<'tcx>,
locations: Locations,
category: ConstraintCategory<'tcx>,
- ) -> Fallible<()> {
+ ) -> Result<(), NoSolution> {
// Use this order of parameters because the sup type is usually the
// "expected" type in diagnostics.
self.relate_types(sup, ty::Variance::Contravariant, sub, locations, category)
@@ -1106,7 +1147,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
found: Ty<'tcx>,
locations: Locations,
category: ConstraintCategory<'tcx>,
- ) -> Fallible<()> {
+ ) -> Result<(), NoSolution> {
self.relate_types(expected, ty::Variance::Invariant, found, locations, category)
}
@@ -1118,7 +1159,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
user_ty: &UserTypeProjection,
locations: Locations,
category: ConstraintCategory<'tcx>,
- ) -> Fallible<()> {
+ ) -> Result<(), NoSolution> {
let annotated_type = self.user_type_annotations[user_ty.base].inferred_ty;
trace!(?annotated_type);
let mut curr_projected_ty = PlaceTy::from_ty(annotated_type);
@@ -1238,8 +1279,12 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
self.check_rvalue(body, rv, location);
if !self.unsized_feature_enabled() {
- let trait_ref =
- tcx.at(self.last_span).mk_trait_ref(LangItem::Sized, [place_ty]);
+ let trait_ref = ty::TraitRef::from_lang_item(
+ tcx,
+ LangItem::Sized,
+ self.last_span,
+ [place_ty],
+ );
self.prove_trait_ref(
trait_ref,
location.to_locations(),
@@ -1405,7 +1450,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
span_mirbug!(self, term, "bad Assert ({:?}, not bool", cond_ty);
}
- if let AssertKind::BoundsCheck { len, index } = msg {
+ if let AssertKind::BoundsCheck { len, index } = &**msg {
if len.ty(body, tcx) != tcx.types.usize {
span_mirbug!(self, len, "bounds-check length non-usize {:?}", len)
}
@@ -1767,7 +1812,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
if let Some(uv) = maybe_uneval {
if uv.promoted.is_none() {
let tcx = self.tcx();
- let def_id = uv.def.def_id_for_type_of();
+ let def_id = uv.def;
if tcx.def_kind(def_id) == DefKind::InlineConst {
let def_id = def_id.expect_local();
let predicates =
@@ -1799,6 +1844,13 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
Rvalue::Repeat(operand, len) => {
self.check_operand(operand, location);
+ let array_ty = rvalue.ty(body.local_decls(), tcx);
+ self.prove_predicate(
+ ty::PredicateKind::WellFormed(array_ty.into()),
+ Locations::Single(location),
+ ConstraintCategory::Boring,
+ );
+
// If the length cannot be evaluated we must assume that the length can be larger
// than 1.
// If the length is larger than 1, the repeat expression will need to copy the
@@ -1811,7 +1863,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
Operand::Move(place) => {
// Make sure that repeated elements implement `Copy`.
let ty = place.ty(body, tcx).ty;
- let trait_ref = tcx.at(span).mk_trait_ref(LangItem::Copy, [ty]);
+ let trait_ref =
+ ty::TraitRef::from_lang_item(tcx, LangItem::Copy, span, [ty]);
self.prove_trait_ref(
trait_ref,
@@ -1824,7 +1877,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
}
&Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, ty) => {
- let trait_ref = tcx.at(span).mk_trait_ref(LangItem::Sized, [ty]);
+ let trait_ref = ty::TraitRef::from_lang_item(tcx, LangItem::Sized, span, [ty]);
self.prove_trait_ref(
trait_ref,
@@ -1836,7 +1889,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
Rvalue::ShallowInitBox(operand, ty) => {
self.check_operand(operand, location);
- let trait_ref = tcx.at(span).mk_trait_ref(LangItem::Sized, [*ty]);
+ let trait_ref = ty::TraitRef::from_lang_item(tcx, LangItem::Sized, span, [*ty]);
self.prove_trait_ref(
trait_ref,
@@ -1933,9 +1986,12 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
CastKind::Pointer(PointerCast::Unsize) => {
let &ty = ty;
- let trait_ref = tcx
- .at(span)
- .mk_trait_ref(LangItem::CoerceUnsized, [op.ty(body, tcx), ty]);
+ let trait_ref = ty::TraitRef::from_lang_item(
+ tcx,
+ LangItem::CoerceUnsized,
+ span,
+ [op.ty(body, tcx), ty],
+ );
self.prove_trait_ref(
trait_ref,
@@ -2307,7 +2363,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
Rvalue::AddressOf(..)
| Rvalue::ThreadLocalRef(..)
| Rvalue::Len(..)
- | Rvalue::Discriminant(..) => {}
+ | Rvalue::Discriminant(..)
+ | Rvalue::NullaryOp(NullOp::OffsetOf(..), _) => {}
}
}
@@ -2698,10 +2755,20 @@ impl<'tcx> TypeOp<'tcx> for InstantiateOpaqueType<'tcx> {
/// constraints in our `InferCtxt`
type ErrorInfo = InstantiateOpaqueType<'tcx>;
- fn fully_perform(mut self, infcx: &InferCtxt<'tcx>) -> Fallible<TypeOpOutput<'tcx, Self>> {
- let (mut output, region_constraints) = scrape_region_constraints(infcx, || {
- Ok(InferOk { value: (), obligations: self.obligations.clone() })
- })?;
+ fn fully_perform(
+ mut self,
+ infcx: &InferCtxt<'tcx>,
+ span: Span,
+ ) -> Result<TypeOpOutput<'tcx, Self>, ErrorGuaranteed> {
+ let (mut output, region_constraints) = scrape_region_constraints(
+ infcx,
+ |ocx| {
+ ocx.register_obligations(self.obligations.clone());
+ Ok(())
+ },
+ "InstantiateOpaqueType",
+ span,
+ )?;
self.region_constraints = Some(region_constraints);
output.error_info = Some(self);
Ok(output)
diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs
index 7e6d17ec3..8c4bfb2c6 100644
--- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs
+++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs
@@ -1,12 +1,13 @@
+use rustc_errors::ErrorGuaranteed;
use rustc_infer::infer::nll_relate::{TypeRelating, TypeRelatingDelegate};
use rustc_infer::infer::NllRegionVariableOrigin;
use rustc_infer::traits::PredicateObligations;
use rustc_middle::mir::ConstraintCategory;
+use rustc_middle::traits::query::NoSolution;
use rustc_middle::ty::relate::TypeRelation;
use rustc_middle::ty::{self, Ty};
use rustc_span::symbol::sym;
use rustc_span::{Span, Symbol};
-use rustc_trait_selection::traits::query::Fallible;
use crate::constraints::OutlivesConstraint;
use crate::diagnostics::UniverseInfo;
@@ -30,7 +31,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
b: Ty<'tcx>,
locations: Locations,
category: ConstraintCategory<'tcx>,
- ) -> Fallible<()> {
+ ) -> Result<(), NoSolution> {
TypeRelating::new(
self.infcx,
NllTypeRelatingDelegate::new(self, locations, category, UniverseInfo::relate(a, b)),
@@ -47,7 +48,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
b: ty::SubstsRef<'tcx>,
locations: Locations,
category: ConstraintCategory<'tcx>,
- ) -> Fallible<()> {
+ ) -> Result<(), NoSolution> {
TypeRelating::new(
self.infcx,
NllTypeRelatingDelegate::new(self, locations, category, UniverseInfo::other()),
@@ -131,9 +132,13 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tcx>
ty::BoundRegionKind::BrEnv => BoundRegionInfo::Name(sym::env),
};
- if cfg!(debug_assertions) && !self.type_checker.infcx.inside_canonicalization_ctxt() {
+ if cfg!(debug_assertions) {
let mut var_to_origin = self.type_checker.infcx.reg_var_to_origin.borrow_mut();
- var_to_origin.insert(reg.as_var(), RegionCtxt::Placeholder(reg_info));
+ let new = RegionCtxt::Placeholder(reg_info);
+ let prev = var_to_origin.insert(reg.as_var(), new);
+ if let Some(prev) = prev {
+ assert_eq!(new, prev);
+ }
}
reg
@@ -146,9 +151,10 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tcx>
universe,
);
- if cfg!(debug_assertions) && !self.type_checker.infcx.inside_canonicalization_ctxt() {
+ if cfg!(debug_assertions) {
let mut var_to_origin = self.type_checker.infcx.reg_var_to_origin.borrow_mut();
- var_to_origin.insert(reg.as_var(), RegionCtxt::Existential(None));
+ let prev = var_to_origin.insert(reg.as_var(), RegionCtxt::Existential(None));
+ assert_eq!(prev, None);
}
reg
@@ -180,17 +186,15 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tcx>
}
fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) {
- self.type_checker
- .fully_perform_op(
- self.locations,
- self.category,
- InstantiateOpaqueType {
- obligations,
- // These fields are filled in during execution of the operation
- base_universe: None,
- region_constraints: None,
- },
- )
- .unwrap();
+ let _: Result<_, ErrorGuaranteed> = self.type_checker.fully_perform_op(
+ self.locations,
+ self.category,
+ InstantiateOpaqueType {
+ obligations,
+ // These fields are filled in during execution of the operation
+ base_universe: None,
+ region_constraints: None,
+ },
+ );
}
}
diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs
index 70fddb105..56f078f2d 100644
--- a/compiler/rustc_borrowck/src/universal_regions.rs
+++ b/compiler/rustc_borrowck/src/universal_regions.rs
@@ -19,7 +19,7 @@ use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::lang_items::LangItem;
use rustc_hir::BodyOwnerKind;
-use rustc_index::vec::{Idx, IndexVec};
+use rustc_index::IndexVec;
use rustc_infer::infer::NllRegionVariableOrigin;
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::{self, InlineConstSubsts, InlineConstSubstsParts, RegionVid, Ty, TyCtxt};
@@ -226,7 +226,7 @@ impl<'tcx> UniversalRegions<'tcx> {
/// known between those regions.
pub fn new(
infcx: &BorrowckInferCtxt<'_, 'tcx>,
- mir_def: ty::WithOptConstParam<LocalDefId>,
+ mir_def: LocalDefId,
param_env: ty::ParamEnv<'tcx>,
) -> Self {
UniversalRegionsBuilder { infcx, mir_def, param_env }.build()
@@ -289,7 +289,7 @@ impl<'tcx> UniversalRegions<'tcx> {
/// Returns an iterator over all the RegionVids corresponding to
/// universally quantified free regions.
pub fn universal_regions(&self) -> impl Iterator<Item = RegionVid> {
- (FIRST_GLOBAL_INDEX..self.num_universals).map(RegionVid::new)
+ (FIRST_GLOBAL_INDEX..self.num_universals).map(RegionVid::from_usize)
}
/// Returns `true` if `r` is classified as an local region.
@@ -335,7 +335,7 @@ impl<'tcx> UniversalRegions<'tcx> {
pub(crate) fn annotate(&self, tcx: TyCtxt<'tcx>, err: &mut Diagnostic) {
match self.defining_ty {
DefiningTy::Closure(def_id, substs) => {
- err.note(&format!(
+ err.note(format!(
"defining type: {} with closure substs {:#?}",
tcx.def_path_str_with_substs(def_id, substs),
&substs[tcx.generics_of(def_id).parent_count..],
@@ -347,11 +347,11 @@ impl<'tcx> UniversalRegions<'tcx> {
// and other things that are not stable across tests!
// So we just include the region-vid. Annoying.
for_each_late_bound_region_in_recursive_scope(tcx, def_id.expect_local(), |r| {
- err.note(&format!("late-bound region is {:?}", self.to_region_vid(r)));
+ err.note(format!("late-bound region is {:?}", self.to_region_vid(r)));
});
}
DefiningTy::Generator(def_id, substs, _) => {
- err.note(&format!(
+ err.note(format!(
"defining type: {} with generator substs {:#?}",
tcx.def_path_str_with_substs(def_id, substs),
&substs[tcx.generics_of(def_id).parent_count..],
@@ -361,23 +361,23 @@ impl<'tcx> UniversalRegions<'tcx> {
// `r` but doing so is not stable across architectures
// and so forth.
for_each_late_bound_region_in_recursive_scope(tcx, def_id.expect_local(), |r| {
- err.note(&format!("late-bound region is {:?}", self.to_region_vid(r)));
+ err.note(format!("late-bound region is {:?}", self.to_region_vid(r)));
});
}
DefiningTy::FnDef(def_id, substs) => {
- err.note(&format!(
+ err.note(format!(
"defining type: {}",
tcx.def_path_str_with_substs(def_id, substs),
));
}
DefiningTy::Const(def_id, substs) => {
- err.note(&format!(
+ err.note(format!(
"defining constant type: {}",
tcx.def_path_str_with_substs(def_id, substs),
));
}
DefiningTy::InlineConst(def_id, substs) => {
- err.note(&format!(
+ err.note(format!(
"defining inline constant type: {}",
tcx.def_path_str_with_substs(def_id, substs),
));
@@ -388,7 +388,7 @@ impl<'tcx> UniversalRegions<'tcx> {
struct UniversalRegionsBuilder<'cx, 'tcx> {
infcx: &'cx BorrowckInferCtxt<'cx, 'tcx>,
- mir_def: ty::WithOptConstParam<LocalDefId>,
+ mir_def: LocalDefId,
param_env: ty::ParamEnv<'tcx>,
}
@@ -417,12 +417,12 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
let mut indices = self.compute_indices(fr_static, defining_ty);
debug!("build: indices={:?}", indices);
- let typeck_root_def_id = self.infcx.tcx.typeck_root_def_id(self.mir_def.did.to_def_id());
+ let typeck_root_def_id = self.infcx.tcx.typeck_root_def_id(self.mir_def.to_def_id());
// If this is a 'root' body (not a closure/generator/inline const), then
// there are no extern regions, so the local regions start at the same
// position as the (empty) sub-list of extern regions
- let first_local_index = if self.mir_def.did.to_def_id() == typeck_root_def_id {
+ let first_local_index = if self.mir_def.to_def_id() == typeck_root_def_id {
first_extern_index
} else {
// If this is a closure, generator, or inline-const, then the late-bound regions from the enclosing
@@ -433,7 +433,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
// }
for_each_late_bound_region_in_recursive_scope(
self.infcx.tcx,
- self.infcx.tcx.local_parent(self.mir_def.did),
+ self.infcx.tcx.local_parent(self.mir_def),
|r| {
debug!(?r);
if !indices.indices.contains_key(&r) {
@@ -462,13 +462,13 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
let inputs_and_output = self.infcx.replace_bound_regions_with_nll_infer_vars(
FR,
- self.mir_def.did,
+ self.mir_def,
bound_inputs_and_output,
&mut indices,
);
// Converse of above, if this is a function/closure then the late-bound regions declared on its
// signature are local.
- for_each_late_bound_region_in_item(self.infcx.tcx, self.mir_def.did, |r| {
+ for_each_late_bound_region_in_item(self.infcx.tcx, self.mir_def, |r| {
debug!(?r);
if !indices.indices.contains_key(&r) {
let region_vid = {
@@ -492,7 +492,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
if self.infcx.tcx.fn_sig(def_id).skip_binder().c_variadic() {
let va_list_did = self.infcx.tcx.require_lang_item(
LangItem::VaList,
- Some(self.infcx.tcx.def_span(self.mir_def.did)),
+ Some(self.infcx.tcx.def_span(self.mir_def)),
);
let reg_vid = self
@@ -544,11 +544,11 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
/// see `DefiningTy` for details.
fn defining_ty(&self) -> DefiningTy<'tcx> {
let tcx = self.infcx.tcx;
- let typeck_root_def_id = tcx.typeck_root_def_id(self.mir_def.did.to_def_id());
+ let typeck_root_def_id = tcx.typeck_root_def_id(self.mir_def.to_def_id());
- match tcx.hir().body_owner_kind(self.mir_def.did) {
+ match tcx.hir().body_owner_kind(self.mir_def) {
BodyOwnerKind::Closure | BodyOwnerKind::Fn => {
- let defining_ty = tcx.type_of(self.mir_def.def_id_for_type_of()).subst_identity();
+ let defining_ty = tcx.type_of(self.mir_def).subst_identity();
debug!("defining_ty (pre-replacement): {:?}", defining_ty);
@@ -562,9 +562,9 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
}
ty::FnDef(def_id, substs) => DefiningTy::FnDef(def_id, substs),
_ => span_bug!(
- tcx.def_span(self.mir_def.did),
+ tcx.def_span(self.mir_def),
"expected defining type for `{:?}`: `{:?}`",
- self.mir_def.did,
+ self.mir_def,
defining_ty
),
}
@@ -572,10 +572,10 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
BodyOwnerKind::Const | BodyOwnerKind::Static(..) => {
let identity_substs = InternalSubsts::identity_for_item(tcx, typeck_root_def_id);
- if self.mir_def.did.to_def_id() == typeck_root_def_id {
+ if self.mir_def.to_def_id() == typeck_root_def_id {
let substs =
self.infcx.replace_free_regions_with_nll_infer_vars(FR, identity_substs);
- DefiningTy::Const(self.mir_def.did.to_def_id(), substs)
+ DefiningTy::Const(self.mir_def.to_def_id(), substs)
} else {
// FIXME this line creates a dependency between borrowck and typeck.
//
@@ -587,15 +587,15 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
// below), so that `type_of(inline_const_def_id).substs(substs)` uses the
// proper type with NLL infer vars.
let ty = tcx
- .typeck(self.mir_def.did)
- .node_type(tcx.local_def_id_to_hir_id(self.mir_def.did));
+ .typeck(self.mir_def)
+ .node_type(tcx.local_def_id_to_hir_id(self.mir_def));
let substs = InlineConstSubsts::new(
tcx,
InlineConstSubstsParts { parent_substs: identity_substs, ty },
)
.substs;
let substs = self.infcx.replace_free_regions_with_nll_infer_vars(FR, substs);
- DefiningTy::InlineConst(self.mir_def.did.to_def_id(), substs)
+ DefiningTy::InlineConst(self.mir_def.to_def_id(), substs)
}
}
}
@@ -611,7 +611,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
defining_ty: DefiningTy<'tcx>,
) -> UniversalRegionIndices<'tcx> {
let tcx = self.infcx.tcx;
- let typeck_root_def_id = tcx.typeck_root_def_id(self.mir_def.did.to_def_id());
+ let typeck_root_def_id = tcx.typeck_root_def_id(self.mir_def.to_def_id());
let identity_substs = InternalSubsts::identity_for_item(tcx, typeck_root_def_id);
let fr_substs = match defining_ty {
DefiningTy::Closure(_, substs)
@@ -647,7 +647,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
let tcx = self.infcx.tcx;
match defining_ty {
DefiningTy::Closure(def_id, substs) => {
- assert_eq!(self.mir_def.did.to_def_id(), def_id);
+ assert_eq!(self.mir_def.to_def_id(), def_id);
let closure_sig = substs.as_closure().sig();
let inputs_and_output = closure_sig.inputs_and_output();
let bound_vars = tcx.mk_bound_variable_kinds_from_iter(
@@ -682,7 +682,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
}
DefiningTy::Generator(def_id, substs, movability) => {
- assert_eq!(self.mir_def.did.to_def_id(), def_id);
+ assert_eq!(self.mir_def.to_def_id(), def_id);
let resume_ty = substs.as_generator().resume_ty();
let output = substs.as_generator().return_ty();
let generator_ty = tcx.mk_generator(def_id, substs, movability);
@@ -700,14 +700,14 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
DefiningTy::Const(def_id, _) => {
// For a constant body, there are no inputs, and one
// "output" (the type of the constant).
- assert_eq!(self.mir_def.did.to_def_id(), def_id);
- let ty = tcx.type_of(self.mir_def.def_id_for_type_of()).subst_identity();
+ assert_eq!(self.mir_def.to_def_id(), def_id);
+ let ty = tcx.type_of(self.mir_def).subst_identity();
let ty = indices.fold_to_region_vids(tcx, ty);
ty::Binder::dummy(tcx.mk_type_list(&[ty]))
}
DefiningTy::InlineConst(def_id, substs) => {
- assert_eq!(self.mir_def.did.to_def_id(), def_id);
+ assert_eq!(self.mir_def.to_def_id(), def_id);
let ty = substs.as_inline_const().ty();
ty::Binder::dummy(tcx.mk_type_list(&[ty]))
}
diff --git a/compiler/rustc_borrowck/src/util/collect_writes.rs b/compiler/rustc_borrowck/src/util/collect_writes.rs
new file mode 100644
index 000000000..8d92bb359
--- /dev/null
+++ b/compiler/rustc_borrowck/src/util/collect_writes.rs
@@ -0,0 +1,36 @@
+use rustc_middle::mir::visit::PlaceContext;
+use rustc_middle::mir::visit::Visitor;
+use rustc_middle::mir::{Body, Local, Location};
+
+pub trait FindAssignments {
+ // Finds all statements that assign directly to local (i.e., X = ...)
+ // and returns their locations.
+ fn find_assignments(&self, local: Local) -> Vec<Location>;
+}
+
+impl<'tcx> FindAssignments for Body<'tcx> {
+ fn find_assignments(&self, local: Local) -> Vec<Location> {
+ let mut visitor = FindLocalAssignmentVisitor { needle: local, locations: vec![] };
+ visitor.visit_body(self);
+ visitor.locations
+ }
+}
+
+// The Visitor walks the MIR to return the assignment statements corresponding
+// to a Local.
+struct FindLocalAssignmentVisitor {
+ needle: Local,
+ locations: Vec<Location>,
+}
+
+impl<'tcx> Visitor<'tcx> for FindLocalAssignmentVisitor {
+ fn visit_local(&mut self, local: Local, place_context: PlaceContext, location: Location) {
+ if self.needle != local {
+ return;
+ }
+
+ if place_context.is_place_assignment() {
+ self.locations.push(location);
+ }
+ }
+}
diff --git a/compiler/rustc_borrowck/src/util/mod.rs b/compiler/rustc_borrowck/src/util/mod.rs
new file mode 100644
index 000000000..7377d4de7
--- /dev/null
+++ b/compiler/rustc_borrowck/src/util/mod.rs
@@ -0,0 +1,3 @@
+mod collect_writes;
+
+pub use collect_writes::FindAssignments;