From 4fd5a13aacd70a67543b434c33138cbccc81b963 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Thu, 19 Jan 2023 22:50:23 +0100 Subject: [PATCH] WIP: Remove `ResumeTy` from async lowering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of using the stdlib supported `ResumeTy`, which is being converting to a `&mut Context<'_>` during the Generator MIR pass, this will use `&mut Context<'_>` directly in HIR lowering. It pretty much reverts #105977 and re-applies an updated version of #105250. This still fails the testcase added in #106264 however, for reasons I don’t understand. --- compiler/rustc_ast_lowering/src/expr.rs | 62 +++++----- compiler/rustc_hir/src/lang_items.rs | 5 - compiler/rustc_middle/src/ty/sty.rs | 9 -- compiler/rustc_mir_transform/src/coroutine.rs | 108 +----------------- compiler/rustc_span/src/symbol.rs | 2 - compiler/rustc_ty_utils/src/abi.rs | 32 +----- library/core/src/future/mod.rs | 4 +- .../issue-69446-fnmut-capture.stderr | 3 + tests/ui/async-await/unreachable-lint.rs | 4 +- tests/ui/async-await/unreachable-lint.stderr | 17 +++ tests/ui/lang-items/required-lang-item.rs | 2 +- tests/ui/lang-items/required-lang-item.stderr | 6 +- .../closure-in-projection-issue-97405.rs | 4 +- .../closure-in-projection-issue-97405.stderr | 22 ++-- 14 files changed, 77 insertions(+), 203 deletions(-) create mode 100644 tests/ui/async-await/unreachable-lint.stderr diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index aafa99b3aa6fb..6679d079b6def 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -621,17 +621,28 @@ impl<'hir> LoweringContext<'_, 'hir> { // whereas a generator does not. let (inputs, params, task_context): (&[_], &[_], _) = match desugaring_kind { hir::CoroutineDesugaring::Async | hir::CoroutineDesugaring::AsyncGen => { - // Resume argument type: `ResumeTy` - let unstable_span = self.mark_span_with_reason( - DesugaringKind::Async, - self.lower_span(span), - Some(self.allow_gen_future.clone()), - ); - let resume_ty = self.make_lang_item_qpath(hir::LangItem::ResumeTy, unstable_span); + // Resume argument type: `&mut Context<'_>`. + let context_lifetime_ident = Ident::with_dummy_span(kw::UnderscoreLifetime); + let context_lifetime = self.arena.alloc(hir::Lifetime { + hir_id: self.next_id(), + ident: context_lifetime_ident, + res: hir::LifetimeName::Infer, + }); + let context_path = + hir::QPath::LangItem(hir::LangItem::Context, self.lower_span(span)); + let context_ty = hir::MutTy { + ty: self.arena.alloc(hir::Ty { + hir_id: self.next_id(), + kind: hir::TyKind::Path(context_path), + span: self.lower_span(span), + }), + mutbl: hir::Mutability::Mut, + }; + let input_ty = hir::Ty { hir_id: self.next_id(), - kind: hir::TyKind::Path(resume_ty), - span: unstable_span, + kind: hir::TyKind::Ref(context_lifetime, context_ty), + span: self.lower_span(span), }; let inputs = arena_vec![self; input_ty]; @@ -731,7 +742,7 @@ impl<'hir> LoweringContext<'_, 'hir> { /// mut __awaitee => loop { /// match unsafe { ::std::future::Future::poll( /// <::std::pin::Pin>::new_unchecked(&mut __awaitee), - /// ::std::future::get_context(task_context), + /// task_context, /// ) } { /// ::std::task::Poll::Ready(result) => break result, /// ::std::task::Poll::Pending => {} @@ -772,29 +783,21 @@ impl<'hir> LoweringContext<'_, 'hir> { FutureKind::AsyncIterator => Some(self.allow_for_await.clone()), }; let span = self.mark_span_with_reason(DesugaringKind::Await, await_kw_span, features); - let gen_future_span = self.mark_span_with_reason( - DesugaringKind::Await, - full_span, - Some(self.allow_gen_future.clone()), - ); let expr_hir_id = expr.hir_id; // Note that the name of this binding must not be changed to something else because // debuggers and debugger extensions expect it to be called `__awaitee`. They use // this name to identify what is being awaited by a suspended async functions. let awaitee_ident = Ident::with_dummy_span(sym::__awaitee); - let (awaitee_pat, awaitee_pat_hid) = self.pat_ident_binding_mode( - gen_future_span, - awaitee_ident, - hir::BindingAnnotation::MUT, - ); + let (awaitee_pat, awaitee_pat_hid) = + self.pat_ident_binding_mode(full_span, awaitee_ident, hir::BindingAnnotation::MUT); let task_context_ident = Ident::with_dummy_span(sym::_task_context); // unsafe { // ::std::future::Future::poll( // ::std::pin::Pin::new_unchecked(&mut __awaitee), - // ::std::future::get_context(task_context), + // task_context, // ) // } let poll_expr = { @@ -812,21 +815,16 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::LangItem::PinNewUnchecked, arena_vec![self; ref_mut_awaitee], ); - let get_context = self.expr_call_lang_item_fn_mut( - gen_future_span, - hir::LangItem::GetContext, - arena_vec![self; task_context], - ); let call = match await_kind { FutureKind::Future => self.expr_call_lang_item_fn( span, hir::LangItem::FuturePoll, - arena_vec![self; new_unchecked, get_context], + arena_vec![self; new_unchecked, task_context], ), FutureKind::AsyncIterator => self.expr_call_lang_item_fn( span, hir::LangItem::AsyncIteratorPollNext, - arena_vec![self; new_unchecked, get_context], + arena_vec![self; new_unchecked, task_context], ), }; self.arena.alloc(self.expr_unsafe(call)) @@ -837,14 +835,14 @@ impl<'hir> LoweringContext<'_, 'hir> { let loop_hir_id = self.lower_node_id(loop_node_id); let ready_arm = { let x_ident = Ident::with_dummy_span(sym::result); - let (x_pat, x_pat_hid) = self.pat_ident(gen_future_span, x_ident); - let x_expr = self.expr_ident(gen_future_span, x_ident, x_pat_hid); - let ready_field = self.single_pat_field(gen_future_span, x_pat); + let (x_pat, x_pat_hid) = self.pat_ident(full_span, x_ident); + let x_expr = self.expr_ident(full_span, x_ident, x_pat_hid); + let ready_field = self.single_pat_field(full_span, x_pat); let ready_pat = self.pat_lang_item_variant(span, hir::LangItem::PollReady, ready_field); let break_x = self.with_loop_scope(loop_node_id, move |this| { let expr_break = hir::ExprKind::Break(this.lower_loop_destination(None), Some(x_expr)); - this.arena.alloc(this.expr(gen_future_span, expr_break)) + this.arena.alloc(this.expr(full_span, expr_break)) }); self.arm(ready_pat, break_x) }; diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 9c9e72d6e6512..ac746e246dadc 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -305,11 +305,6 @@ language_item_table! { AsyncGenPending, sym::AsyncGenPending, async_gen_pending, Target::AssocConst, GenericRequirement::Exact(1); AsyncGenFinished, sym::AsyncGenFinished, async_gen_finished, Target::AssocConst, GenericRequirement::Exact(1); - // FIXME(swatinem): the following lang items are used for async lowering and - // should become obsolete eventually. - ResumeTy, sym::ResumeTy, resume_ty, Target::Struct, GenericRequirement::None; - GetContext, sym::get_context, get_context_fn, Target::Fn, GenericRequirement::None; - Context, sym::Context, context, Target::Struct, GenericRequirement::None; FuturePoll, sym::poll, future_poll_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index a581712526752..349c019ec2240 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1800,15 +1800,6 @@ impl<'tcx> Ty<'tcx> { let def_id = tcx.require_lang_item(LangItem::MaybeUninit, None); Ty::new_generic_adt(tcx, def_id, ty) } - - /// Creates a `&mut Context<'_>` [`Ty`] with erased lifetimes. - pub fn new_task_context(tcx: TyCtxt<'tcx>) -> Ty<'tcx> { - let context_did = tcx.require_lang_item(LangItem::Context, None); - let context_adt_ref = tcx.adt_def(context_did); - let context_args = tcx.mk_args(&[tcx.lifetimes.re_erased.into()]); - let context_ty = Ty::new_adt(tcx, context_adt_ref, context_args); - Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, context_ty) - } } /// Type utilities diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index a0851aa557b86..f648acabae893 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -619,104 +619,6 @@ fn replace_local<'tcx>( new_local } -/// Transforms the `body` of the coroutine applying the following transforms: -/// -/// - Eliminates all the `get_context` calls that async lowering created. -/// - Replace all `Local` `ResumeTy` types with `&mut Context<'_>` (`context_mut_ref`). -/// -/// The `Local`s that have their types replaced are: -/// - The `resume` argument itself. -/// - The argument to `get_context`. -/// - The yielded value of a `yield`. -/// -/// The `ResumeTy` hides a `&mut Context<'_>` behind an unsafe raw pointer, and the -/// `get_context` function is being used to convert that back to a `&mut Context<'_>`. -/// -/// Ideally the async lowering would not use the `ResumeTy`/`get_context` indirection, -/// but rather directly use `&mut Context<'_>`, however that would currently -/// lead to higher-kinded lifetime errors. -/// See . -/// -/// The async lowering step and the type / lifetime inference / checking are -/// still using the `ResumeTy` indirection for the time being, and that indirection -/// is removed here. After this transform, the coroutine body only knows about `&mut Context<'_>`. -fn transform_async_context<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - let context_mut_ref = Ty::new_task_context(tcx); - - // replace the type of the `resume` argument - replace_resume_ty_local(tcx, body, Local::new(2), context_mut_ref); - - let get_context_def_id = tcx.require_lang_item(LangItem::GetContext, None); - - for bb in START_BLOCK..body.basic_blocks.next_index() { - let bb_data = &body[bb]; - if bb_data.is_cleanup { - continue; - } - - match &bb_data.terminator().kind { - TerminatorKind::Call { func, .. } => { - let func_ty = func.ty(body, tcx); - if let ty::FnDef(def_id, _) = *func_ty.kind() { - if def_id == get_context_def_id { - let local = eliminate_get_context_call(&mut body[bb]); - replace_resume_ty_local(tcx, body, local, context_mut_ref); - } - } else { - continue; - } - } - TerminatorKind::Yield { resume_arg, .. } => { - replace_resume_ty_local(tcx, body, resume_arg.local, context_mut_ref); - } - _ => {} - } - } -} - -fn eliminate_get_context_call<'tcx>(bb_data: &mut BasicBlockData<'tcx>) -> Local { - let terminator = bb_data.terminator.take().unwrap(); - if let TerminatorKind::Call { mut args, destination, target, .. } = terminator.kind { - let arg = args.pop().unwrap(); - let local = arg.node.place().unwrap().local; - - let arg = Rvalue::Use(arg.node); - let assign = Statement { - source_info: terminator.source_info, - kind: StatementKind::Assign(Box::new((destination, arg))), - }; - bb_data.statements.push(assign); - bb_data.terminator = Some(Terminator { - source_info: terminator.source_info, - kind: TerminatorKind::Goto { target: target.unwrap() }, - }); - local - } else { - bug!(); - } -} - -#[cfg_attr(not(debug_assertions), allow(unused))] -fn replace_resume_ty_local<'tcx>( - tcx: TyCtxt<'tcx>, - body: &mut Body<'tcx>, - local: Local, - context_mut_ref: Ty<'tcx>, -) { - let local_ty = std::mem::replace(&mut body.local_decls[local].ty, context_mut_ref); - // We have to replace the `ResumeTy` that is used for type and borrow checking - // with `&mut Context<'_>` in MIR. - #[cfg(debug_assertions)] - { - if let ty::Adt(resume_ty_adt, _) = local_ty.kind() { - let expected_adt = tcx.adt_def(tcx.require_lang_item(LangItem::ResumeTy, None)); - assert_eq!(*resume_ty_adt, expected_adt); - } else { - panic!("expected `ResumeTy`, found `{:?}`", local_ty); - }; - } -} - /// Transforms the `body` of the coroutine applying the following transform: /// /// - Remove the `resume` argument. @@ -724,7 +626,7 @@ fn replace_resume_ty_local<'tcx>( /// Ideally the async lowering would not add the `resume` argument. /// /// The async lowering step and the type / lifetime inference / checking are -/// still using the `resume` argument for the time being. After this transform, +/// still using the `resume` argument for the time being. After this transform /// the coroutine body doesn't have the `resume` argument. fn transform_gen_context<'tcx>(body: &mut Body<'tcx>) { // This leaves the local representing the `resume` argument in place, @@ -1655,14 +1557,6 @@ impl<'tcx> MirPass<'tcx> for StateTransform { // RETURN_PLACE then is a fresh unused local with type ret_ty. let old_ret_local = replace_local(RETURN_PLACE, new_ret_ty, body, tcx); - // Replace all occurrences of `ResumeTy` with `&mut Context<'_>` within async bodies. - if matches!( - coroutine_kind, - CoroutineKind::Desugared(CoroutineDesugaring::Async | CoroutineDesugaring::AsyncGen, _) - ) { - transform_async_context(tcx, body); - } - // We also replace the resume argument and insert an `Assign`. // This is needed because the resume argument `_2` might be live across a `yield`, in which // case there is no `Assign` to it that the transform can turn into a store to the coroutine diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 29c88783357b7..8c7961a4f242f 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -286,7 +286,6 @@ symbols! { Relaxed, Release, Result, - ResumeTy, Return, Right, Rust, @@ -841,7 +840,6 @@ symbols! { generic_const_exprs, generic_const_items, generic_param_attrs, - get_context, global_allocator, global_asm, globs, diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index 638c9a53d22ab..6c359f5aca6ae 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -221,21 +221,7 @@ fn fn_sig_for_fn_abi<'tcx>( let poll_args = tcx.mk_args(&[sig.return_ty.into()]); let ret_ty = Ty::new_adt(tcx, poll_adt_ref, poll_args); - // We have to replace the `ResumeTy` that is used for type and borrow checking - // with `&mut Context<'_>` which is used in codegen. - #[cfg(debug_assertions)] - { - if let ty::Adt(resume_ty_adt, _) = sig.resume_ty.kind() { - let expected_adt = - tcx.adt_def(tcx.require_lang_item(LangItem::ResumeTy, None)); - assert_eq!(*resume_ty_adt, expected_adt); - } else { - panic!("expected `ResumeTy`, found `{:?}`", sig.resume_ty); - }; - } - let context_mut_ref = Ty::new_task_context(tcx); - - (Some(context_mut_ref), ret_ty) + (Some(sig.resume_ty), ret_ty) } hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _) => { // The signature should be `Iterator::next(_) -> Option` @@ -257,21 +243,7 @@ fn fn_sig_for_fn_abi<'tcx>( // Yield type is already `Poll>` let ret_ty = sig.yield_ty; - // We have to replace the `ResumeTy` that is used for type and borrow checking - // with `&mut Context<'_>` which is used in codegen. - #[cfg(debug_assertions)] - { - if let ty::Adt(resume_ty_adt, _) = sig.resume_ty.kind() { - let expected_adt = - tcx.adt_def(tcx.require_lang_item(LangItem::ResumeTy, None)); - assert_eq!(*resume_ty_adt, expected_adt); - } else { - panic!("expected `ResumeTy`, found `{:?}`", sig.resume_ty); - }; - } - let context_mut_ref = Ty::new_task_context(tcx); - - (Some(context_mut_ref), ret_ty) + (Some(sig.resume_ty), ret_ty) } hir::CoroutineKind::Coroutine(_) => { // The signature should be `Coroutine::resume(_, Resume) -> CoroutineState` diff --git a/library/core/src/future/mod.rs b/library/core/src/future/mod.rs index 0f77a2d83433f..876915eb50b41 100644 --- a/library/core/src/future/mod.rs +++ b/library/core/src/future/mod.rs @@ -44,7 +44,7 @@ pub use poll_fn::{poll_fn, PollFn}; /// non-Send/Sync as well, and we don't want that. /// /// It also simplifies the HIR lowering of `.await`. -#[lang = "ResumeTy"] +#[cfg_attr(bootstrap, lang = "ResumeTy")] #[doc(hidden)] #[unstable(feature = "gen_future", issue = "50547")] #[derive(Debug, Copy, Clone)] @@ -56,7 +56,7 @@ unsafe impl Send for ResumeTy {} #[unstable(feature = "gen_future", issue = "50547")] unsafe impl Sync for ResumeTy {} -#[lang = "get_context"] +#[cfg_attr(bootstrap, lang = "get_context")] #[doc(hidden)] #[unstable(feature = "gen_future", issue = "50547")] #[must_use] diff --git a/tests/ui/async-await/issue-69446-fnmut-capture.stderr b/tests/ui/async-await/issue-69446-fnmut-capture.stderr index 0366c2f44c0e7..27cea6155f743 100644 --- a/tests/ui/async-await/issue-69446-fnmut-capture.stderr +++ b/tests/ui/async-await/issue-69446-fnmut-capture.stderr @@ -14,6 +14,9 @@ LL | | }); | = note: `FnMut` closures only have access to their captured variables while they are executing... = note: ...therefore, they cannot allow references to captured variables to escape + = note: requirement occurs because of a mutable reference to `Context<'_>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance error: aborting due to 1 previous error diff --git a/tests/ui/async-await/unreachable-lint.rs b/tests/ui/async-await/unreachable-lint.rs index e8a58df384ed0..33bde8076c3e8 100644 --- a/tests/ui/async-await/unreachable-lint.rs +++ b/tests/ui/async-await/unreachable-lint.rs @@ -1,13 +1,13 @@ -//@ check-pass //@ edition:2018 #![deny(unreachable_code)] async fn foo() { endless().await; + //~^ ERROR unreachable expression } async fn endless() -> ! { loop {} } -fn main() { } +fn main() {} diff --git a/tests/ui/async-await/unreachable-lint.stderr b/tests/ui/async-await/unreachable-lint.stderr new file mode 100644 index 0000000000000..0d4b3a279b4cf --- /dev/null +++ b/tests/ui/async-await/unreachable-lint.stderr @@ -0,0 +1,17 @@ +error: unreachable expression + --> $DIR/unreachable-lint.rs:5:5 + | +LL | endless().await; + | ^^^^^^^^^^^^^^^ + | | + | unreachable expression + | any code following this expression is unreachable + | +note: the lint level is defined here + --> $DIR/unreachable-lint.rs:2:9 + | +LL | #![deny(unreachable_code)] + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lang-items/required-lang-item.rs b/tests/ui/lang-items/required-lang-item.rs index 495daf08dd224..3190f6076ef74 100644 --- a/tests/ui/lang-items/required-lang-item.rs +++ b/tests/ui/lang-items/required-lang-item.rs @@ -7,4 +7,4 @@ #[lang="copy"] pub trait Copy { } #[lang="sized"] pub trait Sized { } -async fn x() {} //~ ERROR requires `ResumeTy` lang_item +async fn x() {} //~ ERROR requires `future_trait` lang_item diff --git a/tests/ui/lang-items/required-lang-item.stderr b/tests/ui/lang-items/required-lang-item.stderr index 13c07ee652933..f62c96c05e3ab 100644 --- a/tests/ui/lang-items/required-lang-item.stderr +++ b/tests/ui/lang-items/required-lang-item.stderr @@ -1,8 +1,8 @@ -error: requires `ResumeTy` lang_item - --> $DIR/required-lang-item.rs:10:14 +error: requires `future_trait` lang_item + --> $DIR/required-lang-item.rs:10:1 | LL | async fn x() {} - | ^^ + | ^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/regions/closure-in-projection-issue-97405.rs b/tests/ui/regions/closure-in-projection-issue-97405.rs index fd73e480865a5..81a473ce971db 100644 --- a/tests/ui/regions/closure-in-projection-issue-97405.rs +++ b/tests/ui/regions/closure-in-projection-issue-97405.rs @@ -22,11 +22,11 @@ fn good_generic_fn() { // This should fail because `T` ends up in the upvars of the closure. fn bad_generic_fn(t: T) { assert_static(opaque(async move { t; }).next()); - //~^ ERROR the associated type `::Item` may not live long enough + //~^ ERROR the parameter type `T` may not live long enough assert_static(opaque(move || { t; }).next()); //~^ ERROR the associated type `::Item` may not live long enough assert_static(opaque(opaque(async move { t; }).next()).next()); - //~^ ERROR the associated type `::Item` may not live long enough + //~^ ERROR the parameter type `T` may not live long enough } fn main() {} diff --git a/tests/ui/regions/closure-in-projection-issue-97405.stderr b/tests/ui/regions/closure-in-projection-issue-97405.stderr index 7070dfef138ac..29e24bf2d4c05 100644 --- a/tests/ui/regions/closure-in-projection-issue-97405.stderr +++ b/tests/ui/regions/closure-in-projection-issue-97405.stderr @@ -1,13 +1,16 @@ -error[E0310]: the associated type `::Item` may not live long enough +error[E0310]: the parameter type `T` may not live long enough --> $DIR/closure-in-projection-issue-97405.rs:24:5 | LL | assert_static(opaque(async move { t; }).next()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | the associated type `::Item` must be valid for the static lifetime... - | ...so that the type `::Item` will meet its required lifetime bounds + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds | - = help: consider adding an explicit lifetime bound `::Item: 'static`... +help: consider adding an explicit lifetime bound + | +LL | fn bad_generic_fn(t: T) { + | +++++++++ error[E0310]: the associated type `::Item` may not live long enough --> $DIR/closure-in-projection-issue-97405.rs:26:5 @@ -20,16 +23,19 @@ LL | assert_static(opaque(move || { t; }).next()); | = help: consider adding an explicit lifetime bound `::Item: 'static`... -error[E0310]: the associated type `::Item` may not live long enough +error[E0310]: the parameter type `T` may not live long enough --> $DIR/closure-in-projection-issue-97405.rs:28:5 | LL | assert_static(opaque(opaque(async move { t; }).next()).next()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | the associated type `::Item` must be valid for the static lifetime... - | ...so that the type `::Item` will meet its required lifetime bounds + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds | - = help: consider adding an explicit lifetime bound `::Item: 'static`... +help: consider adding an explicit lifetime bound + | +LL | fn bad_generic_fn(t: T) { + | +++++++++ error: aborting due to 3 previous errors