From 89a42d64a9fc91f72f08430c0744d0322970ab47 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 31 Mar 2025 20:46:48 +0000 Subject: [PATCH 01/15] Feed HIR for by-move coroutine body def, since the inliner tries to read its attrs --- .../src/coroutine/by_move_body.rs | 2 ++ tests/crashes/134335.rs | 12 -------- .../by-move-body-inlined-attrs.rs | 28 +++++++++++++++++++ 3 files changed, 30 insertions(+), 12 deletions(-) delete mode 100644 tests/crashes/134335.rs create mode 100644 tests/ui/async-await/async-closures/by-move-body-inlined-attrs.rs diff --git a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs index 89a306c610477..dd0e07f2218eb 100644 --- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs +++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs @@ -219,6 +219,8 @@ pub(crate) fn coroutine_by_move_body_def_id<'tcx>( mir::MirSource::from_instance(InstanceKind::Item(body_def.def_id().to_def_id())); dump_mir(tcx, false, "built", &"after", &by_move_body, |_, _| Ok(())); + // Feed HIR because we try to access this body's attrs in the inliner. + body_def.feed_hir(); // Inherited from the by-ref coroutine. body_def.codegen_fn_attrs(tcx.codegen_fn_attrs(coroutine_def_id).clone()); body_def.coverage_attr_on(tcx.coverage_attr_on(coroutine_def_id)); diff --git a/tests/crashes/134335.rs b/tests/crashes/134335.rs deleted file mode 100644 index bee6686ff3fa8..0000000000000 --- a/tests/crashes/134335.rs +++ /dev/null @@ -1,12 +0,0 @@ -//@ known-bug: #134335 -//@compile-flags: -Zunstable-options --edition=2024 --crate-type=lib -pub async fn async_closure(x: &mut i32) { - let c = async move || { - *x += 1; - }; - call_once(c).await; -} - -fn call_once(f: impl FnOnce() -> T) -> T { - f() -} diff --git a/tests/ui/async-await/async-closures/by-move-body-inlined-attrs.rs b/tests/ui/async-await/async-closures/by-move-body-inlined-attrs.rs new file mode 100644 index 0000000000000..ecfc06d2bad0c --- /dev/null +++ b/tests/ui/async-await/async-closures/by-move-body-inlined-attrs.rs @@ -0,0 +1,28 @@ +//@ check-pass +//@ compile-flags: -Zinline-mir -Zvalidate-mir +//@ edition: 2024 + +// See comment below. + +use std::future::Future; +use std::pin::pin; +use std::task::{Context, Waker}; + +fn call_once(f: impl FnOnce() -> T) -> T { f() } + +fn main() { + let x = async || {}; + // We first inline `call_once<{async closure}>`. + // + // This gives us a future whose type is the "FnOnce" flavor of the async closure's + // child coroutine. The body of this coroutine is synthetic, which we synthesize in + // the by-move body query. + let fut = pin!(call_once(x)); + // We then try to inline that body in this poll call. + // + // The inliner does some inlinability checks; one of these checks involves checking + // the body for the `#[rustc_no_mir_inline]` attribute. Since the synthetic body had + // no HIR synthesized, but it's still a local def id, we end up ICEing in the + // `local_def_id_to_hir_id` call when trying to read its attrs. + fut.poll(&mut Context::from_waker(Waker::noop())); +} From b05f70bc57662adb9583dd27be8dc20afa16dcd8 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 27 Feb 2025 09:51:23 +0000 Subject: [PATCH 02/15] Add `iter` macro (movable generator) --- library/core/src/iter/mod.rs | 2 ++ library/core/src/iter/sources.rs | 3 +++ library/core/src/iter/sources/generator.rs | 25 +++++++++++++++++++ tests/ui/iterators/generator.rs | 28 ++++++++++++++++++++++ 4 files changed, 58 insertions(+) create mode 100644 library/core/src/iter/sources/generator.rs create mode 100644 tests/ui/iterators/generator.rs diff --git a/library/core/src/iter/mod.rs b/library/core/src/iter/mod.rs index d62a445d704ae..b85841295dab1 100644 --- a/library/core/src/iter/mod.rs +++ b/library/core/src/iter/mod.rs @@ -420,6 +420,8 @@ pub use self::adapters::{Intersperse, IntersperseWith}; issue = "42168" )] pub use self::range::Step; +#[unstable(feature = "iter_macro", issue = "none", reason = "generators are unstable")] +pub use self::sources::iter; #[stable(feature = "iter_empty", since = "1.2.0")] pub use self::sources::{Empty, empty}; #[unstable( diff --git a/library/core/src/iter/sources.rs b/library/core/src/iter/sources.rs index 1eb4367b18372..fd9330201ff4e 100644 --- a/library/core/src/iter/sources.rs +++ b/library/core/src/iter/sources.rs @@ -1,6 +1,7 @@ mod empty; mod from_coroutine; mod from_fn; +mod generator; mod once; mod once_with; mod repeat; @@ -18,6 +19,8 @@ pub use self::empty::{Empty, empty}; pub use self::from_coroutine::{FromCoroutine, from_coroutine}; #[stable(feature = "iter_from_fn", since = "1.34.0")] pub use self::from_fn::{FromFn, from_fn}; +#[unstable(feature = "iter_macro", issue = "none", reason = "generators are unstable")] +pub use self::generator::iter; #[stable(feature = "iter_once", since = "1.2.0")] pub use self::once::{Once, once}; #[stable(feature = "iter_once_with", since = "1.43.0")] diff --git a/library/core/src/iter/sources/generator.rs b/library/core/src/iter/sources/generator.rs new file mode 100644 index 0000000000000..d3711451e72e0 --- /dev/null +++ b/library/core/src/iter/sources/generator.rs @@ -0,0 +1,25 @@ +/// Creates a new closure that returns an iterator where each iteration steps the given +/// generator to the next `yield` statement. +/// +/// Similar to [`iter::from_fn`], but allows arbitrary control flow. +/// +/// [`iter::from_fn`]: crate::iter::from_fn +/// +/// # Examples +/// +/// ``` +/// #![feature(iter_macro, coroutines)] +/// +/// let it = std::iter::iter!{ +/// yield 1; +/// yield 2; +/// yield 3; +/// }; +/// let v: Vec<_> = it.collect(); +/// assert_eq!(v, [1, 2, 3]); +/// ``` +#[unstable(feature = "iter_macro", issue = "none", reason = "generators are unstable")] +#[allow_internal_unstable(coroutines, iter_from_coroutine)] +pub macro iter($($t:tt)*) { + || $crate::iter::from_coroutine(#[coroutine] || { $($t)* }) +} diff --git a/tests/ui/iterators/generator.rs b/tests/ui/iterators/generator.rs new file mode 100644 index 0000000000000..d3827388c2ae5 --- /dev/null +++ b/tests/ui/iterators/generator.rs @@ -0,0 +1,28 @@ +//@ run-pass + +#![feature(iter_macro)] +// FIXME(iter_macro): make `yield` within it legal +#![feature(coroutines)] + +use std::iter::iter; + +fn main() { + let i = iter! { + yield 0; + for x in 5..10 { + yield x * 2; + } + }; + let mut i = i(); + assert_eq!(i.next(), Some(0)); + assert_eq!(i.next(), Some(10)); + assert_eq!(i.next(), Some(12)); + assert_eq!(i.next(), Some(14)); + assert_eq!(i.next(), Some(16)); + assert_eq!(i.next(), Some(18)); + assert_eq!(i.next(), None); + // FIXME(iter_macro): desugar to `gen` instead of coroutines, + // as the latter panic after resuming iteration. + //assert_eq!(i.next(), None); + //assert_eq!(i.next(), None); +} From 38ce680ee78f5b01d219f847299e99e8ea3884f8 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 27 Feb 2025 10:41:04 +0000 Subject: [PATCH 03/15] Make `iter` a builtin macro --- compiler/rustc_ast_lowering/src/expr.rs | 15 ++-- .../src/type_check/input_output.rs | 2 +- compiler/rustc_builtin_macros/src/iter.rs | 78 +++++++++++++++++++ compiler/rustc_builtin_macros/src/lib.rs | 2 + compiler/rustc_hir_typeck/src/closure.rs | 13 +++- compiler/rustc_parse/src/parser/stmt.rs | 2 +- library/core/src/iter/sources/generator.rs | 3 +- tests/ui/iterators/generator.rs | 6 +- tests/ui/iterators/generator_args.rs | 26 +++++++ tests/ui/iterators/generator_capture.rs | 29 +++++++ tests/ui/iterators/generator_capture_.rs | 28 +++++++ tests/ui/iterators/generator_capture_fail.rs | 27 +++++++ .../iterators/generator_capture_fail.stderr | 18 +++++ .../iterators/generator_returned_from_fn.rs | 65 ++++++++++++++++ .../generator_returned_from_fn.stderr | 70 +++++++++++++++++ 15 files changed, 366 insertions(+), 18 deletions(-) create mode 100644 compiler/rustc_builtin_macros/src/iter.rs create mode 100644 tests/ui/iterators/generator_args.rs create mode 100644 tests/ui/iterators/generator_capture.rs create mode 100644 tests/ui/iterators/generator_capture_.rs create mode 100644 tests/ui/iterators/generator_capture_fail.rs create mode 100644 tests/ui/iterators/generator_capture_fail.stderr create mode 100644 tests/ui/iterators/generator_returned_from_fn.rs create mode 100644 tests/ui/iterators/generator_returned_from_fn.stderr diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 52291fdfb3029..adee3b705598e 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1,4 +1,3 @@ -use std::assert_matches::assert_matches; use std::ops::ControlFlow; use std::sync::Arc; @@ -1190,11 +1189,13 @@ impl<'hir> LoweringContext<'_, 'hir> { let closure_def_id = self.local_def_id(closure_id); let (binder_clause, generic_params) = self.lower_closure_binder(binder); - assert_matches!( - coroutine_kind, - CoroutineKind::Async { .. }, - "only async closures are supported currently" - ); + let coroutine_desugaring = match coroutine_kind { + CoroutineKind::Async { .. } => hir::CoroutineDesugaring::Async, + CoroutineKind::Gen { .. } => hir::CoroutineDesugaring::Gen, + CoroutineKind::AsyncGen { span, .. } => { + span_bug!(span, "only async closures and `iter!` closures are supported currently") + } + }; let body = self.with_new_scopes(fn_decl_span, |this| { let inner_decl = @@ -1238,7 +1239,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // Lower this as a `CoroutineClosure`. That will ensure that HIR typeck // knows that a `FnDecl` output type like `-> &str` actually means // "coroutine that returns &str", rather than directly returning a `&str`. - kind: hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async), + kind: hir::ClosureKind::CoroutineClosure(coroutine_desugaring), constness: hir::Constness::NotConst, }); hir::ExprKind::Closure(c) diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs index c6b29fe36fd9c..0c46e0c0c2204 100644 --- a/compiler/rustc_borrowck/src/type_check/input_output.rs +++ b/compiler/rustc_borrowck/src/type_check/input_output.rs @@ -52,7 +52,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { assert_matches!( self.tcx().coroutine_kind(self.tcx().coroutine_for_closure(mir_def_id)), Some(hir::CoroutineKind::Desugared( - hir::CoroutineDesugaring::Async, + hir::CoroutineDesugaring::Async | hir::CoroutineDesugaring::Gen, hir::CoroutineSource::Closure )), "this needs to be modified if we're lowering non-async closures" diff --git a/compiler/rustc_builtin_macros/src/iter.rs b/compiler/rustc_builtin_macros/src/iter.rs new file mode 100644 index 0000000000000..1d9f078b14bf4 --- /dev/null +++ b/compiler/rustc_builtin_macros/src/iter.rs @@ -0,0 +1,78 @@ +use rustc_ast::ptr::P; +use rustc_ast::tokenstream::TokenStream; +use rustc_ast::{ + CaptureBy, ClosureBinder, Const, CoroutineKind, DUMMY_NODE_ID, Expr, ExprKind, ast, token, +}; +use rustc_errors::PResult; +use rustc_expand::base::{self, DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult}; +use rustc_span::Span; + +pub(crate) fn expand<'cx>( + cx: &'cx mut ExtCtxt<'_>, + sp: Span, + tts: TokenStream, +) -> MacroExpanderResult<'cx> { + let closure = match parse_closure(cx, sp, tts) { + Ok(parsed) => parsed, + Err(err) => { + return ExpandResult::Ready(DummyResult::any(sp, err.emit())); + } + }; + + ExpandResult::Ready(base::MacEager::expr(closure)) +} + +fn parse_closure<'a>( + cx: &mut ExtCtxt<'a>, + span: Span, + stream: TokenStream, +) -> PResult<'a, P> { + let mut parser = cx.new_parser_from_tts(stream); + let mut closure_parser = parser.clone(); + + let coroutine_kind = Some(CoroutineKind::Gen { + span, + closure_id: DUMMY_NODE_ID, + return_impl_trait_id: DUMMY_NODE_ID, + }); + + match closure_parser.parse_expr() { + Ok(mut closure) => { + if let ast::ExprKind::Closure(c) = &mut closure.kind { + if let Some(kind) = c.coroutine_kind { + cx.dcx().span_err(kind.span(), "only plain closures allowed in `iter!`"); + } + c.coroutine_kind = coroutine_kind; + if closure_parser.token != token::Eof { + closure_parser.unexpected()?; + } + return Ok(closure); + } + } + Err(diag) => diag.cancel(), + } + + let lo = parser.token.span.shrink_to_lo(); + let block = parser.parse_block_tail( + lo, + ast::BlockCheckMode::Default, + rustc_parse::parser::AttemptLocalParseRecovery::No, + )?; + let fn_decl = cx.fn_decl(Default::default(), ast::FnRetTy::Default(span)); + let closure = ast::Closure { + binder: ClosureBinder::NotPresent, + capture_clause: CaptureBy::Ref, + constness: Const::No, + coroutine_kind, + movability: ast::Movability::Movable, + fn_decl, + body: cx.expr_block(block), + fn_decl_span: span, + fn_arg_span: span, + }; + if parser.token != token::Eof { + parser.unexpected()?; + } + let span = lo.to(parser.token.span); + Ok(cx.expr(span, ExprKind::Closure(Box::new(closure)))) +} diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 606e85577f7e7..53861738a080b 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -50,6 +50,7 @@ mod errors; mod format; mod format_foreign; mod global_allocator; +mod iter; mod log_syntax; mod pattern_type; mod source_util; @@ -98,6 +99,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { include: source_util::expand_include, include_bytes: source_util::expand_include_bytes, include_str: source_util::expand_include_str, + iter: iter::expand, line: source_util::expand_line, log_syntax: log_syntax::expand_log_syntax, module_path: source_util::expand_mod, diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index a4776338f6c39..cb433fb6bd228 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -196,14 +196,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) } hir::ClosureKind::CoroutineClosure(kind) => { - // async closures always return the type ascribed after the `->` (if present), - // and yield `()`. let (bound_return_ty, bound_yield_ty) = match kind { + hir::CoroutineDesugaring::Gen => { + // `iter!` closures always return unit and yield the `Iterator::Item` type + // that we have to infer. + (tcx.types.unit, self.infcx.next_ty_var(expr_span)) + } hir::CoroutineDesugaring::Async => { + // async closures always return the type ascribed after the `->` (if present), + // and yield `()`. (bound_sig.skip_binder().output(), tcx.types.unit) } - hir::CoroutineDesugaring::Gen | hir::CoroutineDesugaring::AsyncGen => { - todo!("`gen` and `async gen` closures not supported yet") + hir::CoroutineDesugaring::AsyncGen => { + todo!("`async gen` closures not supported yet") } }; // Compute all of the variables that will be used to populate the coroutine. diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 97cd4d2117f87..cb4c5be7dc581 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -699,7 +699,7 @@ impl<'a> Parser<'a> { /// Parses the rest of a block expression or function body. /// Precondition: already parsed the '{'. - pub(crate) fn parse_block_tail( + pub fn parse_block_tail( &mut self, lo: Span, s: BlockCheckMode, diff --git a/library/core/src/iter/sources/generator.rs b/library/core/src/iter/sources/generator.rs index d3711451e72e0..11f69db960c2a 100644 --- a/library/core/src/iter/sources/generator.rs +++ b/library/core/src/iter/sources/generator.rs @@ -20,6 +20,7 @@ /// ``` #[unstable(feature = "iter_macro", issue = "none", reason = "generators are unstable")] #[allow_internal_unstable(coroutines, iter_from_coroutine)] +#[cfg_attr(not(bootstrap), rustc_builtin_macro)] pub macro iter($($t:tt)*) { - || $crate::iter::from_coroutine(#[coroutine] || { $($t)* }) + /* compiler-builtin */ } diff --git a/tests/ui/iterators/generator.rs b/tests/ui/iterators/generator.rs index d3827388c2ae5..ee511d598342a 100644 --- a/tests/ui/iterators/generator.rs +++ b/tests/ui/iterators/generator.rs @@ -21,8 +21,6 @@ fn main() { assert_eq!(i.next(), Some(16)); assert_eq!(i.next(), Some(18)); assert_eq!(i.next(), None); - // FIXME(iter_macro): desugar to `gen` instead of coroutines, - // as the latter panic after resuming iteration. - //assert_eq!(i.next(), None); - //assert_eq!(i.next(), None); + assert_eq!(i.next(), None); + assert_eq!(i.next(), None); } diff --git a/tests/ui/iterators/generator_args.rs b/tests/ui/iterators/generator_args.rs new file mode 100644 index 0000000000000..2e64bf7bbad01 --- /dev/null +++ b/tests/ui/iterators/generator_args.rs @@ -0,0 +1,26 @@ +//@ run-pass + +#![feature(iter_macro)] +// FIXME(iter_macro): make `yield` within it legal +#![feature(coroutines)] + +use std::iter::iter; + +fn main() { + let i = iter! {|foo| { + yield foo; + for x in 5..10 { + yield x * 2; + } + }}; + let mut i = i(3); + assert_eq!(i.next(), Some(3)); + assert_eq!(i.next(), Some(10)); + assert_eq!(i.next(), Some(12)); + assert_eq!(i.next(), Some(14)); + assert_eq!(i.next(), Some(16)); + assert_eq!(i.next(), Some(18)); + assert_eq!(i.next(), None); + assert_eq!(i.next(), None); + assert_eq!(i.next(), None); +} diff --git a/tests/ui/iterators/generator_capture.rs b/tests/ui/iterators/generator_capture.rs new file mode 100644 index 0000000000000..9027289116622 --- /dev/null +++ b/tests/ui/iterators/generator_capture.rs @@ -0,0 +1,29 @@ +//@ run-pass + +#![feature(iter_macro)] +// FIXME(iter_macro): make `yield` within it legal +#![feature(coroutines)] + +use std::iter::iter; + +fn main() { + let i = { + let s = String::new(); + iter! { move || { + yield s.len(); + for x in 5..10 { + yield x * 2; + } + }} + }; + let mut i = i(); + assert_eq!(i.next(), Some(0)); + assert_eq!(i.next(), Some(10)); + assert_eq!(i.next(), Some(12)); + assert_eq!(i.next(), Some(14)); + assert_eq!(i.next(), Some(16)); + assert_eq!(i.next(), Some(18)); + assert_eq!(i.next(), None); + assert_eq!(i.next(), None); + assert_eq!(i.next(), None); +} diff --git a/tests/ui/iterators/generator_capture_.rs b/tests/ui/iterators/generator_capture_.rs new file mode 100644 index 0000000000000..7d4edb7af1b43 --- /dev/null +++ b/tests/ui/iterators/generator_capture_.rs @@ -0,0 +1,28 @@ +//@ run-pass + +#![feature(iter_macro)] +// FIXME(iter_macro): make `yield` within it legal +#![feature(coroutines)] + +use std::iter::iter; + +fn main() { + let f = { + let s = "foo".to_string(); + iter! { move || { + for c in s.chars() { + yield c; + } + }} + }; + let mut i = f(); + assert_eq!(i.next(), Some('f')); + assert_eq!(i.next(), Some('o')); + assert_eq!(i.next(), Some('o')); + assert_eq!(i.next(), None); + let mut i = f(); + assert_eq!(i.next(), Some('f')); + assert_eq!(i.next(), Some('o')); + assert_eq!(i.next(), Some('o')); + assert_eq!(i.next(), None); +} diff --git a/tests/ui/iterators/generator_capture_fail.rs b/tests/ui/iterators/generator_capture_fail.rs new file mode 100644 index 0000000000000..b25192fa7aa6d --- /dev/null +++ b/tests/ui/iterators/generator_capture_fail.rs @@ -0,0 +1,27 @@ +#![feature(iter_macro)] +// FIXME(iter_macro): make `yield` within it legal +#![feature(coroutines)] + +use std::iter::iter; + +fn main() { + let i = { + let s = String::new(); + iter! { + yield s.len(); //~ ERROR `s` does not live long enough + for x in 5..10 { + yield x * 2; + } + } + }; + let mut i = i(); + assert_eq!(i.next(), Some(0)); + assert_eq!(i.next(), Some(10)); + assert_eq!(i.next(), Some(12)); + assert_eq!(i.next(), Some(14)); + assert_eq!(i.next(), Some(16)); + assert_eq!(i.next(), Some(18)); + assert_eq!(i.next(), None); + assert_eq!(i.next(), None); + assert_eq!(i.next(), None); +} diff --git a/tests/ui/iterators/generator_capture_fail.stderr b/tests/ui/iterators/generator_capture_fail.stderr new file mode 100644 index 0000000000000..c78164add0896 --- /dev/null +++ b/tests/ui/iterators/generator_capture_fail.stderr @@ -0,0 +1,18 @@ +error[E0597]: `s` does not live long enough + --> $DIR/generator_capture_fail.rs:11:13 + | +LL | let i = { + | - borrow later stored here +... +LL | / yield s.len(); +LL | | for x in 5..10 { +LL | | yield x * 2; +LL | | } + | |_____________^ borrowed value does not live long enough +LL | } +LL | }; + | - `s` dropped here while still borrowed + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/iterators/generator_returned_from_fn.rs b/tests/ui/iterators/generator_returned_from_fn.rs new file mode 100644 index 0000000000000..7446cea113ef0 --- /dev/null +++ b/tests/ui/iterators/generator_returned_from_fn.rs @@ -0,0 +1,65 @@ +#![feature(iter_macro, impl_trait_in_fn_trait_return)] +// FIXME(iter_macro): make `yield` within it legal +#![feature(coroutines)] + +use std::iter::iter; + +fn plain() -> impl Fn() -> impl Iterator { + iter! { + yield 0; + for x in 5..10 { + yield x * 2; + } + } +} + +fn arg() -> impl Fn(u32) -> impl Iterator { + iter! { |arg| { + yield arg; + for x in 5..10 { + yield x * 2; + } + } } +} + +fn capture<'a>(a: &'a u32) -> impl Fn() -> (impl Iterator + 'a) { + iter! { || { //~ ERROR cannot return reference to function parameter `a` + yield *a; + for x in 5..10 { + yield x * 2; + } + } } +} + +fn capture_move(a: &u32) -> impl Fn() -> impl Iterator { + iter! { move || { //~ ERROR does not implement `Fn` because it captures + yield *a; + for x in 5..10 { + yield x * 2; + } + } } +} + +fn capture_move_once(a: &u32) -> impl FnOnce() -> impl Iterator { + iter! { move || { + //~^ ERROR captures lifetime + //~| ERROR: captures lifetime + yield *a; + for x in 5..10 { + yield x * 2; + } + } } +} + +fn capture_move_once_lifetimes<'a>( + a: &'a u32, +) -> impl FnOnce() -> (impl Iterator + 'a) { + iter! { move || { + yield *a; + for x in 5..10 { + yield x * 2; + } + } } +} + +fn main() {} diff --git a/tests/ui/iterators/generator_returned_from_fn.stderr b/tests/ui/iterators/generator_returned_from_fn.stderr new file mode 100644 index 0000000000000..47fb3cc8e3960 --- /dev/null +++ b/tests/ui/iterators/generator_returned_from_fn.stderr @@ -0,0 +1,70 @@ +error[E0515]: cannot return reference to function parameter `a` + --> $DIR/generator_returned_from_fn.rs:26:13 + | +LL | iter! { || { + | _____________^ +LL | | yield *a; +LL | | for x in 5..10 { +LL | | yield x * 2; +LL | | } +LL | | } } + | |_____^ returns a reference to data owned by the current function + +error: async closure does not implement `Fn` because it captures state from its environment + --> $DIR/generator_returned_from_fn.rs:35:13 + | +LL | iter! { move || { + | _____________-^^^^^^ +LL | | yield *a; +LL | | for x in 5..10 { +LL | | yield x * 2; +LL | | } +LL | | } } + | |_____- return type was inferred to be `{gen closure@$DIR/generator_returned_from_fn.rs:35:13: 35:20}` here + +error[E0700]: hidden type for `impl FnOnce() -> impl Iterator` captures lifetime that does not appear in bounds + --> $DIR/generator_returned_from_fn.rs:44:13 + | +LL | fn capture_move_once(a: &u32) -> impl FnOnce() -> impl Iterator { + | ---- ------------------------------------------ opaque type defined here + | | + | hidden type `{gen closure@$DIR/generator_returned_from_fn.rs:44:13: 44:20}` captures the anonymous lifetime defined here +LL | iter! { move || { + | _____________^ +LL | | +LL | | +LL | | yield *a; +... | +LL | | } } + | |_____^ + | +help: add a `use<...>` bound to explicitly capture `'_` + | +LL | fn capture_move_once(a: &u32) -> impl FnOnce() -> impl Iterator + use<'_> { + | +++++++++ + +error[E0700]: hidden type for `impl Iterator` captures lifetime that does not appear in bounds + --> $DIR/generator_returned_from_fn.rs:44:13 + | +LL | fn capture_move_once(a: &u32) -> impl FnOnce() -> impl Iterator { + | ---- ------------------------- opaque type defined here + | | + | hidden type `{gen closure body@$DIR/generator_returned_from_fn.rs:44:21: 51:6}` captures the anonymous lifetime defined here +LL | iter! { move || { + | _____________^ +LL | | +LL | | +LL | | yield *a; +... | +LL | | } } + | |_____^ + | +help: add a `use<...>` bound to explicitly capture `'_` + | +LL | fn capture_move_once(a: &u32) -> impl FnOnce() -> impl Iterator + use<'_> { + | +++++++++ + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0515, E0700. +For more information about an error, try `rustc --explain E0515`. From e84ccac2051aa369773f5cb905bd5201b7143176 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 27 Feb 2025 12:21:01 +0000 Subject: [PATCH 04/15] Make diagnostics know about more coro closures than async closures --- compiler/rustc_hir/src/hir.rs | 2 -- compiler/rustc_trait_selection/messages.ftl | 4 ++-- .../error_reporting/traits/fulfillment_errors.rs | 15 +++++++++++---- compiler/rustc_trait_selection/src/errors.rs | 5 +++-- .../iterators/generator_returned_from_fn.stderr | 2 +- 5 files changed, 17 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index e3e96894ed1f7..1e5665a235d24 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1943,9 +1943,7 @@ impl CoroutineKind { CoroutineKind::Coroutine(mov) => mov, } } -} -impl CoroutineKind { pub fn is_fn_like(self) -> bool { matches!(self, CoroutineKind::Desugared(_, CoroutineSource::Fn)) } diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl index 4db9d9915b139..273ecdcd03478 100644 --- a/compiler/rustc_trait_selection/messages.ftl +++ b/compiler/rustc_trait_selection/messages.ftl @@ -72,8 +72,6 @@ trait_selection_adjust_signature_remove_borrow = consider adjusting the signatur trait_selection_ascribe_user_type_prove_predicate = ...so that the where clause holds -trait_selection_async_closure_not_fn = async closure does not implement `{$kind}` because it captures state from its environment - trait_selection_await_both_futures = consider `await`ing on both `Future`s trait_selection_await_future = consider `await`ing on the `Future` trait_selection_await_note = calling an async function returns a future @@ -123,6 +121,8 @@ trait_selection_closure_kind_requirement = the requirement to implement `{$trait trait_selection_compare_impl_item_obligation = ...so that the definition in impl matches the definition from the trait trait_selection_consider_specifying_length = consider specifying the actual array length +trait_selection_coro_closure_not_fn = {$coro_kind}closure does not implement `{$kind}` because it captures state from its environment + trait_selection_data_flows = ...but data{$label_var1_exists -> [true] {" "}from `{$label_var1}` *[false] {""} diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 07a67cde3be1c..9c3b7ffe1ed12 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -39,9 +39,7 @@ use super::{ use crate::error_reporting::TypeErrCtxt; use crate::error_reporting::infer::TyCategory; use crate::error_reporting::traits::report_dyn_incompatibility; -use crate::errors::{ - AsyncClosureNotFn, ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch, -}; +use crate::errors::{ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch, CoroClosureNotFn}; use crate::infer::{self, InferCtxt, InferCtxtExt as _}; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use crate::traits::{ @@ -906,9 +904,18 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { && let ty::FnPtr(sig_tys, _) = by_ref_captures.kind() && !sig_tys.skip_binder().output().is_unit() { - let mut err = self.dcx().create_err(AsyncClosureNotFn { + let coro_kind = match self + .tcx + .coroutine_kind(self.tcx.coroutine_for_closure(closure_def_id)) + .unwrap() + { + rustc_hir::CoroutineKind::Desugared(desugaring, _) => format!("{desugaring:#}"), + coro => format!("{coro:#}"), + }; + let mut err = self.dcx().create_err(CoroClosureNotFn { span: self.tcx.def_span(closure_def_id), kind: expected_kind.as_str(), + coro_kind, }); self.note_obligation_cause(&mut err, &obligation); return Some(err.emit()); diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index b30390a9330eb..4142c03d0db17 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -170,11 +170,12 @@ pub struct ClosureFnMutLabel { } #[derive(Diagnostic)] -#[diag(trait_selection_async_closure_not_fn)] -pub(crate) struct AsyncClosureNotFn { +#[diag(trait_selection_coro_closure_not_fn)] +pub(crate) struct CoroClosureNotFn { #[primary_span] pub span: Span, pub kind: &'static str, + pub coro_kind: String, } #[derive(Diagnostic)] diff --git a/tests/ui/iterators/generator_returned_from_fn.stderr b/tests/ui/iterators/generator_returned_from_fn.stderr index 47fb3cc8e3960..782ddcde8fa5b 100644 --- a/tests/ui/iterators/generator_returned_from_fn.stderr +++ b/tests/ui/iterators/generator_returned_from_fn.stderr @@ -10,7 +10,7 @@ LL | | } LL | | } } | |_____^ returns a reference to data owned by the current function -error: async closure does not implement `Fn` because it captures state from its environment +error: `gen` closure does not implement `Fn` because it captures state from its environment --> $DIR/generator_returned_from_fn.rs:35:13 | LL | iter! { move || { From 4806ac682eae2ee5d7ad90930052f6fb1faed67e Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Fri, 7 Mar 2025 19:27:58 -0800 Subject: [PATCH 05/15] Use yield_expr feature for iter_macro tests --- compiler/rustc_ast_passes/src/feature_gate.rs | 7 ++++--- tests/ui/iterators/generator.rs | 4 +--- tests/ui/iterators/generator_args.rs | 4 +--- tests/ui/iterators/generator_capture.rs | 4 +--- tests/ui/iterators/generator_capture_.rs | 4 +--- tests/ui/iterators/generator_capture_fail.rs | 4 +--- tests/ui/iterators/generator_capture_fail.stderr | 2 +- tests/ui/iterators/generator_returned_from_fn.rs | 4 +--- .../ui/iterators/generator_returned_from_fn.stderr | 14 +++++++------- 9 files changed, 18 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 2fac881f4c42f..694a668ed21f7 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -475,11 +475,12 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { for span in spans { if (!visitor.features.coroutines() && !span.allows_unstable(sym::coroutines)) && (!visitor.features.gen_blocks() && !span.allows_unstable(sym::gen_blocks)) + && (!visitor.features.yield_expr() && !span.allows_unstable(sym::yield_expr)) { #[allow(rustc::untranslatable_diagnostic)] - // Don't know which of the two features to include in the - // error message, so I am arbitrarily picking one. - feature_err(&visitor.sess, sym::coroutines, *span, "yield syntax is experimental") + // Emit yield_expr as the error, since that will be sufficient. You can think of it + // as coroutines and gen_blocks imply yield_expr. + feature_err(&visitor.sess, sym::yield_expr, *span, "yield syntax is experimental") .emit(); } } diff --git a/tests/ui/iterators/generator.rs b/tests/ui/iterators/generator.rs index ee511d598342a..e86c5ce099878 100644 --- a/tests/ui/iterators/generator.rs +++ b/tests/ui/iterators/generator.rs @@ -1,8 +1,6 @@ //@ run-pass -#![feature(iter_macro)] -// FIXME(iter_macro): make `yield` within it legal -#![feature(coroutines)] +#![feature(iter_macro, yield_expr)] use std::iter::iter; diff --git a/tests/ui/iterators/generator_args.rs b/tests/ui/iterators/generator_args.rs index 2e64bf7bbad01..c9da9e5fba609 100644 --- a/tests/ui/iterators/generator_args.rs +++ b/tests/ui/iterators/generator_args.rs @@ -1,8 +1,6 @@ //@ run-pass -#![feature(iter_macro)] -// FIXME(iter_macro): make `yield` within it legal -#![feature(coroutines)] +#![feature(iter_macro, yield_expr)] use std::iter::iter; diff --git a/tests/ui/iterators/generator_capture.rs b/tests/ui/iterators/generator_capture.rs index 9027289116622..c790b7a4b968e 100644 --- a/tests/ui/iterators/generator_capture.rs +++ b/tests/ui/iterators/generator_capture.rs @@ -1,8 +1,6 @@ //@ run-pass -#![feature(iter_macro)] -// FIXME(iter_macro): make `yield` within it legal -#![feature(coroutines)] +#![feature(iter_macro, yield_expr)] use std::iter::iter; diff --git a/tests/ui/iterators/generator_capture_.rs b/tests/ui/iterators/generator_capture_.rs index 7d4edb7af1b43..3529102a47122 100644 --- a/tests/ui/iterators/generator_capture_.rs +++ b/tests/ui/iterators/generator_capture_.rs @@ -1,8 +1,6 @@ //@ run-pass -#![feature(iter_macro)] -// FIXME(iter_macro): make `yield` within it legal -#![feature(coroutines)] +#![feature(iter_macro, yield_expr)] use std::iter::iter; diff --git a/tests/ui/iterators/generator_capture_fail.rs b/tests/ui/iterators/generator_capture_fail.rs index b25192fa7aa6d..da9eba0dae325 100644 --- a/tests/ui/iterators/generator_capture_fail.rs +++ b/tests/ui/iterators/generator_capture_fail.rs @@ -1,6 +1,4 @@ -#![feature(iter_macro)] -// FIXME(iter_macro): make `yield` within it legal -#![feature(coroutines)] +#![feature(iter_macro, yield_expr)] use std::iter::iter; diff --git a/tests/ui/iterators/generator_capture_fail.stderr b/tests/ui/iterators/generator_capture_fail.stderr index c78164add0896..20c3bd1225e7d 100644 --- a/tests/ui/iterators/generator_capture_fail.stderr +++ b/tests/ui/iterators/generator_capture_fail.stderr @@ -1,5 +1,5 @@ error[E0597]: `s` does not live long enough - --> $DIR/generator_capture_fail.rs:11:13 + --> $DIR/generator_capture_fail.rs:9:13 | LL | let i = { | - borrow later stored here diff --git a/tests/ui/iterators/generator_returned_from_fn.rs b/tests/ui/iterators/generator_returned_from_fn.rs index 7446cea113ef0..283abd6f9dde1 100644 --- a/tests/ui/iterators/generator_returned_from_fn.rs +++ b/tests/ui/iterators/generator_returned_from_fn.rs @@ -1,6 +1,4 @@ -#![feature(iter_macro, impl_trait_in_fn_trait_return)] -// FIXME(iter_macro): make `yield` within it legal -#![feature(coroutines)] +#![feature(iter_macro, impl_trait_in_fn_trait_return, yield_expr)] use std::iter::iter; diff --git a/tests/ui/iterators/generator_returned_from_fn.stderr b/tests/ui/iterators/generator_returned_from_fn.stderr index 782ddcde8fa5b..63a2bb02bc761 100644 --- a/tests/ui/iterators/generator_returned_from_fn.stderr +++ b/tests/ui/iterators/generator_returned_from_fn.stderr @@ -1,5 +1,5 @@ error[E0515]: cannot return reference to function parameter `a` - --> $DIR/generator_returned_from_fn.rs:26:13 + --> $DIR/generator_returned_from_fn.rs:24:13 | LL | iter! { || { | _____________^ @@ -11,7 +11,7 @@ LL | | } } | |_____^ returns a reference to data owned by the current function error: `gen` closure does not implement `Fn` because it captures state from its environment - --> $DIR/generator_returned_from_fn.rs:35:13 + --> $DIR/generator_returned_from_fn.rs:33:13 | LL | iter! { move || { | _____________-^^^^^^ @@ -20,15 +20,15 @@ LL | | for x in 5..10 { LL | | yield x * 2; LL | | } LL | | } } - | |_____- return type was inferred to be `{gen closure@$DIR/generator_returned_from_fn.rs:35:13: 35:20}` here + | |_____- return type was inferred to be `{gen closure@$DIR/generator_returned_from_fn.rs:33:13: 33:20}` here error[E0700]: hidden type for `impl FnOnce() -> impl Iterator` captures lifetime that does not appear in bounds - --> $DIR/generator_returned_from_fn.rs:44:13 + --> $DIR/generator_returned_from_fn.rs:42:13 | LL | fn capture_move_once(a: &u32) -> impl FnOnce() -> impl Iterator { | ---- ------------------------------------------ opaque type defined here | | - | hidden type `{gen closure@$DIR/generator_returned_from_fn.rs:44:13: 44:20}` captures the anonymous lifetime defined here + | hidden type `{gen closure@$DIR/generator_returned_from_fn.rs:42:13: 42:20}` captures the anonymous lifetime defined here LL | iter! { move || { | _____________^ LL | | @@ -44,12 +44,12 @@ LL | fn capture_move_once(a: &u32) -> impl FnOnce() -> impl Iterator | +++++++++ error[E0700]: hidden type for `impl Iterator` captures lifetime that does not appear in bounds - --> $DIR/generator_returned_from_fn.rs:44:13 + --> $DIR/generator_returned_from_fn.rs:42:13 | LL | fn capture_move_once(a: &u32) -> impl FnOnce() -> impl Iterator { | ---- ------------------------- opaque type defined here | | - | hidden type `{gen closure body@$DIR/generator_returned_from_fn.rs:44:21: 51:6}` captures the anonymous lifetime defined here + | hidden type `{gen closure body@$DIR/generator_returned_from_fn.rs:42:21: 49:6}` captures the anonymous lifetime defined here LL | iter! { move || { | _____________^ LL | | From b770e51d5e0bd13ff4b6a5cf42d9597ef4c045e1 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Mon, 10 Mar 2025 16:37:36 -0700 Subject: [PATCH 06/15] Remove support for expr-only yield! --- compiler/rustc_builtin_macros/src/iter.rs | 57 ++++++------------- tests/ui/iterators/generator.rs | 4 +- tests/ui/iterators/generator_capture_fail.rs | 6 +- .../iterators/generator_capture_fail.stderr | 12 ++-- .../iterators/generator_returned_from_fn.rs | 4 +- 5 files changed, 30 insertions(+), 53 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/iter.rs b/compiler/rustc_builtin_macros/src/iter.rs index 1d9f078b14bf4..7ad83903a1be7 100644 --- a/compiler/rustc_builtin_macros/src/iter.rs +++ b/compiler/rustc_builtin_macros/src/iter.rs @@ -1,8 +1,6 @@ use rustc_ast::ptr::P; use rustc_ast::tokenstream::TokenStream; -use rustc_ast::{ - CaptureBy, ClosureBinder, Const, CoroutineKind, DUMMY_NODE_ID, Expr, ExprKind, ast, token, -}; +use rustc_ast::{CoroutineKind, DUMMY_NODE_ID, Expr, ast, token}; use rustc_errors::PResult; use rustc_expand::base::{self, DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult}; use rustc_span::Span; @@ -27,8 +25,7 @@ fn parse_closure<'a>( span: Span, stream: TokenStream, ) -> PResult<'a, P> { - let mut parser = cx.new_parser_from_tts(stream); - let mut closure_parser = parser.clone(); + let mut closure_parser = cx.new_parser_from_tts(stream); let coroutine_kind = Some(CoroutineKind::Gen { span, @@ -36,43 +33,21 @@ fn parse_closure<'a>( return_impl_trait_id: DUMMY_NODE_ID, }); - match closure_parser.parse_expr() { - Ok(mut closure) => { - if let ast::ExprKind::Closure(c) = &mut closure.kind { - if let Some(kind) = c.coroutine_kind { - cx.dcx().span_err(kind.span(), "only plain closures allowed in `iter!`"); - } - c.coroutine_kind = coroutine_kind; - if closure_parser.token != token::Eof { - closure_parser.unexpected()?; - } - return Ok(closure); + let mut closure = closure_parser.parse_expr()?; + match &mut closure.kind { + ast::ExprKind::Closure(c) => { + if let Some(kind) = c.coroutine_kind { + cx.dcx().span_err(kind.span(), "only plain closures allowed in `iter!`"); } + c.coroutine_kind = coroutine_kind; + if closure_parser.token != token::Eof { + closure_parser.unexpected()?; + } + Ok(closure) + } + _ => { + cx.dcx().span_err(closure.span, "`iter!` body must be a closure"); + Err(closure_parser.unexpected().unwrap_err()) } - Err(diag) => diag.cancel(), - } - - let lo = parser.token.span.shrink_to_lo(); - let block = parser.parse_block_tail( - lo, - ast::BlockCheckMode::Default, - rustc_parse::parser::AttemptLocalParseRecovery::No, - )?; - let fn_decl = cx.fn_decl(Default::default(), ast::FnRetTy::Default(span)); - let closure = ast::Closure { - binder: ClosureBinder::NotPresent, - capture_clause: CaptureBy::Ref, - constness: Const::No, - coroutine_kind, - movability: ast::Movability::Movable, - fn_decl, - body: cx.expr_block(block), - fn_decl_span: span, - fn_arg_span: span, - }; - if parser.token != token::Eof { - parser.unexpected()?; } - let span = lo.to(parser.token.span); - Ok(cx.expr(span, ExprKind::Closure(Box::new(closure)))) } diff --git a/tests/ui/iterators/generator.rs b/tests/ui/iterators/generator.rs index e86c5ce099878..e633efb11c1da 100644 --- a/tests/ui/iterators/generator.rs +++ b/tests/ui/iterators/generator.rs @@ -5,12 +5,12 @@ use std::iter::iter; fn main() { - let i = iter! { + let i = iter! { || { yield 0; for x in 5..10 { yield x * 2; } - }; + } }; let mut i = i(); assert_eq!(i.next(), Some(0)); assert_eq!(i.next(), Some(10)); diff --git a/tests/ui/iterators/generator_capture_fail.rs b/tests/ui/iterators/generator_capture_fail.rs index da9eba0dae325..d987b2df01164 100644 --- a/tests/ui/iterators/generator_capture_fail.rs +++ b/tests/ui/iterators/generator_capture_fail.rs @@ -5,12 +5,12 @@ use std::iter::iter; fn main() { let i = { let s = String::new(); - iter! { - yield s.len(); //~ ERROR `s` does not live long enough + iter! { || { //~ ERROR `s` does not live long enough + yield s.len(); for x in 5..10 { yield x * 2; } - } + } } }; let mut i = i(); assert_eq!(i.next(), Some(0)); diff --git a/tests/ui/iterators/generator_capture_fail.stderr b/tests/ui/iterators/generator_capture_fail.stderr index 20c3bd1225e7d..225a385d6a0fa 100644 --- a/tests/ui/iterators/generator_capture_fail.stderr +++ b/tests/ui/iterators/generator_capture_fail.stderr @@ -1,15 +1,17 @@ error[E0597]: `s` does not live long enough - --> $DIR/generator_capture_fail.rs:9:13 + --> $DIR/generator_capture_fail.rs:8:17 | LL | let i = { | - borrow later stored here -... -LL | / yield s.len(); +LL | let s = String::new(); +LL | iter! { || { + | _________________^ +LL | | yield s.len(); LL | | for x in 5..10 { LL | | yield x * 2; LL | | } - | |_____________^ borrowed value does not live long enough -LL | } +LL | | } } + | |_________^ borrowed value does not live long enough LL | }; | - `s` dropped here while still borrowed diff --git a/tests/ui/iterators/generator_returned_from_fn.rs b/tests/ui/iterators/generator_returned_from_fn.rs index 283abd6f9dde1..bd0317b1ffb7f 100644 --- a/tests/ui/iterators/generator_returned_from_fn.rs +++ b/tests/ui/iterators/generator_returned_from_fn.rs @@ -3,12 +3,12 @@ use std::iter::iter; fn plain() -> impl Fn() -> impl Iterator { - iter! { + iter! { || { yield 0; for x in 5..10 { yield x * 2; } - } + } } } fn arg() -> impl Fn(u32) -> impl Iterator { From 3eb7b78ac6df4f3517eb4af69c2d2c02390ac059 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Thu, 13 Mar 2025 15:42:56 -0700 Subject: [PATCH 07/15] Update and fix tests Co-authored-by: Oli Scherer --- .../src/error_reporting/traits/fulfillment_errors.rs | 4 ++-- library/core/src/iter/sources/generator.rs | 7 +++++-- tests/ui/coroutine/gen_block.none.stderr | 4 ++-- .../ui/feature-gates/feature-gate-coroutines.e2024.stderr | 8 ++++---- .../ui/feature-gates/feature-gate-coroutines.none.stderr | 8 ++++---- tests/ui/feature-gates/feature-gate-yield-expr.stderr | 2 +- tests/ui/iterators/generator_returned_from_fn.stderr | 2 +- 7 files changed, 19 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 9c3b7ffe1ed12..efd42850868ca 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -909,8 +909,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { .coroutine_kind(self.tcx.coroutine_for_closure(closure_def_id)) .unwrap() { - rustc_hir::CoroutineKind::Desugared(desugaring, _) => format!("{desugaring:#}"), - coro => format!("{coro:#}"), + rustc_hir::CoroutineKind::Desugared(desugaring, _) => desugaring.to_string(), + coro => coro.to_string(), }; let mut err = self.dcx().create_err(CoroClosureNotFn { span: self.tcx.def_span(closure_def_id), diff --git a/library/core/src/iter/sources/generator.rs b/library/core/src/iter/sources/generator.rs index 11f69db960c2a..c94232e09eb86 100644 --- a/library/core/src/iter/sources/generator.rs +++ b/library/core/src/iter/sources/generator.rs @@ -9,14 +9,17 @@ /// /// ``` /// #![feature(iter_macro, coroutines)] +/// # #[cfg(not(bootstrap))] +/// # { /// -/// let it = std::iter::iter!{ +/// let it = std::iter::iter!{|| { /// yield 1; /// yield 2; /// yield 3; -/// }; +/// } }(); /// let v: Vec<_> = it.collect(); /// assert_eq!(v, [1, 2, 3]); +/// # } /// ``` #[unstable(feature = "iter_macro", issue = "none", reason = "generators are unstable")] #[allow_internal_unstable(coroutines, iter_from_coroutine)] diff --git a/tests/ui/coroutine/gen_block.none.stderr b/tests/ui/coroutine/gen_block.none.stderr index ed744f2957ac5..b793033b5216a 100644 --- a/tests/ui/coroutine/gen_block.none.stderr +++ b/tests/ui/coroutine/gen_block.none.stderr @@ -31,7 +31,7 @@ LL | let _ = || yield true; | ^^^^^^^^^^ | = note: see issue #43122 for more information - = help: add `#![feature(coroutines)]` to the crate attributes to enable + = help: add `#![feature(yield_expr)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: yield syntax is experimental @@ -41,7 +41,7 @@ LL | let _ = #[coroutine] || yield true; | ^^^^^^^^^^ | = note: see issue #43122 for more information - = help: add `#![feature(coroutines)]` to the crate attributes to enable + = help: add `#![feature(yield_expr)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: the `#[coroutine]` attribute is an experimental feature diff --git a/tests/ui/feature-gates/feature-gate-coroutines.e2024.stderr b/tests/ui/feature-gates/feature-gate-coroutines.e2024.stderr index 381e7a210be06..c29c328ac143e 100644 --- a/tests/ui/feature-gates/feature-gate-coroutines.e2024.stderr +++ b/tests/ui/feature-gates/feature-gate-coroutines.e2024.stderr @@ -5,7 +5,7 @@ LL | yield true; | ^^^^^^^^^^ | = note: see issue #43122 for more information - = help: add `#![feature(coroutines)]` to the crate attributes to enable + = help: add `#![feature(yield_expr)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: yield syntax is experimental @@ -15,7 +15,7 @@ LL | let _ = || yield true; | ^^^^^^^^^^ | = note: see issue #43122 for more information - = help: add `#![feature(coroutines)]` to the crate attributes to enable + = help: add `#![feature(yield_expr)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: yield syntax is experimental @@ -25,7 +25,7 @@ LL | yield; | ^^^^^ | = note: see issue #43122 for more information - = help: add `#![feature(coroutines)]` to the crate attributes to enable + = help: add `#![feature(yield_expr)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: yield syntax is experimental @@ -35,7 +35,7 @@ LL | yield 0; | ^^^^^^^ | = note: see issue #43122 for more information - = help: add `#![feature(coroutines)]` to the crate attributes to enable + = help: add `#![feature(yield_expr)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: yield syntax is experimental diff --git a/tests/ui/feature-gates/feature-gate-coroutines.none.stderr b/tests/ui/feature-gates/feature-gate-coroutines.none.stderr index 381e7a210be06..c29c328ac143e 100644 --- a/tests/ui/feature-gates/feature-gate-coroutines.none.stderr +++ b/tests/ui/feature-gates/feature-gate-coroutines.none.stderr @@ -5,7 +5,7 @@ LL | yield true; | ^^^^^^^^^^ | = note: see issue #43122 for more information - = help: add `#![feature(coroutines)]` to the crate attributes to enable + = help: add `#![feature(yield_expr)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: yield syntax is experimental @@ -15,7 +15,7 @@ LL | let _ = || yield true; | ^^^^^^^^^^ | = note: see issue #43122 for more information - = help: add `#![feature(coroutines)]` to the crate attributes to enable + = help: add `#![feature(yield_expr)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: yield syntax is experimental @@ -25,7 +25,7 @@ LL | yield; | ^^^^^ | = note: see issue #43122 for more information - = help: add `#![feature(coroutines)]` to the crate attributes to enable + = help: add `#![feature(yield_expr)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: yield syntax is experimental @@ -35,7 +35,7 @@ LL | yield 0; | ^^^^^^^ | = note: see issue #43122 for more information - = help: add `#![feature(coroutines)]` to the crate attributes to enable + = help: add `#![feature(yield_expr)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: yield syntax is experimental diff --git a/tests/ui/feature-gates/feature-gate-yield-expr.stderr b/tests/ui/feature-gates/feature-gate-yield-expr.stderr index ad8a15a0f3650..bfac9e498037d 100644 --- a/tests/ui/feature-gates/feature-gate-yield-expr.stderr +++ b/tests/ui/feature-gates/feature-gate-yield-expr.stderr @@ -5,7 +5,7 @@ LL | yield (); | ^^^^^^^^ | = note: see issue #43122 for more information - = help: add `#![feature(coroutines)]` to the crate attributes to enable + = help: add `#![feature(yield_expr)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: yield syntax is experimental diff --git a/tests/ui/iterators/generator_returned_from_fn.stderr b/tests/ui/iterators/generator_returned_from_fn.stderr index 63a2bb02bc761..b2324af6d5e40 100644 --- a/tests/ui/iterators/generator_returned_from_fn.stderr +++ b/tests/ui/iterators/generator_returned_from_fn.stderr @@ -10,7 +10,7 @@ LL | | } LL | | } } | |_____^ returns a reference to data owned by the current function -error: `gen` closure does not implement `Fn` because it captures state from its environment +error: gen closure does not implement `Fn` because it captures state from its environment --> $DIR/generator_returned_from_fn.rs:33:13 | LL | iter! { move || { From 2e6e242c84a26356e362394fb097c8c219ca1b4f Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Tue, 18 Mar 2025 16:36:47 -0700 Subject: [PATCH 08/15] Add a test for iterators that capture but don't lend --- .../ui/iterators/generator_capture_no_lend.rs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 tests/ui/iterators/generator_capture_no_lend.rs diff --git a/tests/ui/iterators/generator_capture_no_lend.rs b/tests/ui/iterators/generator_capture_no_lend.rs new file mode 100644 index 0000000000000..822db58d48df2 --- /dev/null +++ b/tests/ui/iterators/generator_capture_no_lend.rs @@ -0,0 +1,30 @@ +//@ run-pass + +#![feature(iter_macro, yield_expr)] + +// This test creates an iterator that captures a reference and ensure that doesn't force the +// iterator to become lending. + +use std::iter::iter; + +fn main() { + let s = "foo".to_string(); + let f = iter! { || { + for c in s.chars() { + yield c; + } + }}; + + let mut i = f(); + let mut j = f(); + + assert_eq!(i.next(), Some('f')); + assert_eq!(i.next(), Some('o')); + assert_eq!(i.next(), Some('o')); + assert_eq!(i.next(), None); + + assert_eq!(j.next(), Some('f')); + assert_eq!(j.next(), Some('o')); + assert_eq!(j.next(), Some('o')); + assert_eq!(j.next(), None); +} From 69a65cd960d38c1953862e400c64013fb72b2727 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Fri, 28 Mar 2025 16:17:57 -0700 Subject: [PATCH 09/15] Add some tests to make sure iter macros do not accidentally become async closures --- ...iter-macro-not-async-closure-simplified.rs | 20 +++++++ ...-macro-not-async-closure-simplified.stderr | 18 ++++++ .../iterators/iter-macro-not-async-closure.rs | 28 +++++++++ .../iter-macro-not-async-closure.stderr | 58 +++++++++++++++++++ 4 files changed, 124 insertions(+) create mode 100644 tests/ui/iterators/iter-macro-not-async-closure-simplified.rs create mode 100644 tests/ui/iterators/iter-macro-not-async-closure-simplified.stderr create mode 100644 tests/ui/iterators/iter-macro-not-async-closure.rs create mode 100644 tests/ui/iterators/iter-macro-not-async-closure.stderr diff --git a/tests/ui/iterators/iter-macro-not-async-closure-simplified.rs b/tests/ui/iterators/iter-macro-not-async-closure-simplified.rs new file mode 100644 index 0000000000000..1f7952a1c79af --- /dev/null +++ b/tests/ui/iterators/iter-macro-not-async-closure-simplified.rs @@ -0,0 +1,20 @@ +// This test ensures iterators created with the `iter!` macro are not accidentally async closures. +//@ edition: 2024 + +#![feature(yield_expr, iter_macro)] + +use std::iter::iter; + +fn call_async_once(_: impl AsyncFnOnce()) { +} + +fn main() { + let f = iter! { move || { + for i in 0..10 { + yield i; + } + }}; + + call_async_once(f); + //~^ ERROR AsyncFnOnce()` is not satisfied +} diff --git a/tests/ui/iterators/iter-macro-not-async-closure-simplified.stderr b/tests/ui/iterators/iter-macro-not-async-closure-simplified.stderr new file mode 100644 index 0000000000000..6263c27d20985 --- /dev/null +++ b/tests/ui/iterators/iter-macro-not-async-closure-simplified.stderr @@ -0,0 +1,18 @@ +error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure-simplified.rs:12:21: 12:28}: AsyncFnOnce()` is not satisfied + --> $DIR/iter-macro-not-async-closure-simplified.rs:18:21 + | +LL | call_async_once(f); + | --------------- ^ unsatisfied trait bound + | | + | required by a bound introduced by this call + | + = help: the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure-simplified.rs:12:21: 12:28}` +note: required by a bound in `call_async_once` + --> $DIR/iter-macro-not-async-closure-simplified.rs:8:28 + | +LL | fn call_async_once(_: impl AsyncFnOnce()) { + | ^^^^^^^^^^^^^ required by this bound in `call_async_once` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/iterators/iter-macro-not-async-closure.rs b/tests/ui/iterators/iter-macro-not-async-closure.rs new file mode 100644 index 0000000000000..ffe53e9f653c2 --- /dev/null +++ b/tests/ui/iterators/iter-macro-not-async-closure.rs @@ -0,0 +1,28 @@ +// This test ensures iterators created with the `iter!` macro are not accidentally async closures. +//@ edition: 2024 + +#![feature(yield_expr, iter_macro)] + +use std::task::{Waker, Context}; +use std::iter::iter; +use std::pin::pin; +use std::future::Future; + +async fn call_async_once(f: impl AsyncFnOnce()) { + f().await +} + +fn main() { + let f = iter! { move || { + for i in 0..10 { + yield i; + } + }}; + + let x = pin!(call_async_once(f)); + //~^ ERROR AsyncFnOnce()` is not satisfied + //~^^ ERROR AsyncFnOnce()` is not satisfied + //~^^^ ERROR AsyncFnOnce()` is not satisfied + x.poll(&mut Context::from_waker(Waker::noop())); + //~^ ERROR AsyncFnOnce()` is not satisfied +} diff --git a/tests/ui/iterators/iter-macro-not-async-closure.stderr b/tests/ui/iterators/iter-macro-not-async-closure.stderr new file mode 100644 index 0000000000000..84a9ed234ac1f --- /dev/null +++ b/tests/ui/iterators/iter-macro-not-async-closure.stderr @@ -0,0 +1,58 @@ +error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:16:21: 16:28}: AsyncFnOnce()` is not satisfied + --> $DIR/iter-macro-not-async-closure.rs:22:34 + | +LL | let x = pin!(call_async_once(f)); + | --------------- ^ unsatisfied trait bound + | | + | required by a bound introduced by this call + | + = help: the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:16:21: 16:28}` +note: required by a bound in `call_async_once` + --> $DIR/iter-macro-not-async-closure.rs:11:34 + | +LL | async fn call_async_once(f: impl AsyncFnOnce()) { + | ^^^^^^^^^^^^^ required by this bound in `call_async_once` + +error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:16:21: 16:28}: AsyncFnOnce()` is not satisfied + --> $DIR/iter-macro-not-async-closure.rs:22:18 + | +LL | let x = pin!(call_async_once(f)); + | ^^^^^^^^^^^^^^^^^^ unsatisfied trait bound + | + = help: the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:16:21: 16:28}` +note: required by a bound in `call_async_once` + --> $DIR/iter-macro-not-async-closure.rs:11:34 + | +LL | async fn call_async_once(f: impl AsyncFnOnce()) { + | ^^^^^^^^^^^^^ required by this bound in `call_async_once` + +error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:16:21: 16:28}: AsyncFnOnce()` is not satisfied + --> $DIR/iter-macro-not-async-closure.rs:22:13 + | +LL | let x = pin!(call_async_once(f)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound + | + = help: the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:16:21: 16:28}` +note: required by a bound in `call_async_once` + --> $DIR/iter-macro-not-async-closure.rs:11:34 + | +LL | async fn call_async_once(f: impl AsyncFnOnce()) { + | ^^^^^^^^^^^^^ required by this bound in `call_async_once` + = note: this error originates in the macro `pin` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:16:21: 16:28}: AsyncFnOnce()` is not satisfied + --> $DIR/iter-macro-not-async-closure.rs:26:5 + | +LL | x.poll(&mut Context::from_waker(Waker::noop())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound + | + = help: the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:16:21: 16:28}` +note: required by a bound in `call_async_once` + --> $DIR/iter-macro-not-async-closure.rs:11:34 + | +LL | async fn call_async_once(f: impl AsyncFnOnce()) { + | ^^^^^^^^^^^^^ required by this bound in `call_async_once` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`. From 5309dbde44c300c82c469ba6b957817921475dce Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Mon, 31 Mar 2025 11:51:40 -0700 Subject: [PATCH 10/15] Remove AsyncFn* impls from gnerator closures --- .../src/check_consts/check.rs | 2 +- compiler/rustc_hir_typeck/src/closure.rs | 7 +++- .../src/traits/select/candidate_assembly.rs | 28 +++++++++++++--- tests/ui/iterators/generator_capture_.rs | 4 +-- tests/ui/iterators/generator_capture_.stderr | 25 +++++++++++++++ .../ui/iterators/generator_capture_fnonce.rs | 32 +++++++++++++++++++ 6 files changed, 90 insertions(+), 8 deletions(-) create mode 100644 tests/ui/iterators/generator_capture_.stderr create mode 100644 tests/ui/iterators/generator_capture_fnonce.rs diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 90002d3f10905..1fcb34650ea2b 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -591,7 +591,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { if let AggregateKind::Coroutine(def_id, ..) = kind.as_ref() && let Some( coroutine_kind @ hir::CoroutineKind::Desugared( - hir::CoroutineDesugaring::Async, + hir::CoroutineDesugaring::Async | hir::CoroutineDesugaring::Gen, _, ), ) = self.tcx.coroutine_kind(def_id) diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index cb433fb6bd228..328d19beb039a 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -462,7 +462,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(trait_def_id) = trait_def_id { let found_kind = match closure_kind { - hir::ClosureKind::Closure => self.tcx.fn_trait_kind_from_def_id(trait_def_id), + hir::ClosureKind::Closure + // FIXME(iter_macro): Someday we'll probably want iterator closures instead of + // just using Fn* for iterators. + | hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Gen) => { + self.tcx.fn_trait_kind_from_def_id(trait_def_id) + } hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async) => self .tcx .async_fn_trait_kind_from_def_id(trait_def_id) diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index d15c9afef3abc..4e1d55513af32 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -11,7 +11,7 @@ use std::ops::ControlFlow; use hir::LangItem; use hir::def_id::DefId; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; -use rustc_hir as hir; +use rustc_hir::{self as hir, CoroutineDesugaring, CoroutineKind}; use rustc_infer::traits::{Obligation, PolyTraitObligation, SelectionError}; use rustc_middle::ty::fast_reject::DeepRejectCtxt; use rustc_middle::ty::{self, Ty, TypeVisitableExt, TypingMode}; @@ -125,11 +125,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.assemble_async_iterator_candidates(obligation, &mut candidates); } else if tcx.is_lang_item(def_id, LangItem::AsyncFnKindHelper) { self.assemble_async_fn_kind_helper_candidates(obligation, &mut candidates); + } else if tcx.is_lang_item(def_id, LangItem::AsyncFn) + || tcx.is_lang_item(def_id, LangItem::AsyncFnOnce) + || tcx.is_lang_item(def_id, LangItem::AsyncFnMut) + { + self.assemble_async_closure_candidates(obligation, &mut candidates); } // FIXME: Put these into `else if` blocks above, since they're built-in. self.assemble_closure_candidates(obligation, &mut candidates); - self.assemble_async_closure_candidates(obligation, &mut candidates); self.assemble_fn_pointer_candidates(obligation, &mut candidates); self.assemble_candidates_from_impls(obligation, &mut candidates); @@ -425,6 +429,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } + #[instrument(level = "debug", skip(self, candidates))] fn assemble_async_closure_candidates( &mut self, obligation: &PolyTraitObligation<'tcx>, @@ -436,15 +441,30 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return; }; + debug!("self_ty = {:?}", obligation.self_ty().skip_binder().kind()); match *obligation.self_ty().skip_binder().kind() { - ty::CoroutineClosure(_, args) => { + ty::CoroutineClosure(def_id, args) => { if let Some(closure_kind) = args.as_coroutine_closure().kind_ty().to_opt_closure_kind() && !closure_kind.extends(goal_kind) { return; } - candidates.vec.push(AsyncClosureCandidate); + + // Make sure this is actually an async closure. + let Some(coroutine_kind) = + self.tcx().coroutine_kind(self.tcx().coroutine_for_closure(def_id)) + else { + bug!("coroutine with no kind"); + }; + + debug!(?coroutine_kind); + match coroutine_kind { + CoroutineKind::Desugared(CoroutineDesugaring::Async, _) => { + candidates.vec.push(AsyncClosureCandidate); + } + _ => (), + } } // Closures and fn pointers implement `AsyncFn*` if their return types // implement `Future`, which is checked later. diff --git a/tests/ui/iterators/generator_capture_.rs b/tests/ui/iterators/generator_capture_.rs index 3529102a47122..f630bc64b974c 100644 --- a/tests/ui/iterators/generator_capture_.rs +++ b/tests/ui/iterators/generator_capture_.rs @@ -1,4 +1,4 @@ -//@ run-pass +// This test exercises lending behavior for iterator closures which is not yet supported. #![feature(iter_macro, yield_expr)] @@ -18,7 +18,7 @@ fn main() { assert_eq!(i.next(), Some('o')); assert_eq!(i.next(), Some('o')); assert_eq!(i.next(), None); - let mut i = f(); + let mut i = f(); //~ ERROR use of moved value: `f` assert_eq!(i.next(), Some('f')); assert_eq!(i.next(), Some('o')); assert_eq!(i.next(), Some('o')); diff --git a/tests/ui/iterators/generator_capture_.stderr b/tests/ui/iterators/generator_capture_.stderr new file mode 100644 index 0000000000000..3d9647ae16f32 --- /dev/null +++ b/tests/ui/iterators/generator_capture_.stderr @@ -0,0 +1,25 @@ +error[E0382]: use of moved value: `f` + --> $DIR/generator_capture_.rs:21:17 + | +LL | let f = { + | - move occurs because `f` has type `{gen closure@$DIR/generator_capture_.rs:10:17: 10:24}`, which does not implement the `Copy` trait +... +LL | let mut i = f(); + | --- `f` moved due to this call +... +LL | let mut i = f(); + | ^ value used here after move + | +note: this value implements `FnOnce`, which causes it to be moved when called + --> $DIR/generator_capture_.rs:16:17 + | +LL | let mut i = f(); + | ^ +help: consider cloning the value if the performance cost is acceptable + | +LL | let mut i = f.clone()(); + | ++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/iterators/generator_capture_fnonce.rs b/tests/ui/iterators/generator_capture_fnonce.rs new file mode 100644 index 0000000000000..090727eb9b7e4 --- /dev/null +++ b/tests/ui/iterators/generator_capture_fnonce.rs @@ -0,0 +1,32 @@ +//@ run-pass + +#![feature(iter_macro, yield_expr)] + +use std::iter::iter; + +fn main() { + let i = { + let s = String::new(); + iter! { move || { + yield s.len(); + for x in 5..10 { + yield x * 2; + } + }} + }; + test_iterator(i); +} + +/// Exercise the iterator in a separate function to ensure it's not capturing anything it shoudln't. +fn test_iterator>(i: impl FnOnce() -> I) { + let mut i = i(); + assert_eq!(i.next(), Some(0)); + assert_eq!(i.next(), Some(10)); + assert_eq!(i.next(), Some(12)); + assert_eq!(i.next(), Some(14)); + assert_eq!(i.next(), Some(16)); + assert_eq!(i.next(), Some(18)); + assert_eq!(i.next(), None); + assert_eq!(i.next(), None); + assert_eq!(i.next(), None); +} From 3d5a21aa86f1d2055df3b654cea677cf3d53574f Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Mon, 31 Mar 2025 17:02:02 -0700 Subject: [PATCH 11/15] Update error message for const_gen_fn.rs --- compiler/rustc_const_eval/src/check_consts/ops.rs | 2 +- compiler/rustc_hir/src/hir.rs | 9 +++++++++ tests/ui/coroutine/const_gen_fn.rs | 1 + tests/ui/coroutine/const_gen_fn.stderr | 10 ++++++++-- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index 7756e51c4c5f2..290c0701f4105 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -503,7 +503,7 @@ impl<'tcx> NonConstOp<'tcx> for Coroutine { } fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { - let msg = format!("{:#}s are not allowed in {}s", self.0, ccx.const_kind()); + let msg = format!("{} are not allowed in {}s", self.0.to_plural_string(), ccx.const_kind()); if let Status::Unstable { gate, .. } = self.status_in_item(ccx) { ccx.tcx.sess.create_feature_err(errors::UnallowedOpInConstContext { span, msg }, gate) } else { diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 1e5665a235d24..9781e8bc790ab 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1947,6 +1947,15 @@ impl CoroutineKind { pub fn is_fn_like(self) -> bool { matches!(self, CoroutineKind::Desugared(_, CoroutineSource::Fn)) } + + pub fn to_plural_string(&self) -> String { + match self { + CoroutineKind::Desugared(d, CoroutineSource::Fn) => format!("{d:#}fn bodies"), + CoroutineKind::Desugared(d, CoroutineSource::Block) => format!("{d:#}blocks"), + CoroutineKind::Desugared(d, CoroutineSource::Closure) => format!("{d:#}closure bodies"), + CoroutineKind::Coroutine(_) => "coroutines".to_string(), + } + } } impl fmt::Display for CoroutineKind { diff --git a/tests/ui/coroutine/const_gen_fn.rs b/tests/ui/coroutine/const_gen_fn.rs index 2701139ffed93..62c83324d3d8e 100644 --- a/tests/ui/coroutine/const_gen_fn.rs +++ b/tests/ui/coroutine/const_gen_fn.rs @@ -4,6 +4,7 @@ const gen fn a() {} //~^ ERROR functions cannot be both `const` and `gen` +//~^^ ERROR `gen` fn bodies are not allowed in constant functions const async gen fn b() {} //~^ ERROR functions cannot be both `const` and `async gen` diff --git a/tests/ui/coroutine/const_gen_fn.stderr b/tests/ui/coroutine/const_gen_fn.stderr index 4f3c73d16787b..6a1dced282d4e 100644 --- a/tests/ui/coroutine/const_gen_fn.stderr +++ b/tests/ui/coroutine/const_gen_fn.stderr @@ -8,7 +8,7 @@ LL | const gen fn a() {} | `const` because of this error: functions cannot be both `const` and `async gen` - --> $DIR/const_gen_fn.rs:8:1 + --> $DIR/const_gen_fn.rs:9:1 | LL | const async gen fn b() {} | ^^^^^-^^^^^^^^^---------- @@ -16,5 +16,11 @@ LL | const async gen fn b() {} | | `async gen` because of this | `const` because of this -error: aborting due to 2 previous errors +error: `gen` fn bodies are not allowed in constant functions + --> $DIR/const_gen_fn.rs:5:18 + | +LL | const gen fn a() {} + | ^^ + +error: aborting due to 3 previous errors From 676bf46233e5a9b0233dd60be77eece53f58831c Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Tue, 1 Apr 2025 11:35:48 -0700 Subject: [PATCH 12/15] Gate const coroutines better --- .../rustc_const_eval/src/check_consts/check.rs | 7 +------ .../rustc_const_eval/src/check_consts/ops.rs | 17 ++++++++--------- tests/ui/coroutine/const_gen_fn.rs | 1 + tests/ui/coroutine/const_gen_fn.stderr | 8 +++++++- tests/ui/suggestions/unnamable-types.rs | 2 +- .../issue-53678-coroutine-and-const-fn.rs | 2 +- 6 files changed, 19 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 1fcb34650ea2b..ac9a8e9ebdc9a 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -589,12 +589,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { Rvalue::Aggregate(kind, ..) => { if let AggregateKind::Coroutine(def_id, ..) = kind.as_ref() - && let Some( - coroutine_kind @ hir::CoroutineKind::Desugared( - hir::CoroutineDesugaring::Async | hir::CoroutineDesugaring::Gen, - _, - ), - ) = self.tcx.coroutine_kind(def_id) + && let Some(coroutine_kind) = self.tcx.coroutine_kind(def_id) { self.check_op(ops::Coroutine(coroutine_kind)); } diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index 290c0701f4105..b8b360e4c490e 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -486,19 +486,18 @@ impl<'tcx> NonConstOp<'tcx> for IntrinsicUnstable { pub(crate) struct Coroutine(pub hir::CoroutineKind); impl<'tcx> NonConstOp<'tcx> for Coroutine { fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status { - if let hir::CoroutineKind::Desugared( - hir::CoroutineDesugaring::Async, - hir::CoroutineSource::Block, - ) = self.0 - { - Status::Unstable { + match self.0 { + hir::CoroutineKind::Desugared( + hir::CoroutineDesugaring::Async, + hir::CoroutineSource::Block, + ) + | hir::CoroutineKind::Coroutine(_) => Status::Unstable { gate: sym::const_async_blocks, gate_already_checked: false, safe_to_expose_on_stable: false, is_function_call: false, - } - } else { - Status::Forbidden + }, + _ => Status::Forbidden, } } diff --git a/tests/ui/coroutine/const_gen_fn.rs b/tests/ui/coroutine/const_gen_fn.rs index 62c83324d3d8e..b044c185e0f06 100644 --- a/tests/ui/coroutine/const_gen_fn.rs +++ b/tests/ui/coroutine/const_gen_fn.rs @@ -8,5 +8,6 @@ const gen fn a() {} const async gen fn b() {} //~^ ERROR functions cannot be both `const` and `async gen` +//~^^ ERROR `async gen` fn bodies are not allowed in constant functions fn main() {} diff --git a/tests/ui/coroutine/const_gen_fn.stderr b/tests/ui/coroutine/const_gen_fn.stderr index 6a1dced282d4e..400ee216d0645 100644 --- a/tests/ui/coroutine/const_gen_fn.stderr +++ b/tests/ui/coroutine/const_gen_fn.stderr @@ -22,5 +22,11 @@ error: `gen` fn bodies are not allowed in constant functions LL | const gen fn a() {} | ^^ -error: aborting due to 3 previous errors +error: `async gen` fn bodies are not allowed in constant functions + --> $DIR/const_gen_fn.rs:9:24 + | +LL | const async gen fn b() {} + | ^^ + +error: aborting due to 4 previous errors diff --git a/tests/ui/suggestions/unnamable-types.rs b/tests/ui/suggestions/unnamable-types.rs index 094584ff850b3..6772217a2f6ff 100644 --- a/tests/ui/suggestions/unnamable-types.rs +++ b/tests/ui/suggestions/unnamable-types.rs @@ -1,7 +1,7 @@ // Test that we do not suggest to add type annotations for unnamable types. #![crate_type="lib"] -#![feature(coroutines, stmt_expr_attributes)] +#![feature(coroutines, stmt_expr_attributes, const_async_blocks)] const A = 5; //~^ ERROR: missing type for `const` item diff --git a/tests/ui/type-alias-impl-trait/issue-53678-coroutine-and-const-fn.rs b/tests/ui/type-alias-impl-trait/issue-53678-coroutine-and-const-fn.rs index f02ccbbb93c4c..b258d2e156d03 100644 --- a/tests/ui/type-alias-impl-trait/issue-53678-coroutine-and-const-fn.rs +++ b/tests/ui/type-alias-impl-trait/issue-53678-coroutine-and-const-fn.rs @@ -1,4 +1,4 @@ -#![feature(coroutines, coroutine_trait, rustc_attrs)] +#![feature(coroutines, coroutine_trait, rustc_attrs, const_async_blocks)] #![feature(type_alias_impl_trait)] //@ check-pass From 51d09def2c495b09b562ac11f1f5d7928b6da272 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Tue, 1 Apr 2025 12:18:54 -0700 Subject: [PATCH 13/15] Add FIXME about const coroutine feature --- compiler/rustc_const_eval/src/check_consts/ops.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index b8b360e4c490e..0438a0d322ade 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -491,6 +491,8 @@ impl<'tcx> NonConstOp<'tcx> for Coroutine { hir::CoroutineDesugaring::Async, hir::CoroutineSource::Block, ) + // FIXME(coroutines): eventually we want to gate const coroutine coroutines behind a + // different feature. | hir::CoroutineKind::Coroutine(_) => Status::Unstable { gate: sym::const_async_blocks, gate_already_checked: false, From 85243d324eeeb1232af466766d9cea619cfcbe99 Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Tue, 8 Apr 2025 01:31:14 +0000 Subject: [PATCH 14/15] Fix CI test failure due to `diagnostic-width` Where the error relevant for these tests is emitted in `report_selection_error` in `fulfillment_errors.rs`, there's a check for the diagostic width. If the explanation is too wide for that width, it's moved to the `help` instead. This was causing the results from CI to not match the results observed in blessing the test locally. Let's update the relevant tests to fix this width and to test it both in a `narrow` and in a `wide` configuration. Thanks to jieyouxu for figuring this out. --- ...ot-async-closure-simplified.narrow.stderr} | 12 ++-- ...iter-macro-not-async-closure-simplified.rs | 15 ++++- ...o-not-async-closure-simplified.wide.stderr | 17 ++++++ ...iter-macro-not-async-closure.narrow.stderr | 58 +++++++++++++++++++ .../iterators/iter-macro-not-async-closure.rs | 12 +++- ... iter-macro-not-async-closure.wide.stderr} | 36 +++++------- 6 files changed, 120 insertions(+), 30 deletions(-) rename tests/ui/iterators/{iter-macro-not-async-closure-simplified.stderr => iter-macro-not-async-closure-simplified.narrow.stderr} (53%) create mode 100644 tests/ui/iterators/iter-macro-not-async-closure-simplified.wide.stderr create mode 100644 tests/ui/iterators/iter-macro-not-async-closure.narrow.stderr rename tests/ui/iterators/{iter-macro-not-async-closure.stderr => iter-macro-not-async-closure.wide.stderr} (53%) diff --git a/tests/ui/iterators/iter-macro-not-async-closure-simplified.stderr b/tests/ui/iterators/iter-macro-not-async-closure-simplified.narrow.stderr similarity index 53% rename from tests/ui/iterators/iter-macro-not-async-closure-simplified.stderr rename to tests/ui/iterators/iter-macro-not-async-closure-simplified.narrow.stderr index 6263c27d20985..4e0dabade2de0 100644 --- a/tests/ui/iterators/iter-macro-not-async-closure-simplified.stderr +++ b/tests/ui/iterators/iter-macro-not-async-closure-simplified.narrow.stderr @@ -1,17 +1,17 @@ -error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure-simplified.rs:12:21: 12:28}: AsyncFnOnce()` is not satisfied - --> $DIR/iter-macro-not-async-closure-simplified.rs:18:21 +error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure-simplified.rs:21:21: 21:28}: AsyncFnOnce()` is not satisfied + --> $DIR/iter-macro-not-async-closure-simplified.rs:27:21 | LL | call_async_once(f); | --------------- ^ unsatisfied trait bound | | | required by a bound introduced by this call | - = help: the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure-simplified.rs:12:21: 12:28}` + = help: the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure-simplified.rs:21:21: 21:28}` note: required by a bound in `call_async_once` - --> $DIR/iter-macro-not-async-closure-simplified.rs:8:28 + --> $DIR/iter-macro-not-async-closure-simplified.rs:18:28 | -LL | fn call_async_once(_: impl AsyncFnOnce()) { - | ^^^^^^^^^^^^^ required by this bound in `call_async_once` +LL | ...pl AsyncFnOnce()) {} + | ^^^^^^^^^^^^^ required by this bound in `call_async_once` error: aborting due to 1 previous error diff --git a/tests/ui/iterators/iter-macro-not-async-closure-simplified.rs b/tests/ui/iterators/iter-macro-not-async-closure-simplified.rs index 1f7952a1c79af..4fa14fda661cf 100644 --- a/tests/ui/iterators/iter-macro-not-async-closure-simplified.rs +++ b/tests/ui/iterators/iter-macro-not-async-closure-simplified.rs @@ -1,12 +1,21 @@ -// This test ensures iterators created with the `iter!` macro are not accidentally async closures. +// This test ensures iterators created with the `iter!` macro are not +// accidentally async closures. +// +// We test this both in a `narrow` and `wide` configuration because +// the way that the diagnostic is emitted varies depending on the +// diagnostic width. If it's too narrow to fit the explanation, that +// explanation is moved to the `help` instead of the span label. +// //@ edition: 2024 +//@ revisions: narrow wide +//@[narrow] compile-flags: --diagnostic-width=20 +//@[wide] compile-flags: --diagnostic-width=300 #![feature(yield_expr, iter_macro)] use std::iter::iter; -fn call_async_once(_: impl AsyncFnOnce()) { -} +fn call_async_once(_: impl AsyncFnOnce()) {} fn main() { let f = iter! { move || { diff --git a/tests/ui/iterators/iter-macro-not-async-closure-simplified.wide.stderr b/tests/ui/iterators/iter-macro-not-async-closure-simplified.wide.stderr new file mode 100644 index 0000000000000..a6c239c181b5f --- /dev/null +++ b/tests/ui/iterators/iter-macro-not-async-closure-simplified.wide.stderr @@ -0,0 +1,17 @@ +error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure-simplified.rs:21:21: 21:28}: AsyncFnOnce()` is not satisfied + --> $DIR/iter-macro-not-async-closure-simplified.rs:27:21 + | +LL | call_async_once(f); + | --------------- ^ the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure-simplified.rs:21:21: 21:28}` + | | + | required by a bound introduced by this call + | +note: required by a bound in `call_async_once` + --> $DIR/iter-macro-not-async-closure-simplified.rs:18:28 + | +LL | fn call_async_once(_: impl AsyncFnOnce()) {} + | ^^^^^^^^^^^^^ required by this bound in `call_async_once` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/iterators/iter-macro-not-async-closure.narrow.stderr b/tests/ui/iterators/iter-macro-not-async-closure.narrow.stderr new file mode 100644 index 0000000000000..f7b7579f71a2f --- /dev/null +++ b/tests/ui/iterators/iter-macro-not-async-closure.narrow.stderr @@ -0,0 +1,58 @@ +error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:26:21: 26:28}: AsyncFnOnce()` is not satisfied + --> $DIR/iter-macro-not-async-closure.rs:32:34 + | +LL | ...n!(call_async_once(f)); + | --------------- ^ unsatisfied trait bound + | | + | required by a bound introduced by this call + | + = help: the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:26:21: 26:28}` +note: required by a bound in `call_async_once` + --> $DIR/iter-macro-not-async-closure.rs:21:34 + | +LL | ...pl AsyncFnOnce()) { + | ^^^^^^^^^^^^^ required by this bound in `call_async_once` + +error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:26:21: 26:28}: AsyncFnOnce()` is not satisfied + --> $DIR/iter-macro-not-async-closure.rs:32:18 + | +LL | ...n!(call_async_once(f)); + | ^^^^^^^^^^^^^^^^^^ unsatisfied trait bound + | + = help: the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:26:21: 26:28}` +note: required by a bound in `call_async_once` + --> $DIR/iter-macro-not-async-closure.rs:21:34 + | +LL | ...pl AsyncFnOnce()) { + | ^^^^^^^^^^^^^ required by this bound in `call_async_once` + +error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:26:21: 26:28}: AsyncFnOnce()` is not satisfied + --> $DIR/iter-macro-not-async-closure.rs:32:13 + | +LL | ... = pin!(call_async_once(f)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound + | + = help: the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:26:21: 26:28}` +note: required by a bound in `call_async_once` + --> $DIR/iter-macro-not-async-closure.rs:21:34 + | +LL | ...pl AsyncFnOnce()) { + | ^^^^^^^^^^^^^ required by this bound in `call_async_once` + = note: this error originates in the macro `pin` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:26:21: 26:28}: AsyncFnOnce()` is not satisfied + --> $DIR/iter-macro-not-async-closure.rs:36:5 + | +LL | ...::noop())); + | ...^^^^^^^^^^ unsatisfied trait bound + | + = help: the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:26:21: 26:28}` +note: required by a bound in `call_async_once` + --> $DIR/iter-macro-not-async-closure.rs:21:34 + | +LL | ...pl AsyncFnOnce()) { + | ^^^^^^^^^^^^^ required by this bound in `call_async_once` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/iterators/iter-macro-not-async-closure.rs b/tests/ui/iterators/iter-macro-not-async-closure.rs index ffe53e9f653c2..4fb56a5f8063a 100644 --- a/tests/ui/iterators/iter-macro-not-async-closure.rs +++ b/tests/ui/iterators/iter-macro-not-async-closure.rs @@ -1,5 +1,15 @@ -// This test ensures iterators created with the `iter!` macro are not accidentally async closures. +// This test ensures iterators created with the `iter!` macro are not +// accidentally async closures. +// +// We test this both in a `narrow` and `wide` configuration because +// the way that the diagnostic is emitted varies depending on the +// diagnostic width. If it's too narrow to fit the explanation, that +// explanation is moved to the `help` instead of the span label. +// //@ edition: 2024 +//@ revisions: narrow wide +//@[narrow] compile-flags: --diagnostic-width=20 +//@[wide] compile-flags: --diagnostic-width=300 #![feature(yield_expr, iter_macro)] diff --git a/tests/ui/iterators/iter-macro-not-async-closure.stderr b/tests/ui/iterators/iter-macro-not-async-closure.wide.stderr similarity index 53% rename from tests/ui/iterators/iter-macro-not-async-closure.stderr rename to tests/ui/iterators/iter-macro-not-async-closure.wide.stderr index 84a9ed234ac1f..0f90676413b8c 100644 --- a/tests/ui/iterators/iter-macro-not-async-closure.stderr +++ b/tests/ui/iterators/iter-macro-not-async-closure.wide.stderr @@ -1,54 +1,50 @@ -error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:16:21: 16:28}: AsyncFnOnce()` is not satisfied - --> $DIR/iter-macro-not-async-closure.rs:22:34 +error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:26:21: 26:28}: AsyncFnOnce()` is not satisfied + --> $DIR/iter-macro-not-async-closure.rs:32:34 | LL | let x = pin!(call_async_once(f)); - | --------------- ^ unsatisfied trait bound + | --------------- ^ the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:26:21: 26:28}` | | | required by a bound introduced by this call | - = help: the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:16:21: 16:28}` note: required by a bound in `call_async_once` - --> $DIR/iter-macro-not-async-closure.rs:11:34 + --> $DIR/iter-macro-not-async-closure.rs:21:34 | LL | async fn call_async_once(f: impl AsyncFnOnce()) { | ^^^^^^^^^^^^^ required by this bound in `call_async_once` -error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:16:21: 16:28}: AsyncFnOnce()` is not satisfied - --> $DIR/iter-macro-not-async-closure.rs:22:18 +error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:26:21: 26:28}: AsyncFnOnce()` is not satisfied + --> $DIR/iter-macro-not-async-closure.rs:32:18 | LL | let x = pin!(call_async_once(f)); - | ^^^^^^^^^^^^^^^^^^ unsatisfied trait bound + | ^^^^^^^^^^^^^^^^^^ the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:26:21: 26:28}` | - = help: the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:16:21: 16:28}` note: required by a bound in `call_async_once` - --> $DIR/iter-macro-not-async-closure.rs:11:34 + --> $DIR/iter-macro-not-async-closure.rs:21:34 | LL | async fn call_async_once(f: impl AsyncFnOnce()) { | ^^^^^^^^^^^^^ required by this bound in `call_async_once` -error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:16:21: 16:28}: AsyncFnOnce()` is not satisfied - --> $DIR/iter-macro-not-async-closure.rs:22:13 +error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:26:21: 26:28}: AsyncFnOnce()` is not satisfied + --> $DIR/iter-macro-not-async-closure.rs:32:13 | LL | let x = pin!(call_async_once(f)); - | ^^^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:26:21: 26:28}` | - = help: the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:16:21: 16:28}` note: required by a bound in `call_async_once` - --> $DIR/iter-macro-not-async-closure.rs:11:34 + --> $DIR/iter-macro-not-async-closure.rs:21:34 | LL | async fn call_async_once(f: impl AsyncFnOnce()) { | ^^^^^^^^^^^^^ required by this bound in `call_async_once` = note: this error originates in the macro `pin` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:16:21: 16:28}: AsyncFnOnce()` is not satisfied - --> $DIR/iter-macro-not-async-closure.rs:26:5 +error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:26:21: 26:28}: AsyncFnOnce()` is not satisfied + --> $DIR/iter-macro-not-async-closure.rs:36:5 | LL | x.poll(&mut Context::from_waker(Waker::noop())); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:26:21: 26:28}` | - = help: the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:16:21: 16:28}` note: required by a bound in `call_async_once` - --> $DIR/iter-macro-not-async-closure.rs:11:34 + --> $DIR/iter-macro-not-async-closure.rs:21:34 | LL | async fn call_async_once(f: impl AsyncFnOnce()) { | ^^^^^^^^^^^^^ required by this bound in `call_async_once` From 121a5aec029f3c9be761443ec9421519d54196c0 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Wed, 9 Apr 2025 07:30:24 +0800 Subject: [PATCH 15/15] [EXPERIMENTAL] Try `//@ remap-src-base` --- .../iterators/iter-macro-not-async-closure.rs | 8 ++- .../iter-macro-not-async-closure.stderr | 54 +++++++++++++++++++ 2 files changed, 57 insertions(+), 5 deletions(-) create mode 100644 tests/ui/iterators/iter-macro-not-async-closure.stderr diff --git a/tests/ui/iterators/iter-macro-not-async-closure.rs b/tests/ui/iterators/iter-macro-not-async-closure.rs index 4fb56a5f8063a..22fc0aa6fe171 100644 --- a/tests/ui/iterators/iter-macro-not-async-closure.rs +++ b/tests/ui/iterators/iter-macro-not-async-closure.rs @@ -7,9 +7,7 @@ // explanation is moved to the `help` instead of the span label. // //@ edition: 2024 -//@ revisions: narrow wide -//@[narrow] compile-flags: --diagnostic-width=20 -//@[wide] compile-flags: --diagnostic-width=300 +//@ remap-src-base #![feature(yield_expr, iter_macro)] @@ -31,8 +29,8 @@ fn main() { let x = pin!(call_async_once(f)); //~^ ERROR AsyncFnOnce()` is not satisfied - //~^^ ERROR AsyncFnOnce()` is not satisfied - //~^^^ ERROR AsyncFnOnce()` is not satisfied + //~| ERROR AsyncFnOnce()` is not satisfied + //~| ERROR AsyncFnOnce()` is not satisfied x.poll(&mut Context::from_waker(Waker::noop())); //~^ ERROR AsyncFnOnce()` is not satisfied } diff --git a/tests/ui/iterators/iter-macro-not-async-closure.stderr b/tests/ui/iterators/iter-macro-not-async-closure.stderr new file mode 100644 index 0000000000000..ec69a042341d4 --- /dev/null +++ b/tests/ui/iterators/iter-macro-not-async-closure.stderr @@ -0,0 +1,54 @@ +error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:24:21: 24:28}: AsyncFnOnce()` is not satisfied + --> $DIR/iter-macro-not-async-closure.rs:30:34 + | +LL | let x = pin!(call_async_once(f)); + | --------------- ^ the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:24:21: 24:28}` + | | + | required by a bound introduced by this call + | +note: required by a bound in `call_async_once` + --> $DIR/iter-macro-not-async-closure.rs:19:34 + | +LL | async fn call_async_once(f: impl AsyncFnOnce()) { + | ^^^^^^^^^^^^^ required by this bound in `call_async_once` + +error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:24:21: 24:28}: AsyncFnOnce()` is not satisfied + --> $DIR/iter-macro-not-async-closure.rs:30:18 + | +LL | let x = pin!(call_async_once(f)); + | ^^^^^^^^^^^^^^^^^^ the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:24:21: 24:28}` + | +note: required by a bound in `call_async_once` + --> $DIR/iter-macro-not-async-closure.rs:19:34 + | +LL | async fn call_async_once(f: impl AsyncFnOnce()) { + | ^^^^^^^^^^^^^ required by this bound in `call_async_once` + +error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:24:21: 24:28}: AsyncFnOnce()` is not satisfied + --> $DIR/iter-macro-not-async-closure.rs:30:13 + | +LL | let x = pin!(call_async_once(f)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:24:21: 24:28}` + | +note: required by a bound in `call_async_once` + --> $DIR/iter-macro-not-async-closure.rs:19:34 + | +LL | async fn call_async_once(f: impl AsyncFnOnce()) { + | ^^^^^^^^^^^^^ required by this bound in `call_async_once` + = note: this error originates in the macro `pin` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:24:21: 24:28}: AsyncFnOnce()` is not satisfied + --> $DIR/iter-macro-not-async-closure.rs:34:5 + | +LL | x.poll(&mut Context::from_waker(Waker::noop())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:24:21: 24:28}` + | +note: required by a bound in `call_async_once` + --> $DIR/iter-macro-not-async-closure.rs:19:34 + | +LL | async fn call_async_once(f: impl AsyncFnOnce()) { + | ^^^^^^^^^^^^^ required by this bound in `call_async_once` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`.