Skip to content

Commit b186d4c

Browse files
committed
Fix incorrect suggestion for undeclared hrtb lifetimes in where clauses.
fixes #122714
1 parent 6e1f7b5 commit b186d4c

5 files changed

+140
-8
lines changed

compiler/rustc_resolve/src/late.rs

+46
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,14 @@ struct DiagMetadata<'ast> {
653653
current_elision_failures: Vec<MissingLifetime>,
654654
}
655655

656+
#[allow(unused)]
657+
#[derive(Debug, Default)]
658+
struct RedundantPolyTraitRef {
659+
poly_trait: Span,
660+
trait_ref: Span,
661+
generic_param_idents: Vec<Ident>,
662+
}
663+
656664
struct LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
657665
r: &'b mut Resolver<'a, 'tcx>,
658666

@@ -693,6 +701,9 @@ struct LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
693701

694702
/// Count the number of places a lifetime is used.
695703
lifetime_uses: FxHashMap<LocalDefId, LifetimeUseSet>,
704+
705+
/// Record poly-trait-ref, only used for diagnostic.
706+
with_poly_trait_ref: FxHashMap<NodeId, RedundantPolyTraitRef>,
696707
}
697708

698709
/// Walks the whole crate in DFS order, visiting each item, resolving names as it goes.
@@ -1197,6 +1208,7 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast,
11971208

11981209
fn visit_where_predicate(&mut self, p: &'ast WherePredicate) {
11991210
debug!("visit_where_predicate {:?}", p);
1211+
self.record_where_bound_predicate_with_poly_trait(p);
12001212
let previous_value = replace(&mut self.diag_metadata.current_where_predicate, Some(p));
12011213
self.with_lifetime_rib(LifetimeRibKind::AnonymousReportError, |this| {
12021214
if let WherePredicate::BoundPredicate(WhereBoundPredicate {
@@ -1306,6 +1318,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
13061318
// errors at module scope should always be reported
13071319
in_func_body: false,
13081320
lifetime_uses: Default::default(),
1321+
with_poly_trait_ref: Default::default(),
13091322
}
13101323
}
13111324

@@ -4702,6 +4715,39 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
47024715
});
47034716
}
47044717
}
4718+
4719+
fn record_where_bound_predicate_with_poly_trait(&mut self, p: &WherePredicate) {
4720+
if let ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate {
4721+
bounded_ty,
4722+
bounds,
4723+
..
4724+
}) = p
4725+
{
4726+
for bound in bounds {
4727+
if let ast::GenericBound::Trait(
4728+
ast::PolyTraitRef {
4729+
span: poly_span,
4730+
trait_ref: ast::TraitRef { path: ast::Path { span, .. }, .. },
4731+
bound_generic_params,
4732+
..
4733+
},
4734+
_,
4735+
) = bound
4736+
{
4737+
let names = bound_generic_params.iter().map(|v| v.ident).collect();
4738+
self.with_poly_trait_ref.insert(
4739+
bounded_ty.node_id(),
4740+
RedundantPolyTraitRef {
4741+
poly_trait: *poly_span,
4742+
trait_ref: *span,
4743+
generic_param_idents: names,
4744+
},
4745+
);
4746+
break;
4747+
}
4748+
}
4749+
}
4750+
}
47054751
}
47064752

47074753
/// Walks the whole crate in DFS order, visiting each item, counting the declared number of

compiler/rustc_resolve/src/late/diagnostics.rs

+62-8
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use rustc_span::edit_distance::find_best_match_for_name;
3131
use rustc_span::edition::Edition;
3232
use rustc_span::hygiene::MacroKind;
3333
use rustc_span::symbol::{kw, sym, Ident, Symbol};
34-
use rustc_span::Span;
34+
use rustc_span::{Span, DUMMY_SP};
3535

3636
use rustc_middle::ty;
3737

@@ -2718,10 +2718,11 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
27182718
suggest: impl Fn(&mut Diag<'_>, bool, Span, Cow<'static, str>, String) -> bool,
27192719
) {
27202720
let mut suggest_note = true;
2721+
27212722
for rib in self.lifetime_ribs.iter().rev() {
27222723
let mut should_continue = true;
27232724
match rib.kind {
2724-
LifetimeRibKind::Generics { binder: _, span, kind } => {
2725+
LifetimeRibKind::Generics { binder: node_id, span, kind } => {
27252726
// Avoid suggesting placing lifetime parameters on constant items unless the relevant
27262727
// feature is enabled. Suggest the parent item as a possible location if applicable.
27272728
if let LifetimeBinderKind::ConstItem = kind
@@ -2750,14 +2751,53 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
27502751
| LifetimeBinderKind::PolyTrait
27512752
| LifetimeBinderKind::WhereBound
27522753
);
2753-
let (span, sugg) = if span.is_empty() {
2754+
2755+
let (span, sugg, rm_poly_trait_span) = if span.is_empty() {
2756+
let (generic_params, poly_trait_span, trait_ref_span) =
2757+
if let Some(with_poly_trait_ref) =
2758+
self.with_poly_trait_ref.get(&node_id)
2759+
&& higher_ranked
2760+
{
2761+
debug!("surechen1 with_poly_trait_ref:{:?}", with_poly_trait_ref);
2762+
let mut generic_params = with_poly_trait_ref
2763+
.generic_param_idents
2764+
.iter()
2765+
.fold("".to_string(), |mut generic_params, x| {
2766+
generic_params += x.as_str();
2767+
generic_params += ",";
2768+
generic_params
2769+
});
2770+
if !generic_params.is_empty() {
2771+
generic_params =
2772+
generic_params[0..generic_params.len() - 1].to_string();
2773+
}
2774+
(
2775+
generic_params,
2776+
with_poly_trait_ref.poly_trait,
2777+
with_poly_trait_ref.trait_ref,
2778+
)
2779+
} else {
2780+
("".to_string(), DUMMY_SP, DUMMY_SP)
2781+
};
2782+
2783+
let rm_poly_trait_span = if generic_params.is_empty() {
2784+
DUMMY_SP
2785+
} else {
2786+
poly_trait_span.with_hi(trait_ref_span.lo())
2787+
};
2788+
2789+
let lifetime_list = format!("{},{}", generic_params, name.unwrap_or("'a"));
27542790
let sugg = format!(
27552791
"{}<{}>{}",
27562792
if higher_ranked { "for" } else { "" },
2757-
name.unwrap_or("'a"),
2793+
if generic_params.is_empty() {
2794+
name.unwrap_or("'a")
2795+
} else {
2796+
&lifetime_list
2797+
},
27582798
if higher_ranked { " " } else { "" },
27592799
);
2760-
(span, sugg)
2800+
(span, sugg, rm_poly_trait_span)
27612801
} else {
27622802
let span = self
27632803
.r
@@ -2767,15 +2807,30 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
27672807
.span_through_char(span, '<')
27682808
.shrink_to_hi();
27692809
let sugg = format!("{}, ", name.unwrap_or("'a"));
2770-
(span, sugg)
2810+
(span, sugg, DUMMY_SP)
27712811
};
2812+
27722813
if higher_ranked {
27732814
let message = Cow::from(format!(
27742815
"consider making the {} lifetime-generic with a new `{}` lifetime",
27752816
kind.descr(),
27762817
name.unwrap_or("'a"),
27772818
));
2778-
should_continue = suggest(err, true, span, message, sugg);
2819+
should_continue = if !rm_poly_trait_span.is_dummy() {
2820+
// For poly-trait-ref like `for<'a> Trait<T>` in
2821+
// `T: for<'a> Trait<T> + 'b { }`.
2822+
// We should merge the higher-ranked lifetimes: existed `for<'a>` and suggestion `for<'b>`
2823+
// or will get another err:
2824+
// `[E0316] nested quantification of lifetimes`.
2825+
err.multipart_suggestion_verbose(
2826+
message,
2827+
vec![(span, sugg), (rm_poly_trait_span, "".to_string())],
2828+
Applicability::MaybeIncorrect,
2829+
);
2830+
false
2831+
} else {
2832+
suggest(err, true, span, message.clone(), sugg.clone())
2833+
};
27792834
err.note_once(
27802835
"for more information on higher-ranked polymorphism, visit \
27812836
https://doc.rust-lang.org/nomicon/hrtb.html",
@@ -3298,7 +3353,6 @@ fn mk_where_bound_predicate(
32983353
poly_trait_ref: &ast::PolyTraitRef,
32993354
ty: &Ty,
33003355
) -> Option<ast::WhereBoundPredicate> {
3301-
use rustc_span::DUMMY_SP;
33023356
let modified_segments = {
33033357
let mut segments = path.segments.clone();
33043358
let [preceding @ .., second_last, last] = segments.as_mut_slice() else {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
//@ run-rustfix
2+
3+
#![allow(dead_code)]
4+
5+
trait Trait<T>
6+
where for<'a,'b> T: Trait<T> + 'b { } //~ ERROR use of undeclared lifetime name `'b`
7+
8+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
//@ run-rustfix
2+
3+
#![allow(dead_code)]
4+
5+
trait Trait<T>
6+
where T: for<'a> Trait<T> + 'b { } //~ ERROR use of undeclared lifetime name `'b`
7+
8+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error[E0261]: use of undeclared lifetime name `'b`
2+
--> $DIR/generic-higher-ranked-lifetime-issue-122714.rs:6:31
3+
|
4+
LL | where T: for<'a> Trait<T> + 'b { }
5+
| ^^ undeclared lifetime
6+
|
7+
= note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html
8+
help: consider making the bound lifetime-generic with a new `'b` lifetime
9+
|
10+
LL - where T: for<'a> Trait<T> + 'b { }
11+
LL + where for<'a,'b> T: Trait<T> + 'b { }
12+
|
13+
14+
error: aborting due to 1 previous error
15+
16+
For more information about this error, try `rustc --explain E0261`.

0 commit comments

Comments
 (0)