Skip to content

Commit 62a1e76

Browse files
author
yanchen4791
committed
Add hint for missing lifetime bound on trait object when type alias is used
1 parent c8e6a9e commit 62a1e76

6 files changed

+176
-19
lines changed

compiler/rustc_borrowck/src/diagnostics/region_errors.rs

+40-19
Original file line numberDiff line numberDiff line change
@@ -813,17 +813,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
813813
if *outlived_f != ty::ReStatic {
814814
return;
815815
}
816+
let suitable_region = self.infcx.tcx.is_suitable_region(f);
817+
let Some(suitable_region) = suitable_region else { return; };
816818

817-
let fn_returns = self
818-
.infcx
819-
.tcx
820-
.is_suitable_region(f)
821-
.map(|r| self.infcx.tcx.return_type_impl_or_dyn_traits(r.def_id))
822-
.unwrap_or_default();
823-
824-
if fn_returns.is_empty() {
825-
return;
826-
}
819+
let fn_returns = self.infcx.tcx.return_type_impl_or_dyn_traits(suitable_region.def_id);
827820

828821
let param = if let Some(param) = find_param_with_region(self.infcx.tcx, f, outlived_f) {
829822
param
@@ -839,15 +832,43 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
839832
};
840833
let captures = format!("captures data from {arg}");
841834

842-
return nice_region_error::suggest_new_region_bound(
843-
self.infcx.tcx,
844-
diag,
845-
fn_returns,
846-
lifetime.to_string(),
847-
Some(arg),
848-
captures,
849-
Some((param.param_ty_span, param.param_ty.to_string())),
850-
self.infcx.tcx.is_suitable_region(f).map(|r| r.def_id),
835+
if !fn_returns.is_empty() {
836+
nice_region_error::suggest_new_region_bound(
837+
self.infcx.tcx,
838+
diag,
839+
fn_returns,
840+
lifetime.to_string(),
841+
Some(arg),
842+
captures,
843+
Some((param.param_ty_span, param.param_ty.to_string())),
844+
Some(suitable_region.def_id),
845+
);
846+
return;
847+
}
848+
849+
let Some((alias_tys, alias_span)) = self
850+
.infcx
851+
.tcx
852+
.return_type_impl_or_dyn_traits_with_type_alias(suitable_region.def_id) else { return; };
853+
854+
// in case the return type of the method is a type alias
855+
let mut spans_suggs: Vec<_> = Vec::new();
856+
for alias_ty in alias_tys {
857+
if alias_ty.span.desugaring_kind().is_some() {
858+
// Skip `async` desugaring `impl Future`.
859+
()
860+
}
861+
if let TyKind::TraitObject(_, lt, _) = alias_ty.kind {
862+
spans_suggs.push((lt.ident.span.shrink_to_hi(), " + 'a".to_string()));
863+
}
864+
}
865+
spans_suggs.push((alias_span.shrink_to_hi(), "<'a>".to_string()));
866+
diag.multipart_suggestion_verbose(
867+
&format!(
868+
"to declare that the trait object {captures}, you can add a lifetime parameter `'a` in the type alias"
869+
),
870+
spans_suggs,
871+
Applicability::MaybeIncorrect,
851872
);
852873
}
853874
}

compiler/rustc_hir/src/hir.rs

+7
Original file line numberDiff line numberDiff line change
@@ -3524,6 +3524,13 @@ impl<'hir> Node<'hir> {
35243524
}
35253525
}
35263526

3527+
pub fn alias_ty(self) -> Option<&'hir Ty<'hir>> {
3528+
match self {
3529+
Node::Item(Item { kind: ItemKind::TyAlias(ty, ..), .. }) => Some(ty),
3530+
_ => None,
3531+
}
3532+
}
3533+
35273534
pub fn body_id(&self) -> Option<BodyId> {
35283535
match self {
35293536
Node::TraitItem(TraitItem {

compiler/rustc_middle/src/ty/context.rs

+24
Original file line numberDiff line numberDiff line change
@@ -997,6 +997,30 @@ impl<'tcx> TyCtxt<'tcx> {
997997
v.0
998998
}
999999

1000+
/// Given a `DefId` for an `fn`, return all the `dyn` and `impl` traits in its return type and associated alias span when type alias is used
1001+
pub fn return_type_impl_or_dyn_traits_with_type_alias(
1002+
self,
1003+
scope_def_id: LocalDefId,
1004+
) -> Option<(Vec<&'tcx hir::Ty<'tcx>>, Span)> {
1005+
let hir_id = self.hir().local_def_id_to_hir_id(scope_def_id);
1006+
let mut v = TraitObjectVisitor(vec![], self.hir());
1007+
// when the return type is a type alias
1008+
if let Some(hir::FnDecl { output: hir::FnRetTy::Return(hir_output), .. }) = self.hir().fn_decl_by_hir_id(hir_id)
1009+
&& let hir::TyKind::Path(hir::QPath::Resolved(
1010+
None,
1011+
hir::Path { res: hir::def::Res::Def(DefKind::TyAlias, def_id), .. }, )) = hir_output.kind
1012+
&& let Some(local_id) = def_id.as_local()
1013+
&& let Some(alias_ty) = self.hir().get_by_def_id(local_id).alias_ty() // it is type alias
1014+
&& let Some(alias_generics) = self.hir().get_by_def_id(local_id).generics()
1015+
{
1016+
v.visit_ty(alias_ty);
1017+
if !v.0.is_empty() {
1018+
return Some((v.0, alias_generics.span));
1019+
}
1020+
}
1021+
return None;
1022+
}
1023+
10001024
pub fn return_type_impl_trait(self, scope_def_id: LocalDefId) -> Option<(Ty<'tcx>, Span)> {
10011025
// `type_of()` will fail on these (#55796, #86483), so only allow `fn`s or closures.
10021026
match self.hir().get_by_def_id(scope_def_id) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// run-rustfix
2+
3+
trait Greeter0 {
4+
fn greet(&self);
5+
}
6+
7+
trait Greeter1 {
8+
fn greet(&self);
9+
}
10+
11+
type BoxedGreeter<'a> = (Box<dyn Greeter0 + 'a>, Box<dyn Greeter1 + 'a>);
12+
//~^ HELP to declare that the trait object captures data from argument `self`, you can add a lifetime parameter `'a` in the type alias
13+
14+
struct FixedGreeter<'a>(pub &'a str);
15+
16+
impl Greeter0 for FixedGreeter<'_> {
17+
fn greet(&self) {
18+
println!("0 {}", self.0)
19+
}
20+
}
21+
22+
impl Greeter1 for FixedGreeter<'_> {
23+
fn greet(&self) {
24+
println!("1 {}", self.0)
25+
}
26+
}
27+
28+
struct Greetings(pub Vec<String>);
29+
30+
impl Greetings {
31+
pub fn get(&self, i: usize) -> BoxedGreeter {
32+
(Box::new(FixedGreeter(&self.0[i])), Box::new(FixedGreeter(&self.0[i])))
33+
//~^ ERROR lifetime may not live long enough
34+
}
35+
}
36+
37+
fn main() {
38+
let mut g = Greetings {0 : vec!()};
39+
g.0.push("a".to_string());
40+
g.0.push("b".to_string());
41+
g.get(0).0.greet();
42+
g.get(0).1.greet();
43+
g.get(1).0.greet();
44+
g.get(1).1.greet();
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// run-rustfix
2+
3+
trait Greeter0 {
4+
fn greet(&self);
5+
}
6+
7+
trait Greeter1 {
8+
fn greet(&self);
9+
}
10+
11+
type BoxedGreeter = (Box<dyn Greeter0>, Box<dyn Greeter1>);
12+
//~^ HELP to declare that the trait object captures data from argument `self`, you can add a lifetime parameter `'a` in the type alias
13+
14+
struct FixedGreeter<'a>(pub &'a str);
15+
16+
impl Greeter0 for FixedGreeter<'_> {
17+
fn greet(&self) {
18+
println!("0 {}", self.0)
19+
}
20+
}
21+
22+
impl Greeter1 for FixedGreeter<'_> {
23+
fn greet(&self) {
24+
println!("1 {}", self.0)
25+
}
26+
}
27+
28+
struct Greetings(pub Vec<String>);
29+
30+
impl Greetings {
31+
pub fn get(&self, i: usize) -> BoxedGreeter {
32+
(Box::new(FixedGreeter(&self.0[i])), Box::new(FixedGreeter(&self.0[i])))
33+
//~^ ERROR lifetime may not live long enough
34+
}
35+
}
36+
37+
fn main() {
38+
let mut g = Greetings {0 : vec!()};
39+
g.0.push("a".to_string());
40+
g.0.push("b".to_string());
41+
g.get(0).0.greet();
42+
g.get(0).1.greet();
43+
g.get(1).0.greet();
44+
g.get(1).1.greet();
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error: lifetime may not live long enough
2+
--> $DIR/issue-103582-hint-for-missing-lifetime-bound-on-trait-object-using-type-alias.rs:32:9
3+
|
4+
LL | pub fn get(&self, i: usize) -> BoxedGreeter {
5+
| - let's call the lifetime of this reference `'1`
6+
LL | (Box::new(FixedGreeter(&self.0[i])), Box::new(FixedGreeter(&self.0[i])))
7+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static`
8+
|
9+
help: to declare that the trait object captures data from argument `self`, you can add a lifetime parameter `'a` in the type alias
10+
|
11+
LL | type BoxedGreeter<'a> = (Box<dyn Greeter0 + 'a>, Box<dyn Greeter1 + 'a>);
12+
| ++++ ++++ ++++
13+
14+
error: aborting due to previous error
15+

0 commit comments

Comments
 (0)