diff options
Diffstat (limited to 'compiler/rustc_borrowck/src/diagnostics/region_errors.rs')
-rw-r--r-- | compiler/rustc_borrowck/src/diagnostics/region_errors.rs | 233 |
1 files changed, 152 insertions, 81 deletions
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); } |