From 5363f350887b1e5b5dd21a86f88c8af9d7fea6da Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:18:25 +0200 Subject: Merging upstream version 1.67.1+dfsg1. Signed-off-by: Daniel Baumann --- .../src/diagnostics/conflict_errors.rs | 355 +++++++++++++++------ 1 file changed, 250 insertions(+), 105 deletions(-) (limited to 'compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs') diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 583bc2e28..5e3745f17 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -7,7 +7,7 @@ use rustc_errors::{ }; use rustc_hir as hir; use rustc_hir::intravisit::{walk_block, walk_expr, Visitor}; -use rustc_hir::{AsyncGeneratorKind, GeneratorKind}; +use rustc_hir::{AsyncGeneratorKind, GeneratorKind, LangItem}; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::ObligationCause; use rustc_middle::mir::tcx::PlaceTy; @@ -23,7 +23,6 @@ use rustc_span::hygiene::DesugaringKind; use rustc_span::symbol::sym; use rustc_span::{BytePos, Span, Symbol}; use rustc_trait_selection::infer::InferCtxtExt; -use rustc_trait_selection::traits::TraitEngineExt as _; use crate::borrow_set::TwoPhaseActivation; use crate::borrowck_errors; @@ -168,10 +167,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); } - self.add_moved_or_invoked_closure_note(location, used_place, &mut err); + let closure = self.add_moved_or_invoked_closure_note(location, used_place, &mut err); let mut is_loop_move = false; let mut in_pattern = false; + let mut seen_spans = FxHashSet::default(); for move_site in &move_site_vec { let move_out = self.move_data.moves[(*move_site).moi]; @@ -192,43 +192,28 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { is_loop_move = true; } - self.explain_captures( - &mut err, - span, - move_span, - move_spans, - *moved_place, - partially_str, - loop_message, - move_msg, - is_loop_move, - maybe_reinitialized_locations.is_empty(), - ); - - if let (UseSpans::PatUse(span), []) = - (move_spans, &maybe_reinitialized_locations[..]) - { - if maybe_reinitialized_locations.is_empty() { - err.span_suggestion_verbose( - span.shrink_to_lo(), - &format!( - "borrow this field in the pattern to avoid moving {}", - self.describe_place(moved_place.as_ref()) - .map(|n| format!("`{}`", n)) - .unwrap_or_else(|| "the value".to_string()) - ), - "ref ", - Applicability::MachineApplicable, - ); - in_pattern = true; + if !seen_spans.contains(&move_span) { + if !closure { + self.suggest_ref_or_clone(mpi, move_span, &mut err, &mut in_pattern); } + + self.explain_captures( + &mut err, + span, + move_span, + move_spans, + *moved_place, + partially_str, + loop_message, + move_msg, + is_loop_move, + maybe_reinitialized_locations.is_empty(), + ); } + seen_spans.insert(move_span); } - use_spans.var_span_label_path_only( - &mut err, - format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()), - ); + use_spans.var_path_only_subdiag(&mut err, desired_action); if !is_loop_move { err.span_label( @@ -280,7 +265,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { DescribePlaceOpt { including_downcast: true, including_tuple_field: true }, ); let note_msg = match opt_name { - Some(ref 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) { @@ -321,6 +306,160 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } + fn suggest_ref_or_clone( + &mut self, + mpi: MovePathIndex, + move_span: Span, + err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, + in_pattern: &mut bool, + ) { + struct ExpressionFinder<'hir> { + expr_span: Span, + expr: Option<&'hir hir::Expr<'hir>>, + pat: Option<&'hir hir::Pat<'hir>>, + parent_pat: Option<&'hir hir::Pat<'hir>>, + } + impl<'hir> Visitor<'hir> for ExpressionFinder<'hir> { + fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) { + if e.span == self.expr_span { + self.expr = Some(e); + } + hir::intravisit::walk_expr(self, e); + } + fn visit_pat(&mut self, p: &'hir hir::Pat<'hir>) { + if p.span == self.expr_span { + self.pat = Some(p); + } + if let hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, i, sub) = p.kind { + if i.span == self.expr_span || p.span == self.expr_span { + self.pat = Some(p); + } + // Check if we are in a situation of `ident @ ident` where we want to suggest + // `ref ident @ ref ident` or `ref ident @ Struct { ref ident }`. + if let Some(subpat) = sub && self.pat.is_none() { + self.visit_pat(subpat); + if self.pat.is_some() { + self.parent_pat = Some(p); + } + return; + } + } + hir::intravisit::walk_pat(self, p); + } + } + let hir = self.infcx.tcx.hir(); + 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())) + && let Some(hir::Node::Expr(expr)) = hir.find(body_id.hir_id) + { + let place = &self.move_data.move_paths[mpi].place; + let span = place.as_local() + .map(|local| self.body.local_decls[local].source_info.span); + let mut finder = ExpressionFinder { + expr_span: move_span, + expr: None, + pat: None, + parent_pat: None, + }; + finder.visit_expr(expr); + if let Some(span) = span && let Some(expr) = finder.expr { + for (_, expr) in hir.parent_iter(expr.hir_id) { + if let hir::Node::Expr(expr) = expr { + if expr.span.contains(span) { + // If the let binding occurs within the same loop, then that + // loop isn't relevant, like in the following, the outermost `loop` + // doesn't play into `x` being moved. + // ``` + // loop { + // let x = String::new(); + // loop { + // foo(x); + // } + // } + // ``` + break; + } + if let hir::ExprKind::Loop(.., loop_span) = expr.kind { + err.span_label(loop_span, "inside of this loop"); + } + } + } + let typeck = self.infcx.tcx.typeck(self.mir_def_id()); + let hir_id = hir.get_parent_node(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 + && let Some(def_id) = typeck.type_dependent_def_id(parent_expr.hir_id) + { + (def_id.as_local(), args, 1) + } else if let hir::Node::Expr(parent_expr) = parent + && let hir::ExprKind::Call(call, args) = parent_expr.kind + && let ty::FnDef(def_id, _) = typeck.node_type(call.hir_id).kind() + { + (def_id.as_local(), args, 0) + } else { + (None, &[][..], 0) + }; + if let Some(def_id) = def_id + && let Some(node) = hir.find(hir.local_def_id_to_hir_id(def_id)) + && let Some(fn_sig) = node.fn_sig() + && let Some(ident) = node.ident() + && let Some(pos) = args.iter().position(|arg| arg.hir_id == expr.hir_id) + && let Some(arg) = fn_sig.decl.inputs.get(pos + offset) + { + let mut span: MultiSpan = arg.span.into(); + span.push_span_label( + arg.span, + "this parameter takes ownership of the value".to_string(), + ); + let descr = match node.fn_kind() { + Some(hir::intravisit::FnKind::ItemFn(..)) | None => "function", + Some(hir::intravisit::FnKind::Method(..)) => "method", + Some(hir::intravisit::FnKind::Closure) => "closure", + }; + span.push_span_label( + ident.span, + format!("in this {descr}"), + ); + err.span_note( + span, + format!( + "consider changing this parameter type in {descr} `{ident}` to \ + borrow instead if owning the value isn't necessary", + ), + ); + } + let place = &self.move_data.move_paths[mpi].place; + let ty = place.ty(self.body, self.infcx.tcx).ty; + if let hir::Node::Expr(parent_expr) = parent + && let hir::ExprKind::Call(call_expr, _) = parent_expr.kind + && let hir::ExprKind::Path( + hir::QPath::LangItem(LangItem::IntoIterIntoIter, _, _) + ) = call_expr.kind + { + // Do not suggest `.clone()` in a `for` loop, we already suggest borrowing. + } else { + self.suggest_cloning(err, ty, move_span); + } + } + } + if let Some(pat) = finder.pat { + *in_pattern = true; + let mut sugg = vec![(pat.span.shrink_to_lo(), "ref ".to_string())]; + if let Some(pat) = finder.parent_pat { + sugg.insert(0, (pat.span.shrink_to_lo(), "ref ".to_string())); + } + err.multipart_suggestion_verbose( + "borrow this binding in the pattern to avoid moving the value", + sugg, + Applicability::MachineApplicable, + ); + } + } + } + fn report_use_of_uninitialized( &self, mpi: MovePathIndex, @@ -405,10 +544,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let used = desired_action.as_general_verb_in_past_tense(); let mut err = struct_span_err!(self, span, E0381, "{used} binding {desc}{isnt_initialized}"); - use_spans.var_span_label_path_only( - &mut err, - format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()), - ); + use_spans.var_path_only_subdiag(&mut err, desired_action); if let InitializationRequiringAction::PartialAssignment | InitializationRequiringAction::Assignment = desired_action @@ -496,12 +632,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // but the type has region variables, so erase those. tcx.infer_ctxt() .build() - .type_implements_trait( - default_trait, - tcx.erase_regions(ty), - ty::List::empty(), - param_env, - ) + .type_implements_trait(default_trait, [tcx.erase_regions(ty)], param_env) .must_apply_modulo_regions() }; @@ -535,15 +666,17 @@ 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 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::Trait(pred) = pred && pred.self_ty() == ty { + 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() { @@ -552,7 +685,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } 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`. @@ -583,25 +716,41 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let moved_place = &self.move_data.move_paths[move_out.path].place; let move_spans = self.move_spans(moved_place.as_ref(), move_out.source); let move_span = move_spans.args_or_use(); - let suggestion = if borrow_level == hir::Mutability::Mut { - "&mut ".to_string() - } else { - "&".to_string() - }; + let suggestion = borrow_level.ref_prefix_str().to_owned(); (move_span.shrink_to_lo(), suggestion) }) .collect(); err.multipart_suggestion_verbose( - &format!( - "consider {}borrowing {value_name}", - if borrow_level == hir::Mutability::Mut { "mutably " } else { "" } - ), + format!("consider {}borrowing {value_name}", borrow_level.mutably_str()), sugg, Applicability::MaybeIncorrect, ); true } + fn suggest_cloning(&self, err: &mut Diagnostic, ty: Ty<'tcx>, span: Span) { + let tcx = self.infcx.tcx; + // Try to find predicates on *generic params* that would allow copying `ty` + let infcx = tcx.infer_ctxt().build(); + + if let Some(clone_trait_def) = tcx.lang_items().clone_trait() + && infcx + .type_implements_trait( + clone_trait_def, + [tcx.erase_regions(ty)], + self.param_env, + ) + .must_apply_modulo_regions() + { + err.span_suggestion_verbose( + span.shrink_to_hi(), + "consider cloning the value if the performance cost is acceptable", + ".clone()".to_string(), + Applicability::MachineApplicable, + ); + } + } + fn suggest_adding_copy_bounds(&self, err: &mut Diagnostic, ty: Ty<'tcx>, span: Span) { let tcx = self.infcx.tcx; let generics = tcx.generics_of(self.mir_def_id()); @@ -613,36 +762,34 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { else { return; }; // Try to find predicates on *generic params* that would allow copying `ty` let infcx = tcx.infer_ctxt().build(); - let mut fulfill_cx = >::new(infcx.tcx); - - let copy_did = infcx.tcx.lang_items().copy_trait().unwrap(); + let copy_did = infcx.tcx.require_lang_item(LangItem::Copy, Some(span)); let cause = ObligationCause::new( span, self.mir_hir_id(), rustc_infer::traits::ObligationCauseCode::MiscObligation, ); - fulfill_cx.register_bound( + let errors = rustc_trait_selection::traits::fully_solve_bound( &infcx, + cause, self.param_env, // Erase any region vids from the type, which may not be resolved infcx.tcx.erase_regions(ty), copy_did, - cause, ); - // Select all, including ambiguous predicates - let errors = fulfill_cx.select_all_or_error(&infcx); // Only emit suggestion if all required predicates are on generic let predicates: Result, _> = errors .into_iter() .map(|err| match err.obligation.predicate.kind().skip_binder() { - PredicateKind::Trait(predicate) => match predicate.self_ty().kind() { - ty::Param(param_ty) => Ok(( - generics.type_param(param_ty, tcx), - predicate.trait_ref.print_only_trait_path().to_string(), - )), - _ => Err(()), - }, + PredicateKind::Clause(ty::Clause::Trait(predicate)) => { + match predicate.self_ty().kind() { + ty::Param(param_ty) => Ok(( + generics.type_param(param_ty, tcx), + predicate.trait_ref.print_only_trait_path().to_string(), + )), + _ => Err(()), + } + } _ => Err(()), }) .collect(); @@ -678,16 +825,16 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let move_spans = self.move_spans(place.as_ref(), location); let span = move_spans.args_or_use(); - let mut err = - self.cannot_move_when_borrowed(span, &self.describe_any_place(place.as_ref())); - err.span_label(borrow_span, format!("borrow of {} occurs here", borrow_msg)); - err.span_label(span, format!("move out of {} occurs here", value_msg)); - - borrow_spans.var_span_label_path_only( - &mut err, - format!("borrow occurs due to use{}", borrow_spans.describe()), + let mut err = self.cannot_move_when_borrowed( + span, + borrow_span, + &self.describe_any_place(place.as_ref()), + &borrow_msg, + &value_msg, ); + borrow_spans.var_path_only_subdiag(&mut err, crate::InitializationRequiringAction::Borrow); + move_spans.var_span_label( &mut err, format!("move occurs due to use{}", move_spans.describe()), @@ -729,16 +876,15 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { borrow_span, &self.describe_any_place(borrow.borrowed_place.as_ref()), ); - - borrow_spans.var_span_label( - &mut err, - { - let place = &borrow.borrowed_place; - let desc_place = self.describe_any_place(place.as_ref()); - format!("borrow occurs due to use of {}{}", desc_place, borrow_spans.describe()) - }, - "mutable", - ); + borrow_spans.var_subdiag(&mut err, Some(borrow.kind), |kind, var_span| { + use crate::session_diagnostics::CaptureVarCause::*; + let place = &borrow.borrowed_place; + let desc_place = self.describe_any_place(place.as_ref()); + match kind { + Some(_) => BorrowUsePlaceGenerator { place: desc_place, var_span }, + None => BorrowUsePlaceClosure { place: desc_place, var_span }, + } + }); self.explain_why_borrow_contains_point(location, borrow, None) .add_explanation_to_diagnostic( @@ -1271,7 +1417,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // then just use the normal error. The closure isn't escaping // and `move` will not help here. ( - Some(ref name), + Some(name), BorrowExplanation::MustBeValidFor { category: category @ (ConstraintCategory::Return(_) @@ -1292,7 +1438,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { &format!("`{}`", name), ), ( - ref name, + name, BorrowExplanation::MustBeValidFor { category: ConstraintCategory::Assignment, from_closure: false, @@ -1304,7 +1450,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { span, .. }, - ) => self.report_escaping_data(borrow_span, name, upvar_span, upvar_name, span), + ) => self.report_escaping_data(borrow_span, &name, upvar_span, upvar_name, span), (Some(name), explanation) => self.report_local_value_does_not_live_long_enough( location, &name, @@ -1556,7 +1702,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } let mut err = self.temporary_value_borrowed_for_too_long(proper_span); - err.span_label(proper_span, "creates a temporary which is freed while still in use"); + err.span_label(proper_span, "creates a temporary value which is freed while still in use"); err.span_label(drop_span, "temporary value is freed at the end of this statement"); match explanation { @@ -1719,7 +1865,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { err.span_label(borrow_span, note); let tcx = self.infcx.tcx; - let ty_params = ty::List::empty(); let return_ty = self.regioncx.universal_regions().unnormalized_output_ty; let return_ty = tcx.erase_regions(return_ty); @@ -1728,7 +1873,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) && self .infcx - .type_implements_trait(iter_trait, return_ty, ty_params, self.param_env) + .type_implements_trait(iter_trait, [return_ty], self.param_env) .must_apply_modulo_regions() { err.span_suggestion_hidden( @@ -2307,7 +2452,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // and it'll make sense. let location = borrow.reserve_location; debug!("annotate_argument_and_return_for_borrow: location={:?}", location); - if let Some(&Statement { kind: StatementKind::Assign(box (ref reservation, _)), .. }) = + if let Some(Statement { kind: StatementKind::Assign(box (reservation, _)), .. }) = &self.body[location.block].statements.get(location.statement_index) { debug!("annotate_argument_and_return_for_borrow: reservation={:?}", reservation); @@ -2335,8 +2480,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // Check if our `target` was captured by a closure. if let Rvalue::Aggregate( box AggregateKind::Closure(def_id, substs), - ref operands, - ) = *rvalue + operands, + ) = rvalue { for operand in operands { let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else { @@ -2360,7 +2505,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // into a place then we should annotate the closure in // case it ends up being assigned into the return place. annotated_closure = - self.annotate_fn_sig(def_id, substs.as_closure().sig()); + self.annotate_fn_sig(*def_id, substs.as_closure().sig()); debug!( "annotate_argument_and_return_for_borrow: \ annotated_closure={:?} assigned_from_local={:?} \ @@ -2527,7 +2672,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if let hir::TyKind::Rptr(lifetime, _) = &fn_decl.inputs[index].kind { // With access to the lifetime, we can get // the span of it. - arguments.push((*argument, lifetime.span)); + arguments.push((*argument, lifetime.ident.span)); } else { bug!("ty type is a ref but hir type is not"); } @@ -2546,7 +2691,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { 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 { - return_span = lifetime.span; + return_span = lifetime.ident.span; } } -- cgit v1.2.3