Skip to content

Commit

Permalink
Fix incorrect suggestion for undeclared hrtb lifetimes in where clauses.
Browse files Browse the repository at this point in the history
  • Loading branch information
surechen committed Mar 27, 2024
1 parent 6e1f7b5 commit c864852
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 8 deletions.
45 changes: 45 additions & 0 deletions compiler/rustc_resolve/src/late.rs
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,13 @@ struct DiagMetadata<'ast> {
current_elision_failures: Vec<MissingLifetime>,
}

#[derive(Debug, Default)]
struct RedundantPolyTraitRef {
poly_trait: Span,
trait_ref: Span,
generic_param_idents: Vec<Ident>,
}

struct LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
r: &'b mut Resolver<'a, 'tcx>,

Expand Down Expand Up @@ -693,6 +700,9 @@ struct LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {

/// Count the number of places a lifetime is used.
lifetime_uses: FxHashMap<LocalDefId, LifetimeUseSet>,

/// Record poly-trait-ref, only used for diagnostic.
with_poly_trait_ref: FxHashMap<NodeId, RedundantPolyTraitRef>,
}

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

fn visit_where_predicate(&mut self, p: &'ast WherePredicate) {
debug!("visit_where_predicate {:?}", p);
self.record_where_bound_predicate_with_poly_trait(p);
let previous_value = replace(&mut self.diag_metadata.current_where_predicate, Some(p));
self.with_lifetime_rib(LifetimeRibKind::AnonymousReportError, |this| {
if let WherePredicate::BoundPredicate(WhereBoundPredicate {
Expand Down Expand Up @@ -1306,6 +1317,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
// errors at module scope should always be reported
in_func_body: false,
lifetime_uses: Default::default(),
with_poly_trait_ref: Default::default(),
}
}

Expand Down Expand Up @@ -4702,6 +4714,39 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
});
}
}

fn record_where_bound_predicate_with_poly_trait(&mut self, p: &WherePredicate) {
if let ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate {
bounded_ty,
bounds,
..
}) = p
{
for bound in bounds {
if let ast::GenericBound::Trait(
ast::PolyTraitRef {
span: poly_span,
trait_ref: ast::TraitRef { path: ast::Path { span, .. }, .. },
bound_generic_params,
..
},
_,
) = bound
{
let names = bound_generic_params.iter().map(|v| v.ident).collect();
self.with_poly_trait_ref.insert(
bounded_ty.node_id(),
RedundantPolyTraitRef {
poly_trait: *poly_span,
trait_ref: *span,
generic_param_idents: names,
},
);
break;
}
}
}
}
}

/// Walks the whole crate in DFS order, visiting each item, counting the declared number of
Expand Down
70 changes: 62 additions & 8 deletions compiler/rustc_resolve/src/late/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::edition::Edition;
use rustc_span::hygiene::MacroKind;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::Span;
use rustc_span::{Span, DUMMY_SP};

use rustc_middle::ty;

Expand Down Expand Up @@ -2718,10 +2718,11 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
suggest: impl Fn(&mut Diag<'_>, bool, Span, Cow<'static, str>, String) -> bool,
) {
let mut suggest_note = true;

for rib in self.lifetime_ribs.iter().rev() {
let mut should_continue = true;
match rib.kind {
LifetimeRibKind::Generics { binder: _, span, kind } => {
LifetimeRibKind::Generics { binder: node_id, span, kind } => {
// Avoid suggesting placing lifetime parameters on constant items unless the relevant
// feature is enabled. Suggest the parent item as a possible location if applicable.
if let LifetimeBinderKind::ConstItem = kind
Expand Down Expand Up @@ -2750,14 +2751,53 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
| LifetimeBinderKind::PolyTrait
| LifetimeBinderKind::WhereBound
);
let (span, sugg) = if span.is_empty() {

let (span, sugg, rm_poly_trait_span) = if span.is_empty() {
let (generic_params, poly_trait_span, trait_ref_span) =
if let Some(with_poly_trait_ref) =
self.with_poly_trait_ref.get(&node_id)
&& higher_ranked
{
debug!("surechen1 with_poly_trait_ref:{:?}", with_poly_trait_ref);
let mut generic_params = with_poly_trait_ref
.generic_param_idents
.iter()
.fold("".to_string(), |mut generic_params, x| {
generic_params += x.as_str();
generic_params += ",";
generic_params
});
if !generic_params.is_empty() {
generic_params =
generic_params[0..generic_params.len() - 1].to_string();
}
(
generic_params,
with_poly_trait_ref.poly_trait,
with_poly_trait_ref.trait_ref,
)
} else {
("".to_string(), DUMMY_SP, DUMMY_SP)
};

let rm_poly_trait_span = if generic_params.is_empty() {
DUMMY_SP
} else {
poly_trait_span.with_hi(trait_ref_span.lo())
};

let lifetime_list = format!("{},{}", generic_params, name.unwrap_or("'a"));
let sugg = format!(
"{}<{}>{}",
if higher_ranked { "for" } else { "" },
name.unwrap_or("'a"),
if generic_params.is_empty() {
name.unwrap_or("'a")
} else {
&lifetime_list
},
if higher_ranked { " " } else { "" },
);
(span, sugg)
(span, sugg, rm_poly_trait_span)
} else {
let span = self
.r
Expand All @@ -2767,15 +2807,30 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
.span_through_char(span, '<')
.shrink_to_hi();
let sugg = format!("{}, ", name.unwrap_or("'a"));
(span, sugg)
(span, sugg, DUMMY_SP)
};

if higher_ranked {
let message = Cow::from(format!(
"consider making the {} lifetime-generic with a new `{}` lifetime",
kind.descr(),
name.unwrap_or("'a"),
));
should_continue = suggest(err, true, span, message, sugg);
should_continue = if !rm_poly_trait_span.is_dummy() {
// For poly-trait-ref like `for<'a> Trait<T>` in
// `T: for<'a> Trait<T> + 'b { }`.
// We should merge the higher-ranked lifetimes: existed `for<'a>` and suggestion `for<'b>`
// or will get err:
// `[E0316] nested quantification of lifetimes`.
err.multipart_suggestion_verbose(
message,
vec![(span, sugg), (rm_poly_trait_span, "".to_string())],
Applicability::MaybeIncorrect,
);
false
} else {
suggest(err, true, span, message.clone(), sugg.clone())
};
err.note_once(
"for more information on higher-ranked polymorphism, visit \
https://doc.rust-lang.org/nomicon/hrtb.html",
Expand Down Expand Up @@ -3298,7 +3353,6 @@ fn mk_where_bound_predicate(
poly_trait_ref: &ast::PolyTraitRef,
ty: &Ty,
) -> Option<ast::WhereBoundPredicate> {
use rustc_span::DUMMY_SP;
let modified_segments = {
let mut segments = path.segments.clone();
let [preceding @ .., second_last, last] = segments.as_mut_slice() else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//@ run-rustfix

#![allow(dead_code)]

trait Trait<T>
where for<'a,'b> T: Trait<T> + 'b { } //~ ERROR use of undeclared lifetime name `'b`

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//@ run-rustfix

#![allow(dead_code)]

trait Trait<T>
where T: for<'a> Trait<T> + 'b { } //~ ERROR use of undeclared lifetime name `'b`

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
error[E0261]: use of undeclared lifetime name `'b`
--> $DIR/generic-higher-ranked-lifetime-issue-122714.rs:6:31
|
LL | where T: for<'a> Trait<T> + 'b { }
| ^^ undeclared lifetime
|
= note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html
help: consider making the bound lifetime-generic with a new `'b` lifetime
|
LL - where T: for<'a> Trait<T> + 'b { }
LL + where for<'a,'b> T: Trait<T> + 'b { }
|

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0261`.

0 comments on commit c864852

Please sign in to comment.