Rollup merge of #106747 - yanchen4791:issue-105507-fix, r=estebank
Add 'static lifetime suggestion when GAT implied 'static requirement from HRTB Fix for issue #105507 The problem: When generic associated types (GATs) are from higher-ranked trait bounds (HRTB), they are implied 'static requirement (see [Implied 'static requirement from higher-ranked trait bounds](https://blog.rust-lang.org/2022/10/28/gats-stabilization.html#implied-static-requirement-from-higher-ranked-trait-bounds) for more details). If the user did not explicitly specify the `'static` lifetime when using the GAT, the current error message will only point out the type `does not live long enough` where the type is used, but not where the GAT is specified and how to fix the problem. The solution: Add notes at the span where the problematic GATs are specified and suggestions of how to fix the problem by adding `'static` lifetime at the right spans.
This commit is contained in:
commit
d26e07b91a
@ -5,8 +5,13 @@
|
||||
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::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,
|
||||
@ -186,6 +191,101 @@ fn is_closure_fn_mut(&self, fr: RegionVid) -> bool {
|
||||
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
|
||||
@ -223,12 +323,21 @@ pub(crate) fn report_region_errors(&mut self, nll_errors: RegionErrors<'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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -527,6 +527,14 @@ pub(crate) fn region_value_str(&self, r: RegionVid) -> String {
|
||||
self.scc_values.region_value_str(scc)
|
||||
}
|
||||
|
||||
pub(crate) fn placeholders_contained_in<'a>(
|
||||
&'a self,
|
||||
r: RegionVid,
|
||||
) -> impl Iterator<Item = ty::PlaceholderRegion> + 'a {
|
||||
let scc = self.constraint_sccs.scc(r.to_region_vid());
|
||||
self.scc_values.placeholders_contained_in(scc)
|
||||
}
|
||||
|
||||
/// Returns access to the value of `r` for debugging purposes.
|
||||
pub(crate) fn region_universe(&self, r: RegionVid) -> ty::UniverseIndex {
|
||||
let scc = self.constraint_sccs.scc(r.to_region_vid());
|
||||
|
@ -100,6 +100,13 @@ pub fn get_name(&self) -> Option<Symbol> {
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn get_id(&self) -> Option<DefId> {
|
||||
match *self {
|
||||
BoundRegionKind::BrNamed(id, _) => return Some(id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Article {
|
||||
|
@ -9,6 +9,16 @@ LL | | // probably should work.
|
||||
LL | | let _x = x;
|
||||
LL | | };
|
||||
| |_____^
|
||||
|
|
||||
note: due to current limitations in the borrow checker, this implies a `'static` lifetime
|
||||
--> $DIR/collectivity-regression.rs:11:16
|
||||
|
|
||||
LL | for<'a> T: Get<Value<'a> = ()>,
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
help: consider restricting the type parameter to the `'static` lifetime
|
||||
|
|
||||
LL | for<'a> T: Get<Value<'a> = ()> + 'static,
|
||||
| +++++++++
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
43
tests/ui/lifetimes/issue-105507.fixed
Normal file
43
tests/ui/lifetimes/issue-105507.fixed
Normal file
@ -0,0 +1,43 @@
|
||||
// run-rustfix
|
||||
//
|
||||
#![allow(warnings)]
|
||||
struct Wrapper<'a, T: ?Sized>(&'a T);
|
||||
|
||||
trait Project {
|
||||
type Projected<'a> where Self: 'a;
|
||||
fn project(this: Wrapper<'_, Self>) -> Self::Projected<'_>;
|
||||
}
|
||||
trait MyTrait {}
|
||||
trait ProjectedMyTrait {}
|
||||
|
||||
impl<T> Project for Option<T> {
|
||||
type Projected<'a> = Option<Wrapper<'a, T>> where T: 'a;
|
||||
fn project(this: Wrapper<'_, Self>) -> Self::Projected<'_> {
|
||||
this.0.as_ref().map(Wrapper)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: MyTrait> MyTrait for Option<Wrapper<'_, T>> {}
|
||||
|
||||
impl<T: ProjectedMyTrait> MyTrait for Wrapper<'_, T> {}
|
||||
|
||||
impl<T> ProjectedMyTrait for T
|
||||
where
|
||||
T: Project,
|
||||
for<'a> T::Projected<'a>: MyTrait,
|
||||
//~^ NOTE due to current limitations in the borrow checker, this implies a `'static` lifetime
|
||||
//~| NOTE due to current limitations in the borrow checker, this implies a `'static` lifetime
|
||||
{}
|
||||
|
||||
fn require_trait<T: MyTrait>(_: T) {}
|
||||
|
||||
fn foo<T : MyTrait + 'static + 'static, U : MyTrait + 'static + 'static>(wrap: Wrapper<'_, Option<T>>, wrap1: Wrapper<'_, Option<U>>) {
|
||||
//~^ HELP consider restricting the type parameter to the `'static` lifetime
|
||||
//~| HELP consider restricting the type parameter to the `'static` lifetime
|
||||
require_trait(wrap);
|
||||
//~^ ERROR `T` does not live long enough
|
||||
require_trait(wrap1);
|
||||
//~^ ERROR `U` does not live long enough
|
||||
}
|
||||
|
||||
fn main() {}
|
43
tests/ui/lifetimes/issue-105507.rs
Normal file
43
tests/ui/lifetimes/issue-105507.rs
Normal file
@ -0,0 +1,43 @@
|
||||
// run-rustfix
|
||||
//
|
||||
#![allow(warnings)]
|
||||
struct Wrapper<'a, T: ?Sized>(&'a T);
|
||||
|
||||
trait Project {
|
||||
type Projected<'a> where Self: 'a;
|
||||
fn project(this: Wrapper<'_, Self>) -> Self::Projected<'_>;
|
||||
}
|
||||
trait MyTrait {}
|
||||
trait ProjectedMyTrait {}
|
||||
|
||||
impl<T> Project for Option<T> {
|
||||
type Projected<'a> = Option<Wrapper<'a, T>> where T: 'a;
|
||||
fn project(this: Wrapper<'_, Self>) -> Self::Projected<'_> {
|
||||
this.0.as_ref().map(Wrapper)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: MyTrait> MyTrait for Option<Wrapper<'_, T>> {}
|
||||
|
||||
impl<T: ProjectedMyTrait> MyTrait for Wrapper<'_, T> {}
|
||||
|
||||
impl<T> ProjectedMyTrait for T
|
||||
where
|
||||
T: Project,
|
||||
for<'a> T::Projected<'a>: MyTrait,
|
||||
//~^ NOTE due to current limitations in the borrow checker, this implies a `'static` lifetime
|
||||
//~| NOTE due to current limitations in the borrow checker, this implies a `'static` lifetime
|
||||
{}
|
||||
|
||||
fn require_trait<T: MyTrait>(_: T) {}
|
||||
|
||||
fn foo<T : MyTrait, U : MyTrait>(wrap: Wrapper<'_, Option<T>>, wrap1: Wrapper<'_, Option<U>>) {
|
||||
//~^ HELP consider restricting the type parameter to the `'static` lifetime
|
||||
//~| HELP consider restricting the type parameter to the `'static` lifetime
|
||||
require_trait(wrap);
|
||||
//~^ ERROR `T` does not live long enough
|
||||
require_trait(wrap1);
|
||||
//~^ ERROR `U` does not live long enough
|
||||
}
|
||||
|
||||
fn main() {}
|
34
tests/ui/lifetimes/issue-105507.stderr
Normal file
34
tests/ui/lifetimes/issue-105507.stderr
Normal file
@ -0,0 +1,34 @@
|
||||
error: `T` does not live long enough
|
||||
--> $DIR/issue-105507.rs:37:5
|
||||
|
|
||||
LL | require_trait(wrap);
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: due to current limitations in the borrow checker, this implies a `'static` lifetime
|
||||
--> $DIR/issue-105507.rs:27:35
|
||||
|
|
||||
LL | for<'a> T::Projected<'a>: MyTrait,
|
||||
| ^^^^^^^
|
||||
help: consider restricting the type parameter to the `'static` lifetime
|
||||
|
|
||||
LL | fn foo<T : MyTrait + 'static, U : MyTrait + 'static>(wrap: Wrapper<'_, Option<T>>, wrap1: Wrapper<'_, Option<U>>) {
|
||||
| +++++++++ +++++++++
|
||||
|
||||
error: `U` does not live long enough
|
||||
--> $DIR/issue-105507.rs:39:5
|
||||
|
|
||||
LL | require_trait(wrap1);
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: due to current limitations in the borrow checker, this implies a `'static` lifetime
|
||||
--> $DIR/issue-105507.rs:27:35
|
||||
|
|
||||
LL | for<'a> T::Projected<'a>: MyTrait,
|
||||
| ^^^^^^^
|
||||
help: consider restricting the type parameter to the `'static` lifetime
|
||||
|
|
||||
LL | fn foo<T : MyTrait + 'static, U : MyTrait + 'static>(wrap: Wrapper<'_, Option<T>>, wrap1: Wrapper<'_, Option<U>>) {
|
||||
| +++++++++ +++++++++
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
Loading…
Reference in New Issue
Block a user