Skip to content

Commit aadd58e

Browse files
author
yanchen4791
committed
Add 'static lifetime suggestion when GAT implied 'static requirement from HRTB
1 parent 38a76f3 commit aadd58e

File tree

7 files changed

+258
-4
lines changed

7 files changed

+258
-4
lines changed

compiler/rustc_borrowck/src/diagnostics/region_errors.rs

+113-4
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,13 @@
55
use rustc_data_structures::fx::FxIndexSet;
66
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan};
77
use rustc_hir as hir;
8+
use rustc_hir::def::Res::Def;
89
use rustc_hir::def_id::DefId;
910
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};
1015
use rustc_infer::infer::{
1116
error_reporting::nice_region_error::{
1217
self, find_anon_type, find_param_with_region, suggest_adding_lifetime_params,
@@ -186,6 +191,101 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
186191
false
187192
}
188193

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+
189289
/// Produces nice borrowck error diagnostics for all the errors collected in `nll_errors`.
190290
pub(crate) fn report_region_errors(&mut self, nll_errors: RegionErrors<'tcx>) {
191291
// Iterate through all the errors, producing a diagnostic for each one. The diagnostics are
@@ -223,12 +323,21 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
223323
// to report it; we could probably handle it by
224324
// iterating over the universal regions and reporting
225325
// 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 {
228328
kind: type_test.generic_kind.to_string(),
229329
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);
232341
}
233342
}
234343

compiler/rustc_borrowck/src/region_infer/mod.rs

+8
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
527527
self.scc_values.region_value_str(scc)
528528
}
529529

530+
pub(crate) fn placeholders_contained_in<'a>(
531+
&'a self,
532+
r: RegionVid,
533+
) -> impl Iterator<Item = ty::PlaceholderRegion> + 'a {
534+
let scc = self.constraint_sccs.scc(r.to_region_vid());
535+
self.scc_values.placeholders_contained_in(scc)
536+
}
537+
530538
/// Returns access to the value of `r` for debugging purposes.
531539
pub(crate) fn region_universe(&self, r: RegionVid) -> ty::UniverseIndex {
532540
let scc = self.constraint_sccs.scc(r.to_region_vid());

compiler/rustc_middle/src/ty/sty.rs

+7
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,13 @@ impl BoundRegionKind {
100100

101101
None
102102
}
103+
104+
pub fn get_id(&self) -> Option<DefId> {
105+
match *self {
106+
BoundRegionKind::BrNamed(id, _) => return Some(id),
107+
_ => None,
108+
}
109+
}
103110
}
104111

105112
pub trait Article {

tests/ui/generic-associated-types/collectivity-regression.stderr

+10
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,16 @@ LL | | // probably should work.
99
LL | | let _x = x;
1010
LL | | };
1111
| |_____^
12+
|
13+
note: due to current limitations in the borrow checker, this implies a `'static` lifetime
14+
--> $DIR/collectivity-regression.rs:11:16
15+
|
16+
LL | for<'a> T: Get<Value<'a> = ()>,
17+
| ^^^^^^^^^^^^^^^^^^^
18+
help: consider restricting the type parameter to the `'static` lifetime
19+
|
20+
LL | for<'a> T: Get<Value<'a> = ()> + 'static,
21+
| +++++++++
1222

1323
error: aborting due to previous error
1424

tests/ui/lifetimes/issue-105507.fixed

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// run-rustfix
2+
//
3+
#![allow(warnings)]
4+
struct Wrapper<'a, T: ?Sized>(&'a T);
5+
6+
trait Project {
7+
type Projected<'a> where Self: 'a;
8+
fn project(this: Wrapper<'_, Self>) -> Self::Projected<'_>;
9+
}
10+
trait MyTrait {}
11+
trait ProjectedMyTrait {}
12+
13+
impl<T> Project for Option<T> {
14+
type Projected<'a> = Option<Wrapper<'a, T>> where T: 'a;
15+
fn project(this: Wrapper<'_, Self>) -> Self::Projected<'_> {
16+
this.0.as_ref().map(Wrapper)
17+
}
18+
}
19+
20+
impl<T: MyTrait> MyTrait for Option<Wrapper<'_, T>> {}
21+
22+
impl<T: ProjectedMyTrait> MyTrait for Wrapper<'_, T> {}
23+
24+
impl<T> ProjectedMyTrait for T
25+
where
26+
T: Project,
27+
for<'a> T::Projected<'a>: MyTrait,
28+
//~^ NOTE due to current limitations in the borrow checker, this implies a `'static` lifetime
29+
//~| NOTE due to current limitations in the borrow checker, this implies a `'static` lifetime
30+
{}
31+
32+
fn require_trait<T: MyTrait>(_: T) {}
33+
34+
fn foo<T : MyTrait + 'static + 'static, U : MyTrait + 'static + 'static>(wrap: Wrapper<'_, Option<T>>, wrap1: Wrapper<'_, Option<U>>) {
35+
//~^ HELP consider restricting the type parameter to the `'static` lifetime
36+
//~| HELP consider restricting the type parameter to the `'static` lifetime
37+
require_trait(wrap);
38+
//~^ ERROR `T` does not live long enough
39+
require_trait(wrap1);
40+
//~^ ERROR `U` does not live long enough
41+
}
42+
43+
fn main() {}

tests/ui/lifetimes/issue-105507.rs

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// run-rustfix
2+
//
3+
#![allow(warnings)]
4+
struct Wrapper<'a, T: ?Sized>(&'a T);
5+
6+
trait Project {
7+
type Projected<'a> where Self: 'a;
8+
fn project(this: Wrapper<'_, Self>) -> Self::Projected<'_>;
9+
}
10+
trait MyTrait {}
11+
trait ProjectedMyTrait {}
12+
13+
impl<T> Project for Option<T> {
14+
type Projected<'a> = Option<Wrapper<'a, T>> where T: 'a;
15+
fn project(this: Wrapper<'_, Self>) -> Self::Projected<'_> {
16+
this.0.as_ref().map(Wrapper)
17+
}
18+
}
19+
20+
impl<T: MyTrait> MyTrait for Option<Wrapper<'_, T>> {}
21+
22+
impl<T: ProjectedMyTrait> MyTrait for Wrapper<'_, T> {}
23+
24+
impl<T> ProjectedMyTrait for T
25+
where
26+
T: Project,
27+
for<'a> T::Projected<'a>: MyTrait,
28+
//~^ NOTE due to current limitations in the borrow checker, this implies a `'static` lifetime
29+
//~| NOTE due to current limitations in the borrow checker, this implies a `'static` lifetime
30+
{}
31+
32+
fn require_trait<T: MyTrait>(_: T) {}
33+
34+
fn foo<T : MyTrait, U : MyTrait>(wrap: Wrapper<'_, Option<T>>, wrap1: Wrapper<'_, Option<U>>) {
35+
//~^ HELP consider restricting the type parameter to the `'static` lifetime
36+
//~| HELP consider restricting the type parameter to the `'static` lifetime
37+
require_trait(wrap);
38+
//~^ ERROR `T` does not live long enough
39+
require_trait(wrap1);
40+
//~^ ERROR `U` does not live long enough
41+
}
42+
43+
fn main() {}
+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
error: `T` does not live long enough
2+
--> $DIR/issue-105507.rs:37:5
3+
|
4+
LL | require_trait(wrap);
5+
| ^^^^^^^^^^^^^^^^^^^
6+
|
7+
note: due to current limitations in the borrow checker, this implies a `'static` lifetime
8+
--> $DIR/issue-105507.rs:27:35
9+
|
10+
LL | for<'a> T::Projected<'a>: MyTrait,
11+
| ^^^^^^^
12+
help: consider restricting the type parameter to the `'static` lifetime
13+
|
14+
LL | fn foo<T : MyTrait + 'static, U : MyTrait + 'static>(wrap: Wrapper<'_, Option<T>>, wrap1: Wrapper<'_, Option<U>>) {
15+
| +++++++++ +++++++++
16+
17+
error: `U` does not live long enough
18+
--> $DIR/issue-105507.rs:39:5
19+
|
20+
LL | require_trait(wrap1);
21+
| ^^^^^^^^^^^^^^^^^^^^
22+
|
23+
note: due to current limitations in the borrow checker, this implies a `'static` lifetime
24+
--> $DIR/issue-105507.rs:27:35
25+
|
26+
LL | for<'a> T::Projected<'a>: MyTrait,
27+
| ^^^^^^^
28+
help: consider restricting the type parameter to the `'static` lifetime
29+
|
30+
LL | fn foo<T : MyTrait + 'static, U : MyTrait + 'static>(wrap: Wrapper<'_, Option<T>>, wrap1: Wrapper<'_, Option<U>>) {
31+
| +++++++++ +++++++++
32+
33+
error: aborting due to 2 previous errors
34+

0 commit comments

Comments
 (0)