|
5 | 5 | use rustc_data_structures::fx::FxIndexSet;
|
6 | 6 | use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan};
|
7 | 7 | use rustc_hir as hir;
|
| 8 | +use rustc_hir::def::Res::Def; |
8 | 9 | use rustc_hir::def_id::DefId;
|
9 | 10 | use rustc_hir::intravisit::Visitor;
|
| 11 | +use rustc_hir::GenericBound::Trait; |
| 12 | +use rustc_hir::QPath::Resolved; |
| 13 | +use rustc_hir::WherePredicate::BoundPredicate; |
| 14 | +use rustc_hir::{PolyTraitRef, TyKind, WhereBoundPredicate}; |
10 | 15 | use rustc_infer::infer::{
|
11 | 16 | error_reporting::nice_region_error::{
|
12 | 17 | self, find_anon_type, find_param_with_region, suggest_adding_lifetime_params,
|
@@ -186,6 +191,101 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
186 | 191 | false
|
187 | 192 | }
|
188 | 193 |
|
| 194 | + // For generic associated types (GATs) which implied 'static requirement |
| 195 | + // from higher-ranked trait bounds (HRTB). Try to locate span of the trait |
| 196 | + // and the span which bounded to the trait for adding 'static lifetime suggestion |
| 197 | + fn suggest_static_lifetime_for_gat_from_hrtb( |
| 198 | + &self, |
| 199 | + diag: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, |
| 200 | + lower_bound: RegionVid, |
| 201 | + ) { |
| 202 | + let mut suggestions = vec![]; |
| 203 | + let hir = self.infcx.tcx.hir(); |
| 204 | + |
| 205 | + // find generic associated types in the given region 'lower_bound' |
| 206 | + let gat_id_and_generics = self |
| 207 | + .regioncx |
| 208 | + .placeholders_contained_in(lower_bound) |
| 209 | + .map(|placeholder| { |
| 210 | + if let Some(id) = placeholder.name.get_id() |
| 211 | + && let Some(placeholder_id) = id.as_local() |
| 212 | + && let gat_hir_id = hir.local_def_id_to_hir_id(placeholder_id) |
| 213 | + && let Some(generics_impl) = hir.get_parent(gat_hir_id).generics() |
| 214 | + { |
| 215 | + Some((gat_hir_id, generics_impl)) |
| 216 | + } else { |
| 217 | + None |
| 218 | + } |
| 219 | + }) |
| 220 | + .collect::<Vec<_>>(); |
| 221 | + debug!(?gat_id_and_generics); |
| 222 | + |
| 223 | + // find higher-ranked trait bounds bounded to the generic associated types |
| 224 | + let mut hrtb_bounds = vec![]; |
| 225 | + gat_id_and_generics.iter().flatten().for_each(|(gat_hir_id, generics)| { |
| 226 | + for pred in generics.predicates { |
| 227 | + let BoundPredicate( |
| 228 | + WhereBoundPredicate { |
| 229 | + bound_generic_params, |
| 230 | + bounds, |
| 231 | + .. |
| 232 | + }) = pred else { continue; }; |
| 233 | + if bound_generic_params |
| 234 | + .iter() |
| 235 | + .rfind(|bgp| hir.local_def_id_to_hir_id(bgp.def_id) == *gat_hir_id) |
| 236 | + .is_some() |
| 237 | + { |
| 238 | + for bound in *bounds { |
| 239 | + hrtb_bounds.push(bound); |
| 240 | + } |
| 241 | + } |
| 242 | + } |
| 243 | + }); |
| 244 | + debug!(?hrtb_bounds); |
| 245 | + |
| 246 | + hrtb_bounds.iter().for_each(|bound| { |
| 247 | + let Trait(PolyTraitRef { trait_ref, span: trait_span, .. }, _) = bound else { return; }; |
| 248 | + diag.span_note( |
| 249 | + *trait_span, |
| 250 | + format!("due to current limitations in the borrow checker, this implies a `'static` lifetime") |
| 251 | + ); |
| 252 | + let Some(generics_fn) = hir.get_generics(self.body.source.def_id().expect_local()) else { return; }; |
| 253 | + let Def(_, trait_res_defid) = trait_ref.path.res else { return; }; |
| 254 | + debug!(?generics_fn); |
| 255 | + generics_fn.predicates.iter().for_each(|predicate| { |
| 256 | + let BoundPredicate( |
| 257 | + WhereBoundPredicate { |
| 258 | + span: bounded_span, |
| 259 | + bounded_ty, |
| 260 | + bounds, |
| 261 | + .. |
| 262 | + } |
| 263 | + ) = predicate else { return; }; |
| 264 | + bounds.iter().for_each(|bd| { |
| 265 | + if let Trait(PolyTraitRef { trait_ref: tr_ref, .. }, _) = bd |
| 266 | + && let Def(_, res_defid) = tr_ref.path.res |
| 267 | + && res_defid == trait_res_defid // trait id matches |
| 268 | + && let TyKind::Path(Resolved(_, path)) = bounded_ty.kind |
| 269 | + && let Def(_, defid) = path.res |
| 270 | + && generics_fn.params |
| 271 | + .iter() |
| 272 | + .rfind(|param| param.def_id.to_def_id() == defid) |
| 273 | + .is_some() { |
| 274 | + suggestions.push((bounded_span.shrink_to_hi(), format!(" + 'static"))); |
| 275 | + } |
| 276 | + }); |
| 277 | + }); |
| 278 | + }); |
| 279 | + if suggestions.len() > 0 { |
| 280 | + suggestions.dedup(); |
| 281 | + diag.multipart_suggestion_verbose( |
| 282 | + format!("consider restricting the type parameter to the `'static` lifetime"), |
| 283 | + suggestions, |
| 284 | + Applicability::MaybeIncorrect, |
| 285 | + ); |
| 286 | + } |
| 287 | + } |
| 288 | + |
189 | 289 | /// Produces nice borrowck error diagnostics for all the errors collected in `nll_errors`.
|
190 | 290 | pub(crate) fn report_region_errors(&mut self, nll_errors: RegionErrors<'tcx>) {
|
191 | 291 | // Iterate through all the errors, producing a diagnostic for each one. The diagnostics are
|
@@ -223,12 +323,21 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
223 | 323 | // to report it; we could probably handle it by
|
224 | 324 | // iterating over the universal regions and reporting
|
225 | 325 | // an error that multiple bounds are required.
|
226 |
| - self.buffer_error(self.infcx.tcx.sess.create_err( |
227 |
| - GenericDoesNotLiveLongEnough { |
| 326 | + let mut diag = |
| 327 | + self.infcx.tcx.sess.create_err(GenericDoesNotLiveLongEnough { |
228 | 328 | kind: type_test.generic_kind.to_string(),
|
229 | 329 | span: type_test_span,
|
230 |
| - }, |
231 |
| - )); |
| 330 | + }); |
| 331 | + |
| 332 | + // Add notes and suggestions for the case of 'static lifetime |
| 333 | + // implied but not specified when a generic associated types |
| 334 | + // are from higher-ranked trait bounds |
| 335 | + self.suggest_static_lifetime_for_gat_from_hrtb( |
| 336 | + &mut diag, |
| 337 | + type_test.lower_bound, |
| 338 | + ); |
| 339 | + |
| 340 | + self.buffer_error(diag); |
232 | 341 | }
|
233 | 342 | }
|
234 | 343 |
|
|
0 commit comments