diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-07 05:48:42 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-07 05:48:42 +0000 |
commit | cec1877e180393eba0f6ddb0cf97bf3a791631c7 (patch) | |
tree | 47b4dac2a9dd9a40c30c251b4d4a72d7ccf77e9f /compiler/rustc_borrowck | |
parent | Adding debian version 1.74.1+dfsg1-1. (diff) | |
download | rustc-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')
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) } |