summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs182
1 files changed, 120 insertions, 62 deletions
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 8bc8964bb..583bc2e28 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -16,7 +16,7 @@ use rustc_middle::mir::{
FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef,
ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm,
};
-use rustc_middle::ty::{self, subst::Subst, suggest_constraining_type_params, PredicateKind, Ty};
+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;
@@ -198,7 +198,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
move_span,
move_spans,
*moved_place,
- Some(used_place),
partially_str,
loop_message,
move_msg,
@@ -258,7 +257,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let ty = place.ty(self.body, self.infcx.tcx).ty;
// If we're in pattern, we do nothing in favor of the previous suggestion (#80913).
- if is_loop_move & !in_pattern {
+ // Same for if we're in a loop, see #101119.
+ if is_loop_move & !in_pattern && !matches!(use_spans, UseSpans::ClosureUse { .. }) {
if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() {
// We have a `&mut` ref, we need to reborrow on each iteration (#62112).
err.span_suggestion_verbose(
@@ -368,6 +368,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let mut visitor = ConditionVisitor { spans: &spans, name: &name, errors: vec![] };
visitor.visit_body(&body);
+ let mut show_assign_sugg = false;
let isnt_initialized = if let InitializationRequiringAction::PartialAssignment
| InitializationRequiringAction::Assignment = desired_action
{
@@ -395,6 +396,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
.count()
== 0
{
+ show_assign_sugg = true;
"isn't initialized"
} else {
"is possibly-uninitialized"
@@ -445,13 +447,87 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}
}
}
+
err.span_label(decl_span, "binding declared here but left uninitialized");
+ if show_assign_sugg {
+ struct LetVisitor {
+ decl_span: Span,
+ sugg_span: Option<Span>,
+ }
+
+ impl<'v> Visitor<'v> for LetVisitor {
+ fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) {
+ 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));
+ }
+ hir::intravisit::walk_stmt(self, ex);
+ }
+ }
+
+ let mut visitor = LetVisitor { decl_span, sugg_span: None };
+ visitor.visit_body(&body);
+ if let Some(span) = visitor.sugg_span {
+ self.suggest_assign_value(&mut err, moved_place, span);
+ }
+ }
err
}
+ fn suggest_assign_value(
+ &self,
+ err: &mut Diagnostic,
+ moved_place: PlaceRef<'tcx>,
+ sugg_span: Span,
+ ) {
+ let ty = moved_place.ty(self.body, self.infcx.tcx).ty;
+ debug!("ty: {:?}, kind: {:?}", ty, ty.kind());
+
+ let tcx = self.infcx.tcx;
+ let implements_default = |ty, param_env| {
+ let Some(default_trait) = tcx.get_diagnostic_item(sym::Default) else {
+ return false;
+ };
+ // Regions are already solved, so we must use a fresh InferCtxt,
+ // 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,
+ )
+ .must_apply_modulo_regions()
+ };
+
+ let assign_value = match ty.kind() {
+ ty::Bool => "false",
+ ty::Float(_) => "0.0",
+ ty::Int(_) | ty::Uint(_) => "0",
+ ty::Never | ty::Error(_) => "",
+ ty::Adt(def, _) if Some(def.did()) == tcx.get_diagnostic_item(sym::Vec) => "vec![]",
+ ty::Adt(_, _) if implements_default(ty, self.param_env) => "Default::default()",
+ _ => "todo!()",
+ };
+
+ if !assign_value.is_empty() {
+ err.span_suggestion_verbose(
+ sugg_span.shrink_to_hi(),
+ format!("consider assigning a value"),
+ format!(" = {}", assign_value),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+
fn suggest_borrow_fn_like(
&self,
- err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>,
+ err: &mut Diagnostic,
ty: Ty<'tcx>,
move_sites: &[MoveSite],
value_name: &str,
@@ -526,12 +602,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
true
}
- fn suggest_adding_copy_bounds(
- &self,
- err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>,
- ty: Ty<'tcx>,
- span: Span,
- ) {
+ 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());
@@ -541,41 +612,40 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
.and_then(|def_id| tcx.hir().get_generics(def_id))
else { return; };
// Try to find predicates on *generic params* that would allow copying `ty`
- let predicates: Result<Vec<_>, _> = tcx.infer_ctxt().enter(|infcx| {
- let mut fulfill_cx = <dyn rustc_infer::traits::TraitEngine<'_>>::new(infcx.tcx);
+ let infcx = tcx.infer_ctxt().build();
+ let mut fulfill_cx = <dyn rustc_infer::traits::TraitEngine<'_>>::new(infcx.tcx);
- let copy_did = infcx.tcx.lang_items().copy_trait().unwrap();
- let cause = ObligationCause::new(
- span,
- self.mir_hir_id(),
- rustc_infer::traits::ObligationCauseCode::MiscObligation,
- );
- fulfill_cx.register_bound(
- &infcx,
- 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
- 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(()),
- },
+ let copy_did = infcx.tcx.lang_items().copy_trait().unwrap();
+ let cause = ObligationCause::new(
+ span,
+ self.mir_hir_id(),
+ rustc_infer::traits::ObligationCauseCode::MiscObligation,
+ );
+ fulfill_cx.register_bound(
+ &infcx,
+ 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<Vec<_>, _> = 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(()),
- })
- .collect()
- });
+ },
+ _ => Err(()),
+ })
+ .collect();
if let Ok(predicates) = predicates {
suggest_constraining_type_params(
@@ -1124,6 +1194,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
/// short a lifetime. (But sometimes it is more useful to report
/// it as a more direct conflict between the execution of a
/// `Drop::drop` with an aliasing borrow.)
+ #[instrument(level = "debug", skip(self))]
pub(crate) fn report_borrowed_value_does_not_live_long_enough(
&mut self,
location: Location,
@@ -1131,13 +1202,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
place_span: (Place<'tcx>, Span),
kind: Option<WriteKind>,
) {
- debug!(
- "report_borrowed_value_does_not_live_long_enough(\
- {:?}, {:?}, {:?}, {:?}\
- )",
- location, borrow, place_span, kind
- );
-
let drop_span = place_span.1;
let root_place =
self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
@@ -1194,10 +1258,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let kind_place = kind.filter(|_| place_desc.is_some()).map(|k| (k, place_span.0));
let explanation = self.explain_why_borrow_contains_point(location, &borrow, kind_place);
- debug!(
- "report_borrowed_value_does_not_live_long_enough(place_desc: {:?}, explanation: {:?})",
- place_desc, explanation
- );
+ debug!(?place_desc, ?explanation);
+
let err = match (place_desc, explanation) {
// If the outlives constraint comes from inside the closure,
// for example:
@@ -1469,6 +1531,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
err
}
+ #[instrument(level = "debug", skip(self))]
fn report_temporary_value_does_not_live_long_enough(
&mut self,
location: Location,
@@ -1478,13 +1541,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
proper_span: Span,
explanation: BorrowExplanation<'tcx>,
) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
- debug!(
- "report_temporary_value_does_not_live_long_enough(\
- {:?}, {:?}, {:?}, {:?}\
- )",
- location, borrow, drop_span, proper_span
- );
-
if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } =
explanation
{
@@ -2164,7 +2220,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}
StorageDeadOrDrop::Destructor(_) => kind,
},
- ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => {
+ ProjectionElem::OpaqueCast { .. }
+ | ProjectionElem::Field(..)
+ | ProjectionElem::Downcast(..) => {
match place_ty.ty.kind() {
ty::Adt(def, _) if def.has_dtor(tcx) => {
// Report the outermost adt with a destructor