Skip to content

Commit f21728f

Browse files
authored
Rollup merge of rust-lang#105345 - yanchen4791:issue-103582-fix, r=jackh726
Add hint for missing lifetime bound on trait object when type alias is used Fix issue rust-lang#103582. The problem: When a type alias is used to specify the return type of the method in a trait impl, the suggestion for fixing the problem of "missing lifetime bound on trait object" of the trait impl will not be created. The issue caused by the code which searches for the return trait objects when constructing the hint suggestion is not able to find the trait objects since they are specified in the type alias path instead of the return path of the trait impl. The solution: Trace the trait objects in the type alias path and provide them along with the alias span to generate the suggestion in case the type alias is used in return type of the method in the trait impl.
2 parents 027c850 + 62a1e76 commit f21728f

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)