Skip to content

Commit 49f9504

Browse files
authored
Rollup merge of #125407 - pacak:no-lending-iterators, r=pnkfelix
Detect when user is trying to create a lending `Iterator` and give a custom explanation The scope for this diagnostic is to detect lending iterators specifically and it's main goal is to help beginners to understand that what they are trying to implement might not be possible for `Iterator` trait specifically. I ended up to changing the wording from originally proposed in the ticket because it might be misleading otherwise: `Data` might have a lifetime parameter but it can be unrelated to items user is planning to return. Fixes #125337
2 parents 28deff4 + b70fb41 commit 49f9504

File tree

7 files changed

+140
-8
lines changed

7 files changed

+140
-8
lines changed

compiler/rustc_resolve/messages.ftl

+8
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ resolve_added_macro_use =
1111
resolve_ancestor_only =
1212
visibilities can only be restricted to ancestor modules
1313
14+
resolve_anonymous_livetime_non_gat_report_error =
15+
in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
16+
.label = this lifetime must come from the implemented type
17+
1418
resolve_arguments_macro_use_not_allowed = arguments to `macro_use` are not allowed here
1519
1620
resolve_associated_const_with_similar_name_exists =
@@ -234,6 +238,10 @@ resolve_items_in_traits_are_not_importable =
234238
resolve_label_with_similar_name_reachable =
235239
a label with a similar name is reachable
236240
241+
resolve_lending_iterator_report_error =
242+
associated type `Iterator::Item` is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
243+
.note = you can't create an `Iterator` that borrows each `Item` from itself, but you can instead create a new type that borrows your existing type and implement `Iterator` for that new type.
244+
237245
resolve_lifetime_param_in_enum_discriminant =
238246
lifetime parameters may not be used in enum discriminant values
239247

compiler/rustc_resolve/src/errors.rs

+17
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,23 @@ pub(crate) struct ElidedAnonymousLivetimeReportError {
882882
pub(crate) suggestion: Option<ElidedAnonymousLivetimeReportErrorSuggestion>,
883883
}
884884

885+
#[derive(Diagnostic)]
886+
#[diag(resolve_lending_iterator_report_error)]
887+
pub(crate) struct LendingIteratorReportError {
888+
#[primary_span]
889+
pub(crate) lifetime: Span,
890+
#[note]
891+
pub(crate) ty: Span,
892+
}
893+
894+
#[derive(Diagnostic)]
895+
#[diag(resolve_anonymous_livetime_non_gat_report_error)]
896+
pub(crate) struct AnonymousLivetimeNonGatReportError {
897+
#[primary_span]
898+
#[label]
899+
pub(crate) lifetime: Span,
900+
}
901+
885902
#[derive(Subdiagnostic)]
886903
#[multipart_suggestion(
887904
resolve_elided_anonymous_lifetime_report_error_suggestion,

compiler/rustc_resolve/src/late.rs

+47-5
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
use crate::{errors, path_names_to_string, rustdoc, BindingError, Finalize, LexicalScopeBinding};
1010
use crate::{BindingKey, Used};
1111
use crate::{Module, ModuleOrUniformRoot, NameBinding, ParentScope, PathResult};
12-
use crate::{ResolutionError, Resolver, Segment, UseError};
12+
use crate::{ResolutionError, Resolver, Segment, TyCtxt, UseError};
1313

1414
use rustc_ast::ptr::P;
1515
use rustc_ast::visit::{visit_opt, walk_list, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor};
@@ -629,6 +629,9 @@ struct DiagMetadata<'ast> {
629629
in_assignment: Option<&'ast Expr>,
630630
is_assign_rhs: bool,
631631

632+
/// If we are setting an associated type in trait impl, is it a non-GAT type?
633+
in_non_gat_assoc_type: Option<bool>,
634+
632635
/// Used to detect possible `.` -> `..` typo when calling methods.
633636
in_range: Option<(&'ast Expr, &'ast Expr)>,
634637

@@ -1703,10 +1706,35 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
17031706
break;
17041707
}
17051708
}
1706-
self.r.dcx().emit_err(errors::ElidedAnonymousLivetimeReportError {
1707-
span: lifetime.ident.span,
1708-
suggestion,
1709-
});
1709+
1710+
// are we trying to use an anonymous lifetime
1711+
// on a non GAT associated trait type?
1712+
if !self.in_func_body
1713+
&& let Some((module, _)) = &self.current_trait_ref
1714+
&& let Some(ty) = &self.diag_metadata.current_self_type
1715+
&& Some(true) == self.diag_metadata.in_non_gat_assoc_type
1716+
&& let crate::ModuleKind::Def(DefKind::Trait, trait_id, _) = module.kind
1717+
{
1718+
if def_id_matches_path(
1719+
self.r.tcx,
1720+
trait_id,
1721+
&["core", "iter", "traits", "iterator", "Iterator"],
1722+
) {
1723+
self.r.dcx().emit_err(errors::LendingIteratorReportError {
1724+
lifetime: lifetime.ident.span,
1725+
ty: ty.span(),
1726+
});
1727+
} else {
1728+
self.r.dcx().emit_err(errors::AnonymousLivetimeNonGatReportError {
1729+
lifetime: lifetime.ident.span,
1730+
});
1731+
}
1732+
} else {
1733+
self.r.dcx().emit_err(errors::ElidedAnonymousLivetimeReportError {
1734+
span: lifetime.ident.span,
1735+
suggestion,
1736+
});
1737+
}
17101738
} else {
17111739
self.r.dcx().emit_err(errors::ExplicitAnonymousLivetimeReportError {
17121740
span: lifetime.ident.span,
@@ -3058,6 +3086,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
30583086
);
30593087
}
30603088
AssocItemKind::Type(box TyAlias { generics, .. }) => {
3089+
self.diag_metadata.in_non_gat_assoc_type = Some(generics.params.is_empty());
30613090
debug!("resolve_implementation AssocItemKind::Type");
30623091
// We also need a new scope for the impl item type parameters.
30633092
self.with_generic_param_rib(
@@ -3086,6 +3115,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
30863115
});
30873116
},
30883117
);
3118+
self.diag_metadata.in_non_gat_assoc_type = None;
30893119
}
30903120
AssocItemKind::Delegation(box delegation) => {
30913121
debug!("resolve_implementation AssocItemKind::Delegation");
@@ -4829,3 +4859,15 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
48294859
}
48304860
}
48314861
}
4862+
4863+
/// Check if definition matches a path
4864+
fn def_id_matches_path(tcx: TyCtxt<'_>, mut def_id: DefId, expected_path: &[&str]) -> bool {
4865+
let mut path = expected_path.iter().rev();
4866+
while let (Some(parent), Some(next_step)) = (tcx.opt_parent(def_id), path.next()) {
4867+
if !tcx.opt_item_name(def_id).map_or(false, |n| n.as_str() == *next_step) {
4868+
return false;
4869+
}
4870+
def_id = parent;
4871+
}
4872+
return true;
4873+
}

tests/ui/impl-header-lifetime-elision/assoc-type.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ trait MyTrait {
99

1010
impl MyTrait for &i32 {
1111
type Output = &i32;
12-
//~^ ERROR `&` without an explicit lifetime name cannot be used here
12+
//~^ ERROR 11:19: 11:20: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
1313
}
1414

1515
impl MyTrait for &u32 {

tests/ui/impl-header-lifetime-elision/assoc-type.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error[E0637]: `&` without an explicit lifetime name cannot be used here
1+
error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
22
--> $DIR/assoc-type.rs:11:19
33
|
44
LL | type Output = &i32;
5-
| ^ explicit lifetime name needed here
5+
| ^ this lifetime must come from the implemented type
66

77
error[E0637]: `'_` cannot be used here
88
--> $DIR/assoc-type.rs:16:20
+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
struct Data(String);
2+
3+
impl Iterator for Data {
4+
type Item = &str;
5+
//~^ ERROR 4:17: 4:18: associated type `Iterator::Item` is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
6+
7+
fn next(&mut self) -> Option<Self::Item> {
8+
Some(&self.0)
9+
}
10+
}
11+
12+
trait Bar {
13+
type Item;
14+
fn poke(&mut self, item: Self::Item);
15+
}
16+
17+
impl Bar for usize {
18+
type Item = &usize;
19+
//~^ ERROR 18:17: 18:18: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
20+
21+
fn poke(&mut self, item: Self::Item) {
22+
self += *item;
23+
}
24+
}
25+
26+
impl Bar for isize {
27+
type Item<'a> = &'a isize;
28+
//~^ ERROR 27:14: 27:18: lifetime parameters or bounds on type `Item` do not match the trait declaration [E0195]
29+
30+
fn poke(&mut self, item: Self::Item) {
31+
self += *item;
32+
}
33+
}
34+
35+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
error: associated type `Iterator::Item` is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
2+
--> $DIR/no_lending_iterators.rs:4:17
3+
|
4+
LL | type Item = &str;
5+
| ^
6+
|
7+
note: you can't create an `Iterator` that borrows each `Item` from itself, but you can instead create a new type that borrows your existing type and implement `Iterator` for that new type.
8+
--> $DIR/no_lending_iterators.rs:3:19
9+
|
10+
LL | impl Iterator for Data {
11+
| ^^^^
12+
13+
error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
14+
--> $DIR/no_lending_iterators.rs:18:17
15+
|
16+
LL | type Item = &usize;
17+
| ^ this lifetime must come from the implemented type
18+
19+
error[E0195]: lifetime parameters or bounds on type `Item` do not match the trait declaration
20+
--> $DIR/no_lending_iterators.rs:27:14
21+
|
22+
LL | type Item;
23+
| - lifetimes in impl do not match this type in trait
24+
...
25+
LL | type Item<'a> = &'a isize;
26+
| ^^^^ lifetimes do not match type in trait
27+
28+
error: aborting due to 3 previous errors
29+
30+
For more information about this error, try `rustc --explain E0195`.

0 commit comments

Comments
 (0)