summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_borrowck
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-07 05:48:42 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-07 05:48:42 +0000
commitcec1877e180393eba0f6ddb0cf97bf3a791631c7 (patch)
tree47b4dac2a9dd9a40c30c251b4d4a72d7ccf77e9f /compiler/rustc_borrowck
parentAdding debian version 1.74.1+dfsg1-1. (diff)
downloadrustc-cec1877e180393eba0f6ddb0cf97bf3a791631c7.tar.xz
rustc-cec1877e180393eba0f6ddb0cf97bf3a791631c7.zip
Merging upstream version 1.75.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_borrowck')
-rw-r--r--compiler/rustc_borrowck/Cargo.toml12
-rw-r--r--compiler/rustc_borrowck/messages.ftl42
-rw-r--r--compiler/rustc_borrowck/src/borrowck_errors.rs5
-rw-r--r--compiler/rustc_borrowck/src/dataflow.rs203
-rw-r--r--compiler/rustc_borrowck/src/def_use.rs2
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs428
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs18
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mod.rs86
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/move_errors.rs176
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs152
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_errors.rs4
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_name.rs38
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/var_name.rs2
-rw-r--r--compiler/rustc_borrowck/src/facts.rs3
-rw-r--r--compiler/rustc_borrowck/src/invalidation.rs2
-rw-r--r--compiler/rustc_borrowck/src/lib.rs265
-rw-r--r--compiler/rustc_borrowck/src/nll.rs42
-rw-r--r--compiler/rustc_borrowck/src/path_utils.rs4
-rw-r--r--compiler/rustc_borrowck/src/region_infer/mod.rs63
-rw-r--r--compiler/rustc_borrowck/src/region_infer/opaque_types.rs18
-rw-r--r--compiler/rustc_borrowck/src/region_infer/values.rs5
-rw-r--r--compiler/rustc_borrowck/src/renumber.rs4
-rw-r--r--compiler/rustc_borrowck/src/session_diagnostics.rs40
-rw-r--r--compiler/rustc_borrowck/src/type_check/canonical.rs4
-rw-r--r--compiler/rustc_borrowck/src/type_check/free_region_relations.rs5
-rw-r--r--compiler/rustc_borrowck/src/type_check/input_output.rs2
-rw-r--r--compiler/rustc_borrowck/src/type_check/liveness/trace.rs99
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs74
-rw-r--r--compiler/rustc_borrowck/src/universal_regions.rs50
29 files changed, 1266 insertions, 582 deletions
diff --git a/compiler/rustc_borrowck/Cargo.toml b/compiler/rustc_borrowck/Cargo.toml
index 56a9deb6a..636817a7c 100644
--- a/compiler/rustc_borrowck/Cargo.toml
+++ b/compiler/rustc_borrowck/Cargo.toml
@@ -3,19 +3,16 @@ name = "rustc_borrowck"
version = "0.0.0"
edition = "2021"
-[lib]
-
[dependencies]
+# tidy-alphabetical-start
either = "1.5.0"
itertools = "0.10.1"
-tracing = "0.1"
polonius-engine = "0.13.0"
-smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
+rustc_fluent_macro = { path = "../rustc_fluent_macro" }
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" }
@@ -24,7 +21,10 @@ rustc_middle = { path = "../rustc_middle" }
rustc_mir_dataflow = { path = "../rustc_mir_dataflow" }
rustc_serialize = { path = "../rustc_serialize" }
rustc_session = { path = "../rustc_session" }
+rustc_span = { path = "../rustc_span" }
rustc_target = { path = "../rustc_target" }
rustc_trait_selection = { path = "../rustc_trait_selection" }
rustc_traits = { path = "../rustc_traits" }
-rustc_span = { path = "../rustc_span" }
+smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
+tracing = "0.1"
+# tidy-alphabetical-end
diff --git a/compiler/rustc_borrowck/messages.ftl b/compiler/rustc_borrowck/messages.ftl
index 2c7b97afa..8c5a1d897 100644
--- a/compiler/rustc_borrowck/messages.ftl
+++ b/compiler/rustc_borrowck/messages.ftl
@@ -1,20 +1,20 @@
borrowck_assign_due_to_use_closure =
assignment occurs due to use in closure
-borrowck_assign_due_to_use_generator =
- assign occurs due to use in generator
+borrowck_assign_due_to_use_coroutine =
+ assign occurs due to use in coroutine
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_assign_part_due_to_use_coroutine =
+ assign to part occurs due to use in coroutine
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_borrow_due_to_use_coroutine =
+ borrow occurs due to use in coroutine
borrowck_calling_operator_moves_lhs =
calling this operator moves the left-hand side
@@ -142,11 +142,11 @@ borrowck_partial_var_move_by_use_in_closure =
*[false] moved
} due to use in closure
-borrowck_partial_var_move_by_use_in_generator =
+borrowck_partial_var_move_by_use_in_coroutine =
variable {$is_partial ->
[true] partially moved
*[false] moved
- } due to use in generator
+ } due to use in coroutine
borrowck_returned_async_block_escaped =
returns an `async` block that contains a reference to a captured variable, which then escapes the closure body
@@ -180,15 +180,15 @@ borrowck_ty_no_impl_copy =
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_use_due_to_use_coroutine =
+ use occurs due to use in coroutine
borrowck_used_impl_require_static =
the used `impl` has a `'static` requirement
borrowck_value_capture_here =
value captured {$is_within ->
- [true] here by generator
+ [true] here by coroutine
*[false] here
}
@@ -207,8 +207,8 @@ borrowck_value_moved_here =
borrowck_var_borrow_by_use_in_closure =
borrow occurs due to use in closure
-borrowck_var_borrow_by_use_in_generator =
- borrow occurs due to use in generator
+borrowck_var_borrow_by_use_in_coroutine =
+ borrow occurs due to use in coroutine
borrowck_var_borrow_by_use_place_in_closure =
{$is_single_var ->
@@ -216,11 +216,11 @@ borrowck_var_borrow_by_use_place_in_closure =
[false] borrows occur
} due to use of {$place} in closure
-borrowck_var_borrow_by_use_place_in_generator =
+borrowck_var_borrow_by_use_place_in_coroutine =
{$is_single_var ->
*[true] borrow occurs
[false] borrows occur
- } due to use of {$place} in generator
+ } due to use of {$place} in coroutine
borrowck_var_cannot_escape_closure =
captured variable cannot escape `FnMut` closure body
@@ -234,8 +234,8 @@ borrowck_var_does_not_need_mut =
borrowck_var_first_borrow_by_use_place_in_closure =
first borrow occurs due to use of {$place} in closure
-borrowck_var_first_borrow_by_use_place_in_generator =
- first borrow occurs due to use of {$place} in generator
+borrowck_var_first_borrow_by_use_place_in_coroutine =
+ first borrow occurs due to use of {$place} in coroutine
borrowck_var_here_captured = variable captured here
@@ -244,8 +244,8 @@ borrowck_var_here_defined = variable defined here
borrowck_var_move_by_use_in_closure =
move occurs due to use in closure
-borrowck_var_move_by_use_in_generator =
- move occurs due to use in generator
+borrowck_var_move_by_use_in_coroutine =
+ move occurs due to use in coroutine
borrowck_var_mutable_borrow_by_use_place_in_closure =
mutable borrow occurs due to use of {$place} in closure
@@ -253,5 +253,5 @@ borrowck_var_mutable_borrow_by_use_place_in_closure =
borrowck_var_second_borrow_by_use_place_in_closure =
second borrow occurs due to use of {$place} in closure
-borrowck_var_second_borrow_by_use_place_in_generator =
- second borrow occurs due to use of {$place} in generator
+borrowck_var_second_borrow_by_use_place_in_coroutine =
+ second borrow occurs due to use of {$place} in coroutine
diff --git a/compiler/rustc_borrowck/src/borrowck_errors.rs b/compiler/rustc_borrowck/src/borrowck_errors.rs
index a2c7e767b..6731ef123 100644
--- a/compiler/rustc_borrowck/src/borrowck_errors.rs
+++ b/compiler/rustc_borrowck/src/borrowck_errors.rs
@@ -368,16 +368,17 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
err
}
- pub(crate) fn cannot_borrow_across_generator_yield(
+ pub(crate) fn cannot_borrow_across_coroutine_yield(
&self,
span: Span,
yield_span: Span,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+ let coroutine_kind = self.body.coroutine.as_ref().unwrap().coroutine_kind;
let mut err = struct_span_err!(
self,
span,
E0626,
- "borrow may still be in use when generator yields",
+ "borrow may still be in use when {coroutine_kind:#} yields",
);
err.span_label(yield_span, "possible yield occurs here");
err
diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs
index 4ac633c26..8676d2ba7 100644
--- a/compiler/rustc_borrowck/src/dataflow.rs
+++ b/compiler/rustc_borrowck/src/dataflow.rs
@@ -1,6 +1,7 @@
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
use rustc_data_structures::fx::FxIndexMap;
+use rustc_data_structures::graph::WithSuccessors;
use rustc_index::bit_set::BitSet;
use rustc_middle::mir::{
self, BasicBlock, Body, CallReturnPlaces, Location, Place, TerminatorEdges,
@@ -222,6 +223,7 @@ impl<'tcx> OutOfScopePrecomputer<'_, 'tcx> {
}
}
+// This is `pub` because it's used by unstable external borrowck data users, see `consumers.rs`.
pub fn calculate_borrows_out_of_scope_at_location<'tcx>(
body: &Body<'tcx>,
regioncx: &RegionInferenceContext<'tcx>,
@@ -238,15 +240,203 @@ pub fn calculate_borrows_out_of_scope_at_location<'tcx>(
prec.borrows_out_of_scope_at_location
}
+struct PoloniusOutOfScopePrecomputer<'a, 'tcx> {
+ visited: BitSet<mir::BasicBlock>,
+ visit_stack: Vec<mir::BasicBlock>,
+ body: &'a Body<'tcx>,
+ regioncx: &'a RegionInferenceContext<'tcx>,
+
+ loans_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
+}
+
+impl<'a, 'tcx> PoloniusOutOfScopePrecomputer<'a, 'tcx> {
+ fn new(body: &'a Body<'tcx>, regioncx: &'a RegionInferenceContext<'tcx>) -> Self {
+ Self {
+ visited: BitSet::new_empty(body.basic_blocks.len()),
+ visit_stack: vec![],
+ body,
+ regioncx,
+ loans_out_of_scope_at_location: FxIndexMap::default(),
+ }
+ }
+}
+
+impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
+ /// Loans are in scope while they are live: whether they are contained within any live region.
+ /// In the location-insensitive analysis, a loan will be contained in a region if the issuing
+ /// region can reach it in the subset graph. So this is a reachability problem.
+ fn precompute_loans_out_of_scope(
+ &mut self,
+ loan_idx: BorrowIndex,
+ issuing_region: RegionVid,
+ loan_issued_at: Location,
+ ) {
+ let sccs = self.regioncx.constraint_sccs();
+ let universal_regions = self.regioncx.universal_regions();
+
+ // We first handle the cases where the loan doesn't go out of scope, depending on the issuing
+ // region's successors.
+ for successor in self.regioncx.region_graph().depth_first_search(issuing_region) {
+ // 1. Via applied member constraints
+ //
+ // The issuing region can flow into the choice regions, and they are either:
+ // - placeholders or free regions themselves,
+ // - or also transitively outlive a free region.
+ //
+ // That is to say, if there are applied member constraints here, the loan escapes the
+ // function and cannot go out of scope. We could early return here.
+ //
+ // For additional insurance via fuzzing and crater, we verify that the constraint's min
+ // choice indeed escapes the function. In the future, we could e.g. turn this check into
+ // a debug assert and early return as an optimization.
+ let scc = sccs.scc(successor);
+ for constraint in self.regioncx.applied_member_constraints(scc) {
+ if universal_regions.is_universal_region(constraint.min_choice) {
+ return;
+ }
+ }
+
+ // 2. Via regions that are live at all points: placeholders and free regions.
+ //
+ // If the issuing region outlives such a region, its loan escapes the function and
+ // cannot go out of scope. We can early return.
+ if self.regioncx.is_region_live_at_all_points(successor) {
+ return;
+ }
+ }
+
+ let first_block = loan_issued_at.block;
+ let first_bb_data = &self.body.basic_blocks[first_block];
+
+ // The first block we visit is the one where the loan is issued, starting from the statement
+ // where the loan is issued: at `loan_issued_at`.
+ let first_lo = loan_issued_at.statement_index;
+ let first_hi = first_bb_data.statements.len();
+
+ if let Some(kill_location) =
+ self.loan_kill_location(loan_idx, loan_issued_at, first_block, first_lo, first_hi)
+ {
+ debug!("loan {:?} gets killed at {:?}", loan_idx, kill_location);
+ self.loans_out_of_scope_at_location.entry(kill_location).or_default().push(loan_idx);
+
+ // The loan dies within the first block, we're done and can early return.
+ return;
+ }
+
+ // The loan is not dead. Add successor BBs to the work list, if necessary.
+ for succ_bb in first_bb_data.terminator().successors() {
+ if self.visited.insert(succ_bb) {
+ self.visit_stack.push(succ_bb);
+ }
+ }
+
+ // We may end up visiting `first_block` again. This is not an issue: we know at this point
+ // that the loan is not killed in the `first_lo..=first_hi` range, so checking the
+ // `0..first_lo` range and the `0..first_hi` range gives the same result.
+ while let Some(block) = self.visit_stack.pop() {
+ let bb_data = &self.body[block];
+ let num_stmts = bb_data.statements.len();
+ if let Some(kill_location) =
+ self.loan_kill_location(loan_idx, loan_issued_at, block, 0, num_stmts)
+ {
+ debug!("loan {:?} gets killed at {:?}", loan_idx, kill_location);
+ self.loans_out_of_scope_at_location
+ .entry(kill_location)
+ .or_default()
+ .push(loan_idx);
+
+ // The loan dies within this block, so we don't need to visit its successors.
+ continue;
+ }
+
+ // Add successor BBs to the work list, if necessary.
+ for succ_bb in bb_data.terminator().successors() {
+ if self.visited.insert(succ_bb) {
+ self.visit_stack.push(succ_bb);
+ }
+ }
+ }
+
+ self.visited.clear();
+ assert!(self.visit_stack.is_empty(), "visit stack should be empty");
+ }
+
+ /// Returns the lowest statement in `start..=end`, where the loan goes out of scope, if any.
+ /// This is the statement where the issuing region can't reach any of the regions that are live
+ /// at this point.
+ fn loan_kill_location(
+ &self,
+ loan_idx: BorrowIndex,
+ loan_issued_at: Location,
+ block: BasicBlock,
+ start: usize,
+ end: usize,
+ ) -> Option<Location> {
+ for statement_index in start..=end {
+ let location = Location { block, statement_index };
+
+ // Check whether the issuing region can reach local regions that are live at this point:
+ // - a loan is always live at its issuing location because it can reach the issuing
+ // region, which is always live at this location.
+ if location == loan_issued_at {
+ continue;
+ }
+
+ // - the loan goes out of scope at `location` if it's not contained within any regions
+ // live at this point.
+ //
+ // FIXME: if the issuing region `i` can reach a live region `r` at point `p`, and `r` is
+ // live at point `q`, then it's guaranteed that `i` would reach `r` at point `q`.
+ // Reachability is location-insensitive, and we could take advantage of that, by jumping
+ // to a further point than just the next statement: we can jump to the furthest point
+ // within the block where `r` is live.
+ if self.regioncx.is_loan_live_at(loan_idx, location) {
+ continue;
+ }
+
+ // No live region is reachable from the issuing region: the loan is killed at this
+ // point.
+ return Some(location);
+ }
+
+ None
+ }
+}
+
impl<'a, 'tcx> Borrows<'a, 'tcx> {
pub fn new(
tcx: TyCtxt<'tcx>,
body: &'a Body<'tcx>,
- nonlexical_regioncx: &'a RegionInferenceContext<'tcx>,
+ regioncx: &'a RegionInferenceContext<'tcx>,
borrow_set: &'a BorrowSet<'tcx>,
) -> Self {
- let borrows_out_of_scope_at_location =
- calculate_borrows_out_of_scope_at_location(body, nonlexical_regioncx, borrow_set);
+ let mut borrows_out_of_scope_at_location =
+ calculate_borrows_out_of_scope_at_location(body, regioncx, borrow_set);
+
+ // The in-tree polonius analysis computes loans going out of scope using the set-of-loans
+ // model, and makes sure they're identical to the existing computation of the set-of-points
+ // model.
+ if tcx.sess.opts.unstable_opts.polonius.is_next_enabled() {
+ let mut polonius_prec = PoloniusOutOfScopePrecomputer::new(body, regioncx);
+ for (loan_idx, loan_data) in borrow_set.iter_enumerated() {
+ let issuing_region = loan_data.region;
+ let loan_issued_at = loan_data.reserve_location;
+
+ polonius_prec.precompute_loans_out_of_scope(
+ loan_idx,
+ issuing_region,
+ loan_issued_at,
+ );
+ }
+
+ assert_eq!(
+ borrows_out_of_scope_at_location, polonius_prec.loans_out_of_scope_at_location,
+ "the loans out of scope must be the same as the borrows out of scope"
+ );
+
+ borrows_out_of_scope_at_location = polonius_prec.loans_out_of_scope_at_location;
+ }
+
Borrows { tcx, body, borrow_set, borrows_out_of_scope_at_location }
}
@@ -333,6 +523,13 @@ impl<'tcx> rustc_mir_dataflow::AnalysisDomain<'tcx> for Borrows<'_, 'tcx> {
}
}
+/// Forward dataflow computation of the set of borrows that are in scope at a particular location.
+/// - we gen the introduced loans
+/// - we kill loans on locals going out of (regular) scope
+/// - we kill the loans going out of their region's NLL scope: in NLL terms, the frontier where a
+/// region stops containing the CFG points reachable from the issuing location.
+/// - we also kill loans of conflicting places when overwriting a shared path: e.g. borrows of
+/// `a.b.c` when `a` is overwritten.
impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
type Idx = BorrowIndex;
diff --git a/compiler/rustc_borrowck/src/def_use.rs b/compiler/rustc_borrowck/src/def_use.rs
index 95db93742..d0cf339e7 100644
--- a/compiler/rustc_borrowck/src/def_use.rs
+++ b/compiler/rustc_borrowck/src/def_use.rs
@@ -44,7 +44,7 @@ pub fn categorize(context: PlaceContext) -> Option<DefUse> {
PlaceContext::MutatingUse(MutatingUseContext::Projection) |
// Borrows only consider their local used at the point of the borrow.
- // This won't affect the results since we use this analysis for generators
+ // This won't affect the results since we use this analysis for coroutines
// and we only care about the result at suspension points. Borrows cannot
// cross suspension points so this behavior is unproblematic.
PlaceContext::MutatingUse(MutatingUseContext::Borrow) |
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index ee352e911..9a8f1c97e 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -8,7 +8,7 @@ use rustc_errors::{
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_hir::{CoroutineKind, CoroutineSource, LangItem};
use rustc_infer::traits::ObligationCause;
use rustc_middle::hir::nested_filter::OnlyBodies;
use rustc_middle::mir::tcx::PlaceTy;
@@ -351,7 +351,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}
// Check if we are in a situation of `ident @ ident` where we want to suggest
// `ref ident @ ref ident` or `ref ident @ Struct { ref ident }`.
- if let Some(subpat) = sub && self.pat.is_none() {
+ if let Some(subpat) = sub
+ && self.pat.is_none()
+ {
self.visit_pat(subpat);
if self.pat.is_some() {
self.parent_pat = Some(p);
@@ -370,7 +372,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let mut finder =
ExpressionFinder { expr_span: move_span, expr: None, pat: None, parent_pat: None };
finder.visit_expr(expr);
- if let Some(span) = span && let Some(expr) = finder.expr {
+ if let Some(span) = span
+ && let Some(expr) = finder.expr
+ {
for (_, expr) in hir.parent_iter(expr.hir_id) {
if let hir::Node::Expr(expr) = expr {
if expr.span.contains(span) {
@@ -425,10 +429,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
Some(hir::intravisit::FnKind::Method(..)) => "method",
Some(hir::intravisit::FnKind::Closure) => "closure",
};
- span.push_span_label(
- ident.span,
- format!("in this {descr}"),
- );
+ span.push_span_label(ident.span, format!("in this {descr}"));
err.span_note(
span,
format!(
@@ -441,15 +442,16 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let ty = place.ty(self.body, self.infcx.tcx).ty;
if let hir::Node::Expr(parent_expr) = parent
&& let hir::ExprKind::Call(call_expr, _) = parent_expr.kind
- && let hir::ExprKind::Path(
- hir::QPath::LangItem(LangItem::IntoIterIntoIter, _, _)
- ) = call_expr.kind
+ && let hir::ExprKind::Path(hir::QPath::LangItem(
+ LangItem::IntoIterIntoIter,
+ _,
+ _,
+ )) = call_expr.kind
{
// Do not suggest `.clone()` in a `for` loop, we already suggest borrowing.
- } else if let UseSpans::FnSelfUse {
- kind: CallKind::Normal { .. },
- ..
- } = move_spans {
+ } else if let UseSpans::FnSelfUse { kind: CallKind::Normal { .. }, .. } =
+ move_spans
+ {
// We already suggest cloning for these cases in `explain_captures`.
} else {
self.suggest_cloning(err, ty, expr, move_span);
@@ -602,10 +604,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
if self.sugg_span.is_some() {
return;
}
- if let hir::StmtKind::Local(hir::Local {
- span, ty, init: None, ..
- }) = &ex.kind && span.contains(self.decl_span) {
- self.sugg_span = ty.map_or(Some(self.decl_span), |ty| Some(ty.span));
+ if let hir::StmtKind::Local(hir::Local { span, ty, init: None, .. }) = &ex.kind
+ && span.contains(self.decl_span)
+ {
+ self.sugg_span = ty.map_or(Some(self.decl_span), |ty| Some(ty.span));
}
hir::intravisit::walk_stmt(self, ex);
}
@@ -743,19 +745,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
".clone()".to_owned()
};
if let Some(clone_trait_def) = tcx.lang_items().clone_trait()
- && self.infcx
- .type_implements_trait(
- clone_trait_def,
- [ty],
- self.param_env,
- )
+ && self
+ .infcx
+ .type_implements_trait(clone_trait_def, [ty], self.param_env)
.must_apply_modulo_regions()
{
let msg = if let ty::Adt(def, _) = ty.kind()
- && [
- tcx.get_diagnostic_item(sym::Arc),
- tcx.get_diagnostic_item(sym::Rc),
- ].contains(&Some(def.did()))
+ && [tcx.get_diagnostic_item(sym::Arc), tcx.get_diagnostic_item(sym::Rc)]
+ .contains(&Some(def.did()))
{
"clone the value to increment its reference count"
} else {
@@ -851,7 +848,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
move_spans.var_subdiag(None, &mut err, None, |kind, var_span| {
use crate::session_diagnostics::CaptureVarCause::*;
match kind {
- Some(_) => MoveUseInGenerator { var_span },
+ Some(_) => MoveUseInCoroutine { var_span },
None => MoveUseInClosure { var_span },
}
});
@@ -897,7 +894,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let desc_place = self.describe_any_place(place.as_ref());
match kind {
Some(_) => {
- BorrowUsePlaceGenerator { place: desc_place, var_span, is_single_var: true }
+ BorrowUsePlaceCoroutine { place: desc_place, var_span, is_single_var: true }
}
None => BorrowUsePlaceClosure { place: desc_place, var_span, is_single_var: true },
}
@@ -929,8 +926,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let borrow_spans = self.borrow_spans(span, location);
let span = borrow_spans.args_or_use();
- let container_name = if issued_spans.for_generator() || borrow_spans.for_generator() {
- "generator"
+ let container_name = if issued_spans.for_coroutine() || borrow_spans.for_coroutine() {
+ "coroutine"
} else {
"closure"
};
@@ -1043,7 +1040,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|kind, var_span| {
use crate::session_diagnostics::CaptureVarCause::*;
match kind {
- Some(_) => BorrowUsePlaceGenerator {
+ Some(_) => BorrowUsePlaceCoroutine {
place: desc_place,
var_span,
is_single_var: true,
@@ -1127,7 +1124,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
borrow_spans.var_subdiag(None, &mut err, Some(gen_borrow_kind), |kind, var_span| {
use crate::session_diagnostics::CaptureVarCause::*;
match kind {
- Some(_) => BorrowUsePlaceGenerator {
+ Some(_) => BorrowUsePlaceCoroutine {
place: desc_place,
var_span,
is_single_var: false,
@@ -1148,7 +1145,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let borrow_place_desc = self.describe_any_place(borrow_place.as_ref());
match kind {
Some(_) => {
- FirstBorrowUsePlaceGenerator { place: borrow_place_desc, var_span }
+ FirstBorrowUsePlaceCoroutine { place: borrow_place_desc, var_span }
}
None => FirstBorrowUsePlaceClosure { place: borrow_place_desc, var_span },
}
@@ -1162,7 +1159,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|kind, var_span| {
use crate::session_diagnostics::CaptureVarCause::*;
match kind {
- Some(_) => SecondBorrowUsePlaceGenerator { place: desc_place, var_span },
+ Some(_) => SecondBorrowUsePlaceCoroutine { place: desc_place, var_span },
None => SecondBorrowUsePlaceClosure { place: desc_place, var_span },
}
},
@@ -1328,42 +1325,160 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
issue_span: Span,
expr_span: Span,
body_expr: Option<&'hir hir::Expr<'hir>>,
- loop_bind: Option<Symbol>,
+ loop_bind: Option<&'hir Ident>,
+ loop_span: Option<Span>,
+ head_span: Option<Span>,
+ pat_span: Option<Span>,
+ head: Option<&'hir hir::Expr<'hir>>,
}
impl<'hir> Visitor<'hir> for ExprFinder<'hir> {
fn visit_expr(&mut self, ex: &'hir hir::Expr<'hir>) {
- if let hir::ExprKind::Loop(hir::Block{ stmts: [stmt, ..], ..}, _, hir::LoopSource::ForLoop, _) = ex.kind &&
- let hir::StmtKind::Expr(hir::Expr{ kind: hir::ExprKind::Match(call, [_, bind, ..], _), ..}) = stmt.kind &&
- let hir::ExprKind::Call(path, _args) = call.kind &&
- let hir::ExprKind::Path(hir::QPath::LangItem(LangItem::IteratorNext, _, _, )) = path.kind &&
- let hir::PatKind::Struct(path, [field, ..], _) = bind.pat.kind &&
- let hir::QPath::LangItem(LangItem::OptionSome, _, _) = path &&
- let PatField { pat: hir::Pat{ kind: hir::PatKind::Binding(_, _, ident, ..), .. }, ..} = field &&
- self.issue_span.source_equal(call.span) {
- self.loop_bind = Some(ident.name);
+ // Try to find
+ // let result = match IntoIterator::into_iter(<head>) {
+ // mut iter => {
+ // [opt_ident]: loop {
+ // match Iterator::next(&mut iter) {
+ // None => break,
+ // Some(<pat>) => <body>,
+ // };
+ // }
+ // }
+ // };
+ // corresponding to the desugaring of a for loop `for <pat> in <head> { <body> }`.
+ if let hir::ExprKind::Call(path, [arg]) = ex.kind
+ && let hir::ExprKind::Path(hir::QPath::LangItem(
+ LangItem::IntoIterIntoIter,
+ _,
+ _,
+ )) = path.kind
+ && arg.span.contains(self.issue_span)
+ {
+ // Find `IntoIterator::into_iter(<head>)`
+ self.head = Some(arg);
+ }
+ if let hir::ExprKind::Loop(
+ hir::Block { stmts: [stmt, ..], .. },
+ _,
+ hir::LoopSource::ForLoop,
+ _,
+ ) = ex.kind
+ && let hir::StmtKind::Expr(hir::Expr {
+ kind: hir::ExprKind::Match(call, [_, bind, ..], _),
+ span: head_span,
+ ..
+ }) = stmt.kind
+ && let hir::ExprKind::Call(path, _args) = call.kind
+ && let hir::ExprKind::Path(hir::QPath::LangItem(LangItem::IteratorNext, _, _)) =
+ path.kind
+ && let hir::PatKind::Struct(path, [field, ..], _) = bind.pat.kind
+ && let hir::QPath::LangItem(LangItem::OptionSome, pat_span, _) = path
+ && call.span.contains(self.issue_span)
+ {
+ // Find `<pat>` and the span for the whole `for` loop.
+ if let PatField {
+ pat: hir::Pat { kind: hir::PatKind::Binding(_, _, ident, ..), .. },
+ ..
+ } = field
+ {
+ self.loop_bind = Some(ident);
}
+ self.head_span = Some(*head_span);
+ self.pat_span = Some(pat_span);
+ self.loop_span = Some(stmt.span);
+ }
- if let hir::ExprKind::MethodCall(body_call, _recv, ..) = ex.kind &&
- body_call.ident.name == sym::next && ex.span.source_equal(self.expr_span) {
- self.body_expr = Some(ex);
+ if let hir::ExprKind::MethodCall(body_call, recv, ..) = ex.kind
+ && body_call.ident.name == sym::next
+ && recv.span.source_equal(self.expr_span)
+ {
+ self.body_expr = Some(ex);
}
hir::intravisit::walk_expr(self, ex);
}
}
- let mut finder =
- ExprFinder { expr_span: span, issue_span, loop_bind: None, body_expr: None };
+ let mut finder = ExprFinder {
+ expr_span: span,
+ issue_span,
+ loop_bind: None,
+ body_expr: None,
+ head_span: None,
+ loop_span: None,
+ pat_span: None,
+ head: None,
+ };
finder.visit_expr(hir.body(body_id).value);
- if let Some(loop_bind) = finder.loop_bind &&
- let Some(body_expr) = finder.body_expr &&
- let Some(def_id) = typeck_results.type_dependent_def_id(body_expr.hir_id) &&
- let Some(trait_did) = tcx.trait_of_item(def_id) &&
- tcx.is_diagnostic_item(sym::Iterator, trait_did) {
- err.note(format!(
- "a for loop advances the iterator for you, the result is stored in `{loop_bind}`."
+ if let Some(body_expr) = finder.body_expr
+ && let Some(loop_span) = finder.loop_span
+ && let Some(def_id) = typeck_results.type_dependent_def_id(body_expr.hir_id)
+ && let Some(trait_did) = tcx.trait_of_item(def_id)
+ && tcx.is_diagnostic_item(sym::Iterator, trait_did)
+ {
+ if let Some(loop_bind) = finder.loop_bind {
+ err.note(format!(
+ "a for loop advances the iterator for you, the result is stored in `{}`",
+ loop_bind.name,
+ ));
+ } else {
+ err.note(
+ "a for loop advances the iterator for you, the result is stored in its pattern",
+ );
+ }
+ let msg = "if you want to call `next` on a iterator within the loop, consider using \
+ `while let`";
+ if let Some(head) = finder.head
+ && let Some(pat_span) = finder.pat_span
+ && loop_span.contains(body_expr.span)
+ && loop_span.contains(head.span)
+ {
+ let sm = self.infcx.tcx.sess.source_map();
+
+ let mut sugg = vec![];
+ if let hir::ExprKind::Path(hir::QPath::Resolved(None, _)) = head.kind {
+ // A bare path doesn't need a `let` assignment, it's already a simple
+ // binding access.
+ // As a new binding wasn't added, we don't need to modify the advancing call.
+ sugg.push((loop_span.with_hi(pat_span.lo()), format!("while let Some(")));
+ sugg.push((
+ pat_span.shrink_to_hi().with_hi(head.span.lo()),
+ ") = ".to_string(),
+ ));
+ sugg.push((head.span.shrink_to_hi(), ".next()".to_string()));
+ } else {
+ // Needs a new a `let` binding.
+ let indent = if let Some(indent) = sm.indentation_before(loop_span) {
+ format!("\n{indent}")
+ } else {
+ " ".to_string()
+ };
+ let Ok(head_str) = sm.span_to_snippet(head.span) else {
+ err.help(msg);
+ return;
+ };
+ sugg.push((
+ loop_span.with_hi(pat_span.lo()),
+ format!("let iter = {head_str};{indent}while let Some("),
+ ));
+ sugg.push((
+ pat_span.shrink_to_hi().with_hi(head.span.hi()),
+ ") = iter.next()".to_string(),
));
- err.help("if you want to call `next` on a iterator within the loop, consider using `while let`.");
+ // As a new binding was added, we should change how the iterator is advanced to
+ // use the newly introduced binding.
+ if let hir::ExprKind::MethodCall(_, recv, ..) = body_expr.kind
+ && let hir::ExprKind::Path(hir::QPath::Resolved(None, ..)) = recv.kind
+ {
+ // As we introduced a `let iter = <head>;`, we need to change where the
+ // already borrowed value was accessed from `<recv>.next()` to
+ // `iter.next()`.
+ sugg.push((recv.span, "iter".to_string()));
+ }
+ }
+ err.multipart_suggestion(msg, sugg, Applicability::MaybeIncorrect);
+ } else {
+ err.help(msg);
+ }
}
}
@@ -1459,7 +1574,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
// Get closure's arguments
let ty::Closure(_, args) = typeck_results.expr_ty(closure_expr).kind() else {
- /* hir::Closure can be a generator too */
+ /* hir::Closure can be a coroutine too */
return;
};
let sig = args.as_closure().sig();
@@ -1539,69 +1654,80 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) {
if e.span.contains(self.capture_span) {
if let hir::ExprKind::Closure(&hir::Closure {
- movability: None,
- body,
- fn_arg_span,
- fn_decl: hir::FnDecl{ inputs, .. },
- ..
- }) = e.kind &&
- let Some(hir::Node::Expr(body )) = self.hir.find(body.hir_id) {
- self.suggest_arg = "this: &Self".to_string();
- if inputs.len() > 0 {
- self.suggest_arg.push_str(", ");
- }
- self.in_closure = true;
- self.closure_arg_span = fn_arg_span;
- self.visit_expr(body);
- self.in_closure = false;
+ movability: None,
+ body,
+ fn_arg_span,
+ fn_decl: hir::FnDecl { inputs, .. },
+ ..
+ }) = e.kind
+ && let Some(hir::Node::Expr(body)) = self.hir.find(body.hir_id)
+ {
+ self.suggest_arg = "this: &Self".to_string();
+ if inputs.len() > 0 {
+ self.suggest_arg.push_str(", ");
+ }
+ self.in_closure = true;
+ self.closure_arg_span = fn_arg_span;
+ self.visit_expr(body);
+ self.in_closure = false;
}
}
if let hir::Expr { kind: hir::ExprKind::Path(path), .. } = e {
- if let hir::QPath::Resolved(_, hir::Path { segments: [seg], ..}) = path &&
- seg.ident.name == kw::SelfLower && self.in_closure {
- self.closure_change_spans.push(e.span);
+ if let hir::QPath::Resolved(_, hir::Path { segments: [seg], .. }) = path
+ && seg.ident.name == kw::SelfLower
+ && self.in_closure
+ {
+ self.closure_change_spans.push(e.span);
}
}
hir::intravisit::walk_expr(self, e);
}
fn visit_local(&mut self, local: &'hir hir::Local<'hir>) {
- if let hir::Pat { kind: hir::PatKind::Binding(_, hir_id, _ident, _), .. } = local.pat &&
- let Some(init) = local.init
+ if let hir::Pat { kind: hir::PatKind::Binding(_, hir_id, _ident, _), .. } =
+ local.pat
+ && let Some(init) = local.init
{
- if let hir::Expr { kind: hir::ExprKind::Closure(&hir::Closure {
- movability: None,
- ..
- }), .. } = init &&
- init.span.contains(self.capture_span) {
- self.closure_local_id = Some(*hir_id);
+ if let hir::Expr {
+ kind: hir::ExprKind::Closure(&hir::Closure { movability: None, .. }),
+ ..
+ } = init
+ && init.span.contains(self.capture_span)
+ {
+ self.closure_local_id = Some(*hir_id);
}
}
hir::intravisit::walk_local(self, local);
}
fn visit_stmt(&mut self, s: &'hir hir::Stmt<'hir>) {
- if let hir::StmtKind::Semi(e) = s.kind &&
- let hir::ExprKind::Call(hir::Expr { kind: hir::ExprKind::Path(path), ..}, args) = e.kind &&
- let hir::QPath::Resolved(_, hir::Path { segments: [seg], ..}) = path &&
- let Res::Local(hir_id) = seg.res &&
- Some(hir_id) == self.closure_local_id {
- let (span, arg_str) = if args.len() > 0 {
- (args[0].span.shrink_to_lo(), "self, ".to_string())
- } else {
- let span = e.span.trim_start(seg.ident.span).unwrap_or(e.span);
- (span, "(self)".to_string())
- };
- self.closure_call_changes.push((span, arg_str));
+ if let hir::StmtKind::Semi(e) = s.kind
+ && let hir::ExprKind::Call(
+ hir::Expr { kind: hir::ExprKind::Path(path), .. },
+ args,
+ ) = e.kind
+ && let hir::QPath::Resolved(_, hir::Path { segments: [seg], .. }) = path
+ && let Res::Local(hir_id) = seg.res
+ && Some(hir_id) == self.closure_local_id
+ {
+ let (span, arg_str) = if args.len() > 0 {
+ (args[0].span.shrink_to_lo(), "self, ".to_string())
+ } else {
+ let span = e.span.trim_start(seg.ident.span).unwrap_or(e.span);
+ (span, "(self)".to_string())
+ };
+ self.closure_call_changes.push((span, arg_str));
}
hir::intravisit::walk_stmt(self, s);
}
}
- if let Some(hir::Node::ImplItem(
- hir::ImplItem { kind: hir::ImplItemKind::Fn(_fn_sig, body_id), .. }
- )) = hir.find(self.mir_hir_id()) &&
- let Some(hir::Node::Expr(expr)) = hir.find(body_id.hir_id) {
+ if let Some(hir::Node::ImplItem(hir::ImplItem {
+ kind: hir::ImplItemKind::Fn(_fn_sig, body_id),
+ ..
+ })) = hir.find(self.mir_hir_id())
+ && let Some(hir::Node::Expr(expr)) = hir.find(body_id.hir_id)
+ {
let mut finder = ExpressionFinder {
capture_span: *capture_kind_span,
closure_change_spans: vec![],
@@ -1822,7 +1948,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
(
Some(name),
BorrowExplanation::UsedLater(LaterUseKind::ClosureCapture, var_or_use_span, _),
- ) if borrow_spans.for_generator() || borrow_spans.for_closure() => self
+ ) if borrow_spans.for_coroutine() || borrow_spans.for_closure() => self
.report_escaping_closure_capture(
borrow_spans,
borrow_span,
@@ -1847,7 +1973,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
span,
..
},
- ) if borrow_spans.for_generator() || borrow_spans.for_closure() => self
+ ) if borrow_spans.for_coroutine() || borrow_spans.for_closure() => self
.report_escaping_closure_capture(
borrow_spans,
borrow_span,
@@ -1950,8 +2076,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
.unwrap_or_else(|| {
match &self.infcx.tcx.def_kind(self.mir_def_id()) {
DefKind::Closure => "enclosing closure",
- DefKind::Generator => "enclosing generator",
- kind => bug!("expected closure or generator, found {:?}", kind),
+ DefKind::Coroutine => "enclosing coroutine",
+ kind => bug!("expected closure or coroutine, found {:?}", kind),
}
.to_string()
})
@@ -1985,7 +2111,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
borrow_spans.args_subdiag(&mut err, |args_span| {
crate::session_diagnostics::CaptureArgLabel::Capture {
- is_within: borrow_spans.for_generator(),
+ is_within: borrow_spans.for_coroutine(),
args_span,
}
});
@@ -2136,6 +2262,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
current: usize,
found: usize,
prop_expr: Option<&'tcx hir::Expr<'tcx>>,
+ call: Option<&'tcx hir::Expr<'tcx>>,
}
impl<'tcx> Visitor<'tcx> for NestedStatementVisitor<'tcx> {
@@ -2145,6 +2272,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
self.current -= 1;
}
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
+ if let hir::ExprKind::MethodCall(_, rcvr, _, _) = expr.kind {
+ if self.span == rcvr.span.source_callsite() {
+ self.call = Some(expr);
+ }
+ }
if self.span == expr.span.source_callsite() {
self.found = self.current;
if self.prop_expr.is_none() {
@@ -2168,25 +2300,43 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
current: 0,
found: 0,
prop_expr: None,
+ call: None,
};
visitor.visit_stmt(stmt);
let typeck_results = self.infcx.tcx.typeck(self.mir_def_id());
- let expr_ty: Option<Ty<'_>> = visitor.prop_expr.map(|expr| typeck_results.expr_ty(expr).peel_refs());
+ let expr_ty: Option<Ty<'_>> =
+ visitor.prop_expr.map(|expr| typeck_results.expr_ty(expr).peel_refs());
- let is_format_arguments_item =
- if let Some(expr_ty) = expr_ty
- && let ty::Adt(adt, _) = expr_ty.kind() {
- self.infcx.tcx.lang_items().get(LangItem::FormatArguments) == Some(adt.did())
- } else {
- false
- };
+ let is_format_arguments_item = if let Some(expr_ty) = expr_ty
+ && let ty::Adt(adt, _) = expr_ty.kind()
+ {
+ self.infcx.tcx.lang_items().get(LangItem::FormatArguments)
+ == Some(adt.did())
+ } else {
+ false
+ };
if visitor.found == 0
&& stmt.span.contains(proper_span)
&& let Some(p) = sm.span_to_margin(stmt.span)
&& let Ok(s) = sm.span_to_snippet(proper_span)
{
+ if let Some(call) = visitor.call
+ && let hir::ExprKind::MethodCall(path, _, [], _) = call.kind
+ && path.ident.name == sym::iter
+ && let Some(ty) = expr_ty
+ {
+ err.span_suggestion_verbose(
+ path.ident.span,
+ format!(
+ "consider consuming the `{ty}` when turning it into an \
+ `Iterator`",
+ ),
+ "into_iter".to_string(),
+ Applicability::MaybeIncorrect,
+ );
+ }
if !is_format_arguments_item {
let addition = format!("let binding = {};\n{}", s, " ".repeat(p));
err.multipart_suggestion_verbose(
@@ -2224,7 +2374,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
borrow_spans.args_subdiag(&mut err, |args_span| {
crate::session_diagnostics::CaptureArgLabel::Capture {
- is_within: borrow_spans.for_generator(),
+ is_within: borrow_spans.for_coroutine(),
args_span,
}
});
@@ -2340,11 +2490,17 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let (sugg_span, suggestion) = match tcx.sess.source_map().span_to_snippet(args_span) {
Ok(string) => {
- if string.starts_with("async ") {
- let pos = args_span.lo() + BytePos(6);
- (args_span.with_lo(pos).with_hi(pos), "move ")
- } else if string.starts_with("async|") {
- let pos = args_span.lo() + BytePos(5);
+ let coro_prefix = if string.starts_with("async") {
+ // `async` is 5 chars long. Not using `.len()` to avoid the cast from `usize` to `u32`
+ Some(5)
+ } else if string.starts_with("gen") {
+ // `gen` is 3 chars long
+ Some(3)
+ } else {
+ None
+ };
+ if let Some(n) = coro_prefix {
+ let pos = args_span.lo() + BytePos(n);
(args_span.with_lo(pos).with_hi(pos), " move")
} else {
(args_span.shrink_to_lo(), "move ")
@@ -2352,14 +2508,19 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}
Err(_) => (args_span, "move |<args>| <body>"),
};
- let kind = match use_span.generator_kind() {
- Some(generator_kind) => match generator_kind {
- GeneratorKind::Async(async_kind) => match async_kind {
- AsyncGeneratorKind::Block => "async block",
- AsyncGeneratorKind::Closure => "async closure",
+ let kind = match use_span.coroutine_kind() {
+ Some(coroutine_kind) => match coroutine_kind {
+ CoroutineKind::Gen(kind) => match kind {
+ CoroutineSource::Block => "gen block",
+ CoroutineSource::Closure => "gen closure",
+ _ => bug!("gen block/closure expected, but gen function found."),
+ },
+ CoroutineKind::Async(async_kind) => match async_kind {
+ CoroutineSource::Block => "async block",
+ CoroutineSource::Closure => "async closure",
_ => bug!("async block/closure expected, but async function found."),
},
- GeneratorKind::Gen => "generator",
+ CoroutineKind::Coroutine => "coroutine",
},
None => "closure",
};
@@ -2388,7 +2549,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}
ConstraintCategory::CallArgument(_) => {
fr_name.highlight_region_name(&mut err);
- if matches!(use_span.generator_kind(), Some(GeneratorKind::Async(_))) {
+ if matches!(use_span.coroutine_kind(), Some(CoroutineKind::Async(_))) {
err.note(
"async blocks are not executed immediately and must either take a \
reference or ownership of outside variables they use",
@@ -2482,9 +2643,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
/* Check if the mpi is initialized as an argument */
let mut is_argument = false;
for arg in self.body.args_iter() {
- let path = self.move_data.rev_lookup.find_local(arg);
- if mpis.contains(&path) {
- is_argument = true;
+ if let Some(path) = self.move_data.rev_lookup.find_local(arg) {
+ if mpis.contains(&path) {
+ is_argument = true;
+ }
}
}
@@ -2656,7 +2818,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
loan_spans.var_subdiag(None, &mut err, Some(loan.kind), |kind, var_span| {
use crate::session_diagnostics::CaptureVarCause::*;
match kind {
- Some(_) => BorrowUseInGenerator { var_span },
+ Some(_) => BorrowUseInCoroutine { var_span },
None => BorrowUseInClosure { var_span },
}
});
@@ -2672,7 +2834,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
loan_spans.var_subdiag(None, &mut err, Some(loan.kind), |kind, var_span| {
use crate::session_diagnostics::CaptureVarCause::*;
match kind {
- Some(_) => BorrowUseInGenerator { var_span },
+ Some(_) => BorrowUseInCoroutine { var_span },
None => BorrowUseInClosure { var_span },
}
});
diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
index c66a24473..8a930ca59 100644
--- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
@@ -76,10 +76,10 @@ impl<'tcx> BorrowExplanation<'tcx> {
expr_finder.visit_expr(body.value);
if let Some(mut expr) = expr_finder.result {
while let hir::ExprKind::AddrOf(_, _, inner)
- | hir::ExprKind::Unary(hir::UnOp::Deref, inner)
- | hir::ExprKind::Field(inner, _)
- | hir::ExprKind::MethodCall(_, inner, _, _)
- | hir::ExprKind::Index(inner, _, _) = &expr.kind
+ | hir::ExprKind::Unary(hir::UnOp::Deref, inner)
+ | hir::ExprKind::Field(inner, _)
+ | hir::ExprKind::MethodCall(_, inner, _, _)
+ | hir::ExprKind::Index(inner, _, _) = &expr.kind
{
expr = inner;
}
@@ -88,10 +88,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
&& let hir::def::Res::Local(hir_id) = p.res
&& let Some(hir::Node::Pat(pat)) = tcx.hir().find(hir_id)
{
- err.span_label(
- pat.span,
- format!("binding `{ident}` declared here"),
- );
+ err.span_label(pat.span, format!("binding `{ident}` declared here"));
}
}
}
@@ -185,7 +182,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
// Otherwise, just report the whole type (and use
// the intentionally fuzzy phrase "destructor")
ty::Closure(..) => ("destructor", "closure".to_owned()),
- ty::Generator(..) => ("destructor", "generator".to_owned()),
+ ty::Coroutine(..) => ("destructor", "coroutine".to_owned()),
_ => ("destructor", format!("type `{}`", local_decl.ty)),
};
@@ -419,7 +416,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
if self.local_names[local].is_some()
&& let Some((WriteKind::StorageDeadOrDrop, place)) = kind_place
&& let Some(borrowed_local) = place.as_local()
- && self.local_names[borrowed_local].is_some() && local != borrowed_local
+ && self.local_names[borrowed_local].is_some()
+ && local != borrowed_local
{
should_note_order = true;
}
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs
index 8d4028de9..c4323fef9 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mod.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -8,7 +8,7 @@ use itertools::Itertools;
use rustc_errors::{Applicability, Diagnostic};
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, Namespace};
-use rustc_hir::GeneratorKind;
+use rustc_hir::CoroutineKind;
use rustc_index::IndexSlice;
use rustc_infer::infer::LateBoundRegionConversionTime;
use rustc_middle::mir::tcx::PlaceTy;
@@ -46,6 +46,7 @@ mod mutability_errors;
mod region_errors;
pub(crate) use bound_region_errors::{ToUniverseInfo, UniverseInfo};
+pub(crate) use move_errors::{IllegalMoveOriginKind, MoveError};
pub(crate) use mutability_errors::AccessKind;
pub(crate) use outlives_suggestion::OutlivesSuggestionBuilder;
pub(crate) use region_errors::{ErrorConstraintInfo, RegionErrorKind, RegionErrors};
@@ -369,7 +370,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
ty::Array(ty, _) | ty::Slice(ty) => {
self.describe_field_from_ty(ty, field, variant_index, including_tuple_field)
}
- ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => {
+ ty::Closure(def_id, _) | ty::Coroutine(def_id, _, _) => {
// We won't be borrowck'ing here if the closure came from another crate,
// so it's safe to call `expect_local`.
//
@@ -470,7 +471,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}
}
- ty.print(printer).unwrap().into_buffer()
+ ty.print(&mut printer).unwrap();
+ printer.into_buffer()
}
/// Returns the name of the provided `Ty` (that must be a reference)'s region with a
@@ -492,7 +494,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
bug!("ty for annotation of borrow region is not a reference");
};
- region.print(printer).unwrap().into_buffer()
+ region.print(&mut printer).unwrap();
+ printer.into_buffer()
}
}
@@ -501,8 +504,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
pub(super) enum UseSpans<'tcx> {
/// The access is caused by capturing a variable for a closure.
ClosureUse {
- /// This is true if the captured variable was from a generator.
- generator_kind: Option<GeneratorKind>,
+ /// This is true if the captured variable was from a coroutine.
+ coroutine_kind: Option<CoroutineKind>,
/// The span of the args of the closure, including the `move` keyword if
/// it's present.
args_span: Span,
@@ -569,9 +572,9 @@ impl UseSpans<'_> {
}
}
- pub(super) fn generator_kind(self) -> Option<GeneratorKind> {
+ pub(super) fn coroutine_kind(self) -> Option<CoroutineKind> {
match self {
- UseSpans::ClosureUse { generator_kind, .. } => generator_kind,
+ UseSpans::ClosureUse { coroutine_kind, .. } => coroutine_kind,
_ => None,
}
}
@@ -596,14 +599,14 @@ impl UseSpans<'_> {
) {
use crate::InitializationRequiringAction::*;
use CaptureVarPathUseCause::*;
- if let UseSpans::ClosureUse { generator_kind, path_span, .. } = self {
- match generator_kind {
+ if let UseSpans::ClosureUse { coroutine_kind, path_span, .. } = self {
+ match coroutine_kind {
Some(_) => {
err.subdiagnostic(match action {
- Borrow => BorrowInGenerator { path_span },
- MatchOn | Use => UseInGenerator { path_span },
- Assignment => AssignInGenerator { path_span },
- PartialAssignment => AssignPartInGenerator { path_span },
+ Borrow => BorrowInCoroutine { path_span },
+ MatchOn | Use => UseInCoroutine { path_span },
+ Assignment => AssignInCoroutine { path_span },
+ PartialAssignment => AssignPartInCoroutine { path_span },
});
}
None => {
@@ -624,9 +627,9 @@ impl UseSpans<'_> {
handler: Option<&rustc_errors::Handler>,
err: &mut Diagnostic,
kind: Option<rustc_middle::mir::BorrowKind>,
- f: impl FnOnce(Option<GeneratorKind>, Span) -> CaptureVarCause,
+ f: impl FnOnce(Option<CoroutineKind>, Span) -> CaptureVarCause,
) {
- if let UseSpans::ClosureUse { generator_kind, capture_kind_span, path_span, .. } = self {
+ if let UseSpans::ClosureUse { coroutine_kind, capture_kind_span, path_span, .. } = self {
if capture_kind_span != path_span {
err.subdiagnostic(match kind {
Some(kd) => match kd {
@@ -642,7 +645,7 @@ impl UseSpans<'_> {
None => CaptureVarKind::Move { kind_span: capture_kind_span },
});
};
- let diag = f(generator_kind, path_span);
+ let diag = f(coroutine_kind, path_span);
match handler {
Some(hd) => err.eager_subdiagnostic(hd, diag),
None => err.subdiagnostic(diag),
@@ -653,15 +656,15 @@ impl UseSpans<'_> {
/// Returns `false` if this place is not used in a closure.
pub(super) fn for_closure(&self) -> bool {
match *self {
- UseSpans::ClosureUse { generator_kind, .. } => generator_kind.is_none(),
+ UseSpans::ClosureUse { coroutine_kind, .. } => coroutine_kind.is_none(),
_ => false,
}
}
- /// Returns `false` if this place is not used in a generator.
- pub(super) fn for_generator(&self) -> bool {
+ /// Returns `false` if this place is not used in a coroutine.
+ pub(super) fn for_coroutine(&self) -> bool {
match *self {
- UseSpans::ClosureUse { generator_kind, .. } => generator_kind.is_some(),
+ UseSpans::ClosureUse { coroutine_kind, .. } => coroutine_kind.is_some(),
_ => false,
}
}
@@ -780,19 +783,15 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
debug!("move_spans: moved_place={:?} location={:?} stmt={:?}", moved_place, location, stmt);
if let StatementKind::Assign(box (_, Rvalue::Aggregate(kind, places))) = &stmt.kind
- && let AggregateKind::Closure(def_id, _) | AggregateKind::Generator(def_id, _, _) = **kind
+ && let AggregateKind::Closure(def_id, _) | AggregateKind::Coroutine(def_id, _, _) =
+ **kind
{
debug!("move_spans: def_id={:?} places={:?}", def_id, places);
let def_id = def_id.expect_local();
- if let Some((args_span, generator_kind, capture_kind_span, path_span)) =
+ if let Some((args_span, coroutine_kind, capture_kind_span, path_span)) =
self.closure_span(def_id, moved_place, places)
{
- return ClosureUse {
- generator_kind,
- args_span,
- capture_kind_span,
- path_span,
- };
+ return ClosureUse { coroutine_kind, args_span, capture_kind_span, path_span };
}
}
@@ -804,11 +803,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
| FakeReadCause::ForLet(Some(closure_def_id)) => {
debug!("move_spans: def_id={:?} place={:?}", closure_def_id, place);
let places = &[Operand::Move(place)];
- if let Some((args_span, generator_kind, capture_kind_span, path_span)) =
+ if let Some((args_span, coroutine_kind, capture_kind_span, path_span)) =
self.closure_span(closure_def_id, moved_place, IndexSlice::from_raw(places))
{
return ClosureUse {
- generator_kind,
+ coroutine_kind,
args_span,
capture_kind_span,
path_span,
@@ -918,21 +917,21 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
for stmt in statements.chain(maybe_additional_statement) {
if let StatementKind::Assign(box (_, Rvalue::Aggregate(kind, places))) = &stmt.kind {
- let (&def_id, is_generator) = match kind {
+ let (&def_id, is_coroutine) = match kind {
box AggregateKind::Closure(def_id, _) => (def_id, false),
- box AggregateKind::Generator(def_id, _, _) => (def_id, true),
+ box AggregateKind::Coroutine(def_id, _, _) => (def_id, true),
_ => continue,
};
let def_id = def_id.expect_local();
debug!(
- "borrow_spans: def_id={:?} is_generator={:?} places={:?}",
- def_id, is_generator, places
+ "borrow_spans: def_id={:?} is_coroutine={:?} places={:?}",
+ def_id, is_coroutine, places
);
- if let Some((args_span, generator_kind, capture_kind_span, path_span)) =
+ if let Some((args_span, coroutine_kind, capture_kind_span, path_span)) =
self.closure_span(def_id, Place::from(target).as_ref(), places)
{
- return ClosureUse { generator_kind, args_span, capture_kind_span, path_span };
+ return ClosureUse { coroutine_kind, args_span, capture_kind_span, path_span };
} else {
return OtherUse(use_span);
}
@@ -946,7 +945,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
OtherUse(use_span)
}
- /// Finds the spans of a captured place within a closure or generator.
+ /// Finds the spans of a captured place within a closure or coroutine.
/// The first span is the location of the use resulting in the capture kind of the capture
/// The second span is the location the use resulting in the captured path of the capture
fn closure_span(
@@ -954,7 +953,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
def_id: LocalDefId,
target_place: PlaceRef<'tcx>,
places: &IndexSlice<FieldIdx, Operand<'tcx>>,
- ) -> Option<(Span, Option<GeneratorKind>, Span, Span)> {
+ ) -> Option<(Span, Option<CoroutineKind>, Span, Span)> {
debug!(
"closure_span: def_id={:?} target_place={:?} places={:?}",
def_id, target_place, places
@@ -972,11 +971,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
{
debug!("closure_span: found captured local {:?}", place);
let body = self.infcx.tcx.hir().body(body);
- let generator_kind = body.generator_kind();
+ let coroutine_kind = body.coroutine_kind();
return Some((
fn_decl_span,
- generator_kind,
+ coroutine_kind,
captured_place.get_capture_kind_span(self.infcx.tcx),
captured_place.get_path_span(self.infcx.tcx),
));
@@ -1123,7 +1122,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
&self.infcx.tcx.sess.parse_sess.span_diagnostic,
CaptureReasonSuggest::FreshReborrow {
span: move_span.shrink_to_hi(),
- });
+ },
+ );
}
if let Some(clone_trait) = tcx.lang_items().clone_trait()
&& let trait_ref = ty::TraitRef::new(tcx, clone_trait, [ty])
@@ -1191,7 +1191,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
// another message for the same span
if !is_loop_message {
move_spans.var_subdiag(None, err, None, |kind, var_span| match kind {
- Some(_) => CaptureVarCause::PartialMoveUseInGenerator { var_span, is_partial },
+ Some(_) => CaptureVarCause::PartialMoveUseInCoroutine { 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 e05c04e11..41d6b98d7 100644
--- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
@@ -1,16 +1,50 @@
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
use rustc_middle::mir::*;
-use rustc_middle::ty;
-use rustc_mir_dataflow::move_paths::{
- IllegalMoveOrigin, IllegalMoveOriginKind, LookupResult, MoveError, MovePathIndex,
-};
-use rustc_span::{BytePos, Span};
+use rustc_middle::ty::{self, Ty};
+use rustc_mir_dataflow::move_paths::{LookupResult, MovePathIndex};
+use rustc_span::{BytePos, ExpnKind, MacroKind, Span};
use crate::diagnostics::CapturedMessageOpt;
use crate::diagnostics::{DescribePlaceOpt, UseSpans};
use crate::prefixes::PrefixSet;
use crate::MirBorrowckCtxt;
+#[derive(Debug)]
+pub enum IllegalMoveOriginKind<'tcx> {
+ /// Illegal move due to attempt to move from behind a reference.
+ BorrowedContent {
+ /// The place the reference refers to: if erroneous code was trying to
+ /// move from `(*x).f` this will be `*x`.
+ target_place: Place<'tcx>,
+ },
+
+ /// Illegal move due to attempt to move from field of an ADT that
+ /// implements `Drop`. Rust maintains invariant that all `Drop`
+ /// ADT's remain fully-initialized so that user-defined destructor
+ /// can safely read from all of the ADT's fields.
+ InteriorOfTypeWithDestructor { container_ty: Ty<'tcx> },
+
+ /// Illegal move due to attempt to move out of a slice or array.
+ InteriorOfSliceOrArray { ty: Ty<'tcx>, is_index: bool },
+}
+
+#[derive(Debug)]
+pub(crate) struct MoveError<'tcx> {
+ place: Place<'tcx>,
+ location: Location,
+ kind: IllegalMoveOriginKind<'tcx>,
+}
+
+impl<'tcx> MoveError<'tcx> {
+ pub(crate) fn new(
+ place: Place<'tcx>,
+ location: Location,
+ kind: IllegalMoveOriginKind<'tcx>,
+ ) -> Self {
+ MoveError { place, location, kind }
+ }
+}
+
// Often when desugaring a pattern match we may have many individual moves in
// MIR that are all part of one operation from the user's point-of-view. For
// example:
@@ -53,20 +87,18 @@ enum GroupedMoveError<'tcx> {
}
impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
- pub(crate) fn report_move_errors(&mut self, move_errors: Vec<(Place<'tcx>, MoveError<'tcx>)>) {
- let grouped_errors = self.group_move_errors(move_errors);
+ pub(crate) fn report_move_errors(&mut self) {
+ let grouped_errors = self.group_move_errors();
for error in grouped_errors {
self.report(error);
}
}
- fn group_move_errors(
- &self,
- errors: Vec<(Place<'tcx>, MoveError<'tcx>)>,
- ) -> Vec<GroupedMoveError<'tcx>> {
+ fn group_move_errors(&mut self) -> Vec<GroupedMoveError<'tcx>> {
let mut grouped_errors = Vec::new();
- for (original_path, error) in errors {
- self.append_to_grouped_errors(&mut grouped_errors, original_path, error);
+ let errors = std::mem::take(&mut self.move_errors);
+ for error in errors {
+ self.append_to_grouped_errors(&mut grouped_errors, error);
}
grouped_errors
}
@@ -74,66 +106,58 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
fn append_to_grouped_errors(
&self,
grouped_errors: &mut Vec<GroupedMoveError<'tcx>>,
- original_path: Place<'tcx>,
error: MoveError<'tcx>,
) {
- match error {
- MoveError::UnionMove { .. } => {
- unimplemented!("don't know how to report union move errors yet.")
- }
- MoveError::IllegalMove { cannot_move_out_of: IllegalMoveOrigin { location, kind } } => {
- // Note: that the only time we assign a place isn't a temporary
- // to a user variable is when initializing it.
- // If that ever stops being the case, then the ever initialized
- // flow could be used.
- if let Some(StatementKind::Assign(box (
- place,
- Rvalue::Use(Operand::Move(move_from)),
- ))) = self.body.basic_blocks[location.block]
- .statements
- .get(location.statement_index)
- .map(|stmt| &stmt.kind)
+ let MoveError { place: original_path, location, kind } = error;
+
+ // Note: that the only time we assign a place isn't a temporary
+ // to a user variable is when initializing it.
+ // If that ever stops being the case, then the ever initialized
+ // flow could be used.
+ if let Some(StatementKind::Assign(box (place, Rvalue::Use(Operand::Move(move_from))))) =
+ self.body.basic_blocks[location.block]
+ .statements
+ .get(location.statement_index)
+ .map(|stmt| &stmt.kind)
+ {
+ if let Some(local) = place.as_local() {
+ let local_decl = &self.body.local_decls[local];
+ // opt_match_place is the
+ // match_span is the span of the expression being matched on
+ // match *x.y { ... } match_place is Some(*x.y)
+ // ^^^^ match_span is the span of *x.y
+ //
+ // opt_match_place is None for let [mut] x = ... statements,
+ // whether or not the right-hand side is a place expression
+ if let LocalInfo::User(BindingForm::Var(VarBindingForm {
+ opt_match_place: Some((opt_match_place, match_span)),
+ binding_mode: _,
+ opt_ty_info: _,
+ pat_span: _,
+ })) = *local_decl.local_info()
{
- if let Some(local) = place.as_local() {
- let local_decl = &self.body.local_decls[local];
- // opt_match_place is the
- // match_span is the span of the expression being matched on
- // match *x.y { ... } match_place is Some(*x.y)
- // ^^^^ match_span is the span of *x.y
- //
- // opt_match_place is None for let [mut] x = ... statements,
- // whether or not the right-hand side is a place expression
- if let LocalInfo::User(BindingForm::Var(VarBindingForm {
- opt_match_place: Some((opt_match_place, match_span)),
- binding_mode: _,
- opt_ty_info: _,
- pat_span: _,
- })) = *local_decl.local_info()
- {
- let stmt_source_info = self.body.source_info(location);
- self.append_binding_error(
- grouped_errors,
- kind,
- original_path,
- *move_from,
- local,
- opt_match_place,
- match_span,
- stmt_source_info.span,
- );
- return;
- }
- }
+ let stmt_source_info = self.body.source_info(location);
+ self.append_binding_error(
+ grouped_errors,
+ kind,
+ original_path,
+ *move_from,
+ local,
+ opt_match_place,
+ match_span,
+ stmt_source_info.span,
+ );
+ return;
}
-
- let move_spans = self.move_spans(original_path.as_ref(), location);
- grouped_errors.push(GroupedMoveError::OtherIllegalMove {
- use_spans: move_spans,
- original_path,
- kind,
- });
}
}
+
+ let move_spans = self.move_spans(original_path.as_ref(), location);
+ grouped_errors.push(GroupedMoveError::OtherIllegalMove {
+ use_spans: move_spans,
+ original_path,
+ kind,
+ });
}
fn append_binding_error(
@@ -464,6 +488,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
args_span,
}
});
+
+ self.add_note_for_packed_struct_derive(err, original_path.local);
}
}
}
@@ -570,4 +596,20 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
);
}
}
+
+ /// Adds an explanatory note if the move error occurs in a derive macro
+ /// expansion of a packed struct.
+ /// Such errors happen because derive macro expansions shy away from taking
+ /// references to the struct's fields since doing so would be undefined behaviour
+ fn add_note_for_packed_struct_derive(&self, err: &mut Diagnostic, local: Local) {
+ let local_place: PlaceRef<'tcx> = local.into();
+ let local_ty = local_place.ty(self.body.local_decls(), self.infcx.tcx).ty.peel_refs();
+
+ if let Some(adt) = local_ty.ty_adt_def()
+ && adt.repr().packed()
+ && let ExpnKind::Macro(MacroKind::Derive, name) = self.body.span.ctxt().outer_expn_data().kind
+ {
+ err.note(format!("`#[derive({name})]` triggers a move because taking references to the fields of a packed struct is undefined behaviour"));
+ }
+ }
}
diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
index 8ca57383e..dde46eef6 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
@@ -9,9 +9,8 @@ use rustc_middle::{
hir::place::PlaceBase,
mir::{self, BindingForm, Local, LocalDecl, LocalInfo, LocalKind, Location},
};
-use rustc_span::source_map::DesugaringKind;
use rustc_span::symbol::{kw, Symbol};
-use rustc_span::{sym, BytePos, Span};
+use rustc_span::{sym, BytePos, DesugaringKind, Span};
use rustc_target::abi::FieldIdx;
use crate::diagnostics::BorrowedContentSource;
@@ -62,7 +61,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
local,
projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)],
} => {
- debug_assert!(is_closure_or_generator(
+ debug_assert!(is_closure_or_coroutine(
Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty
));
@@ -122,7 +121,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
{
item_msg = access_place_desc;
debug_assert!(self.body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty.is_ref());
- debug_assert!(is_closure_or_generator(
+ debug_assert!(is_closure_or_coroutine(
the_place_err.ty(self.body, self.infcx.tcx).ty
));
@@ -385,7 +384,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
local,
projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)],
} => {
- debug_assert!(is_closure_or_generator(
+ debug_assert!(is_closure_or_coroutine(
Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty
));
@@ -396,17 +395,16 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
let upvar_hir_id = captured_place.get_root_variable();
if let Some(Node::Pat(pat)) = self.infcx.tcx.hir().find(upvar_hir_id)
- && let hir::PatKind::Binding(
- hir::BindingAnnotation::NONE,
- _,
- upvar_ident,
- _,
- ) = pat.kind
+ && let hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, upvar_ident, _) =
+ pat.kind
{
if upvar_ident.name == kw::SelfLower {
for (_, node) in self.infcx.tcx.hir().parent_iter(upvar_hir_id) {
if let Some(fn_decl) = node.fn_decl() {
- if !matches!(fn_decl.implicit_self, hir::ImplicitSelfKind::ImmRef | hir::ImplicitSelfKind::MutRef) {
+ if !matches!(
+ fn_decl.implicit_self,
+ hir::ImplicitSelfKind::ImmRef | hir::ImplicitSelfKind::MutRef
+ ) {
err.span_suggestion(
upvar_ident.span,
"consider changing this to be mutable",
@@ -573,7 +571,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
self.ty,
),
vec![
- vec![ // val.insert(index, rv);
+ vec![
+ // val.insert(index, rv);
(
val.span.shrink_to_hi().with_hi(index.span.lo()),
".insert(".to_string(),
@@ -584,7 +583,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
),
(rv.span.shrink_to_hi(), ")".to_string()),
],
- vec![ // val.get_mut(index).map(|v| { *v = rv; });
+ vec![
+ // val.get_mut(index).map(|v| { *v = rv; });
(
val.span.shrink_to_hi().with_hi(index.span.lo()),
".get_mut(".to_string(),
@@ -593,12 +593,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
index.span.shrink_to_hi().with_hi(place.span.hi()),
").map(|val| { *val".to_string(),
),
- (
- rv.span.shrink_to_hi(),
- "; })".to_string(),
- ),
+ (rv.span.shrink_to_hi(), "; })".to_string()),
],
- vec![ // let x = val.entry(index).or_insert(rv);
+ vec![
+ // let x = val.entry(index).or_insert(rv);
(val.span.shrink_to_lo(), "let val = ".to_string()),
(
val.span.shrink_to_hi().with_hi(index.span.lo()),
@@ -747,10 +745,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
&& let Some(body_id) = hir_map.maybe_body_owned_by(local_def_id)
{
let body = hir_map.body(body_id);
- let mut v = BindingFinder {
- span: pat_span,
- hir_id: None,
- };
+ let mut v = BindingFinder { span: pat_span, hir_id: None };
v.visit_body(body);
v.hir_id
} else {
@@ -766,7 +761,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
pat: hir::Pat { kind: hir::PatKind::Ref(_, _), .. },
..
})) = hir_map.find(hir_id)
- && let Ok(name) = self.infcx.tcx.sess.source_map().span_to_snippet(local_decl.source_info.span)
+ && let Ok(name) =
+ self.infcx.tcx.sess.source_map().span_to_snippet(local_decl.source_info.span)
{
err.span_suggestion(
pat_span,
@@ -879,12 +875,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
// `span` corresponds to the expression being iterated, find the `for`-loop desugared
// expression with that span in order to identify potential fixes when encountering a
// read-only iterator that should be mutable.
- let mut v = Finder {
- span,
- expr: None,
- };
+ let mut v = Finder { span, expr: None };
v.visit_block(block);
- if let Some(expr) = v.expr && let Call(_, [expr]) = expr.kind {
+ if let Some(expr) = v.expr
+ && let Call(_, [expr]) = expr.kind
+ {
match expr.kind {
MethodCall(path_segment, _, _, span) => {
// We have `for _ in iter.read_only_iter()`, try to
@@ -1032,38 +1027,42 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
let source = self.body.source;
let hir = self.infcx.tcx.hir();
if let InstanceDef::Item(def_id) = source.instance
- && let Some(Node::Expr(hir::Expr { hir_id, kind, ..})) = hir.get_if_local(def_id)
- && let ExprKind::Closure(closure) = kind && closure.movability == None
- && let Some(Node::Expr(expr)) = hir.find_parent(*hir_id) {
- let mut cur_expr = expr;
- while let ExprKind::MethodCall(path_segment, recv, _, _) = cur_expr.kind {
- if path_segment.ident.name == sym::iter {
- // check `_ty` has `iter_mut` method
- let res = self
- .infcx
- .tcx
- .typeck(path_segment.hir_id.owner.def_id)
- .type_dependent_def_id(cur_expr.hir_id)
- .and_then(|def_id| self.infcx.tcx.impl_of_method(def_id))
- .map(|def_id| self.infcx.tcx.associated_items(def_id))
- .map(|assoc_items| {
- assoc_items.filter_by_name_unhygienic(sym::iter_mut).peekable()
- });
-
- if let Some(mut res) = res && res.peek().is_some() {
- err.span_suggestion_verbose(
- path_segment.ident.span,
- "you may want to use `iter_mut` here",
- "iter_mut",
- Applicability::MaybeIncorrect,
- );
- }
- break;
- } else {
- cur_expr = recv;
+ && let Some(Node::Expr(hir::Expr { hir_id, kind, .. })) = hir.get_if_local(def_id)
+ && let ExprKind::Closure(closure) = kind
+ && closure.movability == None
+ && let Some(Node::Expr(expr)) = hir.find_parent(*hir_id)
+ {
+ let mut cur_expr = expr;
+ while let ExprKind::MethodCall(path_segment, recv, _, _) = cur_expr.kind {
+ if path_segment.ident.name == sym::iter {
+ // check `_ty` has `iter_mut` method
+ let res = self
+ .infcx
+ .tcx
+ .typeck(path_segment.hir_id.owner.def_id)
+ .type_dependent_def_id(cur_expr.hir_id)
+ .and_then(|def_id| self.infcx.tcx.impl_of_method(def_id))
+ .map(|def_id| self.infcx.tcx.associated_items(def_id))
+ .map(|assoc_items| {
+ assoc_items.filter_by_name_unhygienic(sym::iter_mut).peekable()
+ });
+
+ if let Some(mut res) = res
+ && res.peek().is_some()
+ {
+ err.span_suggestion_verbose(
+ path_segment.ident.span,
+ "you may want to use `iter_mut` here",
+ "iter_mut",
+ Applicability::MaybeIncorrect,
+ );
}
+ break;
+ } else {
+ cur_expr = recv;
}
}
+ }
}
fn suggest_make_local_mut(
@@ -1200,14 +1199,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
}
let hir_map = self.infcx.tcx.hir();
let def_id = self.body.source.def_id();
- let hir_id = if let Some(local_def_id) = def_id.as_local() &&
- let Some(body_id) = hir_map.maybe_body_owned_by(local_def_id)
+ let hir_id = if let Some(local_def_id) = def_id.as_local()
+ && let Some(body_id) = hir_map.maybe_body_owned_by(local_def_id)
{
let body = hir_map.body(body_id);
- let mut v = BindingFinder {
- span: err_label_span,
- hir_id: None,
- };
+ let mut v = BindingFinder { span: err_label_span, hir_id: None };
v.visit_body(body);
v.hir_id
} else {
@@ -1215,15 +1211,13 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
};
if let Some(hir_id) = hir_id
- && let Some(hir::Node::Local(local)) = hir_map.find(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}"),
- ),
+ None => {
+ ("specifying", local.pat.span.shrink_to_hi(), format!(": {message}"))
+ }
};
err.span_suggestion_verbose(
span,
@@ -1234,9 +1228,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
} else {
err.span_label(
err_label_span,
- format!(
- "consider changing this binding's type to be: `{message}`"
- ),
+ format!("consider changing this binding's type to be: `{message}`"),
);
}
}
@@ -1359,9 +1351,9 @@ fn suggest_ampmut<'tcx>(
None => (false, decl_span),
};
- // if the binding already exists and is a reference with a explicit
+ // if the binding already exists and is a reference with an explicit
// lifetime, then we can suggest adding ` mut`. this is special-cased from
- // the path without a explicit lifetime.
+ // the path without an 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.
@@ -1380,16 +1372,12 @@ fn suggest_ampmut<'tcx>(
let ty_mut = decl_ty.builtin_deref(true).unwrap();
assert_eq!(ty_mut.mutbl, hir::Mutability::Not);
- (
- false,
- span,
- format!("{}mut {}", if decl_ty.is_ref() {"&"} else {"*"}, ty_mut.ty)
- )
+ (false, span, format!("{}mut {}", if decl_ty.is_ref() { "&" } else { "*" }, ty_mut.ty))
}
}
-fn is_closure_or_generator(ty: Ty<'_>) -> bool {
- ty.is_closure() || ty.is_generator()
+fn is_closure_or_coroutine(ty: Ty<'_>) -> bool {
+ ty.is_closure() || ty.is_coroutine()
}
/// Given a field that needs to be mutable, returns a span where the " mut " could go.
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
index 27072a60f..a0a809123 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
@@ -580,7 +580,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
let err = FnMutError {
span: *span,
ty_err: match output_ty.kind() {
- ty::Generator(def, ..) if self.infcx.tcx.generator_is_async(*def) => {
+ ty::Coroutine(def, ..) if self.infcx.tcx.coroutine_is_async(*def) => {
FnMutReturnTypeErr::ReturnAsyncBlock { span: *span }
}
_ if output_ty.contains_closure() => {
@@ -1036,7 +1036,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
..
}) => {
let body = map.body(*body);
- if !matches!(body.generator_kind, Some(hir::GeneratorKind::Async(..))) {
+ if !matches!(body.coroutine_kind, Some(hir::CoroutineKind::Async(..))) {
closure_span = Some(expr.span.shrink_to_lo());
}
}
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
index 55d581b3a..d38cfbc54 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
@@ -41,7 +41,7 @@ pub(crate) enum RegionNameSource {
AnonRegionFromUpvar(Span, Symbol),
/// The region corresponding to the return type of a closure.
AnonRegionFromOutput(RegionNameHighlight, &'static str),
- /// The region from a type yielded by a generator.
+ /// The region from a type yielded by a coroutine.
AnonRegionFromYieldTy(Span, String),
/// An anonymous region from an async fn.
AnonRegionFromAsyncFn(Span),
@@ -322,7 +322,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
let def_ty = self.regioncx.universal_regions().defining_ty;
let DefiningTy::Closure(_, args) = def_ty else {
- // Can't have BrEnv in functions, constants or generators.
+ // Can't have BrEnv in functions, constants or coroutines.
bug!("BrEnv outside of closure.");
};
let hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }) =
@@ -680,16 +680,16 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
}
hir::FnRetTy::Return(hir_ty) => (fn_decl.output.span(), Some(hir_ty)),
};
- let mir_description = match hir.body(body).generator_kind {
- Some(hir::GeneratorKind::Async(gen)) => match gen {
- hir::AsyncGeneratorKind::Block => " of async block",
- hir::AsyncGeneratorKind::Closure => " of async closure",
- hir::AsyncGeneratorKind::Fn => {
+ let mir_description = match hir.body(body).coroutine_kind {
+ Some(hir::CoroutineKind::Async(gen)) => match gen {
+ hir::CoroutineSource::Block => " of async block",
+ hir::CoroutineSource::Closure => " of async closure",
+ hir::CoroutineSource::Fn => {
let parent_item =
hir.get_by_def_id(hir.get_parent_item(mir_hir_id).def_id);
let output = &parent_item
.fn_decl()
- .expect("generator lowered from async fn should be in fn")
+ .expect("coroutine lowered from async fn should be in fn")
.output;
span = output.span();
if let hir::FnRetTy::Return(ret) = output {
@@ -698,7 +698,21 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
" of async function"
}
},
- Some(hir::GeneratorKind::Gen) => " of generator",
+ Some(hir::CoroutineKind::Gen(gen)) => match gen {
+ hir::CoroutineSource::Block => " of gen block",
+ hir::CoroutineSource::Closure => " of gen closure",
+ hir::CoroutineSource::Fn => {
+ let parent_item =
+ hir.get_by_def_id(hir.get_parent_item(mir_hir_id).def_id);
+ let output = &parent_item
+ .fn_decl()
+ .expect("coroutine lowered from gen fn should be in fn")
+ .output;
+ span = output.span();
+ " of gen function"
+ }
+ },
+ Some(hir::CoroutineKind::Coroutine) => " of coroutine",
None => " of closure",
};
(span, mir_description, hir_ty)
@@ -793,7 +807,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
&self,
fr: RegionVid,
) -> Option<RegionName> {
- // Note: generators from `async fn` yield `()`, so we don't have to
+ // Note: coroutines from `async fn` yield `()`, so we don't have to
// worry about them here.
let yield_ty = self.regioncx.universal_regions().yield_ty?;
debug!("give_name_if_anonymous_region_appears_in_yield_ty: yield_ty = {:?}", yield_ty);
@@ -942,9 +956,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
ty::ClauseKind::Projection(data) if data.projection_ty.self_ty() == ty => {}
_ => return false,
}
- tcx.any_free_region_meets(pred, |r| {
- *r == ty::ReEarlyBound(region)
- })
+ tcx.any_free_region_meets(pred, |r| *r == ty::ReEarlyBound(region))
})
} else {
false
diff --git a/compiler/rustc_borrowck/src/diagnostics/var_name.rs b/compiler/rustc_borrowck/src/diagnostics/var_name.rs
index 8832d345d..3a104c524 100644
--- a/compiler/rustc_borrowck/src/diagnostics/var_name.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/var_name.rs
@@ -6,8 +6,8 @@ use crate::Upvar;
use rustc_index::IndexSlice;
use rustc_middle::mir::{Body, Local};
use rustc_middle::ty::{RegionVid, TyCtxt};
-use rustc_span::source_map::Span;
use rustc_span::symbol::Symbol;
+use rustc_span::Span;
impl<'tcx> RegionInferenceContext<'tcx> {
pub(crate) fn get_var_name_and_span_for_region(
diff --git a/compiler/rustc_borrowck/src/facts.rs b/compiler/rustc_borrowck/src/facts.rs
index 9916ebca3..c54e70704 100644
--- a/compiler/rustc_borrowck/src/facts.rs
+++ b/compiler/rustc_borrowck/src/facts.rs
@@ -41,7 +41,8 @@ pub(crate) trait AllFactsExt {
impl AllFactsExt for AllFacts {
/// Return
fn enabled(tcx: TyCtxt<'_>) -> bool {
- tcx.sess.opts.unstable_opts.nll_facts || tcx.sess.opts.unstable_opts.polonius
+ tcx.sess.opts.unstable_opts.nll_facts
+ || tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled()
}
fn write_to_dir(
diff --git a/compiler/rustc_borrowck/src/invalidation.rs b/compiler/rustc_borrowck/src/invalidation.rs
index 2faf1a529..ec6d7b74e 100644
--- a/compiler/rustc_borrowck/src/invalidation.rs
+++ b/compiler/rustc_borrowck/src/invalidation.rs
@@ -161,7 +161,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> {
}
TerminatorKind::UnwindResume
| TerminatorKind::Return
- | TerminatorKind::GeneratorDrop => {
+ | TerminatorKind::CoroutineDrop => {
// Invalidate all borrows of local places
let borrow_set = self.borrow_set;
let start = self.location_table.start_index(location);
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index 1d17df8b7..5787ea13e 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -1,5 +1,8 @@
//! This query borrow-checks the MIR to (further) ensure it is not broken.
+#![allow(internal_features)]
+#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
+#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![feature(associated_type_bounds)]
#![feature(box_patterns)]
#![feature(let_chains)]
@@ -11,7 +14,6 @@
#![feature(trusted_step)]
#![feature(try_blocks)]
#![recursion_limit = "256"]
-#![allow(internal_features)]
#[macro_use]
extern crate rustc_middle;
@@ -29,13 +31,8 @@ use rustc_index::{IndexSlice, IndexVec};
use rustc_infer::infer::{
InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin, TyCtxtInferExt,
};
-use rustc_middle::mir::{
- traversal, Body, ClearCrossCrate, Local, Location, MutBorrowKind, Mutability,
- NonDivergingIntrinsic, Operand, Place, PlaceElem, PlaceRef, VarDebugInfoContents,
-};
-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::mir::tcx::PlaceTy;
+use rustc_middle::mir::*;
use rustc_middle::query::Providers;
use rustc_middle::traits::DefiningAnchor;
use rustc_middle::ty::{self, CapturedPlace, ParamEnv, RegionVid, TyCtxt};
@@ -53,13 +50,13 @@ use rustc_mir_dataflow::impls::{
EverInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces,
};
use rustc_mir_dataflow::move_paths::{InitIndex, MoveOutIndex, MovePathIndex};
-use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult, MoveData, MoveError};
+use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult, MoveData};
use rustc_mir_dataflow::Analysis;
use rustc_mir_dataflow::MoveDataParamEnv;
use crate::session_diagnostics::VarNeedNotMut;
-use self::diagnostics::{AccessKind, RegionName};
+use self::diagnostics::{AccessKind, IllegalMoveOriginKind, MoveError, RegionName};
use self::location::LocationTable;
use self::prefixes::PrefixSet;
use consumers::{BodyWithBorrowckFacts, ConsumerOptions};
@@ -173,7 +170,9 @@ fn do_mir_borrowck<'tcx>(
for var_debug_info in &input_body.var_debug_info {
if let VarDebugInfoContents::Place(place) = var_debug_info.value {
if let Some(local) = place.as_local() {
- if let Some(prev_name) = local_names[local] && var_debug_info.name != prev_name {
+ if let Some(prev_name) = local_names[local]
+ && var_debug_info.name != prev_name
+ {
span_bug!(
var_debug_info.source_info.span,
"local {:?} has many names (`{}` vs `{}`)",
@@ -220,14 +219,10 @@ fn do_mir_borrowck<'tcx>(
let location_table_owned = LocationTable::new(body);
let location_table = &location_table_owned;
- let (move_data, move_errors): (MoveData<'tcx>, Vec<(Place<'tcx>, MoveError<'tcx>)>) =
- match MoveData::gather_moves(&body, tcx, param_env) {
- Ok(move_data) => (move_data, Vec::new()),
- Err((move_data, move_errors)) => (move_data, move_errors),
- };
- let promoted_errors = promoted
+ let move_data = MoveData::gather_moves(&body, tcx, param_env, |_| true);
+ let promoted_move_data = promoted
.iter_enumerated()
- .map(|(idx, body)| (idx, MoveData::gather_moves(&body, tcx, param_env)));
+ .map(|(idx, body)| (idx, MoveData::gather_moves(&body, tcx, param_env, |_| true)));
let mdpe = MoveDataParamEnv { move_data, param_env };
@@ -298,47 +293,60 @@ fn do_mir_borrowck<'tcx>(
.pass_name("borrowck")
.iterate_to_fixpoint();
- let movable_generator =
- // The first argument is the generator type passed by value
+ let movable_coroutine =
+ // The first argument is the coroutine type passed by value
if let Some(local) = body.local_decls.raw.get(1)
// Get the interior types and args which typeck computed
- && let ty::Generator(_, _, hir::Movability::Static) = local.ty.kind()
+ && let ty::Coroutine(_, _, hir::Movability::Static) = local.ty.kind()
{
false
} else {
true
};
- for (idx, move_data_results) in promoted_errors {
- let promoted_body = &promoted[idx];
+ for (idx, move_data) in promoted_move_data {
+ use rustc_middle::mir::visit::Visitor;
- if let Err((move_data, move_errors)) = move_data_results {
- let mut promoted_mbcx = MirBorrowckCtxt {
- infcx: &infcx,
- param_env,
- body: promoted_body,
- move_data: &move_data,
- location_table, // no need to create a real one for the promoted, it is not used
- movable_generator,
- fn_self_span_reported: Default::default(),
- locals_are_invalidated_at_exit,
- access_place_error_reported: Default::default(),
- reservation_error_reported: Default::default(),
- uninitialized_error_reported: Default::default(),
- regioncx: regioncx.clone(),
- used_mut: Default::default(),
- used_mut_upvars: SmallVec::new(),
- borrow_set: Rc::clone(&borrow_set),
- upvars: Vec::new(),
- local_names: IndexVec::from_elem(None, &promoted_body.local_decls),
- region_names: RefCell::default(),
- next_region_name: RefCell::new(1),
- polonius_output: None,
- errors,
- };
- promoted_mbcx.report_move_errors(move_errors);
- errors = promoted_mbcx.errors;
+ let promoted_body = &promoted[idx];
+ let mut promoted_mbcx = MirBorrowckCtxt {
+ infcx: &infcx,
+ param_env,
+ body: promoted_body,
+ move_data: &move_data,
+ location_table, // no need to create a real one for the promoted, it is not used
+ movable_coroutine,
+ fn_self_span_reported: Default::default(),
+ locals_are_invalidated_at_exit,
+ access_place_error_reported: Default::default(),
+ reservation_error_reported: Default::default(),
+ uninitialized_error_reported: Default::default(),
+ regioncx: regioncx.clone(),
+ used_mut: Default::default(),
+ used_mut_upvars: SmallVec::new(),
+ borrow_set: Rc::clone(&borrow_set),
+ upvars: Vec::new(),
+ local_names: IndexVec::from_elem(None, &promoted_body.local_decls),
+ region_names: RefCell::default(),
+ next_region_name: RefCell::new(1),
+ polonius_output: None,
+ move_errors: Vec::new(),
+ errors,
};
+ MoveVisitor { ctxt: &mut promoted_mbcx }.visit_body(promoted_body);
+ promoted_mbcx.report_move_errors();
+ errors = promoted_mbcx.errors;
+
+ struct MoveVisitor<'a, 'cx, 'tcx> {
+ ctxt: &'a mut MirBorrowckCtxt<'cx, 'tcx>,
+ }
+
+ impl<'tcx> Visitor<'tcx> for MoveVisitor<'_, '_, 'tcx> {
+ fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
+ if let Operand::Move(place) = operand {
+ self.ctxt.check_movable_place(location, *place);
+ }
+ }
+ }
}
let mut mbcx = MirBorrowckCtxt {
@@ -347,7 +355,7 @@ fn do_mir_borrowck<'tcx>(
body,
move_data: &mdpe.move_data,
location_table,
- movable_generator,
+ movable_coroutine,
locals_are_invalidated_at_exit,
fn_self_span_reported: Default::default(),
access_place_error_reported: Default::default(),
@@ -362,6 +370,7 @@ fn do_mir_borrowck<'tcx>(
region_names: RefCell::default(),
next_region_name: RefCell::new(1),
polonius_output,
+ move_errors: Vec::new(),
errors,
};
@@ -374,8 +383,6 @@ fn do_mir_borrowck<'tcx>(
borrows: flow_borrows,
};
- mbcx.report_move_errors(move_errors);
-
rustc_mir_dataflow::visit_results(
body,
traversal::reverse_postorder(body).map(|(bb, _)| bb),
@@ -383,6 +390,8 @@ fn do_mir_borrowck<'tcx>(
&mut mbcx,
);
+ mbcx.report_move_errors();
+
// For each non-user used mutable variable, check if it's been assigned from
// a user-declared local. If so, then put that local into the used_mut set.
// Note that this set is expected to be small - only upvars from closures
@@ -532,7 +541,7 @@ struct MirBorrowckCtxt<'cx, 'tcx> {
/// when MIR borrowck begins.
location_table: &'cx LocationTable,
- movable_generator: bool,
+ movable_coroutine: bool,
/// This keeps track of whether local variables are free-ed when the function
/// exits even without a `StorageDead`, which appears to be the case for
/// constants.
@@ -591,6 +600,7 @@ struct MirBorrowckCtxt<'cx, 'tcx> {
polonius_output: Option<Rc<PoloniusOutput>>,
errors: error::BorrowckErrors<'tcx>,
+ move_errors: Vec<MoveError<'tcx>>,
}
// Check that:
@@ -721,7 +731,6 @@ impl<'cx, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx, R> for MirBorro
}
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 {
self.consume_operand(loc, (len, span), flow_state);
self.consume_operand(loc, (index, span), flow_state);
@@ -774,7 +783,7 @@ impl<'cx, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx, R> for MirBorro
| TerminatorKind::Unreachable
| TerminatorKind::UnwindResume
| TerminatorKind::Return
- | TerminatorKind::GeneratorDrop
+ | TerminatorKind::CoroutineDrop
| TerminatorKind::FalseEdge { real_target: _, imaginary_target: _ }
| TerminatorKind::FalseUnwind { real_target: _, unwind: _ } => {
// no data used, thus irrelevant to borrowck
@@ -793,7 +802,7 @@ impl<'cx, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx, R> for MirBorro
match term.kind {
TerminatorKind::Yield { value: _, resume: _, resume_arg: _, drop: _ } => {
- if self.movable_generator {
+ if self.movable_coroutine {
// Look for any active borrows to locals
let borrow_set = self.borrow_set.clone();
for i in flow_state.borrows.iter() {
@@ -805,7 +814,7 @@ impl<'cx, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx, R> for MirBorro
TerminatorKind::UnwindResume
| TerminatorKind::Return
- | TerminatorKind::GeneratorDrop => {
+ | TerminatorKind::CoroutineDrop => {
// Returning from the function implicitly kills storage for all locals and statics.
// Often, the storage will already have been killed by an explicit
// StorageDead, but we don't always emit those (notably on unwind paths),
@@ -1322,7 +1331,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
// moved into the closure and subsequently used by the closure,
// in order to populate our used_mut set.
match **aggregate_kind {
- AggregateKind::Closure(def_id, _) | AggregateKind::Generator(def_id, _, _) => {
+ AggregateKind::Closure(def_id, _) | AggregateKind::Coroutine(def_id, _, _) => {
let def_id = def_id.expect_local();
let BorrowCheckResult { used_mut_upvars, .. } =
self.infcx.tcx.mir_borrowck(def_id);
@@ -1405,7 +1414,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
// As such we have to search for the local that this
// capture comes from and mark it as being used as mut.
- let temp_mpi = self.move_data.rev_lookup.find_local(local);
+ let Some(temp_mpi) = self.move_data.rev_lookup.find_local(local) else {
+ bug!("temporary should be tracked");
+ };
let init = if let [init_index] = *self.move_data.init_path_map[temp_mpi] {
&self.move_data.inits[init_index]
} else {
@@ -1465,6 +1476,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
);
}
Operand::Move(place) => {
+ // Check if moving from this place makes sense.
+ self.check_movable_place(location, place);
+
// move of place: check if this is move of already borrowed path
self.access_place(
location,
@@ -1545,12 +1559,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}
/// Reports an error if this is a borrow of local data.
- /// This is called for all Yield expressions on movable generators
+ /// This is called for all Yield expressions on movable coroutines
fn check_for_local_borrow(&mut self, borrow: &BorrowData<'tcx>, yield_span: Span) {
debug!("check_for_local_borrow({:?})", borrow);
if borrow_of_local_data(borrow.borrowed_place) {
- let err = self.cannot_borrow_across_generator_yield(
+ let err = self.cannot_borrow_across_coroutine_yield(
self.retrieve_borrow_spans(borrow).var_or_use(),
yield_span,
);
@@ -1586,6 +1600,131 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}
}
+ fn check_movable_place(&mut self, location: Location, place: Place<'tcx>) {
+ use IllegalMoveOriginKind::*;
+
+ let body = self.body;
+ let tcx = self.infcx.tcx;
+ let mut place_ty = PlaceTy::from_ty(body.local_decls[place.local].ty);
+ for (place_ref, elem) in place.iter_projections() {
+ match elem {
+ ProjectionElem::Deref => match place_ty.ty.kind() {
+ ty::Ref(..) | ty::RawPtr(..) => {
+ self.move_errors.push(MoveError::new(
+ place,
+ location,
+ BorrowedContent {
+ target_place: place_ref.project_deeper(&[elem], tcx),
+ },
+ ));
+ return;
+ }
+ ty::Adt(adt, _) => {
+ if !adt.is_box() {
+ bug!("Adt should be a box type when Place is deref");
+ }
+ }
+ ty::Bool
+ | ty::Char
+ | ty::Int(_)
+ | ty::Uint(_)
+ | ty::Float(_)
+ | ty::Foreign(_)
+ | ty::Str
+ | ty::Array(_, _)
+ | ty::Slice(_)
+ | ty::FnDef(_, _)
+ | ty::FnPtr(_)
+ | ty::Dynamic(_, _, _)
+ | ty::Closure(_, _)
+ | ty::Coroutine(_, _, _)
+ | ty::CoroutineWitness(..)
+ | ty::Never
+ | ty::Tuple(_)
+ | ty::Alias(_, _)
+ | ty::Param(_)
+ | ty::Bound(_, _)
+ | ty::Infer(_)
+ | ty::Error(_)
+ | ty::Placeholder(_) => {
+ bug!("When Place is Deref it's type shouldn't be {place_ty:#?}")
+ }
+ },
+ ProjectionElem::Field(_, _) => match place_ty.ty.kind() {
+ ty::Adt(adt, _) => {
+ if adt.has_dtor(tcx) {
+ self.move_errors.push(MoveError::new(
+ place,
+ location,
+ InteriorOfTypeWithDestructor { container_ty: place_ty.ty },
+ ));
+ return;
+ }
+ }
+ ty::Closure(_, _) | ty::Coroutine(_, _, _) | ty::Tuple(_) => (),
+ ty::Bool
+ | ty::Char
+ | ty::Int(_)
+ | ty::Uint(_)
+ | ty::Float(_)
+ | ty::Foreign(_)
+ | ty::Str
+ | ty::Array(_, _)
+ | ty::Slice(_)
+ | ty::RawPtr(_)
+ | ty::Ref(_, _, _)
+ | ty::FnDef(_, _)
+ | ty::FnPtr(_)
+ | ty::Dynamic(_, _, _)
+ | ty::CoroutineWitness(..)
+ | ty::Never
+ | ty::Alias(_, _)
+ | ty::Param(_)
+ | ty::Bound(_, _)
+ | ty::Infer(_)
+ | ty::Error(_)
+ | ty::Placeholder(_) => bug!(
+ "When Place contains ProjectionElem::Field it's type shouldn't be {place_ty:#?}"
+ ),
+ },
+ ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
+ match place_ty.ty.kind() {
+ ty::Slice(_) => {
+ self.move_errors.push(MoveError::new(
+ place,
+ location,
+ InteriorOfSliceOrArray { ty: place_ty.ty, is_index: false },
+ ));
+ return;
+ }
+ ty::Array(_, _) => (),
+ _ => bug!("Unexpected type {:#?}", place_ty.ty),
+ }
+ }
+ ProjectionElem::Index(_) => match place_ty.ty.kind() {
+ ty::Array(..) | ty::Slice(..) => {
+ self.move_errors.push(MoveError::new(
+ place,
+ location,
+ InteriorOfSliceOrArray { ty: place_ty.ty, is_index: true },
+ ));
+ return;
+ }
+ _ => bug!("Unexpected type {place_ty:#?}"),
+ },
+ // `OpaqueCast`: only transmutes the type, so no moves there.
+ // `Downcast` : only changes information about a `Place` without moving.
+ // `Subtype` : only transmutes the type, so no moves.
+ // So it's safe to skip these.
+ ProjectionElem::OpaqueCast(_)
+ | ProjectionElem::Subtype(_)
+ | ProjectionElem::Downcast(_, _) => (),
+ }
+
+ place_ty = place_ty.projection_ty(tcx, elem);
+ }
+ }
+
fn check_if_full_path_is_moved(
&mut self,
location: Location,
@@ -1967,7 +2106,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
Reservation(WriteKind::MutableBorrow(BorrowKind::Mut { kind: mut_borrow_kind }))
| Write(WriteKind::MutableBorrow(BorrowKind::Mut { kind: mut_borrow_kind })) => {
let is_local_mutation_allowed = match mut_borrow_kind {
- // `ClosureCapture` is used for mutable variable with a immutable binding.
+ // `ClosureCapture` is used for mutable variable with an immutable binding.
// This is only behaviour difference between `ClosureCapture` and mutable borrows.
MutBorrowKind::ClosureCapture => LocalMutationIsAllowed::Yes,
MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow => {
@@ -2070,7 +2209,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
local: Local,
flow_state: &Flows<'cx, 'tcx>,
) -> Option<InitIndex> {
- let mpi = self.move_data.rev_lookup.find_local(local);
+ let mpi = self.move_data.rev_lookup.find_local(local)?;
let ii = &self.move_data.init_path_map[mpi];
ii.into_iter().find(|&&index| flow_state.ever_inits.contains(index)).copied()
}
diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs
index 3f60f5aca..0ea4401a8 100644
--- a/compiler/rustc_borrowck/src/nll.rs
+++ b/compiler/rustc_borrowck/src/nll.rs
@@ -169,10 +169,11 @@ pub(crate) fn compute_regions<'cx, 'tcx>(
upvars: &[Upvar<'tcx>],
consumer_options: Option<ConsumerOptions>,
) -> NllOutput<'tcx> {
+ let is_polonius_legacy_enabled = infcx.tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled();
let polonius_input = consumer_options.map(|c| c.polonius_input()).unwrap_or_default()
- || infcx.tcx.sess.opts.unstable_opts.polonius;
+ || is_polonius_legacy_enabled;
let polonius_output = consumer_options.map(|c| c.polonius_output()).unwrap_or_default()
- || infcx.tcx.sess.opts.unstable_opts.polonius;
+ || is_polonius_legacy_enabled;
let mut all_facts =
(polonius_input || AllFacts::enabled(infcx.tcx)).then_some(AllFacts::default());
@@ -181,22 +182,26 @@ pub(crate) fn compute_regions<'cx, 'tcx>(
let elements = &Rc::new(RegionValueElements::new(&body));
// Run the MIR type-checker.
- let MirTypeckResults { constraints, universal_region_relations, opaque_type_values } =
- type_check::type_check(
- infcx,
- param_env,
- body,
- promoted,
- &universal_regions,
- location_table,
- borrow_set,
- &mut all_facts,
- flow_inits,
- move_data,
- elements,
- upvars,
- polonius_input,
- );
+ let MirTypeckResults {
+ constraints,
+ universal_region_relations,
+ opaque_type_values,
+ live_loans,
+ } = type_check::type_check(
+ infcx,
+ param_env,
+ body,
+ promoted,
+ &universal_regions,
+ location_table,
+ borrow_set,
+ &mut all_facts,
+ flow_inits,
+ move_data,
+ elements,
+ upvars,
+ polonius_input,
+ );
if let Some(all_facts) = &mut all_facts {
let _prof_timer = infcx.tcx.prof.generic_activity("polonius_fact_generation");
@@ -274,6 +279,7 @@ pub(crate) fn compute_regions<'cx, 'tcx>(
type_tests,
liveness_constraints,
elements,
+ live_loans,
);
// Generate various additional constraints.
diff --git a/compiler/rustc_borrowck/src/path_utils.rs b/compiler/rustc_borrowck/src/path_utils.rs
index ed93a5609..51e318f08 100644
--- a/compiler/rustc_borrowck/src/path_utils.rs
+++ b/compiler/rustc_borrowck/src/path_utils.rs
@@ -137,7 +137,7 @@ pub(super) fn is_active<'tcx>(
}
/// Determines if a given borrow is borrowing local data
-/// This is called for all Yield expressions on movable generators
+/// This is called for all Yield expressions on movable coroutines
pub(super) fn borrow_of_local_data(place: Place<'_>) -> bool {
// Reborrow of already borrowed data is ignored
// Any errors will be caught on the initial borrow
@@ -165,7 +165,7 @@ pub(crate) fn is_upvar_field_projection<'tcx>(
match place_ref.last_projection() {
Some((place_base, ProjectionElem::Field(field, _ty))) => {
let base_ty = place_base.ty(body, tcx).ty;
- if (base_ty.is_closure() || base_ty.is_generator())
+ if (base_ty.is_closure() || base_ty.is_coroutine())
&& (!by_ref || upvars[field.index()].by_ref)
{
Some(field)
diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs
index 852935676..b1f91a056 100644
--- a/compiler/rustc_borrowck/src/region_infer/mod.rs
+++ b/compiler/rustc_borrowck/src/region_infer/mod.rs
@@ -7,6 +7,7 @@ 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::bit_set::SparseBitMatrix;
use rustc_index::{IndexSlice, IndexVec};
use rustc_infer::infer::outlives::test_type_match;
use rustc_infer::infer::region_constraints::{GenericKind, VarInfos, VerifyBound, VerifyIfEq};
@@ -21,17 +22,17 @@ use rustc_middle::traits::ObligationCauseCode;
use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeFoldable, TypeVisitableExt};
use rustc_span::Span;
+use crate::constraints::graph::{self, NormalConstraintGraph, RegionGraph};
+use crate::dataflow::BorrowIndex;
use crate::{
- constraints::{
- graph::NormalConstraintGraph, ConstraintSccIndex, OutlivesConstraint, OutlivesConstraintSet,
- },
+ constraints::{ConstraintSccIndex, OutlivesConstraint, OutlivesConstraintSet},
diagnostics::{RegionErrorKind, RegionErrors, UniverseInfo},
member_constraints::{MemberConstraintSet, NllMemberConstraintIndex},
nll::PoloniusOutput,
region_infer::reverse_sccs::ReverseSccGraph,
region_infer::values::{
- LivenessValues, PlaceholderIndices, RegionElement, RegionValueElements, RegionValues,
- ToElementIndex,
+ LivenessValues, PlaceholderIndices, PointIndex, RegionElement, RegionValueElements,
+ RegionValues, ToElementIndex,
},
type_check::{free_region_relations::UniversalRegionRelations, Locations},
universal_regions::UniversalRegions,
@@ -119,6 +120,9 @@ pub struct RegionInferenceContext<'tcx> {
/// Information about how the universally quantified regions in
/// scope on this function relate to one another.
universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
+
+ /// The set of loans that are live at a given point in the CFG, when using `-Zpolonius=next`.
+ live_loans: SparseBitMatrix<PointIndex, BorrowIndex>,
}
/// Each time that `apply_member_constraint` is successful, it appends
@@ -330,6 +334,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
type_tests: Vec<TypeTest<'tcx>>,
liveness_constraints: LivenessValues<RegionVid>,
elements: &Rc<RegionValueElements>,
+ live_loans: SparseBitMatrix<PointIndex, BorrowIndex>,
) -> Self {
debug!("universal_regions: {:#?}", universal_regions);
debug!("outlives constraints: {:#?}", outlives_constraints);
@@ -383,6 +388,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
type_tests,
universal_regions,
universal_region_relations,
+ live_loans,
};
result.init_free_and_bound_regions();
@@ -637,11 +643,12 @@ impl<'tcx> RegionInferenceContext<'tcx> {
self.scc_universes[scc]
}
- /// Once region solving has completed, this function will return
- /// the member constraints that were applied to the value of a given
- /// region `r`. See `AppliedMemberConstraint`.
- pub(crate) fn applied_member_constraints(&self, r: RegionVid) -> &[AppliedMemberConstraint] {
- let scc = self.constraint_sccs.scc(r);
+ /// Once region solving has completed, this function will return the member constraints that
+ /// were applied to the value of a given SCC `scc`. See `AppliedMemberConstraint`.
+ pub(crate) fn applied_member_constraints(
+ &self,
+ scc: ConstraintSccIndex,
+ ) -> &[AppliedMemberConstraint] {
binary_search_util::binary_search_slice(
&self.member_constraints_applied,
|applied| applied.member_region_scc,
@@ -683,7 +690,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// In Polonius mode, the errors about missing universal region relations are in the output
// and need to be emitted or propagated. Otherwise, we need to check whether the
// constraints were too strong, and if so, emit or propagate those errors.
- if infcx.tcx.sess.opts.unstable_opts.polonius {
+ if infcx.tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled() {
self.check_polonius_subset_errors(
outlives_requirements.as_mut(),
&mut errors_buffer,
@@ -1938,7 +1945,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// Member constraints can also give rise to `'r: 'x` edges that
// were not part of the graph initially, so watch out for those.
// (But they are extremely rare; this loop is very cold.)
- for constraint in self.applied_member_constraints(r) {
+ for constraint in self.applied_member_constraints(self.constraint_sccs.scc(r)) {
let p_c = &self.member_constraints[constraint.member_constraint_index];
let constraint = OutlivesConstraint {
sup: r,
@@ -2279,6 +2286,38 @@ impl<'tcx> RegionInferenceContext<'tcx> {
}
None
}
+
+ /// Access to the SCC constraint graph.
+ pub(crate) fn constraint_sccs(&self) -> &Sccs<RegionVid, ConstraintSccIndex> {
+ self.constraint_sccs.as_ref()
+ }
+
+ /// Access to the region graph, built from the outlives constraints.
+ pub(crate) fn region_graph(&self) -> RegionGraph<'_, 'tcx, graph::Normal> {
+ self.constraint_graph.region_graph(&self.constraints, self.universal_regions.fr_static)
+ }
+
+ /// Returns whether the given region is considered live at all points: whether it is a
+ /// placeholder or a free region.
+ pub(crate) fn is_region_live_at_all_points(&self, region: RegionVid) -> bool {
+ // FIXME: there must be a cleaner way to find this information. At least, when
+ // higher-ranked subtyping is abstracted away from the borrowck main path, we'll only
+ // need to check whether this is a universal region.
+ let origin = self.region_definition(region).origin;
+ let live_at_all_points = matches!(
+ origin,
+ NllRegionVariableOrigin::Placeholder(_) | NllRegionVariableOrigin::FreeRegion
+ );
+ live_at_all_points
+ }
+
+ /// Returns whether the `loan_idx` is live at the given `location`: whether its issuing
+ /// region is contained within the type of a variable that is live at this point.
+ /// Note: for now, the sets of live loans is only available when using `-Zpolonius=next`.
+ pub(crate) fn is_loan_live_at(&self, loan_idx: BorrowIndex, location: Location) -> bool {
+ let point = self.liveness_constraints.point_from_location(location);
+ self.live_loans.contains(point, loan_idx)
+ }
}
impl<'tcx> RegionDefinition<'tcx> {
diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
index 4da7b6025..fb0e5811c 100644
--- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
+++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
@@ -1,5 +1,6 @@
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_errors::ErrorGuaranteed;
+use rustc_hir::def::DefKind;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::OpaqueTyOrigin;
use rustc_infer::infer::InferCtxt;
@@ -308,20 +309,19 @@ fn check_opaque_type_well_formed<'tcx>(
return Ok(definition_ty);
};
let param_env = tcx.param_env(def_id);
- // HACK This bubble is required for this tests to pass:
- // nested-return-type2-tait2.rs
- // nested-return-type2-tait3.rs
+
+ let mut parent_def_id = def_id;
+ while tcx.def_kind(parent_def_id) == DefKind::OpaqueTy {
+ parent_def_id = tcx.local_parent(parent_def_id);
+ }
+
// FIXME(-Ztrait-solver=next): We probably should use `DefiningAnchor::Error`
// and prepopulate this `InferCtxt` with known opaque values, rather than
// using the `Bind` anchor here. For now it's fine.
let infcx = tcx
.infer_ctxt()
.with_next_trait_solver(next_trait_solver)
- .with_opaque_type_inference(if next_trait_solver {
- DefiningAnchor::Bind(def_id)
- } else {
- DefiningAnchor::Bubble
- })
+ .with_opaque_type_inference(DefiningAnchor::Bind(parent_def_id))
.build();
let ocx = ObligationCtxt::new(&infcx);
let identity_args = GenericArgs::identity_for_item(tcx, def_id);
@@ -361,7 +361,7 @@ fn check_opaque_type_well_formed<'tcx>(
if errors.is_empty() {
Ok(definition_ty)
} else {
- Err(infcx.err_ctxt().report_fulfillment_errors(&errors))
+ Err(infcx.err_ctxt().report_fulfillment_errors(errors))
}
}
diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs
index d205862cd..38452df32 100644
--- a/compiler/rustc_borrowck/src/region_infer/values.rs
+++ b/compiler/rustc_borrowck/src/region_infer/values.rs
@@ -176,6 +176,11 @@ impl<N: Idx> LivenessValues<N> {
pub(crate) fn region_value_str(&self, r: N) -> String {
region_value_str(self.get_elements(r).map(RegionElement::Location))
}
+
+ #[inline]
+ pub(crate) fn point_from_location(&self, location: Location) -> PointIndex {
+ self.elements.point_from_location(location)
+ }
}
/// Maps from `ty::PlaceholderRegion` values that are used in the rest of
diff --git a/compiler/rustc_borrowck/src/renumber.rs b/compiler/rustc_borrowck/src/renumber.rs
index 5d6f5cc89..ec0131c53 100644
--- a/compiler/rustc_borrowck/src/renumber.rs
+++ b/compiler/rustc_borrowck/src/renumber.rs
@@ -81,6 +81,10 @@ impl<'a, 'tcx> MutVisitor<'tcx> for RegionRenumberer<'a, 'tcx> {
#[instrument(skip(self), level = "debug")]
fn visit_ty(&mut self, ty: &mut Ty<'tcx>, ty_context: TyContext) {
+ if matches!(ty_context, TyContext::ReturnTy(_)) {
+ // We will renumber the return ty when called again with `TyContext::LocalDecl`
+ return;
+ }
*ty = self.renumber_regions(*ty, || RegionCtxt::TyContext(ty_context));
debug!(?ty);
diff --git a/compiler/rustc_borrowck/src/session_diagnostics.rs b/compiler/rustc_borrowck/src/session_diagnostics.rs
index ca3ccf439..e321b9260 100644
--- a/compiler/rustc_borrowck/src/session_diagnostics.rs
+++ b/compiler/rustc_borrowck/src/session_diagnostics.rs
@@ -139,23 +139,23 @@ pub(crate) enum RequireStaticErr {
#[derive(Subdiagnostic)]
pub(crate) enum CaptureVarPathUseCause {
- #[label(borrowck_borrow_due_to_use_generator)]
- BorrowInGenerator {
+ #[label(borrowck_borrow_due_to_use_coroutine)]
+ BorrowInCoroutine {
#[primary_span]
path_span: Span,
},
- #[label(borrowck_use_due_to_use_generator)]
- UseInGenerator {
+ #[label(borrowck_use_due_to_use_coroutine)]
+ UseInCoroutine {
#[primary_span]
path_span: Span,
},
- #[label(borrowck_assign_due_to_use_generator)]
- AssignInGenerator {
+ #[label(borrowck_assign_due_to_use_coroutine)]
+ AssignInCoroutine {
#[primary_span]
path_span: Span,
},
- #[label(borrowck_assign_part_due_to_use_generator)]
- AssignPartInGenerator {
+ #[label(borrowck_assign_part_due_to_use_coroutine)]
+ AssignPartInCoroutine {
#[primary_span]
path_span: Span,
},
@@ -202,8 +202,8 @@ pub(crate) enum CaptureVarKind {
#[derive(Subdiagnostic)]
pub(crate) enum CaptureVarCause {
- #[label(borrowck_var_borrow_by_use_place_in_generator)]
- BorrowUsePlaceGenerator {
+ #[label(borrowck_var_borrow_by_use_place_in_coroutine)]
+ BorrowUsePlaceCoroutine {
is_single_var: bool,
place: String,
#[primary_span]
@@ -216,8 +216,8 @@ pub(crate) enum CaptureVarCause {
#[primary_span]
var_span: Span,
},
- #[label(borrowck_var_borrow_by_use_in_generator)]
- BorrowUseInGenerator {
+ #[label(borrowck_var_borrow_by_use_in_coroutine)]
+ BorrowUseInCoroutine {
#[primary_span]
var_span: Span,
},
@@ -226,8 +226,8 @@ pub(crate) enum CaptureVarCause {
#[primary_span]
var_span: Span,
},
- #[label(borrowck_var_move_by_use_in_generator)]
- MoveUseInGenerator {
+ #[label(borrowck_var_move_by_use_in_coroutine)]
+ MoveUseInCoroutine {
#[primary_span]
var_span: Span,
},
@@ -236,8 +236,8 @@ pub(crate) enum CaptureVarCause {
#[primary_span]
var_span: Span,
},
- #[label(borrowck_var_first_borrow_by_use_place_in_generator)]
- FirstBorrowUsePlaceGenerator {
+ #[label(borrowck_var_first_borrow_by_use_place_in_coroutine)]
+ FirstBorrowUsePlaceCoroutine {
place: String,
#[primary_span]
var_span: Span,
@@ -248,8 +248,8 @@ pub(crate) enum CaptureVarCause {
#[primary_span]
var_span: Span,
},
- #[label(borrowck_var_second_borrow_by_use_place_in_generator)]
- SecondBorrowUsePlaceGenerator {
+ #[label(borrowck_var_second_borrow_by_use_place_in_coroutine)]
+ SecondBorrowUsePlaceCoroutine {
place: String,
#[primary_span]
var_span: Span,
@@ -266,8 +266,8 @@ pub(crate) enum CaptureVarCause {
#[primary_span]
var_span: Span,
},
- #[label(borrowck_partial_var_move_by_use_in_generator)]
- PartialMoveUseInGenerator {
+ #[label(borrowck_partial_var_move_by_use_in_coroutine)]
+ PartialMoveUseInCoroutine {
#[primary_span]
var_span: Span,
is_partial: bool,
diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs
index b7adc314f..fc600af1b 100644
--- a/compiler/rustc_borrowck/src/type_check/canonical.rs
+++ b/compiler/rustc_borrowck/src/type_check/canonical.rs
@@ -49,7 +49,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
// If the query has created new universes and errors are going to be emitted, register the
// cause of these new universes for improved diagnostics.
let universe = self.infcx.universe();
- if old_universe != universe && let Some(error_info) = error_info {
+ if old_universe != universe
+ && let Some(error_info) = error_info
+ {
let universe_info = error_info.to_universe_info(old_universe);
for u in (old_universe + 1)..=universe {
self.borrowck_context.constraints.universe_causes.insert(u, universe_info.clone());
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 f22851d76..c84256f8f 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, DUMMY_SP};
+use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP};
use rustc_trait_selection::traits::query::type_op::{self, TypeOp};
use std::rc::Rc;
use type_op::TypeOpOutput;
@@ -318,7 +318,8 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
.param_env
.and(type_op::implied_outlives_bounds::ImpliedOutlivesBounds { ty })
.fully_perform(self.infcx, DUMMY_SP)
- .unwrap_or_else(|_| bug!("failed to compute implied bounds {:?}", ty));
+ .map_err(|_: ErrorGuaranteed| debug!("failed to compute implied bounds {:?}", ty))
+ .ok()?;
debug!(?bounds, ?constraints);
self.add_outlives_bounds(bounds);
constraints
diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs
index eec886b7b..d053d0a4b 100644
--- a/compiler/rustc_borrowck/src/type_check/input_output.rs
+++ b/compiler/rustc_borrowck/src/type_check/input_output.rs
@@ -101,7 +101,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
);
// We will not have a universal_regions.yield_ty if we yield (by accident)
- // outside of a generator and return an `impl Trait`, so emit a delay_span_bug
+ // outside of a coroutine and return an `impl Trait`, so emit a delay_span_bug
// because we don't want to panic in an assert here if we've already got errors.
if body.yield_ty().is_some() != universal_regions.yield_ty.is_some() {
self.tcx().sess.delay_span_bug(
diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
index 5702d39db..e616449cc 100644
--- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
+++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
@@ -1,10 +1,12 @@
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
-use rustc_index::bit_set::HybridBitSet;
+use rustc_data_structures::graph::WithSuccessors;
+use rustc_index::bit_set::{HybridBitSet, SparseBitMatrix};
use rustc_index::interval::IntervalSet;
use rustc_infer::infer::canonical::QueryRegionConstraints;
+use rustc_infer::infer::outlives::for_liveness;
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_middle::ty::{RegionVid, Ty, TyCtxt, TypeVisitable, TypeVisitableExt};
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};
@@ -14,6 +16,7 @@ use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
use rustc_mir_dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex};
use rustc_mir_dataflow::ResultsCursor;
+use crate::dataflow::BorrowIndex;
use crate::{
region_infer::values::{self, PointIndex, RegionValueElements},
type_check::liveness::local_use_map::LocalUseMap,
@@ -50,6 +53,33 @@ pub(super) fn trace<'mir, 'tcx>(
let local_use_map = &LocalUseMap::build(&relevant_live_locals, elements, body);
+ // When using `-Zpolonius=next`, compute the set of loans that can reach a given region.
+ let num_loans = typeck.borrowck_context.borrow_set.len();
+ let mut inflowing_loans = SparseBitMatrix::new(num_loans);
+ if typeck.tcx().sess.opts.unstable_opts.polonius.is_next_enabled() {
+ let borrowck_context = &typeck.borrowck_context;
+ let borrow_set = &borrowck_context.borrow_set;
+ let constraint_set = &borrowck_context.constraints.outlives_constraints;
+
+ let num_region_vars = typeck.infcx.num_region_vars();
+ let graph = constraint_set.graph(num_region_vars);
+ let region_graph =
+ graph.region_graph(&constraint_set, borrowck_context.universal_regions.fr_static);
+
+ // Traverse each issuing region's constraints, and record the loan as flowing into the
+ // outlived region.
+ for (loan, issuing_region_data) in borrow_set.iter_enumerated() {
+ for succ in region_graph.depth_first_search(issuing_region_data.region) {
+ // We don't need to mention that a loan flows into its issuing region.
+ if succ == issuing_region_data.region {
+ continue;
+ }
+
+ inflowing_loans.insert(succ, loan);
+ }
+ }
+ };
+
let cx = LivenessContext {
typeck,
body,
@@ -58,6 +88,7 @@ pub(super) fn trace<'mir, 'tcx>(
local_use_map,
move_data,
drop_data: FxIndexMap::default(),
+ inflowing_loans,
};
let mut results = LivenessResults::new(cx);
@@ -71,7 +102,7 @@ pub(super) fn trace<'mir, 'tcx>(
results.dropck_boring_locals(boring_locals);
}
-/// Contextual state for the type-liveness generator.
+/// Contextual state for the type-liveness coroutine.
struct LivenessContext<'me, 'typeck, 'flow, 'tcx> {
/// Current type-checker, giving us our inference context etc.
typeck: &'me mut TypeChecker<'typeck, 'tcx>,
@@ -95,6 +126,9 @@ struct LivenessContext<'me, 'typeck, 'flow, 'tcx> {
/// Index indicating where each variable is assigned, used, or
/// dropped.
local_use_map: &'me LocalUseMap,
+
+ /// Set of loans that flow into a given region, when using `-Zpolonius=next`.
+ inflowing_loans: SparseBitMatrix<RegionVid, BorrowIndex>,
}
struct DropData<'tcx> {
@@ -284,7 +318,7 @@ impl<'me, 'typeck, 'flow, 'tcx> LivenessResults<'me, 'typeck, 'flow, 'tcx> {
fn compute_drop_live_points_for(&mut self, local: Local) {
debug!("compute_drop_live_points_for(local={:?})", local);
- let mpi = self.cx.move_data.rev_lookup.find_local(local);
+ let Some(mpi) = self.cx.move_data.rev_lookup.find_local(local) else { return };
debug!("compute_drop_live_points_for: mpi = {:?}", mpi);
// Find the drops where `local` is initialized.
@@ -486,7 +520,13 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
) {
debug!("add_use_live_facts_for(value={:?})", value);
- Self::make_all_regions_live(self.elements, &mut self.typeck, value, live_at)
+ Self::make_all_regions_live(
+ self.elements,
+ &mut self.typeck,
+ value,
+ live_at,
+ &self.inflowing_loans,
+ );
}
/// Some variable with type `live_ty` is "drop live" at `location`
@@ -537,7 +577,13 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
// All things in the `outlives` array may be touched by
// the destructor and must be live at this point.
for &kind in &drop_data.dropck_result.kinds {
- Self::make_all_regions_live(self.elements, &mut self.typeck, kind, live_at);
+ Self::make_all_regions_live(
+ self.elements,
+ &mut self.typeck,
+ kind,
+ live_at,
+ &self.inflowing_loans,
+ );
polonius::add_drop_of_var_derefs_origin(&mut self.typeck, dropped_local, &kind);
}
@@ -548,6 +594,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
typeck: &mut TypeChecker<'_, 'tcx>,
value: impl TypeVisitable<TyCtxt<'tcx>>,
live_at: &IntervalSet<PointIndex>,
+ inflowing_loans: &SparseBitMatrix<RegionVid, BorrowIndex>,
) {
debug!("make_all_regions_live(value={:?})", value);
debug!(
@@ -555,16 +602,38 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
values::location_set_str(elements, live_at.iter()),
);
- let tcx = typeck.tcx();
- tcx.for_each_free_region(&value, |live_region| {
- let live_region_vid =
- typeck.borrowck_context.universal_regions.to_region_vid(live_region);
- typeck
- .borrowck_context
- .constraints
- .liveness_constraints
- .add_elements(live_region_vid, live_at);
+ // When using `-Zpolonius=next`, we want to record the loans that flow into this value's
+ // regions as being live at the given `live_at` points: this will be used to compute the
+ // location where a loan goes out of scope.
+ let num_loans = typeck.borrowck_context.borrow_set.len();
+ let value_loans = &mut HybridBitSet::new_empty(num_loans);
+
+ value.visit_with(&mut for_liveness::FreeRegionsVisitor {
+ tcx: typeck.tcx(),
+ param_env: typeck.param_env,
+ op: |r| {
+ let live_region_vid = typeck.borrowck_context.universal_regions.to_region_vid(r);
+
+ typeck
+ .borrowck_context
+ .constraints
+ .liveness_constraints
+ .add_elements(live_region_vid, live_at);
+
+ // There can only be inflowing loans for this region when we are using
+ // `-Zpolonius=next`.
+ if let Some(inflowing) = inflowing_loans.row(live_region_vid) {
+ value_loans.union(inflowing);
+ }
+ },
});
+
+ // Record the loans reaching the value.
+ if !value_loans.is_empty() {
+ for point in live_at.iter() {
+ typeck.borrowck_context.live_loans.union_row(point, value_loans);
+ }
+ }
}
fn compute_drop_data(
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 1f383e533..608d01039 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -14,6 +14,7 @@ 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::bit_set::SparseBitMatrix;
use rustc_index::{IndexSlice, IndexVec};
use rustc_infer::infer::canonical::QueryRegionConstraints;
use rustc_infer::infer::outlives::env::RegionBoundPairs;
@@ -50,6 +51,8 @@ use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
use rustc_mir_dataflow::move_paths::MoveData;
use rustc_mir_dataflow::ResultsCursor;
+use crate::dataflow::BorrowIndex;
+use crate::region_infer::values::PointIndex;
use crate::session_diagnostics::{MoveUnsized, SimdShuffleLastConst};
use crate::{
borrow_set::BorrowSet,
@@ -163,6 +166,9 @@ pub(crate) fn type_check<'mir, 'tcx>(
debug!(?normalized_inputs_and_output);
+ // When using `-Zpolonius=next`, liveness will record the set of live loans per point.
+ let mut live_loans = SparseBitMatrix::new(borrow_set.len());
+
let mut borrowck_context = BorrowCheckContext {
universal_regions,
location_table,
@@ -170,6 +176,7 @@ pub(crate) fn type_check<'mir, 'tcx>(
all_facts,
constraints: &mut constraints,
upvars,
+ live_loans: &mut live_loans,
};
let mut checker = TypeChecker::new(
@@ -181,11 +188,7 @@ pub(crate) fn type_check<'mir, 'tcx>(
&mut borrowck_context,
);
- // FIXME(-Ztrait-solver=next): A bit dubious that we're only registering
- // predefined opaques in the typeck root.
- if infcx.next_trait_solver() && !infcx.tcx.is_typeck_child(body.source.def_id()) {
- checker.register_predefined_opaques_in_new_solver();
- }
+ checker.check_user_type_annotations();
let mut verifier = TypeVerifier::new(&mut checker, promoted);
verifier.visit_body(&body);
@@ -240,7 +243,7 @@ pub(crate) fn type_check<'mir, 'tcx>(
})
.collect();
- MirTypeckResults { constraints, universal_region_relations, opaque_type_values }
+ MirTypeckResults { constraints, universal_region_relations, opaque_type_values, live_loans }
}
fn translate_outlives_facts(typeck: &mut TypeChecker<'_, '_>) {
@@ -664,8 +667,8 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
PlaceTy { ty: base_ty, variant_index: Some(index) }
}
}
- // We do not need to handle generators here, because this runs
- // before the generator transform stage.
+ // We do not need to handle coroutines here, because this runs
+ // before the coroutine transform stage.
_ => {
let ty = if let Some(name) = maybe_name {
span_mirbug_and_err!(
@@ -767,13 +770,13 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
let (variant, args) = match base_ty {
PlaceTy { ty, variant_index: Some(variant_index) } => match *ty.kind() {
ty::Adt(adt_def, args) => (adt_def.variant(variant_index), args),
- ty::Generator(def_id, args, _) => {
- let mut variants = args.as_generator().state_tys(def_id, tcx);
+ ty::Coroutine(def_id, args, _) => {
+ let mut variants = args.as_coroutine().state_tys(def_id, tcx);
let Some(mut variant) = variants.nth(variant_index.into()) else {
bug!(
- "variant_index of generator out of range: {:?}/{:?}",
+ "variant_index of coroutine out of range: {:?}/{:?}",
variant_index,
- args.as_generator().state_tys(def_id, tcx).count()
+ args.as_coroutine().state_tys(def_id, tcx).count()
);
};
return match variant.nth(field.index()) {
@@ -781,7 +784,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
None => Err(FieldAccessError::OutOfRange { field_count: variant.count() }),
};
}
- _ => bug!("can't have downcast of non-adt non-generator type"),
+ _ => bug!("can't have downcast of non-adt non-coroutine type"),
},
PlaceTy { ty, variant_index: None } => match *ty.kind() {
ty::Adt(adt_def, args) if !adt_def.is_enum() => {
@@ -795,13 +798,13 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
}),
};
}
- ty::Generator(_, args, _) => {
+ ty::Coroutine(_, args, _) => {
// Only prefix fields (upvars and current state) are
// accessible without a variant index.
- return match args.as_generator().prefix_tys().get(field.index()) {
+ return match args.as_coroutine().prefix_tys().get(field.index()) {
Some(ty) => Ok(*ty),
None => Err(FieldAccessError::OutOfRange {
- field_count: args.as_generator().prefix_tys().len(),
+ field_count: args.as_coroutine().prefix_tys().len(),
}),
};
}
@@ -855,12 +858,21 @@ struct BorrowCheckContext<'a, 'tcx> {
borrow_set: &'a BorrowSet<'tcx>,
pub(crate) constraints: &'a mut MirTypeckRegionConstraints<'tcx>,
upvars: &'a [Upvar<'tcx>],
+
+ /// The set of loans that are live at a given point in the CFG, filled in by `liveness::trace`,
+ /// when using `-Zpolonius=next`.
+ pub(crate) live_loans: &'a mut SparseBitMatrix<PointIndex, BorrowIndex>,
}
+/// Holder struct for passing results from MIR typeck to the rest of the non-lexical regions
+/// inference computation.
pub(crate) struct MirTypeckResults<'tcx> {
pub(crate) constraints: MirTypeckRegionConstraints<'tcx>,
pub(crate) universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
pub(crate) opaque_type_values: FxIndexMap<OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>>,
+
+ /// The set of loans that are live at a given point in the CFG, when using `-Zpolonius=next`.
+ pub(crate) live_loans: SparseBitMatrix<PointIndex, BorrowIndex>,
}
/// A collection of region constraints that must be satisfied for the
@@ -1005,7 +1017,13 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
borrowck_context,
reported_errors: Default::default(),
};
- checker.check_user_type_annotations();
+
+ // FIXME(-Ztrait-solver=next): A bit dubious that we're only registering
+ // predefined opaques in the typeck root.
+ if infcx.next_trait_solver() && !infcx.tcx.is_typeck_child(body.source.def_id()) {
+ checker.register_predefined_opaques_in_new_solver();
+ }
+
checker
}
@@ -1335,7 +1353,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
| TerminatorKind::UnwindResume
| TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Return
- | TerminatorKind::GeneratorDrop
+ | TerminatorKind::CoroutineDrop
| TerminatorKind::Unreachable
| TerminatorKind::Drop { .. }
| TerminatorKind::FalseEdge { .. }
@@ -1452,7 +1470,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
let value_ty = value.ty(body, tcx);
match body.yield_ty() {
- None => span_mirbug!(self, term, "yield in non-generator"),
+ None => span_mirbug!(self, term, "yield in non-coroutine"),
Some(ty) => {
if let Err(terr) = self.sub_types(
value_ty,
@@ -1624,7 +1642,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
}
TerminatorKind::UnwindTerminate(_) => {
if !is_cleanup {
- span_mirbug!(self, block_data, "abort on non-cleanup block!")
+ span_mirbug!(self, block_data, "terminate on non-cleanup block!")
}
}
TerminatorKind::Return => {
@@ -1632,9 +1650,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
span_mirbug!(self, block_data, "return on cleanup block")
}
}
- TerminatorKind::GeneratorDrop { .. } => {
+ TerminatorKind::CoroutineDrop { .. } => {
if is_cleanup {
- span_mirbug!(self, block_data, "generator_drop in cleanup block")
+ span_mirbug!(self, block_data, "coroutine_drop in cleanup block")
}
}
TerminatorKind::Yield { resume, drop, .. } => {
@@ -1781,14 +1799,14 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
}),
}
}
- AggregateKind::Generator(_, args, _) => {
+ AggregateKind::Coroutine(_, args, _) => {
// It doesn't make sense to look at a field beyond the prefix;
// these require a variant index, and are not initialized in
// aggregate rvalues.
- match args.as_generator().prefix_tys().get(field_index.as_usize()) {
+ match args.as_coroutine().prefix_tys().get(field_index.as_usize()) {
Some(ty) => Ok(*ty),
None => Err(FieldAccessError::OutOfRange {
- field_count: args.as_generator().prefix_tys().len(),
+ field_count: args.as_coroutine().prefix_tys().len(),
}),
}
}
@@ -2381,7 +2399,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
AggregateKind::Array(_) => None,
AggregateKind::Tuple => None,
AggregateKind::Closure(_, _) => None,
- AggregateKind::Generator(_, _, _) => None,
+ AggregateKind::Coroutine(_, _, _) => None,
},
}
}
@@ -2609,7 +2627,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
// desugaring. A closure gets desugared to a struct, and
// these extra requirements are basically like where
// clauses on the struct.
- AggregateKind::Closure(def_id, args) | AggregateKind::Generator(def_id, args, _) => {
+ AggregateKind::Closure(def_id, args) | AggregateKind::Coroutine(def_id, args, _) => {
(def_id, self.prove_closure_bounds(tcx, def_id.expect_local(), args, location))
}
@@ -2657,7 +2675,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
let parent_args = match tcx.def_kind(def_id) {
DefKind::Closure => args.as_closure().parent_args(),
- DefKind::Generator => args.as_generator().parent_args(),
+ DefKind::Coroutine => args.as_coroutine().parent_args(),
DefKind::InlineConst => args.as_inline_const().parent_args(),
other => bug!("unexpected item {:?}", other),
};
diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs
index af437f36b..7897a5a63 100644
--- a/compiler/rustc_borrowck/src/universal_regions.rs
+++ b/compiler/rustc_borrowck/src/universal_regions.rs
@@ -58,7 +58,7 @@ pub struct UniversalRegions<'tcx> {
num_universals: usize,
/// The "defining" type for this function, with all universal
- /// regions instantiated. For a closure or generator, this is the
+ /// regions instantiated. For a closure or coroutine, this is the
/// closure type, but for a top-level function it's the `FnDef`.
pub defining_ty: DefiningTy<'tcx>,
@@ -91,10 +91,10 @@ pub enum DefiningTy<'tcx> {
/// `ClosureArgs::closure_sig_ty`.
Closure(DefId, GenericArgsRef<'tcx>),
- /// The MIR is a generator. The signature is that generators take
+ /// The MIR is a coroutine. The signature is that coroutines take
/// no parameters and return the result of
- /// `ClosureArgs::generator_return_ty`.
- Generator(DefId, GenericArgsRef<'tcx>, hir::Movability),
+ /// `ClosureArgs::coroutine_return_ty`.
+ Coroutine(DefId, GenericArgsRef<'tcx>, hir::Movability),
/// The MIR is a fn item with the given `DefId` and args. The signature
/// of the function can be bound then with the `fn_sig` query.
@@ -112,13 +112,13 @@ pub enum DefiningTy<'tcx> {
impl<'tcx> DefiningTy<'tcx> {
/// Returns a list of all the upvar types for this MIR. If this is
- /// not a closure or generator, there are no upvars, and hence it
+ /// not a closure or coroutine, there are no upvars, and hence it
/// will be an empty list. The order of types in this list will
/// match up with the upvar order in the HIR, typesystem, and MIR.
pub fn upvar_tys(self) -> &'tcx ty::List<Ty<'tcx>> {
match self {
DefiningTy::Closure(_, args) => args.as_closure().upvar_tys(),
- DefiningTy::Generator(_, args, _) => args.as_generator().upvar_tys(),
+ DefiningTy::Coroutine(_, args, _) => args.as_coroutine().upvar_tys(),
DefiningTy::FnDef(..) | DefiningTy::Const(..) | DefiningTy::InlineConst(..) => {
ty::List::empty()
}
@@ -130,7 +130,7 @@ impl<'tcx> DefiningTy<'tcx> {
/// user's code.
pub fn implicit_inputs(self) -> usize {
match self {
- DefiningTy::Closure(..) | DefiningTy::Generator(..) => 1,
+ DefiningTy::Closure(..) | DefiningTy::Coroutine(..) => 1,
DefiningTy::FnDef(..) | DefiningTy::Const(..) | DefiningTy::InlineConst(..) => 0,
}
}
@@ -146,7 +146,7 @@ impl<'tcx> DefiningTy<'tcx> {
pub fn def_id(&self) -> DefId {
match *self {
DefiningTy::Closure(def_id, ..)
- | DefiningTy::Generator(def_id, ..)
+ | DefiningTy::Coroutine(def_id, ..)
| DefiningTy::FnDef(def_id, ..)
| DefiningTy::Const(def_id, ..)
| DefiningTy::InlineConst(def_id, ..) => def_id,
@@ -164,7 +164,7 @@ struct UniversalRegionIndices<'tcx> {
/// be able to map them to our internal `RegionVid`. This is
/// basically equivalent to an `GenericArgs`, except that it also
/// contains an entry for `ReStatic` -- it might be nice to just
- /// use a args, and then handle `ReStatic` another way.
+ /// use an args, and then handle `ReStatic` another way.
indices: FxHashMap<ty::Region<'tcx>, RegionVid>,
/// The vid assigned to `'static`. Used only for diagnostics.
@@ -178,7 +178,7 @@ pub enum RegionClassification {
Global,
/// An **external** region is only relevant for
- /// closures, generators, and inline consts. In that
+ /// closures, coroutines, and inline consts. In that
/// case, it refers to regions that are free in the type
/// -- basically, something bound in the surrounding context.
///
@@ -196,7 +196,7 @@ pub enum RegionClassification {
/// Here, the lifetimes `'a` and `'b` would be **external** to the
/// closure.
///
- /// If we are not analyzing a closure/generator/inline-const,
+ /// If we are not analyzing a closure/coroutine/inline-const,
/// there are no external lifetimes.
External,
@@ -290,7 +290,7 @@ impl<'tcx> UniversalRegions<'tcx> {
(FIRST_GLOBAL_INDEX..self.num_universals).map(RegionVid::from_usize)
}
- /// Returns `true` if `r` is classified as an local region.
+ /// Returns `true` if `r` is classified as a local region.
pub fn is_local_free_region(&self, r: RegionVid) -> bool {
self.region_classification(r) == Some(RegionClassification::Local)
}
@@ -354,7 +354,7 @@ impl<'tcx> UniversalRegions<'tcx> {
err.note(format!("late-bound region is {:?}", self.to_region_vid(r)));
});
}
- DefiningTy::Generator(def_id, args, _) => {
+ DefiningTy::Coroutine(def_id, args, _) => {
let v = with_no_trimmed_paths!(
args[tcx.generics_of(def_id).parent_count..]
.iter()
@@ -362,7 +362,7 @@ impl<'tcx> UniversalRegions<'tcx> {
.collect::<Vec<_>>()
);
err.note(format!(
- "defining type: {} with generator args [\n {},\n]",
+ "defining type: {} with coroutine args [\n {},\n]",
tcx.def_path_str_with_args(def_id, args),
v.join(",\n "),
));
@@ -426,13 +426,13 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
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
+ // If this is a 'root' body (not a closure/coroutine/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.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
+ // If this is a closure, coroutine, or inline-const, then the late-bound regions from the enclosing
// function/closures are actually external regions to us. For example, here, 'a is not local
// to the closure c (although it is local to the fn foo):
// fn foo<'a>() {
@@ -528,7 +528,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
debug!("build: local regions = {}..{}", first_local_index, num_universals);
let yield_ty = match defining_ty {
- DefiningTy::Generator(_, args, _) => Some(args.as_generator().yield_ty()),
+ DefiningTy::Coroutine(_, args, _) => Some(args.as_coroutine().yield_ty()),
_ => None,
};
@@ -563,8 +563,8 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
match *defining_ty.kind() {
ty::Closure(def_id, args) => DefiningTy::Closure(def_id, args),
- ty::Generator(def_id, args, movability) => {
- DefiningTy::Generator(def_id, args, movability)
+ ty::Coroutine(def_id, args, movability) => {
+ DefiningTy::Coroutine(def_id, args, movability)
}
ty::FnDef(def_id, args) => DefiningTy::FnDef(def_id, args),
_ => span_bug!(
@@ -621,7 +621,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
let identity_args = GenericArgs::identity_for_item(tcx, typeck_root_def_id);
let fr_args = match defining_ty {
DefiningTy::Closure(_, args)
- | DefiningTy::Generator(_, args, _)
+ | DefiningTy::Coroutine(_, args, _)
| DefiningTy::InlineConst(_, args) => {
// In the case of closures, we rely on the fact that
// the first N elements in the ClosureArgs are
@@ -686,13 +686,13 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
)
}
- DefiningTy::Generator(def_id, args, movability) => {
+ DefiningTy::Coroutine(def_id, args, movability) => {
assert_eq!(self.mir_def.to_def_id(), def_id);
- let resume_ty = args.as_generator().resume_ty();
- let output = args.as_generator().return_ty();
- let generator_ty = Ty::new_generator(tcx, def_id, args, movability);
+ let resume_ty = args.as_coroutine().resume_ty();
+ let output = args.as_coroutine().return_ty();
+ let coroutine_ty = Ty::new_coroutine(tcx, def_id, args, movability);
let inputs_and_output =
- self.infcx.tcx.mk_type_list(&[generator_ty, resume_ty, output]);
+ self.infcx.tcx.mk_type_list(&[coroutine_ty, resume_ty, output]);
ty::Binder::dummy(inputs_and_output)
}