diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index d5e1c061bf0ba..50048534aaee5 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -2259,9 +2259,99 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } }; - let mut err = match *sub { - ty::ReEarlyBound(ty::EarlyBoundRegion { name, .. }) - | ty::ReFree(ty::FreeRegion { bound_region: ty::BrNamed(_, name), .. }) => { + #[derive(Debug)] + enum SubOrigin<'hir> { + GAT(&'hir hir::Generics<'hir>), + Impl(&'hir hir::Generics<'hir>), + Trait(&'hir hir::Generics<'hir>), + Fn(&'hir hir::Generics<'hir>), + Unknown, + } + let sub_origin = 'origin: { + match *sub { + ty::ReEarlyBound(ty::EarlyBoundRegion { def_id, .. }) => { + let node = self.tcx.hir().get_if_local(def_id).unwrap(); + match node { + Node::GenericParam(param) => { + for h in self.tcx.hir().parent_iter(param.hir_id) { + break 'origin match h.1 { + Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::TyAlias(..), + generics, + .. + }) => SubOrigin::GAT(generics), + Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Fn(..), + generics, + .. + }) => SubOrigin::Fn(generics), + Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Type(..), + generics, + .. + }) => SubOrigin::GAT(generics), + Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(..), + generics, + .. + }) => SubOrigin::Fn(generics), + Node::Item(hir::Item { + kind: hir::ItemKind::Trait(_, _, generics, _, _), + .. + }) => SubOrigin::Trait(generics), + Node::Item(hir::Item { + kind: hir::ItemKind::Impl(hir::Impl { generics, .. }), + .. + }) => SubOrigin::Impl(generics), + Node::Item(hir::Item { + kind: hir::ItemKind::Fn(_, generics, _), + .. + }) => SubOrigin::Fn(generics), + _ => continue, + }; + } + } + _ => {} + } + } + _ => {} + } + SubOrigin::Unknown + }; + debug!(?sub_origin); + + let mut err = match (*sub, sub_origin) { + // In the case of GATs, we have to be careful. If we a type parameter `T` on an impl, + // but a lifetime `'a` on an associated type, then we might need to suggest adding + // `where T: 'a`. Importantly, this is on the GAT span, not on the `T` declaration. + (ty::ReEarlyBound(ty::EarlyBoundRegion { name: _, .. }), SubOrigin::GAT(generics)) => { + // Does the required lifetime have a nice name we can print? + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0309, + "{} may not live long enough", + labeled_user_string + ); + let pred = format!("{}: {}", bound_kind, sub); + let suggestion = format!( + "{} {}", + if !generics.where_clause.predicates.is_empty() { "," } else { " where" }, + pred, + ); + err.span_suggestion( + generics.where_clause.tail_span_for_suggestion(), + "consider adding a where clause".into(), + suggestion, + Applicability::MaybeIncorrect, + ); + err + } + ( + ty::ReEarlyBound(ty::EarlyBoundRegion { name, .. }) + | ty::ReFree(ty::FreeRegion { bound_region: ty::BrNamed(_, name), .. }), + _, + ) => { // Does the required lifetime have a nice name we can print? let mut err = struct_span_err!( self.tcx.sess, @@ -2278,7 +2368,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { err } - ty::ReStatic => { + (ty::ReStatic, _) => { // Does the required lifetime have a nice name we can print? let mut err = struct_span_err!( self.tcx.sess, diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs index ee358c52c2f57..ba7155f63e4b4 100644 --- a/compiler/rustc_infer/src/lib.rs +++ b/compiler/rustc_infer/src/lib.rs @@ -22,6 +22,7 @@ #![feature(in_band_lifetimes)] #![feature(control_flow_enum)] #![feature(min_specialization)] +#![feature(label_break_value)] #![recursion_limit = "512"] // For rustdoc #[macro_use] diff --git a/src/test/ui/generic-associated-types/issue-84931.rs b/src/test/ui/generic-associated-types/issue-84931.rs new file mode 100644 index 0000000000000..9e247de163203 --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-84931.rs @@ -0,0 +1,22 @@ +#![feature(generic_associated_types)] +// check-fail + +trait StreamingIter { + type Item<'a> where Self: 'a; + fn next<'a>(&'a mut self) -> Option>; +} + +struct StreamingSliceIter<'a, T> { + idx: usize, + data: &'a mut [T], +} + +impl<'b, T: 'b> StreamingIter for StreamingSliceIter<'b, T> { + type Item<'a> = &'a mut T; + //~^ the parameter type + fn next(&mut self) -> Option<&mut T> { + loop {} + } +} + +fn main() {} diff --git a/src/test/ui/generic-associated-types/issue-84931.stderr b/src/test/ui/generic-associated-types/issue-84931.stderr new file mode 100644 index 0000000000000..47decb70ae74c --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-84931.stderr @@ -0,0 +1,11 @@ +error[E0309]: the parameter type `T` may not live long enough + --> $DIR/issue-84931.rs:15:21 + | +LL | type Item<'a> = &'a mut T; + | - ^^^^^^^^^ ...so that the reference type `&'a mut T` does not outlive the data it points at + | | + | help: consider adding a where clause: `where T: 'a` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0309`. diff --git a/src/test/ui/generic-associated-types/issue-86483.stderr b/src/test/ui/generic-associated-types/issue-86483.stderr index f5b92beb3b225..d6978794e1e95 100644 --- a/src/test/ui/generic-associated-types/issue-86483.stderr +++ b/src/test/ui/generic-associated-types/issue-86483.stderr @@ -37,11 +37,10 @@ LL | for<'a> T: 'a, error[E0309]: the parameter type `T` may not live long enough --> $DIR/issue-86483.rs:9:32 | -LL | pub trait IceIce - | - help: consider adding an explicit lifetime bound...: `T: 'v` -... LL | type Ice<'v>: IntoIterator; - | ^^^^^^^^^^^^ ...so that the reference type `&'v T` does not outlive the data it points at + | ^^^^^^^^^^^^ - help: consider adding a where clause: `where T: 'v` + | | + | ...so that the reference type `&'v T` does not outlive the data it points at error: aborting due to 3 previous errors diff --git a/src/test/ui/generic-associated-types/issue-86787.rs b/src/test/ui/generic-associated-types/issue-86787.rs new file mode 100644 index 0000000000000..57d478a9ef1e3 --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-86787.rs @@ -0,0 +1,40 @@ +#![feature(generic_associated_types)] +// check-fail + +enum Either { + Left(L), + Right(R), +} + +pub trait HasChildrenOf { + type T; + type TRef<'a>; + + fn ref_children<'a>(&'a self) -> Vec>; + fn take_children(self) -> Vec; +} + +impl HasChildrenOf for Either +where + Left: HasChildrenOf, + Right: HasChildrenOf, +{ + type T = Either; + type TRef<'a> + //~^ the associated type + //~^^ the associated type + where + ::T: 'a, + ::T: 'a + = Either<&'a Left::T, &'a Right::T>; + + fn ref_children<'a>(&'a self) -> Vec> { + todo!() + } + + fn take_children(self) -> Vec { + todo!() + } +} + +fn main() {} diff --git a/src/test/ui/generic-associated-types/issue-86787.stderr b/src/test/ui/generic-associated-types/issue-86787.stderr new file mode 100644 index 0000000000000..04cd84b08011c --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-86787.stderr @@ -0,0 +1,29 @@ +error[E0309]: the associated type `::T` may not live long enough + --> $DIR/issue-86787.rs:23:5 + | +LL | / type TRef<'a> +LL | | +LL | | +LL | | where +LL | | ::T: 'a, +LL | | ::T: 'a + | | - help: consider adding a where clause: `, ::T: 'a` +LL | | = Either<&'a Left::T, &'a Right::T>; + | |________________________________________^ ...so that the type `::T` will meet its required lifetime bounds + +error[E0309]: the associated type `::T` may not live long enough + --> $DIR/issue-86787.rs:23:5 + | +LL | / type TRef<'a> +LL | | +LL | | +LL | | where +LL | | ::T: 'a, +LL | | ::T: 'a + | | - help: consider adding a where clause: `, ::T: 'a` +LL | | = Either<&'a Left::T, &'a Right::T>; + | |________________________________________^ ...so that the type `::T` will meet its required lifetime bounds + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0309`.