diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:19:13 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:19:13 +0000 |
commit | 218caa410aa38c29984be31a5229b9fa717560ee (patch) | |
tree | c54bd55eeb6e4c508940a30e94c0032fbd45d677 /compiler/rustc_borrowck | |
parent | Releasing progress-linux version 1.67.1+dfsg1-1~progress7.99u1. (diff) | |
download | rustc-218caa410aa38c29984be31a5229b9fa717560ee.tar.xz rustc-218caa410aa38c29984be31a5229b9fa717560ee.zip |
Merging upstream version 1.68.2+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_borrowck')
35 files changed, 993 insertions, 732 deletions
diff --git a/compiler/rustc_borrowck/src/borrowck_errors.rs b/compiler/rustc_borrowck/src/borrowck_errors.rs index 01be37912..a4943d112 100644 --- a/compiler/rustc_borrowck/src/borrowck_errors.rs +++ b/compiler/rustc_borrowck/src/borrowck_errors.rs @@ -12,7 +12,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { place: &str, borrow_place: &str, value_place: &str, - ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { self.infcx.tcx.sess.create_err(crate::session_diagnostics::MoveBorrow { place, span, @@ -28,7 +28,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { desc: &str, borrow_span: Span, borrow_desc: &str, - ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { let mut err = struct_span_err!( self, span, @@ -50,7 +50,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { old_loan_span: Span, old_opt_via: &str, old_load_end_span: Option<Span>, - ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { let via = |msg: &str| if msg.is_empty() { "".to_string() } else { format!(" (via {})", msg) }; let mut err = struct_span_err!( @@ -98,7 +98,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { desc: &str, old_loan_span: Span, old_load_end_span: Option<Span>, - ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { let mut err = struct_span_err!( self, new_loan_span, @@ -269,7 +269,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { &self, span: Span, desc: &str, - ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { struct_span_err!(self, span, E0594, "cannot assign to {}", desc) } @@ -348,7 +348,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { span: Span, path: &str, reason: &str, - ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { struct_span_err!(self, span, E0596, "cannot borrow {} as mutable{}", path, reason,) } @@ -359,7 +359,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { immutable_place: &str, immutable_section: &str, action: &str, - ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { let mut err = struct_span_err!( self, mutate_span, @@ -378,7 +378,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { &self, span: Span, yield_span: Span, - ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { let mut err = struct_span_err!( self, span, @@ -392,7 +392,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { pub(crate) fn cannot_borrow_across_destructor( &self, borrow_span: Span, - ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { struct_span_err!( self, borrow_span, @@ -405,7 +405,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { &self, span: Span, path: &str, - ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { struct_span_err!(self, span, E0597, "{} does not live long enough", path,) } @@ -415,7 +415,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { return_kind: &str, reference_desc: &str, path_desc: &str, - ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { let mut err = struct_span_err!( self, span, @@ -440,15 +440,14 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { closure_kind: &str, borrowed_path: &str, capture_span: Span, - ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { + scope: &str, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { let mut err = struct_span_err!( self, closure_span, E0373, - "{} may outlive the current function, but it borrows {}, which is owned by the current \ - function", - closure_kind, - borrowed_path, + "{closure_kind} may outlive the current {scope}, but it borrows {borrowed_path}, \ + which is owned by the current {scope}", ); err.span_label(capture_span, format!("{} is borrowed here", borrowed_path)) .span_label(closure_span, format!("may outlive borrowed value {}", borrowed_path)); @@ -458,14 +457,14 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { pub(crate) fn thread_local_value_does_not_live_long_enough( &self, span: Span, - ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { struct_span_err!(self, span, E0712, "thread-local variable borrowed past end of function",) } pub(crate) fn temporary_value_borrowed_for_too_long( &self, span: Span, - ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { struct_span_err!(self, span, E0716, "temporary value dropped while borrowed",) } diff --git a/compiler/rustc_borrowck/src/constraints/graph.rs b/compiler/rustc_borrowck/src/constraints/graph.rs index 385f15317..c780d0479 100644 --- a/compiler/rustc_borrowck/src/constraints/graph.rs +++ b/compiler/rustc_borrowck/src/constraints/graph.rs @@ -148,7 +148,7 @@ impl<'s, 'tcx, D: ConstraintGraphDirecton> Iterator for Edges<'s, 'tcx, D> { if let Some(p) = self.pointer { self.pointer = self.graph.next_constraints[p]; - Some(self.constraints[p].clone()) + Some(self.constraints[p]) } else if let Some(next_static_idx) = self.next_static_idx { self.next_static_idx = if next_static_idx == (self.graph.first_constraints.len() - 1) { None diff --git a/compiler/rustc_borrowck/src/constraints/mod.rs b/compiler/rustc_borrowck/src/constraints/mod.rs index 84a93e5f7..1f0b8adea 100644 --- a/compiler/rustc_borrowck/src/constraints/mod.rs +++ b/compiler/rustc_borrowck/src/constraints/mod.rs @@ -115,13 +115,11 @@ impl<'tcx> fmt::Debug for OutlivesConstraint<'tcx> { } rustc_index::newtype_index! { - pub struct OutlivesConstraintIndex { - DEBUG_FORMAT = "OutlivesConstraintIndex({})" - } + #[debug_format = "OutlivesConstraintIndex({})"] + pub struct OutlivesConstraintIndex {} } rustc_index::newtype_index! { - pub struct ConstraintSccIndex { - DEBUG_FORMAT = "ConstraintSccIndex({})" - } + #[debug_format = "ConstraintSccIndex({})"] + pub struct ConstraintSccIndex {} } diff --git a/compiler/rustc_borrowck/src/consumers.rs b/compiler/rustc_borrowck/src/consumers.rs index 86da767f3..055b281bc 100644 --- a/compiler/rustc_borrowck/src/consumers.rs +++ b/compiler/rustc_borrowck/src/consumers.rs @@ -25,13 +25,13 @@ pub use super::{ /// can, for example, happen when requesting a body of a `const` function /// because they are evaluated during typechecking. The panic can be avoided /// by overriding the `mir_borrowck` query. You can find a complete example -/// that shows how to do this at `src/test/run-make/obtain-borrowck/`. +/// that shows how to do this at `tests/run-make/obtain-borrowck/`. /// /// * Polonius is highly unstable, so expect regular changes in its signature or other details. -pub fn get_body_with_borrowck_facts<'tcx>( - tcx: TyCtxt<'tcx>, +pub fn get_body_with_borrowck_facts( + tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>, -) -> BodyWithBorrowckFacts<'tcx> { +) -> BodyWithBorrowckFacts<'_> { let (input_body, promoted) = tcx.mir_promoted(def); let infcx = tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bind(def.did)).build(); let input_body: &Body<'_> = &input_body.borrow(); diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index f825b1d8f..8c4885770 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -108,9 +108,8 @@ impl_visitable! { } rustc_index::newtype_index! { - pub struct BorrowIndex { - DEBUG_FORMAT = "bw{}" - } + #[debug_format = "bw{}"] + pub struct BorrowIndex {} } /// `Borrows` stores the data used in the analyses that track the flow diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 5e3745f17..e5a36259f 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -6,6 +6,7 @@ use rustc_errors::{ struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, }; use rustc_hir as hir; +use rustc_hir::def::Res; use rustc_hir::intravisit::{walk_block, walk_expr, Visitor}; use rustc_hir::{AsyncGeneratorKind, GeneratorKind, LangItem}; use rustc_infer::infer::TyCtxtInferExt; @@ -20,7 +21,7 @@ use rustc_middle::ty::{self, suggest_constraining_type_params, PredicateKind, Ty use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex}; use rustc_span::def_id::LocalDefId; use rustc_span::hygiene::DesugaringKind; -use rustc_span::symbol::sym; +use rustc_span::symbol::{kw, sym}; use rustc_span::{BytePos, Span, Symbol}; use rustc_trait_selection::infer::InferCtxtExt; @@ -29,6 +30,7 @@ use crate::borrowck_errors; use crate::diagnostics::conflict_errors::StorageDeadOrDrop::LocalStorageDead; use crate::diagnostics::find_all_local_uses; +use crate::diagnostics::mutability_errors::mut_borrow_of_mutable_ref; use crate::{ borrow_set::BorrowData, diagnostics::Instance, prefixes::IsPrefixOf, InitializationRequiringAction, MirBorrowckCtxt, PrefixSet, WriteKind, @@ -156,7 +158,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { err.span_note( MultiSpan::from_spans(reinit_spans), &if reinits <= 3 { - format!("these {} reinitializations might get skipped", reinits) + format!("these {reinits} reinitializations might get skipped") } else { format!( "these 3 reinitializations and {} other{} might get skipped", @@ -194,7 +196,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if !seen_spans.contains(&move_span) { if !closure { - self.suggest_ref_or_clone(mpi, move_span, &mut err, &mut in_pattern); + self.suggest_ref_or_clone( + mpi, + move_span, + &mut err, + &mut in_pattern, + move_spans, + ); } self.explain_captures( @@ -219,9 +227,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { err.span_label( span, format!( - "value {} here after {}move", + "value {} here after {partial_str}move", desired_action.as_verb_in_past_tense(), - partial_str ), ); } @@ -251,7 +258,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { &format!( "consider creating a fresh reborrow of {} here", self.describe_place(moved_place) - .map(|n| format!("`{}`", n)) + .map(|n| format!("`{n}`")) .unwrap_or_else(|| "the mutable reference".to_string()), ), "&mut *", @@ -265,7 +272,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { DescribePlaceOpt { including_downcast: true, including_tuple_field: true }, ); let note_msg = match opt_name { - Some(name) => format!("`{}`", name), + Some(name) => format!("`{name}`"), None => "value".to_owned(), }; if self.suggest_borrow_fn_like(&mut err, ty, &move_site_vec, ¬e_msg) { @@ -291,9 +298,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } = use_spans { err.note(&format!( - "{} occurs due to deref coercion to `{}`", + "{} occurs due to deref coercion to `{deref_target_ty}`", desired_action.as_noun(), - deref_target_ty )); // Check first whether the source is accessible (issue #87060) @@ -312,6 +318,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { move_span: Span, err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, in_pattern: &mut bool, + move_spans: UseSpans<'_>, ) { struct ExpressionFinder<'hir> { expr_span: Span, @@ -351,7 +358,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. - })) = hir.find(hir.local_def_id_to_hir_id(self.mir_def_id())) + })) = hir.find(self.mir_hir_id()) && let Some(hir::Node::Expr(expr)) = hir.find(body_id.hir_id) { let place = &self.move_data.move_paths[mpi].place; @@ -387,7 +394,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } let typeck = self.infcx.tcx.typeck(self.mir_def_id()); - let hir_id = hir.get_parent_node(expr.hir_id); + let hir_id = hir.parent_id(expr.hir_id); if let Some(parent) = hir.find(hir_id) { let (def_id, args, offset) = if let hir::Node::Expr(parent_expr) = parent && let hir::ExprKind::MethodCall(_, _, args, _) = parent_expr.kind @@ -440,6 +447,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ) = 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 { + // We already suggest cloning for these cases in `explain_captures`. } else { self.suggest_cloning(err, ty, move_span); } @@ -515,26 +527,21 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // that are *partially* initialized by assigning to a field of an uninitialized // binding. We differentiate between them for more accurate wording here. "isn't fully initialized" - } else if spans - .iter() - .filter(|i| { - // We filter these to avoid misleading wording in cases like the following, - // where `x` has an `init`, but it is in the same place we're looking at: - // ``` - // let x; - // x += 1; - // ``` - !i.contains(span) - // We filter these to avoid incorrect main message on `match-cfg-fake-edges.rs` - && !visitor - .errors - .iter() - .map(|(sp, _)| *sp) - .any(|sp| span < sp && !sp.contains(span)) - }) - .count() - == 0 - { + } else if !spans.iter().any(|i| { + // We filter these to avoid misleading wording in cases like the following, + // where `x` has an `init`, but it is in the same place we're looking at: + // ``` + // let x; + // x += 1; + // ``` + !i.contains(span) + // We filter these to avoid incorrect main message on `match-cfg-fake-edges.rs` + && !visitor + .errors + .iter() + .map(|(sp, _)| *sp) + .any(|sp| span < sp && !sp.contains(span)) + }) { show_assign_sugg = true; "isn't initialized" } else { @@ -649,7 +656,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if !assign_value.is_empty() { err.span_suggestion_verbose( sugg_span.shrink_to_hi(), - format!("consider assigning a value"), + "consider assigning a value", format!(" = {}", assign_value), Applicability::MaybeIncorrect, ); @@ -666,40 +673,34 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let tcx = self.infcx.tcx; // Find out if the predicates show that the type is a Fn or FnMut - let find_fn_kind_from_did = |predicates: ty::EarlyBinder< - &[(ty::Predicate<'tcx>, Span)], - >, - substs| { - predicates.0.iter().find_map(|(pred, _)| { - let pred = if let Some(substs) = substs { - predicates.rebind(*pred).subst(tcx, substs).kind().skip_binder() - } else { - pred.kind().skip_binder() - }; - if let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) = pred && pred.self_ty() == ty { - if Some(pred.def_id()) == tcx.lang_items().fn_trait() { - return Some(hir::Mutability::Not); - } else if Some(pred.def_id()) == tcx.lang_items().fn_mut_trait() { - return Some(hir::Mutability::Mut); - } + let find_fn_kind_from_did = |(pred, _): (ty::Predicate<'tcx>, _)| { + if let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) = pred.kind().skip_binder() + && pred.self_ty() == ty + { + if Some(pred.def_id()) == tcx.lang_items().fn_trait() { + return Some(hir::Mutability::Not); + } else if Some(pred.def_id()) == tcx.lang_items().fn_mut_trait() { + return Some(hir::Mutability::Mut); } - None - }) + } + None }; // If the type is opaque/param/closure, and it is Fn or FnMut, let's suggest (mutably) // borrowing the type, since `&mut F: FnMut` iff `F: FnMut` and similarly for `Fn`. // These types seem reasonably opaque enough that they could be substituted with their // borrowed variants in a function body when we see a move error. - let borrow_level = match ty.kind() { - ty::Param(_) => find_fn_kind_from_did( - tcx.bound_explicit_predicates_of(self.mir_def_id().to_def_id()) - .map_bound(|p| p.predicates), - None, - ), - ty::Opaque(did, substs) => { - find_fn_kind_from_did(tcx.bound_explicit_item_bounds(*did), Some(*substs)) - } + let borrow_level = match *ty.kind() { + ty::Param(_) => tcx + .explicit_predicates_of(self.mir_def_id().to_def_id()) + .predicates + .iter() + .copied() + .find_map(find_fn_kind_from_did), + ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => tcx + .bound_explicit_item_bounds(def_id) + .subst_iter_copied(tcx, substs) + .find_map(find_fn_kind_from_did), ty::Closure(_, substs) => match substs.as_closure().kind() { ty::ClosureKind::Fn => Some(hir::Mutability::Not), ty::ClosureKind::FnMut => Some(hir::Mutability::Mut), @@ -745,7 +746,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { err.span_suggestion_verbose( span.shrink_to_hi(), "consider cloning the value if the performance cost is acceptable", - ".clone()".to_string(), + ".clone()", Applicability::MachineApplicable, ); } @@ -943,7 +944,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } (BorrowKind::Mut { .. }, BorrowKind::Shared) => { first_borrow_desc = "immutable "; - self.cannot_reborrow_already_borrowed( + let mut err = self.cannot_reborrow_already_borrowed( span, &desc_place, &msg_place, @@ -953,7 +954,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { "immutable", &msg_borrow, None, - ) + ); + self.suggest_binding_for_closure_capture_self( + &mut err, + issued_borrow.borrowed_place, + &issued_spans, + ); + err } (BorrowKind::Mut { .. }, BorrowKind::Mut { .. }) => { @@ -1235,6 +1242,138 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } + fn suggest_binding_for_closure_capture_self( + &self, + err: &mut Diagnostic, + borrowed_place: Place<'tcx>, + issued_spans: &UseSpans<'tcx>, + ) { + let UseSpans::ClosureUse { capture_kind_span, .. } = issued_spans else { return }; + let hir = self.infcx.tcx.hir(); + + // check whether the borrowed place is capturing `self` by mut reference + let local = borrowed_place.local; + let Some(_) = self + .body + .local_decls + .get(local) + .map(|l| mut_borrow_of_mutable_ref(l, self.local_names[local])) else { return }; + + struct ExpressionFinder<'hir> { + capture_span: Span, + closure_change_spans: Vec<Span>, + closure_arg_span: Option<Span>, + in_closure: bool, + suggest_arg: String, + hir: rustc_middle::hir::map::Map<'hir>, + closure_local_id: Option<hir::HirId>, + closure_call_changes: Vec<(Span, String)>, + } + impl<'hir> Visitor<'hir> for ExpressionFinder<'hir> { + 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; + } + } + 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); + } + } + 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::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)); + } + 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) { + let mut finder = ExpressionFinder { + capture_span: *capture_kind_span, + closure_change_spans: vec![], + closure_arg_span: None, + in_closure: false, + suggest_arg: String::new(), + closure_local_id: None, + closure_call_changes: vec![], + hir, + }; + finder.visit_expr(expr); + + if finder.closure_change_spans.is_empty() || finder.closure_call_changes.is_empty() { + return; + } + + let mut sugg = vec![]; + let sm = self.infcx.tcx.sess.source_map(); + + if let Some(span) = finder.closure_arg_span { + sugg.push((sm.next_point(span.shrink_to_lo()).shrink_to_hi(), finder.suggest_arg)); + } + for span in finder.closure_change_spans { + sugg.push((span, "this".to_string())); + } + + for (span, suggest) in finder.closure_call_changes { + sugg.push((span, suggest)); + } + + err.multipart_suggestion_verbose( + "try explicitly pass `&Self` into the Closure as an argument", + sugg, + Applicability::MachineApplicable, + ); + } + } + /// Returns the description of the root place for a conflicting borrow and the full /// descriptions of the places that caused the conflict. /// @@ -1418,6 +1557,21 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // and `move` will not help here. ( Some(name), + BorrowExplanation::UsedLater(LaterUseKind::ClosureCapture, var_or_use_span, _), + ) => self.report_escaping_closure_capture( + borrow_spans, + borrow_span, + &RegionName { + name: self.synthesize_region_name(), + source: RegionNameSource::Static, + }, + ConstraintCategory::CallArgument(None), + var_or_use_span, + &format!("`{}`", name), + "block", + ), + ( + Some(name), BorrowExplanation::MustBeValidFor { category: category @ (ConstraintCategory::Return(_) @@ -1436,6 +1590,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { category, span, &format!("`{}`", name), + "function", ), ( name, @@ -1716,7 +1871,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { /// We check that there's a single level of block nesting to ensure always correct /// suggestions. If we don't, then we only provide a free-form message to avoid - /// misleading users in cases like `src/test/ui/nll/borrowed-temporary-error.rs`. + /// misleading users in cases like `tests/ui/nll/borrowed-temporary-error.rs`. /// We could expand the analysis to suggest hoising all of the relevant parts of /// the users' code to make the code compile, but that could be too much. struct NestedStatementVisitor { @@ -1888,6 +2043,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { Some(err) } + #[instrument(level = "debug", skip(self))] fn report_escaping_closure_capture( &mut self, use_span: UseSpans<'tcx>, @@ -1896,6 +2052,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { category: ConstraintCategory<'tcx>, constraint_span: Span, captured_var: &str, + scope: &str, ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { let tcx = self.infcx.tcx; let args_span = use_span.args_or_use(); @@ -1926,8 +2083,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { None => "closure", }; - let mut err = - self.cannot_capture_in_long_lived_closure(args_span, kind, captured_var, var_span); + let mut err = self.cannot_capture_in_long_lived_closure( + args_span, + kind, + captured_var, + var_span, + scope, + ); err.span_suggestion_verbose( sugg_span, &format!( @@ -1949,10 +2111,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if matches!(use_span.generator_kind(), Some(GeneratorKind::Async(_))) { err.note( "async blocks are not executed immediately and must either take a \ - reference or ownership of outside variables they use", + reference or ownership of outside variables they use", ); } else { - let msg = format!("function requires argument type to outlive `{}`", fr_name); + let msg = format!("{scope} requires argument type to outlive `{fr_name}`"); err.span_note(constraint_span, &msg); } } @@ -2031,7 +2193,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let mut back_edge_stack = Vec::new(); predecessor_locations(self.body, location).for_each(|predecessor| { - if location.dominates(predecessor, &self.dominators) { + if location.dominates(predecessor, self.dominators()) { back_edge_stack.push(predecessor) } else { stack.push(predecessor); @@ -2143,7 +2305,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let mut has_predecessor = false; predecessor_locations(self.body, location).for_each(|predecessor| { - if location.dominates(predecessor, &self.dominators) { + if location.dominates(predecessor, self.dominators()) { back_edge_stack.push(predecessor) } else { stack.push(predecessor); @@ -2669,7 +2831,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // Need to use the `rustc_middle::ty` types to compare against the // `return_region`. Then use the `rustc_hir` type to get only // the lifetime span. - if let hir::TyKind::Rptr(lifetime, _) = &fn_decl.inputs[index].kind { + if let hir::TyKind::Ref(lifetime, _) = &fn_decl.inputs[index].kind { // With access to the lifetime, we can get // the span of it. arguments.push((*argument, lifetime.ident.span)); @@ -2690,7 +2852,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let return_ty = sig.output().skip_binder(); let mut return_span = fn_decl.output.span(); if let hir::FnRetTy::Return(ty) = &fn_decl.output { - if let hir::TyKind::Rptr(lifetime, _) = ty.kind { + if let hir::TyKind::Ref(lifetime, _) = ty.kind { return_span = lifetime.ident.span; } } diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index 304683618..209574709 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -77,7 +77,7 @@ impl<'tcx> BorrowExplanation<'tcx> { if borrow_span.map(|sp| !sp.overlaps(var_or_use_span)).unwrap_or(true) { err.span_label( var_or_use_span, - format!("{}borrow later {}", borrow_desc, message), + format!("{borrow_desc}borrow later {message}"), ); } } else { @@ -90,7 +90,7 @@ impl<'tcx> BorrowExplanation<'tcx> { let capture_kind_label = message; err.span_label( var_or_use_span, - format!("{}borrow later {}", borrow_desc, capture_kind_label), + format!("{borrow_desc}borrow later {capture_kind_label}"), ); err.span_label(path_span, path_label); } @@ -110,7 +110,7 @@ impl<'tcx> BorrowExplanation<'tcx> { }; // We can use `var_or_use_span` if either `path_span` is not present, or both spans are the same if path_span.map(|path_span| path_span == var_or_use_span).unwrap_or(true) { - err.span_label(var_or_use_span, format!("{}{}", borrow_desc, message)); + err.span_label(var_or_use_span, format!("{borrow_desc}{message}")); } else { // path_span must be `Some` as otherwise the if condition is true let path_span = path_span.unwrap(); @@ -121,7 +121,7 @@ impl<'tcx> BorrowExplanation<'tcx> { let capture_kind_label = message; err.span_label( var_or_use_span, - format!("{}borrow later {}", borrow_desc, capture_kind_label), + format!("{borrow_desc}borrow later {capture_kind_label}"), ); err.span_label(path_span, path_label); } @@ -160,12 +160,8 @@ impl<'tcx> BorrowExplanation<'tcx> { match local_names[dropped_local] { Some(local_name) if !local_decl.from_compiler_desugaring() => { let message = format!( - "{B}borrow might be used here, when `{LOC}` is dropped \ - and runs the {DTOR} for {TYPE}", - B = borrow_desc, - LOC = local_name, - TYPE = type_desc, - DTOR = dtor_desc + "{borrow_desc}borrow might be used here, when `{local_name}` is dropped \ + and runs the {dtor_desc} for {type_desc}", ); err.span_label(body.source_info(drop_loc).span, message); @@ -180,18 +176,14 @@ impl<'tcx> BorrowExplanation<'tcx> { err.span_label( local_decl.source_info.span, format!( - "a temporary with access to the {B}borrow \ + "a temporary with access to the {borrow_desc}borrow \ is created here ...", - B = borrow_desc ), ); let message = format!( - "... and the {B}borrow might be used here, \ + "... and the {borrow_desc}borrow might be used here, \ when that temporary is dropped \ - and runs the {DTOR} for {TYPE}", - B = borrow_desc, - TYPE = type_desc, - DTOR = dtor_desc + and runs the {dtor_desc} for {type_desc}", ); err.span_label(body.source_info(drop_loc).span, message); @@ -249,20 +241,16 @@ impl<'tcx> BorrowExplanation<'tcx> { err.span_label( span, format!( - "{}requires that `{}` is borrowed for `{}`", + "{}requires that `{desc}` is borrowed for `{region_name}`", category.description(), - desc, - region_name, ), ); } else { err.span_label( span, format!( - "{}requires that {}borrow lasts for `{}`", + "{}requires that {borrow_desc}borrow lasts for `{region_name}`", category.description(), - borrow_desc, - region_name, ), ); }; @@ -270,7 +258,7 @@ impl<'tcx> BorrowExplanation<'tcx> { for extra in extra_info { match extra { ExtraConstraintInfo::PlaceholderFromPredicate(span) => { - err.span_note(*span, format!("due to current limitations in the borrow checker, this implies a `'static` lifetime")); + err.span_note(*span, "due to current limitations in the borrow checker, this implies a `'static` lifetime"); } } } @@ -296,15 +284,14 @@ impl<'tcx> BorrowExplanation<'tcx> { if region_name.was_named() { region_name.name } else { kw::UnderscoreLifetime }; let msg = format!( - "you can add a bound to the {}to make it last less than `'static` and match `{}`", + "you can add a bound to the {}to make it last less than `'static` and match `{region_name}`", category.description(), - region_name, ); err.span_suggestion_verbose( span.shrink_to_hi(), &msg, - format!(" + {}", suggestable_name), + format!(" + {suggestable_name}"), Applicability::Unspecified, ); } @@ -444,6 +431,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { /// First span returned points to the location of the conflicting use /// Second span if `Some` is returned in the case of closures and points /// to the use of the path + #[instrument(level = "debug", skip(self))] fn later_use_kind( &self, borrow: &BorrowData<'tcx>, @@ -461,11 +449,18 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let block = &self.body.basic_blocks[location.block]; let kind = if let Some(&Statement { - kind: StatementKind::FakeRead(box (FakeReadCause::ForLet(_), _)), + kind: StatementKind::FakeRead(box (FakeReadCause::ForLet(_), place)), .. }) = block.statements.get(location.statement_index) { - LaterUseKind::FakeLetRead + if let Some(l) = place.as_local() + && let local_decl = &self.body.local_decls[l] + && local_decl.ty.is_closure() + { + LaterUseKind::ClosureCapture + } else { + LaterUseKind::FakeLetRead + } } else if self.was_captured_by_trait_object(borrow) { LaterUseKind::TraitCapture } else if location.statement_index == block.statements.len() { diff --git a/compiler/rustc_borrowck/src/diagnostics/find_all_local_uses.rs b/compiler/rustc_borrowck/src/diagnostics/find_all_local_uses.rs index 498e98343..2c4d953f0 100644 --- a/compiler/rustc_borrowck/src/diagnostics/find_all_local_uses.rs +++ b/compiler/rustc_borrowck/src/diagnostics/find_all_local_uses.rs @@ -9,7 +9,7 @@ use rustc_middle::mir::{Body, Local, Location}; /// Find all uses of (including assignments to) a [`Local`]. /// /// Uses `BTreeSet` so output is deterministic. -pub(super) fn find<'tcx>(body: &Body<'tcx>, local: Local) -> BTreeSet<Location> { +pub(super) fn find(body: &Body<'_>, local: Local) -> BTreeSet<Location> { let mut visitor = AllLocalUsesVisitor { for_local: local, uses: BTreeSet::default() }; visitor.visit_body(body); visitor.uses diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 4e2271a30..1b40b7143 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -6,7 +6,7 @@ use rustc_errors::{Applicability, Diagnostic}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, Namespace}; use rustc_hir::GeneratorKind; -use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::infer::{LateBoundRegionConversionTime, TyCtxtInferExt}; use rustc_middle::mir::tcx::PlaceTy; use rustc_middle::mir::{ AggregateKind, Constant, FakeReadCause, Field, Local, LocalInfo, LocalKind, Location, Operand, @@ -18,7 +18,10 @@ use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult}; use rustc_span::def_id::LocalDefId; use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP}; use rustc_target::abi::VariantIdx; -use rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; +use rustc_trait_selection::traits::{ + type_known_to_meet_bound_modulo_regions, Obligation, ObligationCause, +}; use super::borrow_set::BorrowData; use super::MirBorrowckCtxt; @@ -400,8 +403,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { move_prefix: &str, ) { let message = format!( - "{}move occurs because {} has type `{}`, which does not implement the `Copy` trait", - move_prefix, place_desc, ty, + "{move_prefix}move occurs because {place_desc} has type `{ty}`, which does not implement the `Copy` trait", ); if let Some(span) = span { err.span_label(span, message); @@ -736,11 +738,11 @@ impl<'tcx> BorrowedContentSource<'tcx> { BorrowedContentSource::OverloadedDeref(ty) => ty .ty_adt_def() .and_then(|adt| match tcx.get_diagnostic_name(adt.did())? { - name @ (sym::Rc | sym::Arc) => Some(format!("an `{}`", name)), + name @ (sym::Rc | sym::Arc) => Some(format!("an `{name}`")), _ => None, }) - .unwrap_or_else(|| format!("dereference of `{}`", ty)), - BorrowedContentSource::OverloadedIndex(ty) => format!("index of `{}`", ty), + .unwrap_or_else(|| format!("dereference of `{ty}`")), + BorrowedContentSource::OverloadedIndex(ty) => format!("index of `{ty}`"), } } @@ -766,11 +768,11 @@ impl<'tcx> BorrowedContentSource<'tcx> { BorrowedContentSource::OverloadedDeref(ty) => ty .ty_adt_def() .and_then(|adt| match tcx.get_diagnostic_name(adt.did())? { - name @ (sym::Rc | sym::Arc) => Some(format!("an `{}`", name)), + name @ (sym::Rc | sym::Arc) => Some(format!("an `{name}`")), _ => None, }) - .unwrap_or_else(|| format!("dereference of `{}`", ty)), - BorrowedContentSource::OverloadedIndex(ty) => format!("an index of `{}`", ty), + .unwrap_or_else(|| format!("dereference of `{ty}`")), + BorrowedContentSource::OverloadedIndex(ty) => format!("an index of `{ty}`"), } } @@ -1030,7 +1032,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if let UseSpans::FnSelfUse { var_span, fn_call_span, fn_span, kind } = move_spans { let place_name = self .describe_place(moved_place.as_ref()) - .map(|n| format!("`{}`", n)) + .map(|n| format!("`{n}`")) .unwrap_or_else(|| "value".to_owned()); match kind { CallKind::FnCall { fn_trait_id, .. } @@ -1039,8 +1041,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { err.span_label( fn_call_span, &format!( - "{} {}moved due to this call{}", - place_name, partially_str, loop_message + "{place_name} {partially_str}moved due to this call{loop_message}", ), ); err.span_note( @@ -1053,36 +1054,28 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { err.span_label( fn_call_span, &format!( - "{} {}moved due to usage in operator{}", - place_name, partially_str, loop_message + "{place_name} {partially_str}moved due to usage in operator{loop_message}", ), ); if self.fn_self_span_reported.insert(fn_span) { err.span_note( - // Check whether the source is accessible - if self.infcx.tcx.sess.source_map().is_span_accessible(self_arg.span) { - self_arg.span - } else { - fn_call_span - }, + self_arg.span, "calling this operator moves the left-hand side", ); } } - CallKind::Normal { self_arg, desugaring, is_option_or_result } => { + CallKind::Normal { self_arg, desugaring, method_did } => { let self_arg = self_arg.unwrap(); + let tcx = self.infcx.tcx; if let Some((CallDesugaringKind::ForLoopIntoIter, _)) = desugaring { - let ty = moved_place.ty(self.body, self.infcx.tcx).ty; - let suggest = match self.infcx.tcx.get_diagnostic_item(sym::IntoIterator) { + let ty = moved_place.ty(self.body, tcx).ty; + let suggest = match tcx.get_diagnostic_item(sym::IntoIterator) { Some(def_id) => { let infcx = self.infcx.tcx.infer_ctxt().build(); type_known_to_meet_bound_modulo_regions( &infcx, self.param_env, - infcx.tcx.mk_imm_ref( - infcx.tcx.lifetimes.re_erased, - infcx.tcx.erase_regions(ty), - ), + tcx.mk_imm_ref(tcx.lifetimes.re_erased, tcx.erase_regions(ty)), def_id, DUMMY_SP, ) @@ -1093,9 +1086,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { err.span_suggestion_verbose( move_span.shrink_to_lo(), &format!( - "consider iterating over a slice of the `{}`'s content to \ + "consider iterating over a slice of the `{ty}`'s content to \ avoid moving into the `for` loop", - ty, ), "&", Applicability::MaybeIncorrect, @@ -1105,8 +1097,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { err.span_label( fn_call_span, &format!( - "{} {}moved due to this implicit call to `.into_iter()`{}", - place_name, partially_str, loop_message + "{place_name} {partially_str}moved due to this implicit call to `.into_iter()`{loop_message}", ), ); // If the moved place was a `&mut` ref, then we can @@ -1122,7 +1113,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { &format!( "consider creating a fresh reborrow of {} here", self.describe_place(moved_place.as_ref()) - .map(|n| format!("`{}`", n)) + .map(|n| format!("`{n}`")) .unwrap_or_else(|| "the mutable reference".to_string()), ), "&mut *", @@ -1134,19 +1125,67 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { err.span_label( fn_call_span, &format!( - "{} {}moved due to this method call{}", - place_name, partially_str, loop_message + "{place_name} {partially_str}moved due to this method call{loop_message}", ), ); + let infcx = tcx.infer_ctxt().build(); + let ty = tcx.erase_regions(moved_place.ty(self.body, tcx).ty); + if let ty::Adt(def, substs) = ty.kind() + && Some(def.did()) == tcx.lang_items().pin_type() + && let ty::Ref(_, _, hir::Mutability::Mut) = substs.type_at(0).kind() + && let self_ty = infcx.replace_bound_vars_with_fresh_vars( + fn_call_span, + LateBoundRegionConversionTime::FnCall, + tcx.fn_sig(method_did).input(0), + ) + && infcx.can_eq(self.param_env, ty, self_ty).is_ok() + { + err.span_suggestion_verbose( + fn_call_span.shrink_to_lo(), + "consider reborrowing the `Pin` instead of moving it", + "as_mut().".to_string(), + Applicability::MaybeIncorrect, + ); + } + if let Some(clone_trait) = tcx.lang_items().clone_trait() + && let trait_ref = tcx.mk_trait_ref(clone_trait, [ty]) + && let o = Obligation::new( + tcx, + ObligationCause::dummy(), + self.param_env, + ty::Binder::dummy(trait_ref), + ) + && infcx.predicate_must_hold_modulo_regions(&o) + { + err.span_suggestion_verbose( + fn_call_span.shrink_to_lo(), + "you can `clone` the value and consume it, but this might not be \ + your desired behavior", + "clone().".to_string(), + Applicability::MaybeIncorrect, + ); + } } // Avoid pointing to the same function in multiple different // error messages. if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span) { + let func = tcx.def_path_str(method_did); err.span_note( self_arg.span, - &format!("this function takes ownership of the receiver `self`, which moves {}", place_name) + &format!("`{func}` takes ownership of the receiver `self`, which moves {place_name}") ); } + let parent_did = tcx.parent(method_did); + let parent_self_ty = (tcx.def_kind(parent_did) + == rustc_hir::def::DefKind::Impl) + .then_some(parent_did) + .and_then(|did| match tcx.type_of(did).kind() { + ty::Adt(def, ..) => Some(def.did()), + _ => None, + }); + let is_option_or_result = parent_self_ty.map_or(false, |def_id| { + matches!(tcx.get_diagnostic_name(def_id), Some(sym::Option | sym::Result)) + }); if is_option_or_result && maybe_reinitialized_locations_is_empty { err.span_label( var_span, @@ -1161,7 +1200,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if move_span != span || !loop_message.is_empty() { err.span_label( move_span, - format!("value {}moved{} here{}", partially_str, move_msg, loop_message), + format!("value {partially_str}moved{move_msg} here{loop_message}"), ); } // If the move error occurs due to a loop, don't show @@ -1169,7 +1208,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if loop_message.is_empty() { move_spans.var_span_label( err, - format!("variable {}moved due to use{}", partially_str, move_spans.describe()), + format!("variable {partially_str}moved due to use{}", move_spans.describe()), "moved", ); } diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 5a47f4567..6db3c858a 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -4,7 +4,7 @@ use rustc_middle::ty; use rustc_mir_dataflow::move_paths::{ IllegalMoveOrigin, IllegalMoveOriginKind, LookupResult, MoveError, MovePathIndex, }; -use rustc_span::Span; +use rustc_span::{BytePos, Span}; use crate::diagnostics::{DescribePlaceOpt, UseSpans}; use crate::prefixes::PrefixSet; @@ -148,7 +148,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { match_span: Span, statement_span: Span, ) { - debug!("append_binding_error(match_place={:?}, match_span={:?})", match_place, match_span); + debug!(?match_place, ?match_span, "append_binding_error"); let from_simple_let = match_place.is_none(); let match_place = match_place.unwrap_or(move_from); @@ -160,7 +160,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { if let GroupedMoveError::MovesFromPlace { span, binds_to, .. } = ge && match_span == *span { - debug!("appending local({:?}) to list", bind_to); + debug!("appending local({bind_to:?}) to list"); if !binds_to.is_empty() { binds_to.push(bind_to); } @@ -198,7 +198,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } = ge { if match_span == *span && mpi == *other_mpi { - debug!("appending local({:?}) to list", bind_to); + debug!("appending local({bind_to:?}) to list"); binds_to.push(bind_to); return; } @@ -410,15 +410,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { fn add_move_hints(&self, error: GroupedMoveError<'tcx>, err: &mut Diagnostic, span: Span) { match error { GroupedMoveError::MovesFromPlace { mut binds_to, move_from, .. } => { - if let Ok(snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(span) { - err.span_suggestion( - span, - "consider borrowing here", - format!("&{snippet}"), - Applicability::Unspecified, - ); - } - + self.add_borrow_suggestions(err, span); if binds_to.is_empty() { let place_ty = move_from.ty(self.body, self.infcx.tcx).ty; let place_desc = match self.describe_place(move_from.as_ref()) { @@ -461,39 +453,75 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } } + fn add_borrow_suggestions(&self, err: &mut Diagnostic, span: Span) { + match self.infcx.tcx.sess.source_map().span_to_snippet(span) { + Ok(snippet) if snippet.starts_with('*') => { + err.span_suggestion_verbose( + span.with_hi(span.lo() + BytePos(1)), + "consider removing the dereference here", + String::new(), + Applicability::MaybeIncorrect, + ); + } + _ => { + err.span_suggestion_verbose( + span.shrink_to_lo(), + "consider borrowing here", + "&".to_string(), + Applicability::MaybeIncorrect, + ); + } + } + } + fn add_move_error_suggestions(&self, err: &mut Diagnostic, binds_to: &[Local]) { - let mut suggestions: Vec<(Span, &str, String)> = Vec::new(); + let mut suggestions: Vec<(Span, String, String)> = Vec::new(); for local in binds_to { let bind_to = &self.body.local_decls[*local]; if let Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var( VarBindingForm { pat_span, .. }, )))) = bind_to.local_info { - if let Ok(pat_snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(pat_span) + let Ok(pat_snippet) = + self.infcx.tcx.sess.source_map().span_to_snippet(pat_span) else { continue; }; + let Some(stripped) = pat_snippet.strip_prefix('&') else { + suggestions.push(( + bind_to.source_info.span.shrink_to_lo(), + "consider borrowing the pattern binding".to_string(), + "ref ".to_string(), + )); + continue; + }; + let inner_pat_snippet = stripped.trim_start(); + let (pat_span, suggestion, to_remove) = if inner_pat_snippet.starts_with("mut") + && inner_pat_snippet["mut".len()..].starts_with(rustc_lexer::is_whitespace) { - if let Some(stripped) = pat_snippet.strip_prefix('&') { - let pat_snippet = stripped.trim_start(); - let (suggestion, to_remove) = if pat_snippet.starts_with("mut") - && pat_snippet["mut".len()..].starts_with(rustc_lexer::is_whitespace) - { - (pat_snippet["mut".len()..].trim_start(), "&mut") - } else { - (pat_snippet, "&") - }; - suggestions.push((pat_span, to_remove, suggestion.to_owned())); - } - } + let inner_pat_snippet = inner_pat_snippet["mut".len()..].trim_start(); + let pat_span = pat_span.with_hi( + pat_span.lo() + + BytePos((pat_snippet.len() - inner_pat_snippet.len()) as u32), + ); + (pat_span, String::new(), "mutable borrow") + } else { + let pat_span = pat_span.with_hi( + pat_span.lo() + + BytePos( + (pat_snippet.len() - inner_pat_snippet.trim_start().len()) as u32, + ), + ); + (pat_span, String::new(), "borrow") + }; + suggestions.push(( + pat_span, + format!("consider removing the {to_remove}"), + suggestion.to_string(), + )); } } suggestions.sort_unstable_by_key(|&(span, _, _)| span); suggestions.dedup_by_key(|&mut (span, _, _)| span); - for (span, to_remove, suggestion) in suggestions { - err.span_suggestion( - span, - &format!("consider removing the `{to_remove}`"), - suggestion, - Applicability::MachineApplicable, - ); + for (span, msg, suggestion) in suggestions { + err.span_suggestion_verbose(span, &msg, suggestion, Applicability::MachineApplicable); } } @@ -521,8 +549,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { if binds_to.len() > 1 { err.note( - "move occurs because these variables have types that \ - don't implement the `Copy` trait", + "move occurs because these variables have types that don't implement the `Copy` \ + trait", ); } } diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 3319a8068..45b15c2c5 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -180,6 +180,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { // the verbs used in some diagnostic messages. let act; let acted_on; + let mut suggest = true; + let mut mut_error = None; + let mut count = 1; let span = match error_access { AccessKind::Mutate => { @@ -194,15 +197,50 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let borrow_spans = self.borrow_spans(span, location); let borrow_span = borrow_spans.args_or_use(); - err = self.cannot_borrow_path_as_mutable_because(borrow_span, &item_msg, &reason); - borrow_spans.var_span_label( - &mut err, - format!( - "mutable borrow occurs due to use of {} in closure", - self.describe_any_place(access_place.as_ref()), - ), - "mutable", - ); + match the_place_err { + PlaceRef { local, projection: [] } + if self.body.local_decls[local].can_be_made_mutable() => + { + let span = self.body.local_decls[local].source_info.span; + mut_error = Some(span); + if let Some((buffer, c)) = self.get_buffered_mut_error(span) { + // We've encountered a second (or more) attempt to mutably borrow an + // immutable binding, so the likely problem is with the binding + // declaration, not the use. We collect these in a single diagnostic + // and make the binding the primary span of the error. + err = buffer; + count = c + 1; + if count == 2 { + err.replace_span_with(span, false); + err.span_label(span, "not mutable"); + } + suggest = false; + } else { + err = self.cannot_borrow_path_as_mutable_because( + borrow_span, + &item_msg, + &reason, + ); + } + } + _ => { + err = self.cannot_borrow_path_as_mutable_because( + borrow_span, + &item_msg, + &reason, + ); + } + } + if suggest { + borrow_spans.var_span_label( + &mut err, + format!( + "mutable borrow occurs due to use of {} in closure", + self.describe_any_place(access_place.as_ref()), + ), + "mutable", + ); + } borrow_span } }; @@ -226,7 +264,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { ProjectionElem::Deref, ], } => { - err.span_label(span, format!("cannot {ACT}", ACT = act)); + err.span_label(span, format!("cannot {act}")); if let Some(span) = get_mut_span_in_struct_field( self.infcx.tcx, @@ -252,7 +290,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { .unwrap_or(false) => { let decl = &self.body.local_decls[local]; - err.span_label(span, format!("cannot {ACT}", ACT = act)); + err.span_label(span, format!("cannot {act}")); if let Some(mir::Statement { source_info, kind: @@ -276,7 +314,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { pat_span: _, }, )))) => { - err.span_note(sp, "the binding is already a mutable borrow"); + if suggest { + err.span_note(sp, "the binding is already a mutable borrow"); + } } _ => { err.span_note( @@ -304,20 +344,25 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } else { err.span_help(source_info.span, "try removing `&mut` here"); } - } else if decl.mutability == Mutability::Not - && !matches!( + } else if decl.mutability == Mutability::Not { + if matches!( decl.local_info, Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::ImplicitSelf( hir::ImplicitSelfKind::MutRef - )))) - ) - { - err.span_suggestion_verbose( - decl.source_info.span.shrink_to_lo(), - "consider making the binding mutable", - "mut ", - Applicability::MachineApplicable, - ); + ),))) + ) { + err.note( + "as `Self` may be unsized, this call attempts to take `&mut &mut self`", + ); + err.note("however, `&mut self` expands to `self: &mut Self`, therefore `self` cannot be borrowed mutably"); + } else { + err.span_suggestion_verbose( + decl.source_info.span.shrink_to_lo(), + "consider making the binding mutable", + "mut ", + Applicability::MachineApplicable, + ); + }; } } @@ -333,16 +378,20 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let local_decl = &self.body.local_decls[local]; assert_eq!(local_decl.mutability, Mutability::Not); - err.span_label(span, format!("cannot {ACT}", ACT = act)); - err.span_suggestion( - local_decl.source_info.span, - "consider changing this to be mutable", - format!("mut {}", self.local_names[local].unwrap()), - Applicability::MachineApplicable, - ); - let tcx = self.infcx.tcx; - if let ty::Closure(id, _) = *the_place_err.ty(self.body, tcx).ty.kind() { - self.show_mutating_upvar(tcx, id.expect_local(), the_place_err, &mut err); + if count < 10 { + err.span_label(span, format!("cannot {act}")); + } + if suggest { + err.span_suggestion_verbose( + local_decl.source_info.span.shrink_to_lo(), + "consider changing this to be mutable", + "mut ".to_string(), + Applicability::MachineApplicable, + ); + let tcx = self.infcx.tcx; + if let ty::Closure(id, _) = *the_place_err.ty(self.body, tcx).ty.kind() { + self.show_mutating_upvar(tcx, id.expect_local(), the_place_err, &mut err); + } } } @@ -357,7 +406,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let captured_place = &self.upvars[upvar_index.index()].place; - err.span_label(span, format!("cannot {ACT}", ACT = act)); + err.span_label(span, format!("cannot {act}")); let upvar_hir_id = captured_place.get_root_variable(); @@ -397,7 +446,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { .span_to_snippet(span) .map_or(false, |snippet| snippet.starts_with("&mut ")) => { - err.span_label(span, format!("cannot {ACT}", ACT = act)); + err.span_label(span, format!("cannot {act}")); err.span_suggestion( span, "try removing `&mut` here", @@ -409,7 +458,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { PlaceRef { local, projection: [ProjectionElem::Deref] } if self.body.local_decls[local].is_ref_for_guard() => { - err.span_label(span, format!("cannot {ACT}", ACT = act)); + err.span_label(span, format!("cannot {act}")); err.note( "variables bound in patterns are immutable until the end of the pattern guard", ); @@ -537,7 +586,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { Some((true, err_help_span, suggested_code)) => { let (is_trait_sig, local_trait) = self.is_error_in_trait(local); if !is_trait_sig { - err.span_suggestion( + err.span_suggestion_verbose( err_help_span, &format!( "consider changing this to be a mutable {pointer_desc}" @@ -546,7 +595,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { Applicability::MachineApplicable, ); } else if let Some(x) = local_trait { - err.span_suggestion( + err.span_suggestion_verbose( x, &format!( "consider changing that to be a mutable {pointer_desc}" @@ -569,24 +618,15 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { err.span_label( span, format!( - "`{NAME}` is a `{SIGIL}` {DESC}, \ - so the data it refers to cannot be {ACTED_ON}", - NAME = name, - SIGIL = pointer_sigil, - DESC = pointer_desc, - ACTED_ON = acted_on + "`{name}` is a `{pointer_sigil}` {pointer_desc}, \ + so the data it refers to cannot be {acted_on}", ), ); } _ => { err.span_label( span, - format!( - "cannot {ACT} through `{SIGIL}` {DESC}", - ACT = act, - SIGIL = pointer_sigil, - DESC = pointer_desc - ), + format!("cannot {act} through `{pointer_sigil}` {pointer_desc}"), ); } } @@ -599,19 +639,19 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } PlaceRef { local: _, projection: [.., ProjectionElem::Deref] } => { - err.span_label(span, format!("cannot {ACT}", ACT = act)); + err.span_label(span, format!("cannot {act}")); match opt_source { Some(BorrowedContentSource::OverloadedDeref(ty)) => { err.help(&format!( "trait `DerefMut` is required to modify through a dereference, \ - but it is not implemented for `{ty}`", + but it is not implemented for `{ty}`", )); } Some(BorrowedContentSource::OverloadedIndex(ty)) => { err.help(&format!( "trait `IndexMut` is required to modify indexed content, \ - but it is not implemented for `{ty}`", + but it is not implemented for `{ty}`", )); self.suggest_map_index_mut_alternatives(ty, &mut err, span); } @@ -620,11 +660,15 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } _ => { - err.span_label(span, format!("cannot {ACT}", ACT = act)); + err.span_label(span, format!("cannot {act}")); } } - self.buffer_error(err); + if let Some(span) = mut_error { + self.buffer_mut_error(span, err, count); + } else { + self.buffer_error(err); + } } fn suggest_map_index_mut_alternatives(&self, ty: Ty<'tcx>, err: &mut Diagnostic, span: Span) { @@ -965,7 +1009,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let hir = self.infcx.tcx.hir(); let closure_id = self.mir_hir_id(); let closure_span = self.infcx.tcx.def_span(self.mir_def_id()); - let fn_call_id = hir.get_parent_node(closure_id); + let fn_call_id = hir.parent_id(closure_id); let node = hir.get(fn_call_id); let def_id = hir.enclosing_body_owner(fn_call_id); let mut look_at_return = true; @@ -1050,7 +1094,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } } -fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<Symbol>) -> bool { +pub fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<Symbol>) -> bool { debug!("local_info: {:?}, ty.kind(): {:?}", local_decl.local_info, local_decl.ty.kind()); match local_decl.local_info.as_deref() { @@ -1168,7 +1212,7 @@ fn suggest_ampmut<'tcx>( { let lt_name = &src[1..ws_pos]; let ty = &src[ws_pos..]; - return (true, highlight_span, format!("&{} mut{}", lt_name, ty)); + return (true, highlight_span, format!("&{lt_name} mut{ty}")); } let ty_mut = local_decl.ty.builtin_deref(true).unwrap(); @@ -1209,7 +1253,7 @@ fn get_mut_span_in_struct_field<'tcx>( // Now we're dealing with the actual struct that we're going to suggest a change to, // we can expect a field that is an immutable reference to a type. && let hir::Node::Field(field) = node - && let hir::TyKind::Rptr(lt, hir::MutTy { mutbl: hir::Mutability::Not, ty }) = field.ty.kind + && let hir::TyKind::Ref(lt, hir::MutTy { mutbl: hir::Mutability::Not, ty }) = field.ty.kind { return Some(lt.ident.span.between(ty.span)); } diff --git a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs index 35c3df768..1eaf0a2f1 100644 --- a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs +++ b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs @@ -209,14 +209,14 @@ impl OutlivesSuggestionBuilder { let mut diag = if suggested.len() == 1 { mbcx.infcx.tcx.sess.diagnostic().struct_help(&match suggested.last().unwrap() { SuggestedConstraint::Outlives(a, bs) => { - let bs: SmallVec<[String; 2]> = bs.iter().map(|r| format!("{}", r)).collect(); - format!("add bound `{}: {}`", a, bs.join(" + ")) + let bs: SmallVec<[String; 2]> = bs.iter().map(|r| r.to_string()).collect(); + format!("add bound `{a}: {}`", bs.join(" + ")) } SuggestedConstraint::Equal(a, b) => { - format!("`{}` and `{}` must be the same: replace one with the other", a, b) + format!("`{a}` and `{b}` must be the same: replace one with the other") } - SuggestedConstraint::Static(a) => format!("replace `{}` with `'static`", a), + SuggestedConstraint::Static(a) => format!("replace `{a}` with `'static`"), }) } else { // Create a new diagnostic. @@ -231,18 +231,16 @@ impl OutlivesSuggestionBuilder { for constraint in suggested { match constraint { SuggestedConstraint::Outlives(a, bs) => { - let bs: SmallVec<[String; 2]> = - bs.iter().map(|r| format!("{}", r)).collect(); - diag.help(&format!("add bound `{}: {}`", a, bs.join(" + "))); + let bs: SmallVec<[String; 2]> = bs.iter().map(|r| r.to_string()).collect(); + diag.help(&format!("add bound `{a}: {}`", bs.join(" + "))); } SuggestedConstraint::Equal(a, b) => { diag.help(&format!( - "`{}` and `{}` must be the same: replace one with the other", - a, b + "`{a}` and `{b}` must be the same: replace one with the other", )); } SuggestedConstraint::Static(a) => { - diag.help(&format!("replace `{}` with `'static`", a)); + diag.help(&format!("replace `{a}` with `'static`")); } } } diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 9bc2e79e2..187861ba1 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -4,9 +4,14 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan}; +use rustc_hir as hir; +use rustc_hir::def::Res::Def; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; -use rustc_hir::{self as hir, Item, ItemKind, Node}; +use rustc_hir::GenericBound::Trait; +use rustc_hir::QPath::Resolved; +use rustc_hir::WherePredicate::BoundPredicate; +use rustc_hir::{PolyTraitRef, TyKind, WhereBoundPredicate}; use rustc_infer::infer::{ error_reporting::nice_region_error::{ self, find_anon_type, find_param_with_region, suggest_adding_lifetime_params, @@ -18,11 +23,11 @@ use rustc_infer::infer::{ use rustc_middle::hir::place::PlaceBase; use rustc_middle::mir::{ConstraintCategory, ReturnConstraint}; use rustc_middle::ty::subst::InternalSubsts; -use rustc_middle::ty::Region; use rustc_middle::ty::TypeVisitor; use rustc_middle::ty::{self, RegionVid, Ty}; +use rustc_middle::ty::{Region, TyCtxt}; use rustc_span::symbol::{kw, Ident}; -use rustc_span::Span; +use rustc_span::{Span, DUMMY_SP}; use crate::borrowck_errors; use crate::session_diagnostics::{ @@ -70,7 +75,25 @@ impl<'tcx> ConstraintDescription for ConstraintCategory<'tcx> { /// /// Usually we expect this to either be empty or contain a small number of items, so we can avoid /// allocation most of the time. -pub(crate) type RegionErrors<'tcx> = Vec<RegionErrorKind<'tcx>>; +pub(crate) struct RegionErrors<'tcx>(Vec<RegionErrorKind<'tcx>>, TyCtxt<'tcx>); + +impl<'tcx> RegionErrors<'tcx> { + pub fn new(tcx: TyCtxt<'tcx>) -> Self { + Self(vec![], tcx) + } + #[track_caller] + pub fn push(&mut self, val: impl Into<RegionErrorKind<'tcx>>) { + let val = val.into(); + self.1.sess.delay_span_bug(DUMMY_SP, format!("{val:?}")); + self.0.push(val); + } + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + pub fn into_iter(self) -> impl Iterator<Item = RegionErrorKind<'tcx>> { + self.0.into_iter() + } +} #[derive(Clone, Debug)] pub(crate) enum RegionErrorKind<'tcx> { @@ -168,12 +191,109 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { false } + // For generic associated types (GATs) which implied 'static requirement + // from higher-ranked trait bounds (HRTB). Try to locate span of the trait + // and the span which bounded to the trait for adding 'static lifetime suggestion + fn suggest_static_lifetime_for_gat_from_hrtb( + &self, + diag: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, + lower_bound: RegionVid, + ) { + let mut suggestions = vec![]; + let hir = self.infcx.tcx.hir(); + + // find generic associated types in the given region 'lower_bound' + let gat_id_and_generics = self + .regioncx + .placeholders_contained_in(lower_bound) + .map(|placeholder| { + if let Some(id) = placeholder.name.get_id() + && let Some(placeholder_id) = id.as_local() + && let gat_hir_id = hir.local_def_id_to_hir_id(placeholder_id) + && let Some(generics_impl) = hir.get_parent(gat_hir_id).generics() + { + Some((gat_hir_id, generics_impl)) + } else { + None + } + }) + .collect::<Vec<_>>(); + debug!(?gat_id_and_generics); + + // find higher-ranked trait bounds bounded to the generic associated types + let mut hrtb_bounds = vec![]; + gat_id_and_generics.iter().flatten().for_each(|(gat_hir_id, generics)| { + for pred in generics.predicates { + let BoundPredicate( + WhereBoundPredicate { + bound_generic_params, + bounds, + .. + }) = pred else { continue; }; + if bound_generic_params + .iter() + .rfind(|bgp| hir.local_def_id_to_hir_id(bgp.def_id) == *gat_hir_id) + .is_some() + { + for bound in *bounds { + hrtb_bounds.push(bound); + } + } + } + }); + debug!(?hrtb_bounds); + + hrtb_bounds.iter().for_each(|bound| { + let Trait(PolyTraitRef { trait_ref, span: trait_span, .. }, _) = bound else { return; }; + diag.span_note( + *trait_span, + format!("due to current limitations in the borrow checker, this implies a `'static` lifetime") + ); + let Some(generics_fn) = hir.get_generics(self.body.source.def_id().expect_local()) else { return; }; + let Def(_, trait_res_defid) = trait_ref.path.res else { return; }; + debug!(?generics_fn); + generics_fn.predicates.iter().for_each(|predicate| { + let BoundPredicate( + WhereBoundPredicate { + span: bounded_span, + bounded_ty, + bounds, + .. + } + ) = predicate else { return; }; + bounds.iter().for_each(|bd| { + if let Trait(PolyTraitRef { trait_ref: tr_ref, .. }, _) = bd + && let Def(_, res_defid) = tr_ref.path.res + && res_defid == trait_res_defid // trait id matches + && let TyKind::Path(Resolved(_, path)) = bounded_ty.kind + && let Def(_, defid) = path.res + && generics_fn.params + .iter() + .rfind(|param| param.def_id.to_def_id() == defid) + .is_some() { + suggestions.push((bounded_span.shrink_to_hi(), format!(" + 'static"))); + } + }); + }); + }); + if suggestions.len() > 0 { + suggestions.dedup(); + diag.multipart_suggestion_verbose( + format!("consider restricting the type parameter to the `'static` lifetime"), + suggestions, + Applicability::MaybeIncorrect, + ); + } + } + /// Produces nice borrowck error diagnostics for all the errors collected in `nll_errors`. pub(crate) fn report_region_errors(&mut self, nll_errors: RegionErrors<'tcx>) { // Iterate through all the errors, producing a diagnostic for each one. The diagnostics are // buffered in the `MirBorrowckCtxt`. let mut outlives_suggestion = OutlivesSuggestionBuilder::default(); + let mut last_unexpected_hidden_region: Option<(Span, Ty<'_>, ty::OpaqueTypeKey<'tcx>)> = + None; for nll_error in nll_errors.into_iter() { match nll_error { @@ -203,12 +323,21 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { // to report it; we could probably handle it by // iterating over the universal regions and reporting // an error that multiple bounds are required. - self.buffer_error(self.infcx.tcx.sess.create_err( - GenericDoesNotLiveLongEnough { + let mut diag = + self.infcx.tcx.sess.create_err(GenericDoesNotLiveLongEnough { kind: type_test.generic_kind.to_string(), span: type_test_span, - }, - )); + }); + + // Add notes and suggestions for the case of 'static lifetime + // implied but not specified when a generic associated types + // are from higher-ranked trait bounds + self.suggest_static_lifetime_for_gat_from_hrtb( + &mut diag, + type_test.lower_bound, + ); + + self.buffer_error(diag); } } @@ -216,13 +345,19 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let named_ty = self.regioncx.name_regions(self.infcx.tcx, hidden_ty); let named_key = self.regioncx.name_regions(self.infcx.tcx, key); let named_region = self.regioncx.name_regions(self.infcx.tcx, member_region); - self.buffer_error(unexpected_hidden_region_diagnostic( + let mut diag = unexpected_hidden_region_diagnostic( self.infcx.tcx, span, named_ty, named_region, named_key, - )); + ); + if last_unexpected_hidden_region != Some((span, named_ty, named_key)) { + self.buffer_error(diag); + last_unexpected_hidden_region = Some((span, named_ty, named_key)); + } else { + diag.delay_as_bug(); + } } RegionErrorKind::BoundUniversalRegionError { @@ -273,71 +408,6 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { outlives_suggestion.add_suggestion(self); } - fn get_impl_ident_and_self_ty_from_trait( - &self, - def_id: DefId, - trait_objects: &FxIndexSet<DefId>, - ) -> Option<(Ident, &'tcx hir::Ty<'tcx>)> { - let tcx = self.infcx.tcx; - match tcx.hir().get_if_local(def_id) { - Some(Node::ImplItem(impl_item)) => { - match tcx.hir().find_by_def_id(tcx.hir().get_parent_item(impl_item.hir_id()).def_id) - { - Some(Node::Item(Item { - kind: ItemKind::Impl(hir::Impl { self_ty, .. }), - .. - })) => Some((impl_item.ident, self_ty)), - _ => None, - } - } - Some(Node::TraitItem(trait_item)) => { - let trait_did = tcx.hir().get_parent_item(trait_item.hir_id()); - match tcx.hir().find_by_def_id(trait_did.def_id) { - Some(Node::Item(Item { kind: ItemKind::Trait(..), .. })) => { - // The method being called is defined in the `trait`, but the `'static` - // obligation comes from the `impl`. Find that `impl` so that we can point - // at it in the suggestion. - let trait_did = trait_did.to_def_id(); - match tcx - .hir() - .trait_impls(trait_did) - .iter() - .filter_map(|&impl_did| { - match tcx.hir().get_if_local(impl_did.to_def_id()) { - Some(Node::Item(Item { - kind: ItemKind::Impl(hir::Impl { self_ty, .. }), - .. - })) if trait_objects.iter().all(|did| { - // FIXME: we should check `self_ty` against the receiver - // type in the `UnifyReceiver` context, but for now, use - // this imperfect proxy. This will fail if there are - // multiple `impl`s for the same trait like - // `impl Foo for Box<dyn Bar>` and `impl Foo for dyn Bar`. - // In that case, only the first one will get suggestions. - let mut traits = vec![]; - let mut hir_v = HirTraitObjectVisitor(&mut traits, *did); - hir_v.visit_ty(self_ty); - !traits.is_empty() - }) => - { - Some(self_ty) - } - _ => None, - } - }) - .next() - { - Some(self_ty) => Some((trait_item.ident, self_ty)), - _ => None, - } - } - _ => None, - } - } - _ => None, - } - } - /// Report an error because the universal region `fr` was required to outlive /// `outlived_fr` but it is not known to do so. For example: /// @@ -461,7 +531,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { ); (desc, note) } - _ => panic!("Unexpected type {:?}", ty), + _ => panic!("Unexpected type {ty:?}"), }; diag.note(&format!("requirement occurs because of {desc}",)); diag.note(¬e); @@ -472,7 +542,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { for extra in extra_info { match extra { ExtraConstraintInfo::PlaceholderFromPredicate(span) => { - diag.span_note(span, format!("due to current limitations in the borrow checker, this implies a `'static` lifetime")); + diag.span_note(span, "due to current limitations in the borrow checker, this implies a `'static` lifetime"); } } } @@ -504,7 +574,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let ErrorConstraintInfo { outlived_fr, span, .. } = errci; let mut output_ty = self.regioncx.universal_regions().unnormalized_output_ty; - if let ty::Opaque(def_id, _) = *output_ty.kind() { + if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *output_ty.kind() { output_ty = self.infcx.tcx.type_of(def_id) }; @@ -764,10 +834,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let lifetime = if f.has_name() { fr_name.name } else { kw::UnderscoreLifetime }; let arg = match param.param.pat.simple_ident() { - Some(simple_ident) => format!("argument `{}`", simple_ident), + Some(simple_ident) => format!("argument `{simple_ident}`"), None => "the argument".to_string(), }; - let captures = format!("captures data from {}", arg); + let captures = format!("captures data from {arg}"); return nice_region_error::suggest_new_region_bound( self.infcx.tcx, @@ -777,6 +847,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { Some(arg), captures, Some((param.param_ty_span, param.param_ty.to_string())), + self.infcx.tcx.is_suitable_region(f).map(|r| r.def_id), ); } } @@ -832,7 +903,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { visitor.visit_ty(param.param_ty); let Some((ident, self_ty)) = - self.get_impl_ident_and_self_ty_from_trait(instance.def_id(), &visitor.0) else {return}; + NiceRegionError::get_impl_ident_and_self_ty_from_trait(tcx, instance.def_id(), &visitor.0) else { return; }; self.suggest_constrain_dyn_trait_in_impl(diag, &visitor.0, ident, self_ty); } diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index 171e62d91..9233287cf 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -200,9 +200,9 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { /// increment the counter. /// /// This is _not_ idempotent. Call `give_region_a_name` when possible. - fn synthesize_region_name(&self) -> Symbol { + pub(crate) fn synthesize_region_name(&self) -> Symbol { let c = self.next_region_name.replace_with(|counter| *counter + 1); - Symbol::intern(&format!("'{:?}", c)) + Symbol::intern(&format!("'{c:?}")) } /// Maps from an internal MIR region vid to something that we can @@ -493,10 +493,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { // // & // - let's call the lifetime of this reference `'1` - ( - ty::Ref(region, referent_ty, _), - hir::TyKind::Rptr(_lifetime, referent_hir_ty), - ) => { + (ty::Ref(region, referent_ty, _), hir::TyKind::Ref(_lifetime, referent_hir_ty)) => { if region.to_region_vid() == needle_fr { // Just grab the first character, the `&`. let source_map = self.infcx.tcx.sess.source_map(); @@ -622,7 +619,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { // programs, so we need to use delay_span_bug here. See #82126. self.infcx.tcx.sess.delay_span_bug( hir_arg.span(), - &format!("unmatched subst and hir arg: found {:?} vs {:?}", kind, hir_arg), + &format!("unmatched subst and hir arg: found {kind:?} vs {hir_arg:?}"), ); } } @@ -786,8 +783,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { } else { span_bug!( hir_ty.span, - "bounds from lowered return type of async fn did not match expected format: {:?}", - opaque_ty + "bounds from lowered return type of async fn did not match expected format: {opaque_ty:?}", ); } } diff --git a/compiler/rustc_borrowck/src/diagnostics/var_name.rs b/compiler/rustc_borrowck/src/diagnostics/var_name.rs index b385f95b6..ada3310d8 100644 --- a/compiler/rustc_borrowck/src/diagnostics/var_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/var_name.rs @@ -18,7 +18,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { upvars: &[Upvar<'tcx>], fr: RegionVid, ) -> Option<(Option<Symbol>, Span)> { - debug!("get_var_name_and_span_for_region(fr={:?})", fr); + debug!("get_var_name_and_span_for_region(fr={fr:?})"); assert!(self.universal_regions().is_universal_region(fr)); debug!("get_var_name_and_span_for_region: attempting upvar"); @@ -44,10 +44,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { ) -> Option<usize> { let upvar_index = self.universal_regions().defining_ty.upvar_tys().position(|upvar_ty| { - debug!("get_upvar_index_for_region: upvar_ty={:?}", upvar_ty); + debug!("get_upvar_index_for_region: upvar_ty={upvar_ty:?}"); tcx.any_free_region_meets(&upvar_ty, |r| { let r = r.to_region_vid(); - debug!("get_upvar_index_for_region: r={:?} fr={:?}", r, fr); + debug!("get_upvar_index_for_region: r={r:?} fr={fr:?}"); r == fr }) })?; @@ -55,8 +55,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { let upvar_ty = self.universal_regions().defining_ty.upvar_tys().nth(upvar_index); debug!( - "get_upvar_index_for_region: found {:?} in upvar {} which has type {:?}", - fr, upvar_index, upvar_ty, + "get_upvar_index_for_region: found {fr:?} in upvar {upvar_index} which has type {upvar_ty:?}", ); Some(upvar_index) @@ -71,13 +70,12 @@ impl<'tcx> RegionInferenceContext<'tcx> { upvar_index: usize, ) -> (Symbol, Span) { let upvar_hir_id = upvars[upvar_index].place.get_root_variable(); - debug!("get_upvar_name_and_span_for_region: upvar_hir_id={:?}", upvar_hir_id); + debug!("get_upvar_name_and_span_for_region: upvar_hir_id={upvar_hir_id:?}"); let upvar_name = tcx.hir().name(upvar_hir_id); let upvar_span = tcx.hir().span(upvar_hir_id); debug!( - "get_upvar_name_and_span_for_region: upvar_name={:?} upvar_span={:?}", - upvar_name, upvar_span + "get_upvar_name_and_span_for_region: upvar_name={upvar_name:?} upvar_span={upvar_span:?}", ); (upvar_name, upvar_span) @@ -97,15 +95,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { let argument_index = self.universal_regions().unnormalized_input_tys.iter().skip(implicit_inputs).position( |arg_ty| { - debug!("get_argument_index_for_region: arg_ty = {:?}", arg_ty); + debug!("get_argument_index_for_region: arg_ty = {arg_ty:?}"); tcx.any_free_region_meets(arg_ty, |r| r.to_region_vid() == fr) }, )?; debug!( - "get_argument_index_for_region: found {:?} in argument {} which has type {:?}", - fr, - argument_index, + "get_argument_index_for_region: found {fr:?} in argument {argument_index} which has type {:?}", self.universal_regions().unnormalized_input_tys[argument_index], ); @@ -122,13 +118,12 @@ impl<'tcx> RegionInferenceContext<'tcx> { ) -> (Option<Symbol>, Span) { let implicit_inputs = self.universal_regions().defining_ty.implicit_inputs(); let argument_local = Local::new(implicit_inputs + argument_index + 1); - debug!("get_argument_name_and_span_for_region: argument_local={:?}", argument_local); + debug!("get_argument_name_and_span_for_region: argument_local={argument_local:?}"); let argument_name = local_names[argument_local]; let argument_span = body.local_decls[argument_local].source_info.span; debug!( - "get_argument_name_and_span_for_region: argument_name={:?} argument_span={:?}", - argument_name, argument_span + "get_argument_name_and_span_for_region: argument_name={argument_name:?} argument_span={argument_span:?}", ); (argument_name, argument_span) diff --git a/compiler/rustc_borrowck/src/facts.rs b/compiler/rustc_borrowck/src/facts.rs index 51ed27c16..02ffb51fb 100644 --- a/compiler/rustc_borrowck/src/facts.rs +++ b/compiler/rustc_borrowck/src/facts.rs @@ -192,7 +192,7 @@ fn write_row( ) -> Result<(), Box<dyn Error>> { for (index, c) in columns.iter().enumerate() { let tail = if index == columns.len() - 1 { "\n" } else { "\t" }; - write!(out, "{:?}{}", c.to_string(location_table), tail)?; + write!(out, "{:?}{tail}", c.to_string(location_table))?; } Ok(()) } diff --git a/compiler/rustc_borrowck/src/invalidation.rs b/compiler/rustc_borrowck/src/invalidation.rs index f66a7ab3c..6fd929005 100644 --- a/compiler/rustc_borrowck/src/invalidation.rs +++ b/compiler/rustc_borrowck/src/invalidation.rs @@ -106,7 +106,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { self.check_activations(location); match &terminator.kind { - TerminatorKind::SwitchInt { discr, switch_ty: _, targets: _ } => { + TerminatorKind::SwitchInt { discr, targets: _ } => { self.consume_operand(location, discr); } TerminatorKind::Drop { place: drop_place, target: _, unwind: _ } => { diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 74b4e4a0c..73ea7314b 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -5,6 +5,7 @@ #![feature(let_chains)] #![feature(min_specialization)] #![feature(never_type)] +#![feature(once_cell)] #![feature(rustc_attrs)] #![feature(stmt_expr_attributes)] #![feature(trusted_step)] @@ -39,6 +40,7 @@ use rustc_span::{Span, Symbol}; use either::Either; use smallvec::SmallVec; +use std::cell::OnceCell; use std::cell::RefCell; use std::collections::BTreeMap; use std::rc::Rc; @@ -124,10 +126,7 @@ pub fn provide(providers: &mut Providers) { }; } -fn mir_borrowck<'tcx>( - tcx: TyCtxt<'tcx>, - def: ty::WithOptConstParam<LocalDefId>, -) -> &'tcx BorrowCheckResult<'tcx> { +fn mir_borrowck(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -> &BorrowCheckResult<'_> { let (input_body, promoted) = tcx.mir_promoted(def); debug!("run query mir_borrowck: {}", tcx.def_path_str(def.did.to_def_id())); @@ -336,7 +335,7 @@ fn do_mir_borrowck<'tcx>( used_mut: Default::default(), used_mut_upvars: SmallVec::new(), borrow_set: Rc::clone(&borrow_set), - dominators: Dominators::dummy(), // not used + dominators: Default::default(), upvars: Vec::new(), local_names: IndexVec::from_elem(None, &promoted_body.local_decls), region_names: RefCell::default(), @@ -349,8 +348,6 @@ fn do_mir_borrowck<'tcx>( }; } - let dominators = body.basic_blocks.dominators(); - let mut mbcx = MirBorrowckCtxt { infcx, param_env, @@ -367,7 +364,7 @@ fn do_mir_borrowck<'tcx>( used_mut: Default::default(), used_mut_upvars: SmallVec::new(), borrow_set: Rc::clone(&borrow_set), - dominators, + dominators: Default::default(), upvars, local_names, region_names: RefCell::default(), @@ -537,7 +534,7 @@ struct MirBorrowckCtxt<'cx, 'tcx> { borrow_set: Rc<BorrowSet<'tcx>>, /// Dominators for MIR - dominators: Dominators<BasicBlock>, + dominators: OnceCell<Dominators<BasicBlock>>, /// Information about upvars not necessarily preserved in types or MIR upvars: Vec<Upvar<'tcx>>, @@ -644,7 +641,7 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx self.check_activations(loc, span, flow_state); match &term.kind { - TerminatorKind::SwitchInt { discr, switch_ty: _, targets: _ } => { + TerminatorKind::SwitchInt { discr, targets: _ } => { self.consume_operand(loc, (discr, span), flow_state); } TerminatorKind::Drop { place, target: _, unwind: _ } => { @@ -866,7 +863,6 @@ enum WriteKind { /// local place can be mutated. // // FIXME: @nikomatsakis suggested that this flag could be removed with the following modifications: -// - Merge `check_access_permissions()` and `check_if_reassignment_to_immutable_state()`. // - Split `is_mutable()` into `is_assignable()` (can be directly assigned) and // `is_declared_mutable()`. // - Take flow state into consideration in `is_assignable()` for local variables. @@ -1055,7 +1051,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { (Read(kind), BorrowKind::Unique | BorrowKind::Mut { .. }) => { // Reading from mere reservations of mutable-borrows is OK. - if !is_active(&this.dominators, borrow, location) { + if !is_active(this.dominators(), borrow, location) { assert!(allow_two_phase_borrow(borrow.kind)); return Control::Continue; } @@ -1135,20 +1131,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // Write of P[i] or *P requires P init'd. self.check_if_assigned_path_is_moved(location, place_span, flow_state); - // Special case: you can assign an immutable local variable - // (e.g., `x = ...`) so long as it has never been initialized - // before (at this point in the flow). - if let Some(local) = place_span.0.as_local() { - if let Mutability::Not = self.body.local_decls[local].mutability { - // check for reassignments to immutable local variables - self.check_if_reassignment_to_immutable_state( - location, local, place_span, flow_state, - ); - return; - } - } - - // Otherwise, use the normal access permission rules. self.access_place( location, place_span, @@ -1557,24 +1539,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } - fn check_if_reassignment_to_immutable_state( - &mut self, - location: Location, - local: Local, - place_span: (Place<'tcx>, Span), - flow_state: &Flows<'cx, 'tcx>, - ) { - debug!("check_if_reassignment_to_immutable_state({:?})", local); - - // Check if any of the initializations of `local` have happened yet: - if let Some(init_index) = self.is_local_ever_initialized(local, flow_state) { - // And, if so, report an error. - let init = &self.move_data.inits[init_index]; - let span = init.span(&self.body); - self.report_illegal_reassignment(location, place_span, span, place_span.0); - } - } - fn check_if_full_path_is_moved( &mut self, location: Location, @@ -2040,12 +2004,19 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // partial initialization, do not complain about mutability // errors except for actual mutation (as opposed to an attempt // to do a partial initialization). - let previously_initialized = - self.is_local_ever_initialized(place.local, flow_state).is_some(); + let previously_initialized = self.is_local_ever_initialized(place.local, flow_state); // at this point, we have set up the error reporting state. - if previously_initialized { - self.report_mutability_error(place, span, the_place_err, error_access, location); + if let Some(init_index) = previously_initialized { + if let (AccessKind::Mutate, Some(_)) = (error_access, place.as_local()) { + // If this is a mutate access to an immutable local variable with no projections + // report the error as an illegal reassignment + let init = &self.move_data.inits[init_index]; + let assigned_span = init.span(&self.body); + self.report_illegal_reassignment(location, (place, span), assigned_span, place); + } else { + self.report_mutability_error(place, span, the_place_err, error_access, location) + } true } else { false @@ -2059,12 +2030,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ) -> Option<InitIndex> { let mpi = self.move_data.rev_lookup.find_local(local); let ii = &self.move_data.init_path_map[mpi]; - for &index in ii { - if flow_state.ever_inits.contains(index) { - return Some(index); - } - } - None + ii.into_iter().find(|&&index| flow_state.ever_inits.contains(index)).copied() } /// Adds the place into the used mutable variables set @@ -2207,7 +2173,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // `self.foo` -- we want to double // check that the location `*self` // is mutable (i.e., this is not a - // `Fn` closure). But if that + // `Fn` closure). But if that // check succeeds, we want to // *blame* the mutability on // `place` (that is, @@ -2253,6 +2219,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { fn is_upvar_field_projection(&self, place_ref: PlaceRef<'tcx>) -> Option<Field> { path_utils::is_upvar_field_projection(self.infcx.tcx, &self.upvars, place_ref, self.body()) } + + fn dominators(&self) -> &Dominators<BasicBlock> { + self.dominators.get_or_init(|| self.body.basic_blocks.dominators()) + } } mod error { @@ -2278,6 +2248,7 @@ mod error { /// same primary span come out in a consistent order. buffered_move_errors: BTreeMap<Vec<MoveOutIndex>, (PlaceRef<'tcx>, DiagnosticBuilder<'tcx, ErrorGuaranteed>)>, + buffered_mut_errors: FxHashMap<Span, (DiagnosticBuilder<'tcx, ErrorGuaranteed>, usize)>, /// Diagnostics to be reported buffer. buffered: Vec<Diagnostic>, /// Set to Some if we emit an error during borrowck @@ -2289,6 +2260,7 @@ mod error { BorrowckErrors { tcx, buffered_move_errors: BTreeMap::new(), + buffered_mut_errors: Default::default(), buffered: Default::default(), tainted_by_errors: None, } @@ -2339,12 +2311,34 @@ mod error { } } + pub fn get_buffered_mut_error( + &mut self, + span: Span, + ) -> Option<(DiagnosticBuilder<'tcx, ErrorGuaranteed>, usize)> { + self.errors.buffered_mut_errors.remove(&span) + } + + pub fn buffer_mut_error( + &mut self, + span: Span, + t: DiagnosticBuilder<'tcx, ErrorGuaranteed>, + count: usize, + ) { + self.errors.buffered_mut_errors.insert(span, (t, count)); + } + pub fn emit_errors(&mut self) -> Option<ErrorGuaranteed> { // Buffer any move errors that we collected and de-duplicated. for (_, (_, diag)) in std::mem::take(&mut self.errors.buffered_move_errors) { // We have already set tainted for this error, so just buffer it. diag.buffer(&mut self.errors.buffered); } + for (_, (mut diag, count)) in std::mem::take(&mut self.errors.buffered_mut_errors) { + if count > 10 { + diag.note(&format!("...and {} other attempted mutable borrows", count - 10)); + } + diag.buffer(&mut self.errors.buffered); + } if !self.errors.buffered.is_empty() { self.errors.buffered.sort_by_key(|diag| diag.sort_span); diff --git a/compiler/rustc_borrowck/src/location.rs b/compiler/rustc_borrowck/src/location.rs index 9fa7e218b..288b7d85b 100644 --- a/compiler/rustc_borrowck/src/location.rs +++ b/compiler/rustc_borrowck/src/location.rs @@ -20,9 +20,8 @@ pub struct LocationTable { } rustc_index::newtype_index! { - pub struct LocationIndex { - DEBUG_FORMAT = "LocationIndex({})" - } + #[debug_format = "LocationIndex({})"] + pub struct LocationIndex {} } #[derive(Copy, Clone, Debug)] diff --git a/compiler/rustc_borrowck/src/member_constraints.rs b/compiler/rustc_borrowck/src/member_constraints.rs index b5e00f471..4af324f74 100644 --- a/compiler/rustc_borrowck/src/member_constraints.rs +++ b/compiler/rustc_borrowck/src/member_constraints.rs @@ -55,9 +55,8 @@ pub(crate) struct NllMemberConstraint<'tcx> { } rustc_index::newtype_index! { - pub(crate) struct NllMemberConstraintIndex { - DEBUG_FORMAT = "MemberConstraintIndex({})" - } + #[debug_format = "MemberConstraintIndex({})"] + pub(crate) struct NllMemberConstraintIndex {} } impl Default for MemberConstraintSet<'_, ty::RegionVid> { @@ -110,7 +109,7 @@ where R1: Copy + Hash + Eq, { /// Remap the "member region" key using `map_fn`, producing a new - /// member constraint set. This is used in the NLL code to map from + /// member constraint set. This is used in the NLL code to map from /// the original `RegionVid` to an scc index. In some cases, we /// may have multiple `R1` values mapping to the same `R2` key -- that /// is ok, the two sets will be merged. @@ -159,7 +158,7 @@ where } /// Iterate down the constraint indices associated with a given - /// peek-region. You can then use `choice_regions` and other + /// peek-region. You can then use `choice_regions` and other /// methods to access data. pub(crate) fn indices( &self, diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index e379e6470..b2d92d0db 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -385,7 +385,7 @@ pub(super) fn dump_annotation<'tcx>( // When the enclosing function is tagged with `#[rustc_regions]`, // we dump out various bits of state as warnings. This is useful - // for verifying that the compiler is behaving as expected. These + // for verifying that the compiler is behaving as expected. These // warnings focus on the closure region requirements -- for // viewing the intraprocedural state, the -Zdump-mir output is // better. diff --git a/compiler/rustc_borrowck/src/place_ext.rs b/compiler/rustc_borrowck/src/place_ext.rs index 9f6b1fdfc..85d207b2f 100644 --- a/compiler/rustc_borrowck/src/place_ext.rs +++ b/compiler/rustc_borrowck/src/place_ext.rs @@ -63,7 +63,7 @@ impl<'tcx> PlaceExt<'tcx> for Place<'tcx> { ty::RawPtr(..) | ty::Ref(_, _, hir::Mutability::Not) => { // For both derefs of raw pointers and `&T` // references, the original path is `Copy` and - // therefore not significant. In particular, + // therefore not significant. In particular, // there is nothing the user can do to the // original path that would invalidate the // newly created reference -- and if there diff --git a/compiler/rustc_borrowck/src/places_conflict.rs b/compiler/rustc_borrowck/src/places_conflict.rs index 89ac0dfa4..918fb2d69 100644 --- a/compiler/rustc_borrowck/src/places_conflict.rs +++ b/compiler/rustc_borrowck/src/places_conflict.rs @@ -209,7 +209,7 @@ fn place_components_conflict<'tcx>( match (elem, &base_ty.kind(), access) { (_, _, Shallow(Some(ArtificialField::ArrayLength))) | (_, _, Shallow(Some(ArtificialField::ShallowBorrow))) => { - // The array length is like additional fields on the + // The array length is like additional fields on the // type; it does not overlap any existing data there. // Furthermore, if cannot actually be a prefix of any // borrowed place (at least in MIR as it is currently.) diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 90e2b6b69..238172ea3 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -527,6 +527,14 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.scc_values.region_value_str(scc) } + pub(crate) fn placeholders_contained_in<'a>( + &'a self, + r: RegionVid, + ) -> impl Iterator<Item = ty::PlaceholderRegion> + 'a { + let scc = self.constraint_sccs.scc(r.to_region_vid()); + self.scc_values.placeholders_contained_in(scc) + } + /// Returns access to the value of `r` for debugging purposes. pub(crate) fn region_universe(&self, r: RegionVid) -> ty::UniverseIndex { let scc = self.constraint_sccs.scc(r.to_region_vid()); @@ -562,7 +570,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { let mir_def_id = body.source.def_id(); self.propagate_constraints(body); - let mut errors_buffer = RegionErrors::new(); + let mut errors_buffer = RegionErrors::new(infcx.tcx); // If this is a closure, we can propagate unsatisfied // `outlives_requirements` to our creator, so create a vector @@ -680,7 +688,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// enforce the constraint). /// /// The current value of `scc` at the time the method is invoked - /// is considered a *lower bound*. If possible, we will modify + /// is considered a *lower bound*. If possible, we will modify /// the constraint to set it equal to one of the option regions. /// If we make any changes, returns true, else false. #[instrument(skip(self, member_constraint_index), level = "debug")] @@ -747,27 +755,14 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Otherwise, we need to find the minimum remaining choice, if // any, and take that. debug!("choice_regions remaining are {:#?}", choice_regions); - let min = |r1: ty::RegionVid, r2: ty::RegionVid| -> Option<ty::RegionVid> { - let r1_outlives_r2 = self.universal_region_relations.outlives(r1, r2); - let r2_outlives_r1 = self.universal_region_relations.outlives(r2, r1); - match (r1_outlives_r2, r2_outlives_r1) { - (true, true) => Some(r1.min(r2)), - (true, false) => Some(r2), - (false, true) => Some(r1), - (false, false) => None, - } + let Some(&min_choice) = choice_regions.iter().find(|&r1| { + choice_regions.iter().all(|&r2| { + self.universal_region_relations.outlives(r2, *r1) + }) + }) else { + debug!("no choice region outlived by all others"); + return false; }; - let mut min_choice = choice_regions[0]; - for &other_option in &choice_regions[1..] { - debug!(?min_choice, ?other_option,); - match min(min_choice, other_option) { - Some(m) => min_choice = m, - None => { - debug!(?min_choice, ?other_option, "incomparable; no min choice",); - return false; - } - } - } let min_choice_scc = self.constraint_sccs.scc(min_choice); debug!(?min_choice, ?min_choice_scc); @@ -844,7 +839,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { if self.eval_verify_bound( infcx, param_env, - body, generic_ty, type_test.lower_bound, &type_test.verify_bound, @@ -973,16 +967,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { // // This is needed because -- particularly in the case // where `ur` is a local bound -- we are sometimes in a - // position to prove things that our caller cannot. See + // position to prove things that our caller cannot. See // #53570 for an example. - if self.eval_verify_bound( - infcx, - param_env, - body, - generic_ty, - ur, - &type_test.verify_bound, - ) { + if self.eval_verify_bound(infcx, param_env, generic_ty, ur, &type_test.verify_bound) { continue; } @@ -1203,7 +1190,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { &self, infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, - body: &Body<'tcx>, generic_ty: Ty<'tcx>, lower_bound: RegionVid, verify_bound: &VerifyBound<'tcx>, @@ -1226,25 +1212,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { } VerifyBound::AnyBound(verify_bounds) => verify_bounds.iter().any(|verify_bound| { - self.eval_verify_bound( - infcx, - param_env, - body, - generic_ty, - lower_bound, - verify_bound, - ) + self.eval_verify_bound(infcx, param_env, generic_ty, lower_bound, verify_bound) }), VerifyBound::AllBounds(verify_bounds) => verify_bounds.iter().all(|verify_bound| { - self.eval_verify_bound( - infcx, - param_env, - body, - generic_ty, - lower_bound, - verify_bound, - ) + self.eval_verify_bound(infcx, param_env, generic_ty, lower_bound, verify_bound) }), } } @@ -1683,26 +1655,29 @@ impl<'tcx> RegionInferenceContext<'tcx> { let longer_fr_scc = self.constraint_sccs.scc(longer_fr); debug!("check_bound_universal_region: longer_fr_scc={:?}", longer_fr_scc,); - // If we have some bound universal region `'a`, then the only - // elements it can contain is itself -- we don't know anything - // else about it! - let Some(error_element) = ({ - self.scc_values.elements_contained_in(longer_fr_scc).find(|element| match element { - RegionElement::Location(_) => true, - RegionElement::RootUniversalRegion(_) => true, - RegionElement::PlaceholderRegion(placeholder1) => placeholder != *placeholder1, - }) - }) else { - return; - }; - debug!("check_bound_universal_region: error_element = {:?}", error_element); + for error_element in self.scc_values.elements_contained_in(longer_fr_scc) { + match error_element { + RegionElement::Location(_) | RegionElement::RootUniversalRegion(_) => {} + // If we have some bound universal region `'a`, then the only + // elements it can contain is itself -- we don't know anything + // else about it! + RegionElement::PlaceholderRegion(placeholder1) => { + if placeholder == placeholder1 { + continue; + } + } + } - // Find the region that introduced this `error_element`. - errors_buffer.push(RegionErrorKind::BoundUniversalRegionError { - longer_fr, - error_element, - placeholder, - }); + errors_buffer.push(RegionErrorKind::BoundUniversalRegionError { + longer_fr, + error_element, + placeholder, + }); + + // Stop after the first error, it gets too noisy otherwise, and does not provide more information. + break; + } + debug!("check_bound_universal_region: all bounds satisfied"); } #[instrument(level = "debug", skip(self, infcx, errors_buffer))] @@ -2068,7 +2043,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // '5: '6 ('6 is the target) // // Some of those regions are unified with `'6` (in the same - // SCC). We want to screen those out. After that point, the + // SCC). We want to screen those out. After that point, the // "closest" constraint we have to the end is going to be the // most likely to be the point where the value escapes -- but // we still want to screen for an "interesting" point to diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 516a08077..db5a67a8b 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -12,6 +12,8 @@ use rustc_span::Span; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; use rustc_trait_selection::traits::ObligationCtxt; +use crate::session_diagnostics::NonGenericOpaqueTypeParam; + use super::RegionInferenceContext; impl<'tcx> RegionInferenceContext<'tcx> { @@ -235,7 +237,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { /// # Parameters /// /// - `def_id`, the `impl Trait` type - /// - `substs`, the substs used to instantiate this opaque type + /// - `substs`, the substs used to instantiate this opaque type /// - `instantiated_ty`, the inferred type C1 -- fully resolved, lifted version of /// `opaque_defn.concrete_ty` #[instrument(level = "debug", skip(self))] @@ -262,7 +264,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { return self.tcx.ty_error(); } - // Only check this for TAIT. RPIT already supports `src/test/ui/impl-trait/nested-return-type2.rs` + // Only check this for TAIT. RPIT already supports `tests/ui/impl-trait/nested-return-type2.rs` // on stable and we'd break that. let OpaqueTyOrigin::TyAlias = origin else { return definition_ty; @@ -318,7 +320,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { // This is still required for many(half of the tests in ui/type-alias-impl-trait) // tests to pass - let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); + let _ = infcx.take_opaque_types(); if errors.is_empty() { definition_ty @@ -389,17 +391,13 @@ fn check_opaque_type_parameter_valid( } else { // Prevent `fn foo() -> Foo<u32>` from being defining. let opaque_param = opaque_generics.param_at(i, tcx); - tcx.sess - .struct_span_err(span, "non-defining opaque type use in defining scope") - .span_note( - tcx.def_span(opaque_param.def_id), - &format!( - "used non-generic {} `{}` for generic parameter", - opaque_param.kind.descr(), - arg, - ), - ) - .emit(); + let kind = opaque_param.kind.descr(); + tcx.sess.emit_err(NonGenericOpaqueTypeParam { + ty: arg, + kind, + span, + param_span: tcx.def_span(opaque_param.def_id), + }); return false; } } diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs index 7498ddccf..c3dfeedc2 100644 --- a/compiler/rustc_borrowck/src/region_infer/values.rs +++ b/compiler/rustc_borrowck/src/region_infer/values.rs @@ -90,12 +90,14 @@ impl RegionValueElements { rustc_index::newtype_index! { /// A single integer representing a `Location` in the MIR control-flow /// graph. Constructed efficiently from `RegionValueElements`. - pub struct PointIndex { DEBUG_FORMAT = "PointIndex({})" } + #[debug_format = "PointIndex({})"] + pub struct PointIndex {} } rustc_index::newtype_index! { /// A single integer representing a `ty::Placeholder`. - pub struct PlaceholderIndex { DEBUG_FORMAT = "PlaceholderIndex({})" } + #[debug_format = "PlaceholderIndex({})"] + pub struct PlaceholderIndex {} } /// An individual element in a region value -- the value of a diff --git a/compiler/rustc_borrowck/src/session_diagnostics.rs b/compiler/rustc_borrowck/src/session_diagnostics.rs index 577332c07..23acf1592 100644 --- a/compiler/rustc_borrowck/src/session_diagnostics.rs +++ b/compiler/rustc_borrowck/src/session_diagnostics.rs @@ -1,6 +1,6 @@ use rustc_errors::{IntoDiagnosticArg, MultiSpan}; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; -use rustc_middle::ty::Ty; +use rustc_middle::ty::{GenericArg, Ty}; use rustc_span::Span; use crate::diagnostics::RegionName; @@ -240,3 +240,14 @@ pub(crate) struct MoveBorrow<'a> { #[label] pub borrow_span: Span, } + +#[derive(Diagnostic)] +#[diag(borrowck_opaque_type_non_generic_param, code = "E0792")] +pub(crate) struct NonGenericOpaqueTypeParam<'a, 'tcx> { + pub ty: GenericArg<'tcx>, + pub kind: &'a str, + #[primary_span] + pub span: Span, + #[label] + pub param_span: Span, +} diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs index 3617bf58b..11729e2c8 100644 --- a/compiler/rustc_borrowck/src/type_check/canonical.rs +++ b/compiler/rustc_borrowck/src/type_check/canonical.rs @@ -1,13 +1,13 @@ use std::fmt; -use rustc_infer::infer::canonical::Canonical; -use rustc_infer::traits::query::NoSolution; +use rustc_infer::infer::{canonical::Canonical, InferOk}; use rustc_middle::mir::ConstraintCategory; -use rustc_middle::ty::{self, ToPredicate, TypeFoldable}; +use rustc_middle::ty::{self, ToPredicate, Ty, TypeFoldable}; use rustc_span::def_id::DefId; use rustc_span::Span; use rustc_trait_selection::traits::query::type_op::{self, TypeOpOutput}; -use rustc_trait_selection::traits::query::Fallible; +use rustc_trait_selection::traits::query::{Fallible, NoSolution}; +use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt}; use crate::diagnostics::{ToUniverseInfo, UniverseInfo}; @@ -107,11 +107,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { instantiated_predicates: ty::InstantiatedPredicates<'tcx>, locations: Locations, ) { - for (predicate, span) in instantiated_predicates - .predicates - .into_iter() - .zip(instantiated_predicates.spans.into_iter()) - { + for (predicate, span) in instantiated_predicates { debug!(?predicate); let category = ConstraintCategory::Predicate(span); let predicate = self.normalize_with_category(predicate, locations, category); @@ -177,4 +173,74 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { value }) } + + #[instrument(skip(self), level = "debug")] + pub(super) fn ascribe_user_type( + &mut self, + mir_ty: Ty<'tcx>, + user_ty: ty::UserType<'tcx>, + span: Span, + ) { + // FIXME: Ideally MIR types are normalized, but this is not always true. + let mir_ty = self.normalize(mir_ty, Locations::All(span)); + + self.fully_perform_op( + Locations::All(span), + ConstraintCategory::Boring, + self.param_env.and(type_op::ascribe_user_type::AscribeUserType::new(mir_ty, user_ty)), + ) + .unwrap_or_else(|err| { + span_mirbug!( + self, + span, + "ascribe_user_type `{mir_ty:?}=={user_ty:?}` failed with `{err:?}`", + ); + }); + } + + /// *Incorrectly* skips the WF checks we normally do in `ascribe_user_type`. + /// + /// FIXME(#104478, #104477): This is a hack for backward-compatibility. + #[instrument(skip(self), level = "debug")] + pub(super) fn ascribe_user_type_skip_wf( + &mut self, + mir_ty: Ty<'tcx>, + user_ty: ty::UserType<'tcx>, + span: Span, + ) { + let ty::UserType::Ty(user_ty) = user_ty else { bug!() }; + + // A fast path for a common case with closure input/output types. + if let ty::Infer(_) = user_ty.kind() { + self.eq_types(user_ty, mir_ty, Locations::All(span), ConstraintCategory::Boring) + .unwrap(); + return; + } + + let mir_ty = self.normalize(mir_ty, Locations::All(span)); + let cause = ObligationCause::dummy_with_span(span); + let param_env = self.param_env; + let op = |infcx: &'_ _| { + let ocx = ObligationCtxt::new_in_snapshot(infcx); + let user_ty = ocx.normalize(&cause, param_env, user_ty); + ocx.eq(&cause, param_env, user_ty, mir_ty)?; + if !ocx.select_all_or_error().is_empty() { + return Err(NoSolution); + } + Ok(InferOk { value: (), obligations: vec![] }) + }; + + self.fully_perform_op( + Locations::All(span), + ConstraintCategory::Boring, + type_op::custom::CustomTypeOp::new(op, || "ascribe_user_type_skip_wf".to_string()), + ) + .unwrap_or_else(|err| { + span_mirbug!( + self, + span, + "ascribe_user_type_skip_wf `{mir_ty:?}=={user_ty:?}` failed with `{err:?}`", + ); + }); + } } diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index ce7f857e2..e15d1b99a 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -107,7 +107,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { closure_substs: ty::SubstsRef<'tcx>, ) { // Extract the values of the free regions in `closure_substs` - // into a vector. These are the regions that we will be + // into a vector. These are the regions that we will be // relating to one another. let closure_mapping = &UniversalRegions::closure_mapping( self.tcx, 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 14cfc3613..82ff86247 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -85,7 +85,7 @@ impl UniversalRegionRelations<'_> { /// outlives `fr` and (b) is not local. /// /// (*) If there are multiple competing choices, we return all of them. - pub(crate) fn non_local_upper_bounds<'a>(&'a self, fr: RegionVid) -> Vec<RegionVid> { + pub(crate) fn non_local_upper_bounds(&self, fr: RegionVid) -> Vec<RegionVid> { debug!("non_local_upper_bound(fr={:?})", fr); let res = self.non_local_bounds(&self.inverse_outlives, fr); assert!(!res.is_empty(), "can't find an upper bound!?"); @@ -98,7 +98,7 @@ impl UniversalRegionRelations<'_> { let upper_bounds = self.non_local_upper_bounds(fr); // In case we find more than one, reduce to one for - // convenience. This is to prevent us from generating more + // convenience. This is to prevent us from generating more // complex constraints, but it will cause spurious errors. let post_dom = self.inverse_outlives.mutual_immediate_postdominator(upper_bounds); @@ -128,7 +128,7 @@ impl UniversalRegionRelations<'_> { let lower_bounds = self.non_local_bounds(&self.outlives, fr); // In case we find more than one, reduce to one for - // convenience. This is to prevent us from generating more + // convenience. This is to prevent us from generating more // complex constraints, but it will cause spurious errors. let post_dom = self.outlives.mutual_immediate_postdominator(lower_bounds); @@ -148,9 +148,9 @@ impl UniversalRegionRelations<'_> { /// Helper for `non_local_upper_bounds` and `non_local_lower_bounds`. /// Repeatedly invokes `postdom_parent` until we find something that is not /// local. Returns `None` if we never do so. - fn non_local_bounds<'a>( + fn non_local_bounds( &self, - relation: &'a TransitiveRelation<RegionVid>, + relation: &TransitiveRelation<RegionVid>, fr0: RegionVid, ) -> Vec<RegionVid> { // This method assumes that `fr0` is one of the universally @@ -359,14 +359,9 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { .insert(ty::OutlivesPredicate(GenericKind::Param(param_b), r_a)); } - OutlivesBound::RegionSubProjection(r_a, projection_b) => { + OutlivesBound::RegionSubAlias(r_a, alias_b) => { self.region_bound_pairs - .insert(ty::OutlivesPredicate(GenericKind::Projection(projection_b), r_a)); - } - - OutlivesBound::RegionSubOpaque(r_a, def_id, substs) => { - self.region_bound_pairs - .insert(ty::OutlivesPredicate(GenericKind::Opaque(def_id, substs), r_a)); + .insert(ty::OutlivesPredicate(GenericKind::Alias(alias_b), r_a)); } } } diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs index 62c6f9581..fa9ea769a 100644 --- a/compiler/rustc_borrowck/src/type_check/input_output.rs +++ b/compiler/rustc_borrowck/src/type_check/input_output.rs @@ -10,7 +10,7 @@ use rustc_index::vec::Idx; use rustc_infer::infer::LateBoundRegionConversionTime; use rustc_middle::mir::*; -use rustc_middle::ty::Ty; +use rustc_middle::ty::{self, Ty}; use rustc_span::Span; use crate::universal_regions::UniversalRegions; @@ -18,6 +18,52 @@ use crate::universal_regions::UniversalRegions; use super::{Locations, TypeChecker}; impl<'a, 'tcx> TypeChecker<'a, 'tcx> { + /// Check explicit closure signature annotation, + /// e.g., `|x: FxHashMap<_, &'static u32>| ...`. + #[instrument(skip(self, body), level = "debug")] + pub(super) fn check_signature_annotation(&mut self, body: &Body<'tcx>) { + let mir_def_id = body.source.def_id().expect_local(); + if !self.tcx().is_closure(mir_def_id.to_def_id()) { + return; + } + let Some(user_provided_poly_sig) = + self.tcx().typeck(mir_def_id).user_provided_sigs.get(&mir_def_id) + else { + return; + }; + + // Instantiate the canonicalized variables from user-provided signature + // (e.g., the `_` in the code above) with fresh variables. + // Then replace the bound items in the fn sig with fresh variables, + // so that they represent the view from "inside" the closure. + let user_provided_sig = self + .instantiate_canonical_with_fresh_inference_vars(body.span, &user_provided_poly_sig); + let user_provided_sig = self.infcx.replace_bound_vars_with_fresh_vars( + body.span, + LateBoundRegionConversionTime::FnCall, + user_provided_sig, + ); + + for (&user_ty, arg_decl) in user_provided_sig.inputs().iter().zip( + // In MIR, closure args begin with an implicit `self`. Skip it! + body.args_iter().skip(1).map(|local| &body.local_decls[local]), + ) { + self.ascribe_user_type_skip_wf( + arg_decl.ty, + ty::UserType::Ty(user_ty), + arg_decl.source_info.span, + ); + } + + // If the user explicitly annotated the output type, enforce it. + let output_decl = &body.local_decls[RETURN_PLACE]; + self.ascribe_user_type_skip_wf( + output_decl.ty, + ty::UserType::Ty(user_provided_sig.output()), + output_decl.source_info.span, + ); + } + #[instrument(skip(self, body, universal_regions), level = "debug")] pub(super) fn equate_inputs_and_outputs( &mut self, @@ -31,40 +77,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { debug!(?normalized_output_ty); debug!(?normalized_input_tys); - let mir_def_id = body.source.def_id().expect_local(); - - // If the user explicitly annotated the input types, extract - // those. - // - // e.g., `|x: FxHashMap<_, &'static u32>| ...` - let user_provided_sig; - if !self.tcx().is_closure(mir_def_id.to_def_id()) { - user_provided_sig = None; - } else { - let typeck_results = self.tcx().typeck(mir_def_id); - user_provided_sig = - typeck_results.user_provided_sigs.get(&mir_def_id).map(|user_provided_poly_sig| { - // Instantiate the canonicalized variables from - // user-provided signature (e.g., the `_` in the code - // above) with fresh variables. - let poly_sig = self.instantiate_canonical_with_fresh_inference_vars( - body.span, - &user_provided_poly_sig, - ); - - // Replace the bound items in the fn sig with fresh - // variables, so that they represent the view from - // "inside" the closure. - self.infcx.replace_bound_vars_with_fresh_vars( - body.span, - LateBoundRegionConversionTime::FnCall, - poly_sig, - ) - }); - } - - debug!(?normalized_input_tys, ?body.local_decls); - // Equate expected input tys with those in the MIR. for (argument_index, &normalized_input_ty) in normalized_input_tys.iter().enumerate() { if argument_index + 1 >= body.local_decls.len() { @@ -87,28 +99,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ); } - if let Some(user_provided_sig) = user_provided_sig { - for (argument_index, &user_provided_input_ty) in - user_provided_sig.inputs().iter().enumerate() - { - // In MIR, closures begin an implicit `self`, so - // argument N is stored in local N+2. - let local = Local::new(argument_index + 2); - let mir_input_ty = body.local_decls[local].ty; - let mir_input_span = body.local_decls[local].source_info.span; - - // If the user explicitly annotated the input types, enforce those. - let user_provided_input_ty = - self.normalize(user_provided_input_ty, Locations::All(mir_input_span)); - - self.equate_normalized_input_or_output( - user_provided_input_ty, - mir_input_ty, - mir_input_span, - ); - } - } - debug!( "equate_inputs_and_outputs: body.yield_ty {:?}, universal_regions.yield_ty {:?}", body.yield_ty(), @@ -154,29 +144,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { terr ); }; - - // If the user explicitly annotated the output types, enforce those. - // Note that this only happens for closures. - if let Some(user_provided_sig) = user_provided_sig { - let user_provided_output_ty = user_provided_sig.output(); - let user_provided_output_ty = - self.normalize(user_provided_output_ty, Locations::All(output_span)); - if let Err(err) = self.eq_types( - user_provided_output_ty, - mir_output_ty, - Locations::All(output_span), - ConstraintCategory::BoringNoLocation, - ) { - span_mirbug!( - self, - Location::START, - "equate_inputs_and_outputs: `{:?}=={:?}` failed with `{:?}`", - mir_output_ty, - user_provided_output_ty, - err - ); - } - } } #[instrument(skip(self), level = "debug")] diff --git a/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs b/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs index fda2cee43..8023ef60d 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs @@ -46,7 +46,7 @@ struct Appearance { } rustc_index::newtype_index! { - pub struct AppearanceIndex { .. } + pub struct AppearanceIndex {} } impl vll::LinkElem for Appearance { diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index 42b577175..3ff5d188a 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -328,7 +328,7 @@ impl<'me, 'typeck, 'flow, 'tcx> LivenessResults<'me, 'typeck, 'flow, 'tcx> { debug_assert!(self.drop_live_at.contains(term_point)); // Otherwise, scan backwards through the statements in the - // block. One of them may be either a definition or use + // block. One of them may be either a definition or use // live point. let term_location = self.cx.elements.to_location(term_point); debug_assert_eq!(self.cx.body.terminator_loc(term_location.block), term_location,); diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 6d4ec6b72..81bd4c2a7 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -38,7 +38,6 @@ use rustc_middle::ty::{ use rustc_span::def_id::CRATE_DEF_ID; use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::VariantIdx; -use rustc_trait_selection::traits::query::type_op; use rustc_trait_selection::traits::query::type_op::custom::scrape_region_constraints; use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp; use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput}; @@ -197,6 +196,8 @@ pub(crate) fn type_check<'mir, 'tcx>( } checker.equate_inputs_and_outputs(&body, universal_regions, &normalized_inputs_and_output); + checker.check_signature_annotation(&body); + liveness::generate( &mut checker, body, @@ -208,7 +209,7 @@ pub(crate) fn type_check<'mir, 'tcx>( ); translate_outlives_facts(&mut checker); - let opaque_type_values = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); + let opaque_type_values = infcx.take_opaque_types(); let opaque_type_values = opaque_type_values .into_iter() @@ -391,23 +392,14 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { check_err(self, promoted_body, ty, promoted_ty); } } else { - if let Err(terr) = self.cx.fully_perform_op( - locations, - ConstraintCategory::Boring, - self.cx.param_env.and(type_op::ascribe_user_type::AscribeUserType::new( - constant.literal.ty(), + self.cx.ascribe_user_type( + constant.literal.ty(), + UserType::TypeOf( uv.def.did, UserSubsts { substs: uv.substs, user_self_ty: None }, - )), - ) { - span_mirbug!( - self, - constant, - "bad constant type {:?} ({:?})", - constant, - terr - ); - } + ), + locations.span(&self.cx.body), + ); } } else if let Some(static_def_id) = constant.check_static_ptr(tcx) { let unnormalized_ty = tcx.type_of(static_def_id); @@ -612,7 +604,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { let locations = location.to_locations(); for constraint in constraints.outlives().iter() { - let mut constraint = constraint.clone(); + let mut constraint = *constraint; constraint.locations = locations; if let ConstraintCategory::Return(_) | ConstraintCategory::UseAsConst @@ -1041,58 +1033,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { debug!(?self.user_type_annotations); for user_annotation in self.user_type_annotations { let CanonicalUserTypeAnnotation { span, ref user_ty, inferred_ty } = *user_annotation; - let inferred_ty = self.normalize(inferred_ty, Locations::All(span)); let annotation = self.instantiate_canonical_with_fresh_inference_vars(span, user_ty); - debug!(?annotation); - match annotation { - UserType::Ty(mut ty) => { - ty = self.normalize(ty, Locations::All(span)); - - if let Err(terr) = self.eq_types( - ty, - inferred_ty, - Locations::All(span), - ConstraintCategory::BoringNoLocation, - ) { - span_mirbug!( - self, - user_annotation, - "bad user type ({:?} = {:?}): {:?}", - ty, - inferred_ty, - terr - ); - } - - self.prove_predicate( - ty::Binder::dummy(ty::PredicateKind::WellFormed(inferred_ty.into())), - Locations::All(span), - ConstraintCategory::TypeAnnotation, - ); - } - UserType::TypeOf(def_id, user_substs) => { - if let Err(terr) = self.fully_perform_op( - Locations::All(span), - ConstraintCategory::BoringNoLocation, - self.param_env.and(type_op::ascribe_user_type::AscribeUserType::new( - inferred_ty, - def_id, - user_substs, - )), - ) { - span_mirbug!( - self, - user_annotation, - "bad user type AscribeUserType({:?}, {:?} {:?}, type_of={:?}): {:?}", - inferred_ty, - def_id, - user_substs, - self.tcx().type_of(def_id), - terr, - ); - } - } - } + self.ascribe_user_type(inferred_ty, annotation, span); } } @@ -1153,16 +1095,23 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { category: ConstraintCategory<'tcx>, ) -> Fallible<()> { let annotated_type = self.user_type_annotations[user_ty.base].inferred_ty; + trace!(?annotated_type); let mut curr_projected_ty = PlaceTy::from_ty(annotated_type); let tcx = self.infcx.tcx; for proj in &user_ty.projs { + if let ty::Alias(ty::Opaque, ..) = curr_projected_ty.ty.kind() { + // There is nothing that we can compare here if we go through an opaque type. + // We're always in its defining scope as we can otherwise not project through + // it, so we're constraining it anyways. + return Ok(()); + } let projected_ty = curr_projected_ty.projection_ty_core( tcx, self.param_env, proj, - |this, field, _| { + |this, field, ()| { let ty = this.field_ty(tcx, field); self.normalize(ty, locations) }, @@ -1170,10 +1119,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ); curr_projected_ty = projected_ty; } - debug!( - "user_ty base: {:?} freshened: {:?} projs: {:?} yields: {:?}", - user_ty.base, annotated_type, user_ty.projs, curr_projected_ty - ); + trace!(?curr_projected_ty); let ty = curr_projected_ty.ty; self.relate_types(ty, v.xform(ty::Variance::Contravariant), a, locations, category)?; @@ -1360,25 +1306,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ); } } - TerminatorKind::SwitchInt { discr, switch_ty, .. } => { + TerminatorKind::SwitchInt { discr, .. } => { self.check_operand(discr, term_location); - let discr_ty = discr.ty(body, tcx); - if let Err(terr) = self.sub_types( - discr_ty, - *switch_ty, - term_location.to_locations(), - ConstraintCategory::Assignment, - ) { - span_mirbug!( - self, - term, - "bad SwitchInt ({:?} on {:?}): {:?}", - switch_ty, - discr_ty, - terr - ); - } + let switch_ty = discr.ty(body, tcx); if !switch_ty.is_integral() && !switch_ty.is_char() && !switch_ty.is_bool() { span_mirbug!(self, term, "bad SwitchInt discr ty {:?}", switch_ty); } @@ -1734,7 +1665,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { fn ensure_place_sized(&mut self, ty: Ty<'tcx>, span: Span) { let tcx = self.tcx(); - // Erase the regions from `ty` to get a global type. The + // Erase the regions from `ty` to get a global type. The // `Sized` bound in no way depends on precise regions, so this // shouldn't affect `is_sized`. let erased_ty = tcx.erase_regions(ty); diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index a4a0c5b90..5b4d99682 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -637,7 +637,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { let closure_ty = tcx.closure_env_ty(def_id, substs, env_region).unwrap(); // The "inputs" of the closure in the - // signature appear as a tuple. The MIR side + // signature appear as a tuple. The MIR side // flattens this tuple. let (&output, tuplized_inputs) = inputs_and_output.skip_binder().split_last().unwrap(); |