From d6e4fe569c84c6a9d20690f139ad15a3d3da9550 Mon Sep 17 00:00:00 2001 From: Michael Baikov Date: Wed, 22 May 2024 09:26:02 -0400 Subject: [PATCH 1/2] A custom error message for lending iterators --- compiler/rustc_resolve/messages.ftl | 4 ++ compiler/rustc_resolve/src/errors.rs | 9 +++++ compiler/rustc_resolve/src/late.rs | 40 ++++++++++++++++--- tests/ui/lifetimes/no_lending_iterators.rs | 12 ++++++ .../ui/lifetimes/no_lending_iterators.stderr | 14 +++++++ 5 files changed, 74 insertions(+), 5 deletions(-) create mode 100644 tests/ui/lifetimes/no_lending_iterators.rs create mode 100644 tests/ui/lifetimes/no_lending_iterators.stderr diff --git a/compiler/rustc_resolve/messages.ftl b/compiler/rustc_resolve/messages.ftl index f824e4faf5d..90339b2498b 100644 --- a/compiler/rustc_resolve/messages.ftl +++ b/compiler/rustc_resolve/messages.ftl @@ -234,6 +234,10 @@ resolve_items_in_traits_are_not_importable = resolve_label_with_similar_name_reachable = a label with a similar name is reachable +resolve_lending_iterator_report_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. + .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. + resolve_lifetime_param_in_enum_discriminant = lifetime parameters may not be used in enum discriminant values diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index edfeacec7e3..e9989b803e8 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -882,6 +882,15 @@ pub(crate) struct ElidedAnonymousLivetimeReportError { pub(crate) suggestion: Option, } +#[derive(Diagnostic)] +#[diag(resolve_lending_iterator_report_error)] +pub(crate) struct LendingIteratorReportError { + #[primary_span] + pub(crate) lifetime: Span, + #[note] + pub(crate) ty: Span, +} + #[derive(Subdiagnostic)] #[multipart_suggestion( resolve_elided_anonymous_lifetime_report_error_suggestion, diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 63d0d6c260d..08196619bef 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -9,7 +9,7 @@ use crate::{errors, path_names_to_string, rustdoc, BindingError, Finalize, LexicalScopeBinding}; use crate::{BindingKey, Used}; use crate::{Module, ModuleOrUniformRoot, NameBinding, ParentScope, PathResult}; -use crate::{ResolutionError, Resolver, Segment, UseError}; +use crate::{ResolutionError, Resolver, Segment, TyCtxt, UseError}; use rustc_ast::ptr::P; use rustc_ast::visit::{visit_opt, walk_list, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor}; @@ -1703,10 +1703,28 @@ fn resolve_anonymous_lifetime(&mut self, lifetime: &Lifetime, elided: bool) { break; } } - self.r.dcx().emit_err(errors::ElidedAnonymousLivetimeReportError { - span: lifetime.ident.span, - suggestion, - }); + + // Is it caused by user trying to implement a lending iterator? + if !self.in_func_body + && let Some((module, _)) = &self.current_trait_ref + && let Some(ty) = &self.diag_metadata.current_self_type + && let crate::ModuleKind::Def(DefKind::Trait, trait_id, _) = module.kind + && def_id_matches_path( + self.r.tcx, + trait_id, + &["core", "iter", "traits", "iterator", "Iterator"], + ) + { + self.r.dcx().emit_err(errors::LendingIteratorReportError { + lifetime: lifetime.ident.span, + ty: ty.span(), + }); + } else { + self.r.dcx().emit_err(errors::ElidedAnonymousLivetimeReportError { + span: lifetime.ident.span, + suggestion, + }); + } } else { self.r.dcx().emit_err(errors::ExplicitAnonymousLivetimeReportError { span: lifetime.ident.span, @@ -4824,3 +4842,15 @@ pub(crate) fn late_resolve_crate(&mut self, krate: &Crate) { } } } + +/// Check if definition matches a path +fn def_id_matches_path(tcx: TyCtxt<'_>, mut def_id: DefId, expected_path: &[&str]) -> bool { + let mut path = expected_path.iter().rev(); + while let (Some(parent), Some(next_step)) = (tcx.opt_parent(def_id), path.next()) { + if !tcx.opt_item_name(def_id).map_or(false, |n| n.as_str() == *next_step) { + return false; + } + def_id = parent; + } + return true; +} diff --git a/tests/ui/lifetimes/no_lending_iterators.rs b/tests/ui/lifetimes/no_lending_iterators.rs new file mode 100644 index 00000000000..1f834067218 --- /dev/null +++ b/tests/ui/lifetimes/no_lending_iterators.rs @@ -0,0 +1,12 @@ +struct Data(String); + +impl Iterator for Data { + type Item = &str; + //~^ 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. + + fn next(&mut self) -> Option { + Some(&self.0) + } +} + +fn main() {} diff --git a/tests/ui/lifetimes/no_lending_iterators.stderr b/tests/ui/lifetimes/no_lending_iterators.stderr new file mode 100644 index 00000000000..d052bfac384 --- /dev/null +++ b/tests/ui/lifetimes/no_lending_iterators.stderr @@ -0,0 +1,14 @@ +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. + --> $DIR/no_lending_iterators.rs:4:17 + | +LL | type Item = &str; + | ^ + | +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. + --> $DIR/no_lending_iterators.rs:3:19 + | +LL | impl Iterator for Data { + | ^^^^ + +error: aborting due to 1 previous error + From b70fb4159be7ea3e549a242cf7a6e4ebcb41f168 Mon Sep 17 00:00:00 2001 From: Michael Baikov Date: Fri, 24 May 2024 11:20:33 -0400 Subject: [PATCH 2/2] And more general error --- compiler/rustc_resolve/messages.ftl | 6 +++- compiler/rustc_resolve/src/errors.rs | 8 ++++++ compiler/rustc_resolve/src/late.rs | 28 +++++++++++++------ .../assoc-type.rs | 2 +- .../assoc-type.stderr | 4 +-- tests/ui/lifetimes/no_lending_iterators.rs | 25 ++++++++++++++++- .../ui/lifetimes/no_lending_iterators.stderr | 20 +++++++++++-- 7 files changed, 78 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_resolve/messages.ftl b/compiler/rustc_resolve/messages.ftl index 90339b2498b..358f25e2334 100644 --- a/compiler/rustc_resolve/messages.ftl +++ b/compiler/rustc_resolve/messages.ftl @@ -11,6 +11,10 @@ resolve_added_macro_use = resolve_ancestor_only = visibilities can only be restricted to ancestor modules +resolve_anonymous_livetime_non_gat_report_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 + .label = this lifetime must come from the implemented type + resolve_arguments_macro_use_not_allowed = arguments to `macro_use` are not allowed here resolve_associated_const_with_similar_name_exists = @@ -235,7 +239,7 @@ resolve_label_with_similar_name_reachable = a label with a similar name is reachable resolve_lending_iterator_report_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. + 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 .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. resolve_lifetime_param_in_enum_discriminant = diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index e9989b803e8..0620f3d709e 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -891,6 +891,14 @@ pub(crate) struct LendingIteratorReportError { pub(crate) ty: Span, } +#[derive(Diagnostic)] +#[diag(resolve_anonymous_livetime_non_gat_report_error)] +pub(crate) struct AnonymousLivetimeNonGatReportError { + #[primary_span] + #[label] + pub(crate) lifetime: Span, +} + #[derive(Subdiagnostic)] #[multipart_suggestion( resolve_elided_anonymous_lifetime_report_error_suggestion, diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 08196619bef..8f80b9ad5fd 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -629,6 +629,9 @@ struct DiagMetadata<'ast> { in_assignment: Option<&'ast Expr>, is_assign_rhs: bool, + /// If we are setting an associated type in trait impl, is it a non-GAT type? + in_non_gat_assoc_type: Option, + /// Used to detect possible `.` -> `..` typo when calling methods. in_range: Option<(&'ast Expr, &'ast Expr)>, @@ -1704,21 +1707,28 @@ fn resolve_anonymous_lifetime(&mut self, lifetime: &Lifetime, elided: bool) { } } - // Is it caused by user trying to implement a lending iterator? + // are we trying to use an anonymous lifetime + // on a non GAT associated trait type? if !self.in_func_body && let Some((module, _)) = &self.current_trait_ref && let Some(ty) = &self.diag_metadata.current_self_type + && Some(true) == self.diag_metadata.in_non_gat_assoc_type && let crate::ModuleKind::Def(DefKind::Trait, trait_id, _) = module.kind - && def_id_matches_path( + { + if def_id_matches_path( self.r.tcx, trait_id, &["core", "iter", "traits", "iterator", "Iterator"], - ) - { - self.r.dcx().emit_err(errors::LendingIteratorReportError { - lifetime: lifetime.ident.span, - ty: ty.span(), - }); + ) { + self.r.dcx().emit_err(errors::LendingIteratorReportError { + lifetime: lifetime.ident.span, + ty: ty.span(), + }); + } else { + self.r.dcx().emit_err(errors::AnonymousLivetimeNonGatReportError { + lifetime: lifetime.ident.span, + }); + } } else { self.r.dcx().emit_err(errors::ElidedAnonymousLivetimeReportError { span: lifetime.ident.span, @@ -3076,6 +3086,7 @@ fn resolve_impl_item( ); } AssocItemKind::Type(box TyAlias { generics, .. }) => { + self.diag_metadata.in_non_gat_assoc_type = Some(generics.params.is_empty()); debug!("resolve_implementation AssocItemKind::Type"); // We also need a new scope for the impl item type parameters. self.with_generic_param_rib( @@ -3104,6 +3115,7 @@ fn resolve_impl_item( }); }, ); + self.diag_metadata.in_non_gat_assoc_type = None; } AssocItemKind::Delegation(box delegation) => { debug!("resolve_implementation AssocItemKind::Delegation"); diff --git a/tests/ui/impl-header-lifetime-elision/assoc-type.rs b/tests/ui/impl-header-lifetime-elision/assoc-type.rs index b0089a37aa0..db3c416540f 100644 --- a/tests/ui/impl-header-lifetime-elision/assoc-type.rs +++ b/tests/ui/impl-header-lifetime-elision/assoc-type.rs @@ -9,7 +9,7 @@ trait MyTrait { impl MyTrait for &i32 { type Output = &i32; - //~^ ERROR `&` without an explicit lifetime name cannot be used here + //~^ 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 } impl MyTrait for &u32 { diff --git a/tests/ui/impl-header-lifetime-elision/assoc-type.stderr b/tests/ui/impl-header-lifetime-elision/assoc-type.stderr index c4f27e0b80e..e650eeca48a 100644 --- a/tests/ui/impl-header-lifetime-elision/assoc-type.stderr +++ b/tests/ui/impl-header-lifetime-elision/assoc-type.stderr @@ -1,8 +1,8 @@ -error[E0637]: `&` without an explicit lifetime name cannot be used here +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 --> $DIR/assoc-type.rs:11:19 | LL | type Output = &i32; - | ^ explicit lifetime name needed here + | ^ this lifetime must come from the implemented type error[E0637]: `'_` cannot be used here --> $DIR/assoc-type.rs:16:20 diff --git a/tests/ui/lifetimes/no_lending_iterators.rs b/tests/ui/lifetimes/no_lending_iterators.rs index 1f834067218..21395475fb3 100644 --- a/tests/ui/lifetimes/no_lending_iterators.rs +++ b/tests/ui/lifetimes/no_lending_iterators.rs @@ -2,11 +2,34 @@ impl Iterator for Data { type Item = &str; - //~^ 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. + //~^ 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 fn next(&mut self) -> Option { Some(&self.0) } } +trait Bar { + type Item; + fn poke(&mut self, item: Self::Item); +} + +impl Bar for usize { + type Item = &usize; + //~^ 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 + + fn poke(&mut self, item: Self::Item) { + self += *item; + } +} + +impl Bar for isize { + type Item<'a> = &'a isize; + //~^ ERROR 27:14: 27:18: lifetime parameters or bounds on type `Item` do not match the trait declaration [E0195] + + fn poke(&mut self, item: Self::Item) { + self += *item; + } +} + fn main() {} diff --git a/tests/ui/lifetimes/no_lending_iterators.stderr b/tests/ui/lifetimes/no_lending_iterators.stderr index d052bfac384..c3784770d79 100644 --- a/tests/ui/lifetimes/no_lending_iterators.stderr +++ b/tests/ui/lifetimes/no_lending_iterators.stderr @@ -1,4 +1,4 @@ -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. +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 --> $DIR/no_lending_iterators.rs:4:17 | LL | type Item = &str; @@ -10,5 +10,21 @@ note: you can't create an `Iterator` that borrows each `Item` from itself, but y LL | impl Iterator for Data { | ^^^^ -error: aborting due to 1 previous error +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 + --> $DIR/no_lending_iterators.rs:18:17 + | +LL | type Item = &usize; + | ^ this lifetime must come from the implemented type +error[E0195]: lifetime parameters or bounds on type `Item` do not match the trait declaration + --> $DIR/no_lending_iterators.rs:27:14 + | +LL | type Item; + | - lifetimes in impl do not match this type in trait +... +LL | type Item<'a> = &'a isize; + | ^^^^ lifetimes do not match type in trait + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0195`.