diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-07 05:48:48 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-07 05:48:48 +0000 |
commit | ef24de24a82fe681581cc130f342363c47c0969a (patch) | |
tree | 0d494f7e1a38b95c92426f58fe6eaa877303a86c /compiler/rustc_borrowck/src/diagnostics | |
parent | Releasing progress-linux version 1.74.1+dfsg1-1~progress7.99u1. (diff) | |
download | rustc-ef24de24a82fe681581cc130f342363c47c0969a.tar.xz rustc-ef24de24a82fe681581cc130f342363c47c0969a.zip |
Merging upstream version 1.75.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_borrowck/src/diagnostics')
8 files changed, 553 insertions, 351 deletions
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index ee352e911..9a8f1c97e 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -8,7 +8,7 @@ use rustc_errors::{ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{walk_block, walk_expr, Visitor}; -use rustc_hir::{AsyncGeneratorKind, GeneratorKind, LangItem}; +use rustc_hir::{CoroutineKind, CoroutineSource, LangItem}; use rustc_infer::traits::ObligationCause; use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_middle::mir::tcx::PlaceTy; @@ -351,7 +351,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } // Check if we are in a situation of `ident @ ident` where we want to suggest // `ref ident @ ref ident` or `ref ident @ Struct { ref ident }`. - if let Some(subpat) = sub && self.pat.is_none() { + if let Some(subpat) = sub + && self.pat.is_none() + { self.visit_pat(subpat); if self.pat.is_some() { self.parent_pat = Some(p); @@ -370,7 +372,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let mut finder = ExpressionFinder { expr_span: move_span, expr: None, pat: None, parent_pat: None }; finder.visit_expr(expr); - if let Some(span) = span && let Some(expr) = finder.expr { + if let Some(span) = span + && let Some(expr) = finder.expr + { for (_, expr) in hir.parent_iter(expr.hir_id) { if let hir::Node::Expr(expr) = expr { if expr.span.contains(span) { @@ -425,10 +429,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { Some(hir::intravisit::FnKind::Method(..)) => "method", Some(hir::intravisit::FnKind::Closure) => "closure", }; - span.push_span_label( - ident.span, - format!("in this {descr}"), - ); + span.push_span_label(ident.span, format!("in this {descr}")); err.span_note( span, format!( @@ -441,15 +442,16 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let ty = place.ty(self.body, self.infcx.tcx).ty; if let hir::Node::Expr(parent_expr) = parent && let hir::ExprKind::Call(call_expr, _) = parent_expr.kind - && let hir::ExprKind::Path( - hir::QPath::LangItem(LangItem::IntoIterIntoIter, _, _) - ) = call_expr.kind + && let hir::ExprKind::Path(hir::QPath::LangItem( + LangItem::IntoIterIntoIter, + _, + _, + )) = call_expr.kind { // Do not suggest `.clone()` in a `for` loop, we already suggest borrowing. - } else if let UseSpans::FnSelfUse { - kind: CallKind::Normal { .. }, - .. - } = move_spans { + } else if let UseSpans::FnSelfUse { kind: CallKind::Normal { .. }, .. } = + move_spans + { // We already suggest cloning for these cases in `explain_captures`. } else { self.suggest_cloning(err, ty, expr, move_span); @@ -602,10 +604,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if self.sugg_span.is_some() { return; } - if let hir::StmtKind::Local(hir::Local { - span, ty, init: None, .. - }) = &ex.kind && span.contains(self.decl_span) { - self.sugg_span = ty.map_or(Some(self.decl_span), |ty| Some(ty.span)); + if let hir::StmtKind::Local(hir::Local { span, ty, init: None, .. }) = &ex.kind + && span.contains(self.decl_span) + { + self.sugg_span = ty.map_or(Some(self.decl_span), |ty| Some(ty.span)); } hir::intravisit::walk_stmt(self, ex); } @@ -743,19 +745,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ".clone()".to_owned() }; if let Some(clone_trait_def) = tcx.lang_items().clone_trait() - && self.infcx - .type_implements_trait( - clone_trait_def, - [ty], - self.param_env, - ) + && self + .infcx + .type_implements_trait(clone_trait_def, [ty], self.param_env) .must_apply_modulo_regions() { let msg = if let ty::Adt(def, _) = ty.kind() - && [ - tcx.get_diagnostic_item(sym::Arc), - tcx.get_diagnostic_item(sym::Rc), - ].contains(&Some(def.did())) + && [tcx.get_diagnostic_item(sym::Arc), tcx.get_diagnostic_item(sym::Rc)] + .contains(&Some(def.did())) { "clone the value to increment its reference count" } else { @@ -851,7 +848,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { move_spans.var_subdiag(None, &mut err, None, |kind, var_span| { use crate::session_diagnostics::CaptureVarCause::*; match kind { - Some(_) => MoveUseInGenerator { var_span }, + Some(_) => MoveUseInCoroutine { var_span }, None => MoveUseInClosure { var_span }, } }); @@ -897,7 +894,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let desc_place = self.describe_any_place(place.as_ref()); match kind { Some(_) => { - BorrowUsePlaceGenerator { place: desc_place, var_span, is_single_var: true } + BorrowUsePlaceCoroutine { place: desc_place, var_span, is_single_var: true } } None => BorrowUsePlaceClosure { place: desc_place, var_span, is_single_var: true }, } @@ -929,8 +926,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let borrow_spans = self.borrow_spans(span, location); let span = borrow_spans.args_or_use(); - let container_name = if issued_spans.for_generator() || borrow_spans.for_generator() { - "generator" + let container_name = if issued_spans.for_coroutine() || borrow_spans.for_coroutine() { + "coroutine" } else { "closure" }; @@ -1043,7 +1040,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { |kind, var_span| { use crate::session_diagnostics::CaptureVarCause::*; match kind { - Some(_) => BorrowUsePlaceGenerator { + Some(_) => BorrowUsePlaceCoroutine { place: desc_place, var_span, is_single_var: true, @@ -1127,7 +1124,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { borrow_spans.var_subdiag(None, &mut err, Some(gen_borrow_kind), |kind, var_span| { use crate::session_diagnostics::CaptureVarCause::*; match kind { - Some(_) => BorrowUsePlaceGenerator { + Some(_) => BorrowUsePlaceCoroutine { place: desc_place, var_span, is_single_var: false, @@ -1148,7 +1145,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let borrow_place_desc = self.describe_any_place(borrow_place.as_ref()); match kind { Some(_) => { - FirstBorrowUsePlaceGenerator { place: borrow_place_desc, var_span } + FirstBorrowUsePlaceCoroutine { place: borrow_place_desc, var_span } } None => FirstBorrowUsePlaceClosure { place: borrow_place_desc, var_span }, } @@ -1162,7 +1159,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { |kind, var_span| { use crate::session_diagnostics::CaptureVarCause::*; match kind { - Some(_) => SecondBorrowUsePlaceGenerator { place: desc_place, var_span }, + Some(_) => SecondBorrowUsePlaceCoroutine { place: desc_place, var_span }, None => SecondBorrowUsePlaceClosure { place: desc_place, var_span }, } }, @@ -1328,42 +1325,160 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { issue_span: Span, expr_span: Span, body_expr: Option<&'hir hir::Expr<'hir>>, - loop_bind: Option<Symbol>, + loop_bind: Option<&'hir Ident>, + loop_span: Option<Span>, + head_span: Option<Span>, + pat_span: Option<Span>, + head: Option<&'hir hir::Expr<'hir>>, } impl<'hir> Visitor<'hir> for ExprFinder<'hir> { fn visit_expr(&mut self, ex: &'hir hir::Expr<'hir>) { - if let hir::ExprKind::Loop(hir::Block{ stmts: [stmt, ..], ..}, _, hir::LoopSource::ForLoop, _) = ex.kind && - let hir::StmtKind::Expr(hir::Expr{ kind: hir::ExprKind::Match(call, [_, bind, ..], _), ..}) = stmt.kind && - let hir::ExprKind::Call(path, _args) = call.kind && - let hir::ExprKind::Path(hir::QPath::LangItem(LangItem::IteratorNext, _, _, )) = path.kind && - let hir::PatKind::Struct(path, [field, ..], _) = bind.pat.kind && - let hir::QPath::LangItem(LangItem::OptionSome, _, _) = path && - let PatField { pat: hir::Pat{ kind: hir::PatKind::Binding(_, _, ident, ..), .. }, ..} = field && - self.issue_span.source_equal(call.span) { - self.loop_bind = Some(ident.name); + // Try to find + // let result = match IntoIterator::into_iter(<head>) { + // mut iter => { + // [opt_ident]: loop { + // match Iterator::next(&mut iter) { + // None => break, + // Some(<pat>) => <body>, + // }; + // } + // } + // }; + // corresponding to the desugaring of a for loop `for <pat> in <head> { <body> }`. + if let hir::ExprKind::Call(path, [arg]) = ex.kind + && let hir::ExprKind::Path(hir::QPath::LangItem( + LangItem::IntoIterIntoIter, + _, + _, + )) = path.kind + && arg.span.contains(self.issue_span) + { + // Find `IntoIterator::into_iter(<head>)` + self.head = Some(arg); + } + if let hir::ExprKind::Loop( + hir::Block { stmts: [stmt, ..], .. }, + _, + hir::LoopSource::ForLoop, + _, + ) = ex.kind + && let hir::StmtKind::Expr(hir::Expr { + kind: hir::ExprKind::Match(call, [_, bind, ..], _), + span: head_span, + .. + }) = stmt.kind + && let hir::ExprKind::Call(path, _args) = call.kind + && let hir::ExprKind::Path(hir::QPath::LangItem(LangItem::IteratorNext, _, _)) = + path.kind + && let hir::PatKind::Struct(path, [field, ..], _) = bind.pat.kind + && let hir::QPath::LangItem(LangItem::OptionSome, pat_span, _) = path + && call.span.contains(self.issue_span) + { + // Find `<pat>` and the span for the whole `for` loop. + if let PatField { + pat: hir::Pat { kind: hir::PatKind::Binding(_, _, ident, ..), .. }, + .. + } = field + { + self.loop_bind = Some(ident); } + self.head_span = Some(*head_span); + self.pat_span = Some(pat_span); + self.loop_span = Some(stmt.span); + } - if let hir::ExprKind::MethodCall(body_call, _recv, ..) = ex.kind && - body_call.ident.name == sym::next && ex.span.source_equal(self.expr_span) { - self.body_expr = Some(ex); + if let hir::ExprKind::MethodCall(body_call, recv, ..) = ex.kind + && body_call.ident.name == sym::next + && recv.span.source_equal(self.expr_span) + { + self.body_expr = Some(ex); } hir::intravisit::walk_expr(self, ex); } } - let mut finder = - ExprFinder { expr_span: span, issue_span, loop_bind: None, body_expr: None }; + let mut finder = ExprFinder { + expr_span: span, + issue_span, + loop_bind: None, + body_expr: None, + head_span: None, + loop_span: None, + pat_span: None, + head: None, + }; finder.visit_expr(hir.body(body_id).value); - if let Some(loop_bind) = finder.loop_bind && - let Some(body_expr) = finder.body_expr && - let Some(def_id) = typeck_results.type_dependent_def_id(body_expr.hir_id) && - let Some(trait_did) = tcx.trait_of_item(def_id) && - tcx.is_diagnostic_item(sym::Iterator, trait_did) { - err.note(format!( - "a for loop advances the iterator for you, the result is stored in `{loop_bind}`." + if let Some(body_expr) = finder.body_expr + && let Some(loop_span) = finder.loop_span + && let Some(def_id) = typeck_results.type_dependent_def_id(body_expr.hir_id) + && let Some(trait_did) = tcx.trait_of_item(def_id) + && tcx.is_diagnostic_item(sym::Iterator, trait_did) + { + if let Some(loop_bind) = finder.loop_bind { + err.note(format!( + "a for loop advances the iterator for you, the result is stored in `{}`", + loop_bind.name, + )); + } else { + err.note( + "a for loop advances the iterator for you, the result is stored in its pattern", + ); + } + let msg = "if you want to call `next` on a iterator within the loop, consider using \ + `while let`"; + if let Some(head) = finder.head + && let Some(pat_span) = finder.pat_span + && loop_span.contains(body_expr.span) + && loop_span.contains(head.span) + { + let sm = self.infcx.tcx.sess.source_map(); + + let mut sugg = vec![]; + if let hir::ExprKind::Path(hir::QPath::Resolved(None, _)) = head.kind { + // A bare path doesn't need a `let` assignment, it's already a simple + // binding access. + // As a new binding wasn't added, we don't need to modify the advancing call. + sugg.push((loop_span.with_hi(pat_span.lo()), format!("while let Some("))); + sugg.push(( + pat_span.shrink_to_hi().with_hi(head.span.lo()), + ") = ".to_string(), + )); + sugg.push((head.span.shrink_to_hi(), ".next()".to_string())); + } else { + // Needs a new a `let` binding. + let indent = if let Some(indent) = sm.indentation_before(loop_span) { + format!("\n{indent}") + } else { + " ".to_string() + }; + let Ok(head_str) = sm.span_to_snippet(head.span) else { + err.help(msg); + return; + }; + sugg.push(( + loop_span.with_hi(pat_span.lo()), + format!("let iter = {head_str};{indent}while let Some("), + )); + sugg.push(( + pat_span.shrink_to_hi().with_hi(head.span.hi()), + ") = iter.next()".to_string(), )); - err.help("if you want to call `next` on a iterator within the loop, consider using `while let`."); + // As a new binding was added, we should change how the iterator is advanced to + // use the newly introduced binding. + if let hir::ExprKind::MethodCall(_, recv, ..) = body_expr.kind + && let hir::ExprKind::Path(hir::QPath::Resolved(None, ..)) = recv.kind + { + // As we introduced a `let iter = <head>;`, we need to change where the + // already borrowed value was accessed from `<recv>.next()` to + // `iter.next()`. + sugg.push((recv.span, "iter".to_string())); + } + } + err.multipart_suggestion(msg, sugg, Applicability::MaybeIncorrect); + } else { + err.help(msg); + } } } @@ -1459,7 +1574,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // Get closure's arguments let ty::Closure(_, args) = typeck_results.expr_ty(closure_expr).kind() else { - /* hir::Closure can be a generator too */ + /* hir::Closure can be a coroutine too */ return; }; let sig = args.as_closure().sig(); @@ -1539,69 +1654,80 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) { if e.span.contains(self.capture_span) { if let hir::ExprKind::Closure(&hir::Closure { - movability: None, - body, - fn_arg_span, - fn_decl: hir::FnDecl{ inputs, .. }, - .. - }) = e.kind && - let Some(hir::Node::Expr(body )) = self.hir.find(body.hir_id) { - self.suggest_arg = "this: &Self".to_string(); - if inputs.len() > 0 { - self.suggest_arg.push_str(", "); - } - self.in_closure = true; - self.closure_arg_span = fn_arg_span; - self.visit_expr(body); - self.in_closure = false; + movability: None, + body, + fn_arg_span, + fn_decl: hir::FnDecl { inputs, .. }, + .. + }) = e.kind + && let Some(hir::Node::Expr(body)) = self.hir.find(body.hir_id) + { + self.suggest_arg = "this: &Self".to_string(); + if inputs.len() > 0 { + self.suggest_arg.push_str(", "); + } + self.in_closure = true; + self.closure_arg_span = fn_arg_span; + self.visit_expr(body); + self.in_closure = false; } } if let hir::Expr { kind: hir::ExprKind::Path(path), .. } = e { - if let hir::QPath::Resolved(_, hir::Path { segments: [seg], ..}) = path && - seg.ident.name == kw::SelfLower && self.in_closure { - self.closure_change_spans.push(e.span); + if let hir::QPath::Resolved(_, hir::Path { segments: [seg], .. }) = path + && seg.ident.name == kw::SelfLower + && self.in_closure + { + self.closure_change_spans.push(e.span); } } hir::intravisit::walk_expr(self, e); } fn visit_local(&mut self, local: &'hir hir::Local<'hir>) { - if let hir::Pat { kind: hir::PatKind::Binding(_, hir_id, _ident, _), .. } = local.pat && - let Some(init) = local.init + if let hir::Pat { kind: hir::PatKind::Binding(_, hir_id, _ident, _), .. } = + local.pat + && let Some(init) = local.init { - if let hir::Expr { kind: hir::ExprKind::Closure(&hir::Closure { - movability: None, - .. - }), .. } = init && - init.span.contains(self.capture_span) { - self.closure_local_id = Some(*hir_id); + if let hir::Expr { + kind: hir::ExprKind::Closure(&hir::Closure { movability: None, .. }), + .. + } = init + && init.span.contains(self.capture_span) + { + self.closure_local_id = Some(*hir_id); } } hir::intravisit::walk_local(self, local); } fn visit_stmt(&mut self, s: &'hir hir::Stmt<'hir>) { - if let hir::StmtKind::Semi(e) = s.kind && - let hir::ExprKind::Call(hir::Expr { kind: hir::ExprKind::Path(path), ..}, args) = e.kind && - let hir::QPath::Resolved(_, hir::Path { segments: [seg], ..}) = path && - let Res::Local(hir_id) = seg.res && - Some(hir_id) == self.closure_local_id { - let (span, arg_str) = if args.len() > 0 { - (args[0].span.shrink_to_lo(), "self, ".to_string()) - } else { - let span = e.span.trim_start(seg.ident.span).unwrap_or(e.span); - (span, "(self)".to_string()) - }; - self.closure_call_changes.push((span, arg_str)); + if let hir::StmtKind::Semi(e) = s.kind + && let hir::ExprKind::Call( + hir::Expr { kind: hir::ExprKind::Path(path), .. }, + args, + ) = e.kind + && let hir::QPath::Resolved(_, hir::Path { segments: [seg], .. }) = path + && let Res::Local(hir_id) = seg.res + && Some(hir_id) == self.closure_local_id + { + let (span, arg_str) = if args.len() > 0 { + (args[0].span.shrink_to_lo(), "self, ".to_string()) + } else { + let span = e.span.trim_start(seg.ident.span).unwrap_or(e.span); + (span, "(self)".to_string()) + }; + self.closure_call_changes.push((span, arg_str)); } hir::intravisit::walk_stmt(self, s); } } - if let Some(hir::Node::ImplItem( - hir::ImplItem { kind: hir::ImplItemKind::Fn(_fn_sig, body_id), .. } - )) = hir.find(self.mir_hir_id()) && - let Some(hir::Node::Expr(expr)) = hir.find(body_id.hir_id) { + if let Some(hir::Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Fn(_fn_sig, body_id), + .. + })) = hir.find(self.mir_hir_id()) + && let Some(hir::Node::Expr(expr)) = hir.find(body_id.hir_id) + { let mut finder = ExpressionFinder { capture_span: *capture_kind_span, closure_change_spans: vec![], @@ -1822,7 +1948,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ( Some(name), BorrowExplanation::UsedLater(LaterUseKind::ClosureCapture, var_or_use_span, _), - ) if borrow_spans.for_generator() || borrow_spans.for_closure() => self + ) if borrow_spans.for_coroutine() || borrow_spans.for_closure() => self .report_escaping_closure_capture( borrow_spans, borrow_span, @@ -1847,7 +1973,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { span, .. }, - ) if borrow_spans.for_generator() || borrow_spans.for_closure() => self + ) if borrow_spans.for_coroutine() || borrow_spans.for_closure() => self .report_escaping_closure_capture( borrow_spans, borrow_span, @@ -1950,8 +2076,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { .unwrap_or_else(|| { match &self.infcx.tcx.def_kind(self.mir_def_id()) { DefKind::Closure => "enclosing closure", - DefKind::Generator => "enclosing generator", - kind => bug!("expected closure or generator, found {:?}", kind), + DefKind::Coroutine => "enclosing coroutine", + kind => bug!("expected closure or coroutine, found {:?}", kind), } .to_string() }) @@ -1985,7 +2111,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { borrow_spans.args_subdiag(&mut err, |args_span| { crate::session_diagnostics::CaptureArgLabel::Capture { - is_within: borrow_spans.for_generator(), + is_within: borrow_spans.for_coroutine(), args_span, } }); @@ -2136,6 +2262,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { current: usize, found: usize, prop_expr: Option<&'tcx hir::Expr<'tcx>>, + call: Option<&'tcx hir::Expr<'tcx>>, } impl<'tcx> Visitor<'tcx> for NestedStatementVisitor<'tcx> { @@ -2145,6 +2272,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { self.current -= 1; } fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { + if let hir::ExprKind::MethodCall(_, rcvr, _, _) = expr.kind { + if self.span == rcvr.span.source_callsite() { + self.call = Some(expr); + } + } if self.span == expr.span.source_callsite() { self.found = self.current; if self.prop_expr.is_none() { @@ -2168,25 +2300,43 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { current: 0, found: 0, prop_expr: None, + call: None, }; visitor.visit_stmt(stmt); let typeck_results = self.infcx.tcx.typeck(self.mir_def_id()); - let expr_ty: Option<Ty<'_>> = visitor.prop_expr.map(|expr| typeck_results.expr_ty(expr).peel_refs()); + let expr_ty: Option<Ty<'_>> = + visitor.prop_expr.map(|expr| typeck_results.expr_ty(expr).peel_refs()); - let is_format_arguments_item = - if let Some(expr_ty) = expr_ty - && let ty::Adt(adt, _) = expr_ty.kind() { - self.infcx.tcx.lang_items().get(LangItem::FormatArguments) == Some(adt.did()) - } else { - false - }; + let is_format_arguments_item = if let Some(expr_ty) = expr_ty + && let ty::Adt(adt, _) = expr_ty.kind() + { + self.infcx.tcx.lang_items().get(LangItem::FormatArguments) + == Some(adt.did()) + } else { + false + }; if visitor.found == 0 && stmt.span.contains(proper_span) && let Some(p) = sm.span_to_margin(stmt.span) && let Ok(s) = sm.span_to_snippet(proper_span) { + if let Some(call) = visitor.call + && let hir::ExprKind::MethodCall(path, _, [], _) = call.kind + && path.ident.name == sym::iter + && let Some(ty) = expr_ty + { + err.span_suggestion_verbose( + path.ident.span, + format!( + "consider consuming the `{ty}` when turning it into an \ + `Iterator`", + ), + "into_iter".to_string(), + Applicability::MaybeIncorrect, + ); + } if !is_format_arguments_item { let addition = format!("let binding = {};\n{}", s, " ".repeat(p)); err.multipart_suggestion_verbose( @@ -2224,7 +2374,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { borrow_spans.args_subdiag(&mut err, |args_span| { crate::session_diagnostics::CaptureArgLabel::Capture { - is_within: borrow_spans.for_generator(), + is_within: borrow_spans.for_coroutine(), args_span, } }); @@ -2340,11 +2490,17 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let (sugg_span, suggestion) = match tcx.sess.source_map().span_to_snippet(args_span) { Ok(string) => { - if string.starts_with("async ") { - let pos = args_span.lo() + BytePos(6); - (args_span.with_lo(pos).with_hi(pos), "move ") - } else if string.starts_with("async|") { - let pos = args_span.lo() + BytePos(5); + let coro_prefix = if string.starts_with("async") { + // `async` is 5 chars long. Not using `.len()` to avoid the cast from `usize` to `u32` + Some(5) + } else if string.starts_with("gen") { + // `gen` is 3 chars long + Some(3) + } else { + None + }; + if let Some(n) = coro_prefix { + let pos = args_span.lo() + BytePos(n); (args_span.with_lo(pos).with_hi(pos), " move") } else { (args_span.shrink_to_lo(), "move ") @@ -2352,14 +2508,19 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } Err(_) => (args_span, "move |<args>| <body>"), }; - let kind = match use_span.generator_kind() { - Some(generator_kind) => match generator_kind { - GeneratorKind::Async(async_kind) => match async_kind { - AsyncGeneratorKind::Block => "async block", - AsyncGeneratorKind::Closure => "async closure", + let kind = match use_span.coroutine_kind() { + Some(coroutine_kind) => match coroutine_kind { + CoroutineKind::Gen(kind) => match kind { + CoroutineSource::Block => "gen block", + CoroutineSource::Closure => "gen closure", + _ => bug!("gen block/closure expected, but gen function found."), + }, + CoroutineKind::Async(async_kind) => match async_kind { + CoroutineSource::Block => "async block", + CoroutineSource::Closure => "async closure", _ => bug!("async block/closure expected, but async function found."), }, - GeneratorKind::Gen => "generator", + CoroutineKind::Coroutine => "coroutine", }, None => "closure", }; @@ -2388,7 +2549,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } ConstraintCategory::CallArgument(_) => { fr_name.highlight_region_name(&mut err); - if matches!(use_span.generator_kind(), Some(GeneratorKind::Async(_))) { + if matches!(use_span.coroutine_kind(), Some(CoroutineKind::Async(_))) { err.note( "async blocks are not executed immediately and must either take a \ reference or ownership of outside variables they use", @@ -2482,9 +2643,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { /* Check if the mpi is initialized as an argument */ let mut is_argument = false; for arg in self.body.args_iter() { - let path = self.move_data.rev_lookup.find_local(arg); - if mpis.contains(&path) { - is_argument = true; + if let Some(path) = self.move_data.rev_lookup.find_local(arg) { + if mpis.contains(&path) { + is_argument = true; + } } } @@ -2656,7 +2818,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { loan_spans.var_subdiag(None, &mut err, Some(loan.kind), |kind, var_span| { use crate::session_diagnostics::CaptureVarCause::*; match kind { - Some(_) => BorrowUseInGenerator { var_span }, + Some(_) => BorrowUseInCoroutine { var_span }, None => BorrowUseInClosure { var_span }, } }); @@ -2672,7 +2834,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { loan_spans.var_subdiag(None, &mut err, Some(loan.kind), |kind, var_span| { use crate::session_diagnostics::CaptureVarCause::*; match kind { - Some(_) => BorrowUseInGenerator { var_span }, + Some(_) => BorrowUseInCoroutine { var_span }, None => BorrowUseInClosure { var_span }, } }); diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index c66a24473..8a930ca59 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -76,10 +76,10 @@ impl<'tcx> BorrowExplanation<'tcx> { expr_finder.visit_expr(body.value); if let Some(mut expr) = expr_finder.result { while let hir::ExprKind::AddrOf(_, _, inner) - | hir::ExprKind::Unary(hir::UnOp::Deref, inner) - | hir::ExprKind::Field(inner, _) - | hir::ExprKind::MethodCall(_, inner, _, _) - | hir::ExprKind::Index(inner, _, _) = &expr.kind + | hir::ExprKind::Unary(hir::UnOp::Deref, inner) + | hir::ExprKind::Field(inner, _) + | hir::ExprKind::MethodCall(_, inner, _, _) + | hir::ExprKind::Index(inner, _, _) = &expr.kind { expr = inner; } @@ -88,10 +88,7 @@ impl<'tcx> BorrowExplanation<'tcx> { && let hir::def::Res::Local(hir_id) = p.res && let Some(hir::Node::Pat(pat)) = tcx.hir().find(hir_id) { - err.span_label( - pat.span, - format!("binding `{ident}` declared here"), - ); + err.span_label(pat.span, format!("binding `{ident}` declared here")); } } } @@ -185,7 +182,7 @@ impl<'tcx> BorrowExplanation<'tcx> { // Otherwise, just report the whole type (and use // the intentionally fuzzy phrase "destructor") ty::Closure(..) => ("destructor", "closure".to_owned()), - ty::Generator(..) => ("destructor", "generator".to_owned()), + ty::Coroutine(..) => ("destructor", "coroutine".to_owned()), _ => ("destructor", format!("type `{}`", local_decl.ty)), }; @@ -419,7 +416,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if self.local_names[local].is_some() && let Some((WriteKind::StorageDeadOrDrop, place)) = kind_place && let Some(borrowed_local) = place.as_local() - && self.local_names[borrowed_local].is_some() && local != borrowed_local + && self.local_names[borrowed_local].is_some() + && local != borrowed_local { should_note_order = true; } diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 8d4028de9..c4323fef9 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -8,7 +8,7 @@ use itertools::Itertools; use rustc_errors::{Applicability, Diagnostic}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, Namespace}; -use rustc_hir::GeneratorKind; +use rustc_hir::CoroutineKind; use rustc_index::IndexSlice; use rustc_infer::infer::LateBoundRegionConversionTime; use rustc_middle::mir::tcx::PlaceTy; @@ -46,6 +46,7 @@ mod mutability_errors; mod region_errors; pub(crate) use bound_region_errors::{ToUniverseInfo, UniverseInfo}; +pub(crate) use move_errors::{IllegalMoveOriginKind, MoveError}; pub(crate) use mutability_errors::AccessKind; pub(crate) use outlives_suggestion::OutlivesSuggestionBuilder; pub(crate) use region_errors::{ErrorConstraintInfo, RegionErrorKind, RegionErrors}; @@ -369,7 +370,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ty::Array(ty, _) | ty::Slice(ty) => { self.describe_field_from_ty(ty, field, variant_index, including_tuple_field) } - ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => { + ty::Closure(def_id, _) | ty::Coroutine(def_id, _, _) => { // We won't be borrowck'ing here if the closure came from another crate, // so it's safe to call `expect_local`. // @@ -470,7 +471,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } - ty.print(printer).unwrap().into_buffer() + ty.print(&mut printer).unwrap(); + printer.into_buffer() } /// Returns the name of the provided `Ty` (that must be a reference)'s region with a @@ -492,7 +494,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { bug!("ty for annotation of borrow region is not a reference"); }; - region.print(printer).unwrap().into_buffer() + region.print(&mut printer).unwrap(); + printer.into_buffer() } } @@ -501,8 +504,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { pub(super) enum UseSpans<'tcx> { /// The access is caused by capturing a variable for a closure. ClosureUse { - /// This is true if the captured variable was from a generator. - generator_kind: Option<GeneratorKind>, + /// This is true if the captured variable was from a coroutine. + coroutine_kind: Option<CoroutineKind>, /// The span of the args of the closure, including the `move` keyword if /// it's present. args_span: Span, @@ -569,9 +572,9 @@ impl UseSpans<'_> { } } - pub(super) fn generator_kind(self) -> Option<GeneratorKind> { + pub(super) fn coroutine_kind(self) -> Option<CoroutineKind> { match self { - UseSpans::ClosureUse { generator_kind, .. } => generator_kind, + UseSpans::ClosureUse { coroutine_kind, .. } => coroutine_kind, _ => None, } } @@ -596,14 +599,14 @@ impl UseSpans<'_> { ) { use crate::InitializationRequiringAction::*; use CaptureVarPathUseCause::*; - if let UseSpans::ClosureUse { generator_kind, path_span, .. } = self { - match generator_kind { + if let UseSpans::ClosureUse { coroutine_kind, path_span, .. } = self { + match coroutine_kind { Some(_) => { err.subdiagnostic(match action { - Borrow => BorrowInGenerator { path_span }, - MatchOn | Use => UseInGenerator { path_span }, - Assignment => AssignInGenerator { path_span }, - PartialAssignment => AssignPartInGenerator { path_span }, + Borrow => BorrowInCoroutine { path_span }, + MatchOn | Use => UseInCoroutine { path_span }, + Assignment => AssignInCoroutine { path_span }, + PartialAssignment => AssignPartInCoroutine { path_span }, }); } None => { @@ -624,9 +627,9 @@ impl UseSpans<'_> { handler: Option<&rustc_errors::Handler>, err: &mut Diagnostic, kind: Option<rustc_middle::mir::BorrowKind>, - f: impl FnOnce(Option<GeneratorKind>, Span) -> CaptureVarCause, + f: impl FnOnce(Option<CoroutineKind>, Span) -> CaptureVarCause, ) { - if let UseSpans::ClosureUse { generator_kind, capture_kind_span, path_span, .. } = self { + if let UseSpans::ClosureUse { coroutine_kind, capture_kind_span, path_span, .. } = self { if capture_kind_span != path_span { err.subdiagnostic(match kind { Some(kd) => match kd { @@ -642,7 +645,7 @@ impl UseSpans<'_> { None => CaptureVarKind::Move { kind_span: capture_kind_span }, }); }; - let diag = f(generator_kind, path_span); + let diag = f(coroutine_kind, path_span); match handler { Some(hd) => err.eager_subdiagnostic(hd, diag), None => err.subdiagnostic(diag), @@ -653,15 +656,15 @@ impl UseSpans<'_> { /// Returns `false` if this place is not used in a closure. pub(super) fn for_closure(&self) -> bool { match *self { - UseSpans::ClosureUse { generator_kind, .. } => generator_kind.is_none(), + UseSpans::ClosureUse { coroutine_kind, .. } => coroutine_kind.is_none(), _ => false, } } - /// Returns `false` if this place is not used in a generator. - pub(super) fn for_generator(&self) -> bool { + /// Returns `false` if this place is not used in a coroutine. + pub(super) fn for_coroutine(&self) -> bool { match *self { - UseSpans::ClosureUse { generator_kind, .. } => generator_kind.is_some(), + UseSpans::ClosureUse { coroutine_kind, .. } => coroutine_kind.is_some(), _ => false, } } @@ -780,19 +783,15 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { debug!("move_spans: moved_place={:?} location={:?} stmt={:?}", moved_place, location, stmt); if let StatementKind::Assign(box (_, Rvalue::Aggregate(kind, places))) = &stmt.kind - && let AggregateKind::Closure(def_id, _) | AggregateKind::Generator(def_id, _, _) = **kind + && let AggregateKind::Closure(def_id, _) | AggregateKind::Coroutine(def_id, _, _) = + **kind { debug!("move_spans: def_id={:?} places={:?}", def_id, places); let def_id = def_id.expect_local(); - if let Some((args_span, generator_kind, capture_kind_span, path_span)) = + if let Some((args_span, coroutine_kind, capture_kind_span, path_span)) = self.closure_span(def_id, moved_place, places) { - return ClosureUse { - generator_kind, - args_span, - capture_kind_span, - path_span, - }; + return ClosureUse { coroutine_kind, args_span, capture_kind_span, path_span }; } } @@ -804,11 +803,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { | FakeReadCause::ForLet(Some(closure_def_id)) => { debug!("move_spans: def_id={:?} place={:?}", closure_def_id, place); let places = &[Operand::Move(place)]; - if let Some((args_span, generator_kind, capture_kind_span, path_span)) = + if let Some((args_span, coroutine_kind, capture_kind_span, path_span)) = self.closure_span(closure_def_id, moved_place, IndexSlice::from_raw(places)) { return ClosureUse { - generator_kind, + coroutine_kind, args_span, capture_kind_span, path_span, @@ -918,21 +917,21 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { for stmt in statements.chain(maybe_additional_statement) { if let StatementKind::Assign(box (_, Rvalue::Aggregate(kind, places))) = &stmt.kind { - let (&def_id, is_generator) = match kind { + let (&def_id, is_coroutine) = match kind { box AggregateKind::Closure(def_id, _) => (def_id, false), - box AggregateKind::Generator(def_id, _, _) => (def_id, true), + box AggregateKind::Coroutine(def_id, _, _) => (def_id, true), _ => continue, }; let def_id = def_id.expect_local(); debug!( - "borrow_spans: def_id={:?} is_generator={:?} places={:?}", - def_id, is_generator, places + "borrow_spans: def_id={:?} is_coroutine={:?} places={:?}", + def_id, is_coroutine, places ); - if let Some((args_span, generator_kind, capture_kind_span, path_span)) = + if let Some((args_span, coroutine_kind, capture_kind_span, path_span)) = self.closure_span(def_id, Place::from(target).as_ref(), places) { - return ClosureUse { generator_kind, args_span, capture_kind_span, path_span }; + return ClosureUse { coroutine_kind, args_span, capture_kind_span, path_span }; } else { return OtherUse(use_span); } @@ -946,7 +945,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { OtherUse(use_span) } - /// Finds the spans of a captured place within a closure or generator. + /// Finds the spans of a captured place within a closure or coroutine. /// The first span is the location of the use resulting in the capture kind of the capture /// The second span is the location the use resulting in the captured path of the capture fn closure_span( @@ -954,7 +953,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { def_id: LocalDefId, target_place: PlaceRef<'tcx>, places: &IndexSlice<FieldIdx, Operand<'tcx>>, - ) -> Option<(Span, Option<GeneratorKind>, Span, Span)> { + ) -> Option<(Span, Option<CoroutineKind>, Span, Span)> { debug!( "closure_span: def_id={:?} target_place={:?} places={:?}", def_id, target_place, places @@ -972,11 +971,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { { debug!("closure_span: found captured local {:?}", place); let body = self.infcx.tcx.hir().body(body); - let generator_kind = body.generator_kind(); + let coroutine_kind = body.coroutine_kind(); return Some(( fn_decl_span, - generator_kind, + coroutine_kind, captured_place.get_capture_kind_span(self.infcx.tcx), captured_place.get_path_span(self.infcx.tcx), )); @@ -1123,7 +1122,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { &self.infcx.tcx.sess.parse_sess.span_diagnostic, CaptureReasonSuggest::FreshReborrow { span: move_span.shrink_to_hi(), - }); + }, + ); } if let Some(clone_trait) = tcx.lang_items().clone_trait() && let trait_ref = ty::TraitRef::new(tcx, clone_trait, [ty]) @@ -1191,7 +1191,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // another message for the same span if !is_loop_message { move_spans.var_subdiag(None, err, None, |kind, var_span| match kind { - Some(_) => CaptureVarCause::PartialMoveUseInGenerator { var_span, is_partial }, + Some(_) => CaptureVarCause::PartialMoveUseInCoroutine { var_span, is_partial }, None => CaptureVarCause::PartialMoveUseInClosure { var_span, is_partial }, }) } diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index e05c04e11..41d6b98d7 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -1,16 +1,50 @@ use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; use rustc_middle::mir::*; -use rustc_middle::ty; -use rustc_mir_dataflow::move_paths::{ - IllegalMoveOrigin, IllegalMoveOriginKind, LookupResult, MoveError, MovePathIndex, -}; -use rustc_span::{BytePos, Span}; +use rustc_middle::ty::{self, Ty}; +use rustc_mir_dataflow::move_paths::{LookupResult, MovePathIndex}; +use rustc_span::{BytePos, ExpnKind, MacroKind, Span}; use crate::diagnostics::CapturedMessageOpt; use crate::diagnostics::{DescribePlaceOpt, UseSpans}; use crate::prefixes::PrefixSet; use crate::MirBorrowckCtxt; +#[derive(Debug)] +pub enum IllegalMoveOriginKind<'tcx> { + /// Illegal move due to attempt to move from behind a reference. + BorrowedContent { + /// The place the reference refers to: if erroneous code was trying to + /// move from `(*x).f` this will be `*x`. + target_place: Place<'tcx>, + }, + + /// Illegal move due to attempt to move from field of an ADT that + /// implements `Drop`. Rust maintains invariant that all `Drop` + /// ADT's remain fully-initialized so that user-defined destructor + /// can safely read from all of the ADT's fields. + InteriorOfTypeWithDestructor { container_ty: Ty<'tcx> }, + + /// Illegal move due to attempt to move out of a slice or array. + InteriorOfSliceOrArray { ty: Ty<'tcx>, is_index: bool }, +} + +#[derive(Debug)] +pub(crate) struct MoveError<'tcx> { + place: Place<'tcx>, + location: Location, + kind: IllegalMoveOriginKind<'tcx>, +} + +impl<'tcx> MoveError<'tcx> { + pub(crate) fn new( + place: Place<'tcx>, + location: Location, + kind: IllegalMoveOriginKind<'tcx>, + ) -> Self { + MoveError { place, location, kind } + } +} + // Often when desugaring a pattern match we may have many individual moves in // MIR that are all part of one operation from the user's point-of-view. For // example: @@ -53,20 +87,18 @@ enum GroupedMoveError<'tcx> { } impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { - pub(crate) fn report_move_errors(&mut self, move_errors: Vec<(Place<'tcx>, MoveError<'tcx>)>) { - let grouped_errors = self.group_move_errors(move_errors); + pub(crate) fn report_move_errors(&mut self) { + let grouped_errors = self.group_move_errors(); for error in grouped_errors { self.report(error); } } - fn group_move_errors( - &self, - errors: Vec<(Place<'tcx>, MoveError<'tcx>)>, - ) -> Vec<GroupedMoveError<'tcx>> { + fn group_move_errors(&mut self) -> Vec<GroupedMoveError<'tcx>> { let mut grouped_errors = Vec::new(); - for (original_path, error) in errors { - self.append_to_grouped_errors(&mut grouped_errors, original_path, error); + let errors = std::mem::take(&mut self.move_errors); + for error in errors { + self.append_to_grouped_errors(&mut grouped_errors, error); } grouped_errors } @@ -74,66 +106,58 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { fn append_to_grouped_errors( &self, grouped_errors: &mut Vec<GroupedMoveError<'tcx>>, - original_path: Place<'tcx>, error: MoveError<'tcx>, ) { - match error { - MoveError::UnionMove { .. } => { - unimplemented!("don't know how to report union move errors yet.") - } - MoveError::IllegalMove { cannot_move_out_of: IllegalMoveOrigin { location, kind } } => { - // Note: that the only time we assign a place isn't a temporary - // to a user variable is when initializing it. - // If that ever stops being the case, then the ever initialized - // flow could be used. - if let Some(StatementKind::Assign(box ( - place, - Rvalue::Use(Operand::Move(move_from)), - ))) = self.body.basic_blocks[location.block] - .statements - .get(location.statement_index) - .map(|stmt| &stmt.kind) + let MoveError { place: original_path, location, kind } = error; + + // Note: that the only time we assign a place isn't a temporary + // to a user variable is when initializing it. + // If that ever stops being the case, then the ever initialized + // flow could be used. + if let Some(StatementKind::Assign(box (place, Rvalue::Use(Operand::Move(move_from))))) = + self.body.basic_blocks[location.block] + .statements + .get(location.statement_index) + .map(|stmt| &stmt.kind) + { + if let Some(local) = place.as_local() { + let local_decl = &self.body.local_decls[local]; + // opt_match_place is the + // match_span is the span of the expression being matched on + // match *x.y { ... } match_place is Some(*x.y) + // ^^^^ match_span is the span of *x.y + // + // opt_match_place is None for let [mut] x = ... statements, + // whether or not the right-hand side is a place expression + if let LocalInfo::User(BindingForm::Var(VarBindingForm { + opt_match_place: Some((opt_match_place, match_span)), + binding_mode: _, + opt_ty_info: _, + pat_span: _, + })) = *local_decl.local_info() { - if let Some(local) = place.as_local() { - let local_decl = &self.body.local_decls[local]; - // opt_match_place is the - // match_span is the span of the expression being matched on - // match *x.y { ... } match_place is Some(*x.y) - // ^^^^ match_span is the span of *x.y - // - // opt_match_place is None for let [mut] x = ... statements, - // whether or not the right-hand side is a place expression - if let LocalInfo::User(BindingForm::Var(VarBindingForm { - opt_match_place: Some((opt_match_place, match_span)), - binding_mode: _, - opt_ty_info: _, - pat_span: _, - })) = *local_decl.local_info() - { - let stmt_source_info = self.body.source_info(location); - self.append_binding_error( - grouped_errors, - kind, - original_path, - *move_from, - local, - opt_match_place, - match_span, - stmt_source_info.span, - ); - return; - } - } + let stmt_source_info = self.body.source_info(location); + self.append_binding_error( + grouped_errors, + kind, + original_path, + *move_from, + local, + opt_match_place, + match_span, + stmt_source_info.span, + ); + return; } - - let move_spans = self.move_spans(original_path.as_ref(), location); - grouped_errors.push(GroupedMoveError::OtherIllegalMove { - use_spans: move_spans, - original_path, - kind, - }); } } + + let move_spans = self.move_spans(original_path.as_ref(), location); + grouped_errors.push(GroupedMoveError::OtherIllegalMove { + use_spans: move_spans, + original_path, + kind, + }); } fn append_binding_error( @@ -464,6 +488,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { args_span, } }); + + self.add_note_for_packed_struct_derive(err, original_path.local); } } } @@ -570,4 +596,20 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { ); } } + + /// Adds an explanatory note if the move error occurs in a derive macro + /// expansion of a packed struct. + /// Such errors happen because derive macro expansions shy away from taking + /// references to the struct's fields since doing so would be undefined behaviour + fn add_note_for_packed_struct_derive(&self, err: &mut Diagnostic, local: Local) { + let local_place: PlaceRef<'tcx> = local.into(); + let local_ty = local_place.ty(self.body.local_decls(), self.infcx.tcx).ty.peel_refs(); + + if let Some(adt) = local_ty.ty_adt_def() + && adt.repr().packed() + && let ExpnKind::Macro(MacroKind::Derive, name) = self.body.span.ctxt().outer_expn_data().kind + { + err.note(format!("`#[derive({name})]` triggers a move because taking references to the fields of a packed struct is undefined behaviour")); + } + } } diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 8ca57383e..dde46eef6 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -9,9 +9,8 @@ use rustc_middle::{ hir::place::PlaceBase, mir::{self, BindingForm, Local, LocalDecl, LocalInfo, LocalKind, Location}, }; -use rustc_span::source_map::DesugaringKind; use rustc_span::symbol::{kw, Symbol}; -use rustc_span::{sym, BytePos, Span}; +use rustc_span::{sym, BytePos, DesugaringKind, Span}; use rustc_target::abi::FieldIdx; use crate::diagnostics::BorrowedContentSource; @@ -62,7 +61,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { local, projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)], } => { - debug_assert!(is_closure_or_generator( + debug_assert!(is_closure_or_coroutine( Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty )); @@ -122,7 +121,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { { item_msg = access_place_desc; debug_assert!(self.body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty.is_ref()); - debug_assert!(is_closure_or_generator( + debug_assert!(is_closure_or_coroutine( the_place_err.ty(self.body, self.infcx.tcx).ty )); @@ -385,7 +384,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { local, projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)], } => { - debug_assert!(is_closure_or_generator( + debug_assert!(is_closure_or_coroutine( Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty )); @@ -396,17 +395,16 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let upvar_hir_id = captured_place.get_root_variable(); if let Some(Node::Pat(pat)) = self.infcx.tcx.hir().find(upvar_hir_id) - && let hir::PatKind::Binding( - hir::BindingAnnotation::NONE, - _, - upvar_ident, - _, - ) = pat.kind + && let hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, upvar_ident, _) = + pat.kind { if upvar_ident.name == kw::SelfLower { for (_, node) in self.infcx.tcx.hir().parent_iter(upvar_hir_id) { if let Some(fn_decl) = node.fn_decl() { - if !matches!(fn_decl.implicit_self, hir::ImplicitSelfKind::ImmRef | hir::ImplicitSelfKind::MutRef) { + if !matches!( + fn_decl.implicit_self, + hir::ImplicitSelfKind::ImmRef | hir::ImplicitSelfKind::MutRef + ) { err.span_suggestion( upvar_ident.span, "consider changing this to be mutable", @@ -573,7 +571,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { self.ty, ), vec![ - vec![ // val.insert(index, rv); + vec![ + // val.insert(index, rv); ( val.span.shrink_to_hi().with_hi(index.span.lo()), ".insert(".to_string(), @@ -584,7 +583,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { ), (rv.span.shrink_to_hi(), ")".to_string()), ], - vec![ // val.get_mut(index).map(|v| { *v = rv; }); + vec![ + // val.get_mut(index).map(|v| { *v = rv; }); ( val.span.shrink_to_hi().with_hi(index.span.lo()), ".get_mut(".to_string(), @@ -593,12 +593,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { index.span.shrink_to_hi().with_hi(place.span.hi()), ").map(|val| { *val".to_string(), ), - ( - rv.span.shrink_to_hi(), - "; })".to_string(), - ), + (rv.span.shrink_to_hi(), "; })".to_string()), ], - vec![ // let x = val.entry(index).or_insert(rv); + vec![ + // let x = val.entry(index).or_insert(rv); (val.span.shrink_to_lo(), "let val = ".to_string()), ( val.span.shrink_to_hi().with_hi(index.span.lo()), @@ -747,10 +745,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { && let Some(body_id) = hir_map.maybe_body_owned_by(local_def_id) { let body = hir_map.body(body_id); - let mut v = BindingFinder { - span: pat_span, - hir_id: None, - }; + let mut v = BindingFinder { span: pat_span, hir_id: None }; v.visit_body(body); v.hir_id } else { @@ -766,7 +761,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { pat: hir::Pat { kind: hir::PatKind::Ref(_, _), .. }, .. })) = hir_map.find(hir_id) - && let Ok(name) = self.infcx.tcx.sess.source_map().span_to_snippet(local_decl.source_info.span) + && let Ok(name) = + self.infcx.tcx.sess.source_map().span_to_snippet(local_decl.source_info.span) { err.span_suggestion( pat_span, @@ -879,12 +875,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { // `span` corresponds to the expression being iterated, find the `for`-loop desugared // expression with that span in order to identify potential fixes when encountering a // read-only iterator that should be mutable. - let mut v = Finder { - span, - expr: None, - }; + let mut v = Finder { span, expr: None }; v.visit_block(block); - if let Some(expr) = v.expr && let Call(_, [expr]) = expr.kind { + if let Some(expr) = v.expr + && let Call(_, [expr]) = expr.kind + { match expr.kind { MethodCall(path_segment, _, _, span) => { // We have `for _ in iter.read_only_iter()`, try to @@ -1032,38 +1027,42 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let source = self.body.source; let hir = self.infcx.tcx.hir(); if let InstanceDef::Item(def_id) = source.instance - && let Some(Node::Expr(hir::Expr { hir_id, kind, ..})) = hir.get_if_local(def_id) - && let ExprKind::Closure(closure) = kind && closure.movability == None - && let Some(Node::Expr(expr)) = hir.find_parent(*hir_id) { - let mut cur_expr = expr; - while let ExprKind::MethodCall(path_segment, recv, _, _) = cur_expr.kind { - if path_segment.ident.name == sym::iter { - // check `_ty` has `iter_mut` method - let res = self - .infcx - .tcx - .typeck(path_segment.hir_id.owner.def_id) - .type_dependent_def_id(cur_expr.hir_id) - .and_then(|def_id| self.infcx.tcx.impl_of_method(def_id)) - .map(|def_id| self.infcx.tcx.associated_items(def_id)) - .map(|assoc_items| { - assoc_items.filter_by_name_unhygienic(sym::iter_mut).peekable() - }); - - if let Some(mut res) = res && res.peek().is_some() { - err.span_suggestion_verbose( - path_segment.ident.span, - "you may want to use `iter_mut` here", - "iter_mut", - Applicability::MaybeIncorrect, - ); - } - break; - } else { - cur_expr = recv; + && let Some(Node::Expr(hir::Expr { hir_id, kind, .. })) = hir.get_if_local(def_id) + && let ExprKind::Closure(closure) = kind + && closure.movability == None + && let Some(Node::Expr(expr)) = hir.find_parent(*hir_id) + { + let mut cur_expr = expr; + while let ExprKind::MethodCall(path_segment, recv, _, _) = cur_expr.kind { + if path_segment.ident.name == sym::iter { + // check `_ty` has `iter_mut` method + let res = self + .infcx + .tcx + .typeck(path_segment.hir_id.owner.def_id) + .type_dependent_def_id(cur_expr.hir_id) + .and_then(|def_id| self.infcx.tcx.impl_of_method(def_id)) + .map(|def_id| self.infcx.tcx.associated_items(def_id)) + .map(|assoc_items| { + assoc_items.filter_by_name_unhygienic(sym::iter_mut).peekable() + }); + + if let Some(mut res) = res + && res.peek().is_some() + { + err.span_suggestion_verbose( + path_segment.ident.span, + "you may want to use `iter_mut` here", + "iter_mut", + Applicability::MaybeIncorrect, + ); } + break; + } else { + cur_expr = recv; } } + } } fn suggest_make_local_mut( @@ -1200,14 +1199,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } let hir_map = self.infcx.tcx.hir(); let def_id = self.body.source.def_id(); - let hir_id = if let Some(local_def_id) = def_id.as_local() && - let Some(body_id) = hir_map.maybe_body_owned_by(local_def_id) + let hir_id = if let Some(local_def_id) = def_id.as_local() + && let Some(body_id) = hir_map.maybe_body_owned_by(local_def_id) { let body = hir_map.body(body_id); - let mut v = BindingFinder { - span: err_label_span, - hir_id: None, - }; + let mut v = BindingFinder { span: err_label_span, hir_id: None }; v.visit_body(body); v.hir_id } else { @@ -1215,15 +1211,13 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { }; if let Some(hir_id) = hir_id - && let Some(hir::Node::Local(local)) = hir_map.find(hir_id) + && let Some(hir::Node::Local(local)) = hir_map.find(hir_id) { let (changing, span, sugg) = match local.ty { Some(ty) => ("changing", ty.span, message), - None => ( - "specifying", - local.pat.span.shrink_to_hi(), - format!(": {message}"), - ), + None => { + ("specifying", local.pat.span.shrink_to_hi(), format!(": {message}")) + } }; err.span_suggestion_verbose( span, @@ -1234,9 +1228,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } else { err.span_label( err_label_span, - format!( - "consider changing this binding's type to be: `{message}`" - ), + format!("consider changing this binding's type to be: `{message}`"), ); } } @@ -1359,9 +1351,9 @@ fn suggest_ampmut<'tcx>( None => (false, decl_span), }; - // if the binding already exists and is a reference with a explicit + // if the binding already exists and is a reference with an explicit // lifetime, then we can suggest adding ` mut`. this is special-cased from - // the path without a explicit lifetime. + // the path without an explicit lifetime. if let Ok(src) = tcx.sess.source_map().span_to_snippet(span) && src.starts_with("&'") // note that `& 'a T` is invalid so this is correct. @@ -1380,16 +1372,12 @@ fn suggest_ampmut<'tcx>( let ty_mut = decl_ty.builtin_deref(true).unwrap(); assert_eq!(ty_mut.mutbl, hir::Mutability::Not); - ( - false, - span, - format!("{}mut {}", if decl_ty.is_ref() {"&"} else {"*"}, ty_mut.ty) - ) + (false, span, format!("{}mut {}", if decl_ty.is_ref() { "&" } else { "*" }, ty_mut.ty)) } } -fn is_closure_or_generator(ty: Ty<'_>) -> bool { - ty.is_closure() || ty.is_generator() +fn is_closure_or_coroutine(ty: Ty<'_>) -> bool { + ty.is_closure() || ty.is_coroutine() } /// Given a field that needs to be mutable, returns a span where the " mut " could go. diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 27072a60f..a0a809123 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -580,7 +580,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let err = FnMutError { span: *span, ty_err: match output_ty.kind() { - ty::Generator(def, ..) if self.infcx.tcx.generator_is_async(*def) => { + ty::Coroutine(def, ..) if self.infcx.tcx.coroutine_is_async(*def) => { FnMutReturnTypeErr::ReturnAsyncBlock { span: *span } } _ if output_ty.contains_closure() => { @@ -1036,7 +1036,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { .. }) => { let body = map.body(*body); - if !matches!(body.generator_kind, Some(hir::GeneratorKind::Async(..))) { + if !matches!(body.coroutine_kind, Some(hir::CoroutineKind::Async(..))) { closure_span = Some(expr.span.shrink_to_lo()); } } diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index 55d581b3a..d38cfbc54 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -41,7 +41,7 @@ pub(crate) enum RegionNameSource { AnonRegionFromUpvar(Span, Symbol), /// The region corresponding to the return type of a closure. AnonRegionFromOutput(RegionNameHighlight, &'static str), - /// The region from a type yielded by a generator. + /// The region from a type yielded by a coroutine. AnonRegionFromYieldTy(Span, String), /// An anonymous region from an async fn. AnonRegionFromAsyncFn(Span), @@ -322,7 +322,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { let def_ty = self.regioncx.universal_regions().defining_ty; let DefiningTy::Closure(_, args) = def_ty else { - // Can't have BrEnv in functions, constants or generators. + // Can't have BrEnv in functions, constants or coroutines. bug!("BrEnv outside of closure."); }; let hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }) = @@ -680,16 +680,16 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { } hir::FnRetTy::Return(hir_ty) => (fn_decl.output.span(), Some(hir_ty)), }; - let mir_description = match hir.body(body).generator_kind { - Some(hir::GeneratorKind::Async(gen)) => match gen { - hir::AsyncGeneratorKind::Block => " of async block", - hir::AsyncGeneratorKind::Closure => " of async closure", - hir::AsyncGeneratorKind::Fn => { + let mir_description = match hir.body(body).coroutine_kind { + Some(hir::CoroutineKind::Async(gen)) => match gen { + hir::CoroutineSource::Block => " of async block", + hir::CoroutineSource::Closure => " of async closure", + hir::CoroutineSource::Fn => { let parent_item = hir.get_by_def_id(hir.get_parent_item(mir_hir_id).def_id); let output = &parent_item .fn_decl() - .expect("generator lowered from async fn should be in fn") + .expect("coroutine lowered from async fn should be in fn") .output; span = output.span(); if let hir::FnRetTy::Return(ret) = output { @@ -698,7 +698,21 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { " of async function" } }, - Some(hir::GeneratorKind::Gen) => " of generator", + Some(hir::CoroutineKind::Gen(gen)) => match gen { + hir::CoroutineSource::Block => " of gen block", + hir::CoroutineSource::Closure => " of gen closure", + hir::CoroutineSource::Fn => { + let parent_item = + hir.get_by_def_id(hir.get_parent_item(mir_hir_id).def_id); + let output = &parent_item + .fn_decl() + .expect("coroutine lowered from gen fn should be in fn") + .output; + span = output.span(); + " of gen function" + } + }, + Some(hir::CoroutineKind::Coroutine) => " of coroutine", None => " of closure", }; (span, mir_description, hir_ty) @@ -793,7 +807,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { &self, fr: RegionVid, ) -> Option<RegionName> { - // Note: generators from `async fn` yield `()`, so we don't have to + // Note: coroutines from `async fn` yield `()`, so we don't have to // worry about them here. let yield_ty = self.regioncx.universal_regions().yield_ty?; debug!("give_name_if_anonymous_region_appears_in_yield_ty: yield_ty = {:?}", yield_ty); @@ -942,9 +956,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { ty::ClauseKind::Projection(data) if data.projection_ty.self_ty() == ty => {} _ => return false, } - tcx.any_free_region_meets(pred, |r| { - *r == ty::ReEarlyBound(region) - }) + tcx.any_free_region_meets(pred, |r| *r == ty::ReEarlyBound(region)) }) } else { false diff --git a/compiler/rustc_borrowck/src/diagnostics/var_name.rs b/compiler/rustc_borrowck/src/diagnostics/var_name.rs index 8832d345d..3a104c524 100644 --- a/compiler/rustc_borrowck/src/diagnostics/var_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/var_name.rs @@ -6,8 +6,8 @@ use crate::Upvar; use rustc_index::IndexSlice; use rustc_middle::mir::{Body, Local}; use rustc_middle::ty::{RegionVid, TyCtxt}; -use rustc_span::source_map::Span; use rustc_span::symbol::Symbol; +use rustc_span::Span; impl<'tcx> RegionInferenceContext<'tcx> { pub(crate) fn get_var_name_and_span_for_region( |