about summary refs log tree commit diff
diff options
context:
space:
mode:
authorOli Scherer <github333195615777966@oli-obk.de>2025-02-27 09:51:23 +0000
committerEric Holk <ericholk@microsoft.com>2025-06-03 10:52:32 -0700
commit5fbdfc3e1018072e0c272d19308d72ac5165f01a (patch)
treeb8b697529f74f05d2d0636f5387e600ed449b822
parentaae43c4532690153af7465227816c93036bb1604 (diff)
downloadrust-5fbdfc3e1018072e0c272d19308d72ac5165f01a.tar.gz
rust-5fbdfc3e1018072e0c272d19308d72ac5165f01a.zip
Add `iter` macro
This adds an `iter!` macro that can be used to create movable
generators.

This also adds a yield_expr feature so the `yield` keyword can be used
within iter! macro bodies. This was needed because several unstable
features each need `yield` expressions, so this allows us to stabilize
them separately from any individual feature.

Co-authored-by: Oli Scherer <github35764891676564198441@oli-obk.de>
Co-authored-by: Jieyou Xu <jieyouxu@outlook.com>
Co-authored-by: Travis Cross <tc@traviscross.com>
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs15
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs7
-rw-r--r--compiler/rustc_borrowck/src/type_check/input_output.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/iter.rs53
-rw-r--r--compiler/rustc_builtin_macros/src/lib.rs2
-rw-r--r--compiler/rustc_const_eval/src/check_consts/check.rs7
-rw-r--r--compiler/rustc_const_eval/src/check_consts/ops.rs21
-rw-r--r--compiler/rustc_hir/src/hir.rs11
-rw-r--r--compiler/rustc_hir_typeck/src/closure.rs20
-rw-r--r--compiler/rustc_parse/src/parser/stmt.rs2
-rw-r--r--compiler/rustc_trait_selection/messages.ftl4
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs15
-rw-r--r--compiler/rustc_trait_selection/src/errors.rs5
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs22
-rw-r--r--library/core/src/iter/mod.rs2
-rw-r--r--library/core/src/iter/sources.rs3
-rw-r--r--library/core/src/iter/sources/generator.rs29
-rw-r--r--src/tools/miri/tests/pass/iter_macro.rs22
-rw-r--r--tests/ui/coroutine/const_gen_fn.rs2
-rw-r--r--tests/ui/coroutine/const_gen_fn.stderr16
-rw-r--r--tests/ui/coroutine/gen_block.none.stderr4
-rw-r--r--tests/ui/feature-gates/feature-gate-coroutines.e2024.stderr8
-rw-r--r--tests/ui/feature-gates/feature-gate-coroutines.none.stderr8
-rw-r--r--tests/ui/feature-gates/feature-gate-yield-expr.stderr2
-rw-r--r--tests/ui/iterators/generator.rs24
-rw-r--r--tests/ui/iterators/generator_args.rs24
-rw-r--r--tests/ui/iterators/generator_capture.rs27
-rw-r--r--tests/ui/iterators/generator_capture_.rs26
-rw-r--r--tests/ui/iterators/generator_capture_.stderr25
-rw-r--r--tests/ui/iterators/generator_capture_fail.rs25
-rw-r--r--tests/ui/iterators/generator_capture_fail.stderr20
-rw-r--r--tests/ui/iterators/generator_capture_fnonce.rs32
-rw-r--r--tests/ui/iterators/generator_capture_no_lend.rs30
-rw-r--r--tests/ui/iterators/generator_returned_from_fn.rs63
-rw-r--r--tests/ui/iterators/generator_returned_from_fn.stderr70
-rw-r--r--tests/ui/iterators/iter-macro-not-async-closure-simplified.narrow.stderr18
-rw-r--r--tests/ui/iterators/iter-macro-not-async-closure-simplified.rs29
-rw-r--r--tests/ui/iterators/iter-macro-not-async-closure-simplified.wide.stderr17
-rw-r--r--tests/ui/iterators/iter-macro-not-async-closure.narrow.stderr72
-rw-r--r--tests/ui/iterators/iter-macro-not-async-closure.rs32
-rw-r--r--tests/ui/iterators/iter-macro-not-async-closure.stderr67
-rw-r--r--tests/ui/suggestions/unnamable-types.rs2
-rw-r--r--tests/ui/type-alias-impl-trait/issue-53678-coroutine-and-const-fn.rs2
43 files changed, 826 insertions, 61 deletions
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 9f3aed9216c..537d4a2a6af 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;
 
@@ -1199,11 +1198,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 =
@@ -1247,7 +1248,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_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 915613a3913..3682d25d341 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -477,11 +477,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/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs
index c6b29fe36fd..0c46e0c0c22 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 00000000000..7ad83903a1b
--- /dev/null
+++ b/compiler/rustc_builtin_macros/src/iter.rs
@@ -0,0 +1,53 @@
+use rustc_ast::ptr::P;
+use rustc_ast::tokenstream::TokenStream;
+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;
+
+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<Expr>> {
+    let mut closure_parser = cx.new_parser_from_tts(stream);
+
+    let coroutine_kind = Some(CoroutineKind::Gen {
+        span,
+        closure_id: DUMMY_NODE_ID,
+        return_impl_trait_id: DUMMY_NODE_ID,
+    });
+
+    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())
+        }
+    }
+}
diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs
index 667d90429f2..aa52c3bd281 100644
--- a/compiler/rustc_builtin_macros/src/lib.rs
+++ b/compiler/rustc_builtin_macros/src/lib.rs
@@ -47,6 +47,7 @@ mod errors;
 mod format;
 mod format_foreign;
 mod global_allocator;
+mod iter;
 mod log_syntax;
 mod pattern_type;
 mod source_util;
@@ -95,6 +96,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_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs
index b67a3ce03a9..6167f8cd4b5 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,
-                            _,
-                        ),
-                    ) = 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 177ba56b165..d701646719a 100644
--- a/compiler/rustc_const_eval/src/check_consts/ops.rs
+++ b/compiler/rustc_const_eval/src/check_consts/ops.rs
@@ -486,24 +486,25 @@ 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,
+            )
+            // 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,
                 safe_to_expose_on_stable: false,
                 is_function_call: false,
-            }
-        } else {
-            Status::Forbidden
+            },
+            _ => Status::Forbidden,
         }
     }
 
     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 4f05e1c816c..433d5f98829 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -2061,12 +2061,19 @@ impl CoroutineKind {
             CoroutineKind::Coroutine(mov) => mov,
         }
     }
-}
 
-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/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs
index b1cb3ef4d79..cd3746be1d1 100644
--- a/compiler/rustc_hir_typeck/src/closure.rs
+++ b/compiler/rustc_hir_typeck/src/closure.rs
@@ -204,14 +204,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.
@@ -465,7 +470,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_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index ccc3410674b..c37cb0881c3 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -713,7 +713,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/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl
index 9b949a0a795..8232da4df43 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 9b5e421e0e4..fc5be111144 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
@@ -42,9 +42,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::{
@@ -886,9 +884,18 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         // is unimplemented is because async closures don't implement `Fn`/`FnMut`
         // if they have captures.
         if has_self_borrows && expected_kind != ty::ClosureKind::FnOnce {
-            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, _) => desugaring.to_string(),
+                coro => coro.to_string(),
+            };
+            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 779c861637a..0bea308ed5c 100644
--- a/compiler/rustc_trait_selection/src/errors.rs
+++ b/compiler/rustc_trait_selection/src/errors.rs
@@ -201,11 +201,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/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
index 7d869b27445..97ecf9702e6 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, elaborate};
@@ -438,6 +438,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         }
     }
 
+    #[instrument(level = "debug", skip(self, candidates))]
     fn assemble_async_closure_candidates(
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
@@ -446,15 +447,30 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         let goal_kind =
             self.tcx().async_fn_trait_kind_from_def_id(obligation.predicate.def_id()).unwrap();
 
+        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/library/core/src/iter/mod.rs b/library/core/src/iter/mod.rs
index d62a445d704..b85841295da 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 1eb4367b183..fd9330201ff 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 00000000000..c94232e09eb
--- /dev/null
+++ b/library/core/src/iter/sources/generator.rs
@@ -0,0 +1,29 @@
+/// 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)]
+/// # #[cfg(not(bootstrap))]
+/// # {
+///
+/// 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)]
+#[cfg_attr(not(bootstrap), rustc_builtin_macro)]
+pub macro iter($($t:tt)*) {
+    /* compiler-builtin */
+}
diff --git a/src/tools/miri/tests/pass/iter_macro.rs b/src/tools/miri/tests/pass/iter_macro.rs
new file mode 100644
index 00000000000..367c13f9969
--- /dev/null
+++ b/src/tools/miri/tests/pass/iter_macro.rs
@@ -0,0 +1,22 @@
+#![feature(iter_macro, yield_expr)]
+
+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);
+    assert_eq!(i.next(), None);
+    assert_eq!(i.next(), None);
+}
diff --git a/tests/ui/coroutine/const_gen_fn.rs b/tests/ui/coroutine/const_gen_fn.rs
index 2701139ffed..b044c185e0f 100644
--- a/tests/ui/coroutine/const_gen_fn.rs
+++ b/tests/ui/coroutine/const_gen_fn.rs
@@ -4,8 +4,10 @@
 
 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`
+//~^^ 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 4f3c73d1678..400ee216d06 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,17 @@ 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: `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/coroutine/gen_block.none.stderr b/tests/ui/coroutine/gen_block.none.stderr
index ed744f2957a..b793033b521 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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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 381e7a210be..c29c328ac14 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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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 381e7a210be..c29c328ac14 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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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 ad8a15a0f36..bfac9e49803 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 <https://github.com/rust-lang/rust/issues/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.rs b/tests/ui/iterators/generator.rs
new file mode 100644
index 00000000000..e633efb11c1
--- /dev/null
+++ b/tests/ui/iterators/generator.rs
@@ -0,0 +1,24 @@
+//@ run-pass
+
+#![feature(iter_macro, yield_expr)]
+
+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);
+    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 00000000000..c9da9e5fba6
--- /dev/null
+++ b/tests/ui/iterators/generator_args.rs
@@ -0,0 +1,24 @@
+//@ run-pass
+
+#![feature(iter_macro, yield_expr)]
+
+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 00000000000..c790b7a4b96
--- /dev/null
+++ b/tests/ui/iterators/generator_capture.rs
@@ -0,0 +1,27 @@
+//@ 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;
+            }
+        }}
+    };
+    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 00000000000..f630bc64b97
--- /dev/null
+++ b/tests/ui/iterators/generator_capture_.rs
@@ -0,0 +1,26 @@
+// This test exercises lending behavior for iterator closures which is not yet supported.
+
+#![feature(iter_macro, yield_expr)]
+
+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(); //~ ERROR use of moved value: `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_.stderr b/tests/ui/iterators/generator_capture_.stderr
new file mode 100644
index 00000000000..3d9647ae16f
--- /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_fail.rs b/tests/ui/iterators/generator_capture_fail.rs
new file mode 100644
index 00000000000..d987b2df011
--- /dev/null
+++ b/tests/ui/iterators/generator_capture_fail.rs
@@ -0,0 +1,25 @@
+#![feature(iter_macro, yield_expr)]
+
+use std::iter::iter;
+
+fn main() {
+    let i = {
+        let s = String::new();
+        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));
+    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 00000000000..225a385d6a0
--- /dev/null
+++ b/tests/ui/iterators/generator_capture_fail.stderr
@@ -0,0 +1,20 @@
+error[E0597]: `s` does not live long enough
+  --> $DIR/generator_capture_fail.rs:8:17
+   |
+LL |       let i = {
+   |           - borrow later stored here
+LL |           let s = String::new();
+LL |           iter! { || {
+   |  _________________^
+LL | |             yield s.len();
+LL | |             for x in 5..10 {
+LL | |                 yield x * 2;
+LL | |             }
+LL | |         } }
+   | |_________^ borrowed value does not live long enough
+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_capture_fnonce.rs b/tests/ui/iterators/generator_capture_fnonce.rs
new file mode 100644
index 00000000000..090727eb9b7
--- /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: Iterator<Item = usize>>(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);
+}
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 00000000000..822db58d48d
--- /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);
+}
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 00000000000..bd0317b1ffb
--- /dev/null
+++ b/tests/ui/iterators/generator_returned_from_fn.rs
@@ -0,0 +1,63 @@
+#![feature(iter_macro, impl_trait_in_fn_trait_return, yield_expr)]
+
+use std::iter::iter;
+
+fn plain() -> impl Fn() -> impl Iterator<Item = u32> {
+    iter! { || {
+        yield 0;
+        for x in 5..10 {
+            yield x * 2;
+        }
+    } }
+}
+
+fn arg() -> impl Fn(u32) -> impl Iterator<Item = u32> {
+    iter! { |arg| {
+        yield arg;
+        for x in 5..10 {
+            yield x * 2;
+        }
+    } }
+}
+
+fn capture<'a>(a: &'a u32) -> impl Fn() -> (impl Iterator<Item = u32> + '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<Item = u32> {
+    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<Item = u32> {
+    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<Item = u32> + '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 00000000000..b2324af6d5e
--- /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:24: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: gen closure does not implement `Fn` because it captures state from its environment
+  --> $DIR/generator_returned_from_fn.rs:33: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:33:13: 33:20}` here
+
+error[E0700]: hidden type for `impl FnOnce() -> impl Iterator<Item = u32>` captures lifetime that does not appear in bounds
+  --> $DIR/generator_returned_from_fn.rs:42:13
+   |
+LL |   fn capture_move_once(a: &u32) -> impl FnOnce() -> impl Iterator<Item = u32> {
+   |                           ----     ------------------------------------------ opaque type 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 | |
+LL | |
+LL | |         yield *a;
+...  |
+LL | |     } }
+   | |_____^
+   |
+help: add a `use<...>` bound to explicitly capture `'_`
+   |
+LL | fn capture_move_once(a: &u32) -> impl FnOnce() -> impl Iterator<Item = u32> + use<'_> {
+   |                                                                             +++++++++
+
+error[E0700]: hidden type for `impl Iterator<Item = u32>` captures lifetime that does not appear in bounds
+  --> $DIR/generator_returned_from_fn.rs:42:13
+   |
+LL |   fn capture_move_once(a: &u32) -> impl FnOnce() -> impl Iterator<Item = u32> {
+   |                           ----                      ------------------------- opaque type 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 | |
+LL | |
+LL | |         yield *a;
+...  |
+LL | |     } }
+   | |_____^
+   |
+help: add a `use<...>` bound to explicitly capture `'_`
+   |
+LL | fn capture_move_once(a: &u32) -> impl FnOnce() -> impl Iterator<Item = u32> + 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`.
diff --git a/tests/ui/iterators/iter-macro-not-async-closure-simplified.narrow.stderr b/tests/ui/iterators/iter-macro-not-async-closure-simplified.narrow.stderr
new file mode 100644
index 00000000000..4e0dabade2d
--- /dev/null
+++ b/tests/ui/iterators/iter-macro-not-async-closure-simplified.narrow.stderr
@@ -0,0 +1,18 @@
+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:21:21: 21:28}`
+note: required by a bound in `call_async_once`
+  --> $DIR/iter-macro-not-async-closure-simplified.rs:18:28
+   |
+LL | ...pl 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-simplified.rs b/tests/ui/iterators/iter-macro-not-async-closure-simplified.rs
new file mode 100644
index 00000000000..4fa14fda661
--- /dev/null
+++ b/tests/ui/iterators/iter-macro-not-async-closure-simplified.rs
@@ -0,0 +1,29 @@
+// 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 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.wide.stderr b/tests/ui/iterators/iter-macro-not-async-closure-simplified.wide.stderr
new file mode 100644
index 00000000000..a6c239c181b
--- /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 00000000000..af3289c3d4e
--- /dev/null
+++ b/tests/ui/iterators/iter-macro-not-async-closure.narrow.stderr
@@ -0,0 +1,72 @@
+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: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:37: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 5 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
new file mode 100644
index 00000000000..634391883ea
--- /dev/null
+++ b/tests/ui/iterators/iter-macro-not-async-closure.rs
@@ -0,0 +1,32 @@
+// This test ensures iterators created with the `iter!` macro are not
+// accidentally async closures.
+//
+//@ edition: 2024
+//@ remap-src-base
+
+#![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
+    //~^^^^ 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 00000000000..2f0343a2d0d
--- /dev/null
+++ b/tests/ui/iterators/iter-macro-not-async-closure.stderr
@@ -0,0 +1,67 @@
+error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:19:21: 19:28}: AsyncFnOnce()` is not satisfied
+  --> $DIR/iter-macro-not-async-closure.rs:25: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:19:21: 19: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:14: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:19:21: 19:28}: AsyncFnOnce()` is not satisfied
+  --> $DIR/iter-macro-not-async-closure.rs:25: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:19:21: 19:28}`
+   |
+note: required by a bound in `call_async_once`
+  --> $DIR/iter-macro-not-async-closure.rs:14: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:19:21: 19:28}: AsyncFnOnce()` is not satisfied
+  --> $DIR/iter-macro-not-async-closure.rs:25: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:19:21: 19:28}`
+   |
+note: required by a bound in `call_async_once`
+  --> $DIR/iter-macro-not-async-closure.rs:14: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:19:21: 19:28}: AsyncFnOnce()` is not satisfied
+  --> $DIR/iter-macro-not-async-closure.rs:25: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:19:21: 19:28}`
+   |
+note: required by a bound in `call_async_once`
+  --> $DIR/iter-macro-not-async-closure.rs:14: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:19:21: 19:28}: AsyncFnOnce()` is not satisfied
+  --> $DIR/iter-macro-not-async-closure.rs:30: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:19:21: 19:28}`
+   |
+note: required by a bound in `call_async_once`
+  --> $DIR/iter-macro-not-async-closure.rs:14:34
+   |
+LL | async fn call_async_once(f: impl AsyncFnOnce()) {
+   |                                  ^^^^^^^^^^^^^ required by this bound in `call_async_once`
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/suggestions/unnamable-types.rs b/tests/ui/suggestions/unnamable-types.rs
index 094584ff850..6772217a2f6 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 f02ccbbb93c..b258d2e156d 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