diff options
| author | bors <bors@rust-lang.org> | 2023-10-29 00:03:52 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2023-10-29 00:03:52 +0000 |
| commit | 2cad938a8173520f2bc39137b475f136be0aeeda (patch) | |
| tree | 668c1a104a33221cc0a6082cbe42e752d82690b3 | |
| parent | e5cfc55477eceed1317a02189fdf77a4a98f2124 (diff) | |
| parent | eb66d10cc3a6947cad6b4b169ed86b8c07f464d3 (diff) | |
| download | rust-2cad938a8173520f2bc39137b475f136be0aeeda.tar.gz rust-2cad938a8173520f2bc39137b475f136be0aeeda.zip | |
Auto merge of #116447 - oli-obk:gen_fn, r=compiler-errors
Implement `gen` blocks in the 2024 edition
Coroutines tracking issue https://github.com/rust-lang/rust/issues/43122
`gen` block tracking issue https://github.com/rust-lang/rust/issues/117078
This PR implements `gen` blocks that implement `Iterator`. Most of the logic with `async` blocks is shared, and thus I renamed various types that were referring to `async` specifically.
An example usage of `gen` blocks is
```rust
fn foo() -> impl Iterator<Item = i32> {
gen {
yield 42;
for i in 5..18 {
if i.is_even() { continue }
yield i * 2;
}
}
}
```
The limitations (to be resolved) of the implementation are listed in the tracking issue
75 files changed, 1096 insertions, 148 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index e09ba03d881..146a4db200c 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1235,7 +1235,7 @@ impl Expr { ExprKind::Closure(..) => ExprPrecedence::Closure, ExprKind::Block(..) => ExprPrecedence::Block, ExprKind::TryBlock(..) => ExprPrecedence::TryBlock, - ExprKind::Async(..) => ExprPrecedence::Async, + ExprKind::Gen(..) => ExprPrecedence::Gen, ExprKind::Await(..) => ExprPrecedence::Await, ExprKind::Assign(..) => ExprPrecedence::Assign, ExprKind::AssignOp(..) => ExprPrecedence::AssignOp, @@ -1405,11 +1405,9 @@ pub enum ExprKind { Closure(Box<Closure>), /// A block (`'label: { ... }`). Block(P<Block>, Option<Label>), - /// An async block (`async move { ... }`). - /// - /// The async block used to have a `NodeId`, which was removed in favor of - /// using the parent `NodeId` of the parent `Expr`. - Async(CaptureBy, P<Block>), + /// An `async` block (`async move { ... }`), + /// or a `gen` block (`gen move { ... }`) + Gen(CaptureBy, P<Block>, GenBlockKind), /// An await expression (`my_future.await`). Span is of await keyword. Await(P<Expr>, Span), @@ -1499,6 +1497,28 @@ pub enum ExprKind { Err, } +/// Used to differentiate between `async {}` blocks and `gen {}` blocks. +#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq)] +pub enum GenBlockKind { + Async, + Gen, +} + +impl fmt::Display for GenBlockKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.modifier().fmt(f) + } +} + +impl GenBlockKind { + pub fn modifier(&self) -> &'static str { + match self { + GenBlockKind::Async => "async", + GenBlockKind::Gen => "gen", + } + } +} + /// The explicit `Self` type in a "qualified path". The actual /// path, including the trait and the associated item, is stored /// separately. `position` represents the index of the associated @@ -2363,6 +2383,12 @@ pub enum Async { No, } +#[derive(Copy, Clone, Encodable, Decodable, Debug)] +pub enum Gen { + Yes { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId }, + No, +} + impl Async { pub fn is_async(self) -> bool { matches!(self, Async::Yes { .. }) diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index ba2887146cf..0634ee970ec 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1418,7 +1418,7 @@ pub fn noop_visit_expr<T: MutVisitor>( vis.visit_block(blk); visit_opt(label, |label| vis.visit_label(label)); } - ExprKind::Async(_capture_by, body) => { + ExprKind::Gen(_capture_by, body, _) => { vis.visit_block(body); } ExprKind::Await(expr, await_kw_span) => { diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index dd879a14567..914c97a14ac 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -197,6 +197,7 @@ pub fn ident_can_begin_expr(name: Symbol, span: Span, is_raw: bool) -> bool { kw::Continue, kw::False, kw::For, + kw::Gen, kw::If, kw::Let, kw::Loop, diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs index f9f1c0cf956..821fca6656c 100644 --- a/compiler/rustc_ast/src/util/classify.rs +++ b/compiler/rustc_ast/src/util/classify.rs @@ -46,7 +46,7 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> { Closure(closure) => { expr = &closure.body; } - Async(..) | Block(..) | ForLoop(..) | If(..) | Loop(..) | Match(..) | Struct(..) + Gen(..) | Block(..) | ForLoop(..) | If(..) | Loop(..) | Match(..) | Struct(..) | TryBlock(..) | While(..) => break Some(expr), _ => break None, } diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs index d3e43e20235..13768c12017 100644 --- a/compiler/rustc_ast/src/util/parser.rs +++ b/compiler/rustc_ast/src/util/parser.rs @@ -285,7 +285,7 @@ pub enum ExprPrecedence { Block, TryBlock, Struct, - Async, + Gen, Await, Err, } @@ -351,7 +351,7 @@ impl ExprPrecedence { | ExprPrecedence::ConstBlock | ExprPrecedence::Block | ExprPrecedence::TryBlock - | ExprPrecedence::Async + | ExprPrecedence::Gen | ExprPrecedence::Struct | ExprPrecedence::Err => PREC_PAREN, } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index e66c4a9ee26..e091961a144 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -872,7 +872,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { walk_list!(visitor, visit_label, opt_label); visitor.visit_block(block); } - ExprKind::Async(_, body) => { + ExprKind::Gen(_, body, _) => { visitor.visit_block(body); } ExprKind::Await(expr, _) => visitor.visit_expr(expr), diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index b82f878ea87..d6fa74dc3e6 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -183,7 +183,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self.arena.alloc_from_iter(arms.iter().map(|x| self.lower_arm(x))), hir::MatchSource::Normal, ), - ExprKind::Async(capture_clause, block) => self.make_async_expr( + ExprKind::Gen(capture_clause, block, GenBlockKind::Async) => self.make_async_expr( *capture_clause, e.id, None, @@ -317,6 +317,14 @@ impl<'hir> LoweringContext<'_, 'hir> { rest, ) } + ExprKind::Gen(capture_clause, block, GenBlockKind::Gen) => self.make_gen_expr( + *capture_clause, + e.id, + None, + e.span, + hir::CoroutineSource::Block, + |this| this.with_new_scopes(|this| this.lower_block_expr(block)), + ), ExprKind::Yield(opt_expr) => self.lower_expr_yield(e.span, opt_expr.as_deref()), ExprKind::Err => hir::ExprKind::Err( self.tcx.sess.delay_span_bug(e.span, "lowered ExprKind::Err"), @@ -661,6 +669,57 @@ impl<'hir> LoweringContext<'_, 'hir> { })) } + /// Lower a `gen` construct to a generator that implements `Iterator`. + /// + /// This results in: + /// + /// ```text + /// static move? |()| -> () { + /// <body> + /// } + /// ``` + pub(super) fn make_gen_expr( + &mut self, + capture_clause: CaptureBy, + closure_node_id: NodeId, + _yield_ty: Option<hir::FnRetTy<'hir>>, + span: Span, + gen_kind: hir::CoroutineSource, + body: impl FnOnce(&mut Self) -> hir::Expr<'hir>, + ) -> hir::ExprKind<'hir> { + let output = hir::FnRetTy::DefaultReturn(self.lower_span(span)); + + // The closure/generator `FnDecl` takes a single (resume) argument of type `input_ty`. + let fn_decl = self.arena.alloc(hir::FnDecl { + inputs: &[], + output, + c_variadic: false, + implicit_self: hir::ImplicitSelfKind::None, + lifetime_elision_allowed: false, + }); + + let body = self.lower_body(move |this| { + this.coroutine_kind = Some(hir::CoroutineKind::Gen(gen_kind)); + + let res = body(this); + (&[], res) + }); + + // `static |()| -> () { body }`: + hir::ExprKind::Closure(self.arena.alloc(hir::Closure { + def_id: self.local_def_id(closure_node_id), + binder: hir::ClosureBinder::Default, + capture_clause, + bound_generic_params: &[], + fn_decl, + body, + fn_decl_span: self.lower_span(span), + fn_arg_span: None, + movability: Some(Movability::Movable), + constness: hir::Constness::NotConst, + })) + } + /// Forwards a possible `#[track_caller]` annotation from `outer_hir_id` to /// `inner_hir_id` in case the `async_fn_track_caller` feature is enabled. pub(super) fn maybe_forward_track_caller( @@ -712,7 +771,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let full_span = expr.span.to(await_kw_span); match self.coroutine_kind { Some(hir::CoroutineKind::Async(_)) => {} - Some(hir::CoroutineKind::Coroutine) | None => { + Some(hir::CoroutineKind::Coroutine) | Some(hir::CoroutineKind::Gen(_)) | None => { self.tcx.sess.emit_err(AwaitOnlyInAsyncFnAndBlocks { await_kw_span, item_span: self.current_item, @@ -936,8 +995,8 @@ impl<'hir> LoweringContext<'_, 'hir> { } Some(movability) } - Some(hir::CoroutineKind::Async(_)) => { - panic!("non-`async` closure body turned `async` during lowering"); + Some(hir::CoroutineKind::Gen(_)) | Some(hir::CoroutineKind::Async(_)) => { + panic!("non-`async`/`gen` closure body turned `async`/`gen` during lowering"); } None => { if movability == Movability::Static { @@ -1445,11 +1504,22 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_expr_yield(&mut self, span: Span, opt_expr: Option<&Expr>) -> hir::ExprKind<'hir> { match self.coroutine_kind { - Some(hir::CoroutineKind::Coroutine) => {} + Some(hir::CoroutineKind::Gen(_)) => {} Some(hir::CoroutineKind::Async(_)) => { self.tcx.sess.emit_err(AsyncCoroutinesNotSupported { span }); } - None => self.coroutine_kind = Some(hir::CoroutineKind::Coroutine), + Some(hir::CoroutineKind::Coroutine) | None => { + if !self.tcx.features().coroutines { + rustc_session::parse::feature_err( + &self.tcx.sess.parse_sess, + sym::coroutines, + span, + "yield syntax is experimental", + ) + .emit(); + } + self.coroutine_kind = Some(hir::CoroutineKind::Coroutine) + } } let expr = diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 34532967d9e..a1bd2679137 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -554,7 +554,12 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { "consider removing `for<...>`" ); gate_all!(more_qualified_paths, "usage of qualified paths in this context is experimental"); - gate_all!(coroutines, "yield syntax is experimental"); + for &span in spans.get(&sym::yield_expr).iter().copied().flatten() { + if !span.at_least_rust_2024() { + gate_feature_post!(&visitor, coroutines, span, "yield syntax is experimental"); + } + } + gate_all!(gen_blocks, "gen blocks are experimental"); gate_all!(raw_ref_op, "raw address of syntax is experimental"); gate_all!(const_trait_impl, "const trait impls are experimental"); gate_all!( diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 269cb8f2380..e84af12d3f9 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -445,8 +445,8 @@ impl<'a> State<'a> { self.ibox(0); self.print_block_with_attrs(blk, attrs); } - ast::ExprKind::Async(capture_clause, blk) => { - self.word_nbsp("async"); + ast::ExprKind::Gen(capture_clause, blk, kind) => { + self.word_nbsp(kind.modifier()); self.print_capture_clause(*capture_clause); // cbox/ibox in analogy to the `ExprKind::Block` arm above self.cbox(0); diff --git a/compiler/rustc_borrowck/src/borrowck_errors.rs b/compiler/rustc_borrowck/src/borrowck_errors.rs index 2ceec1b5658..6731ef12306 100644 --- a/compiler/rustc_borrowck/src/borrowck_errors.rs +++ b/compiler/rustc_borrowck/src/borrowck_errors.rs @@ -373,11 +373,12 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { span: Span, yield_span: Span, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { + let coroutine_kind = self.body.coroutine.as_ref().unwrap().coroutine_kind; let mut err = struct_span_err!( self, span, E0626, - "borrow may still be in use when coroutine yields", + "borrow may still be in use when {coroutine_kind:#} yields", ); err.span_label(yield_span, "possible yield occurs here"); err diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 928c25d1061..247200dcd26 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -2491,11 +2491,17 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let (sugg_span, suggestion) = match tcx.sess.source_map().span_to_snippet(args_span) { Ok(string) => { - if string.starts_with("async ") { - let pos = args_span.lo() + BytePos(6); - (args_span.with_lo(pos).with_hi(pos), "move ") - } else if string.starts_with("async|") { - let pos = args_span.lo() + BytePos(5); + let coro_prefix = if string.starts_with("async") { + // `async` is 5 chars long. Not using `.len()` to avoid the cast from `usize` to `u32` + Some(5) + } else if string.starts_with("gen") { + // `gen` is 3 chars long + Some(3) + } else { + None + }; + if let Some(n) = coro_prefix { + let pos = args_span.lo() + BytePos(n); (args_span.with_lo(pos).with_hi(pos), " move") } else { (args_span.shrink_to_lo(), "move ") @@ -2505,6 +2511,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { }; let kind = match use_span.coroutine_kind() { Some(coroutine_kind) => match coroutine_kind { + CoroutineKind::Gen(kind) => match kind { + CoroutineSource::Block => "gen block", + CoroutineSource::Closure => "gen closure", + _ => bug!("gen block/closure expected, but gen function found."), + }, CoroutineKind::Async(async_kind) => match async_kind { CoroutineSource::Block => "async block", CoroutineSource::Closure => "async closure", diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index e630ac7c4fa..d38cfbc54d7 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -698,6 +698,20 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { " of async function" } }, + Some(hir::CoroutineKind::Gen(gen)) => match gen { + hir::CoroutineSource::Block => " of gen block", + hir::CoroutineSource::Closure => " of gen closure", + hir::CoroutineSource::Fn => { + let parent_item = + hir.get_by_def_id(hir.get_parent_item(mir_hir_id).def_id); + let output = &parent_item + .fn_decl() + .expect("coroutine lowered from gen fn should be in fn") + .output; + span = output.span(); + " of gen function" + } + }, Some(hir::CoroutineKind::Coroutine) => " of coroutine", None => " of closure", }; diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs index 6733b9e56ad..2a4bfe9e200 100644 --- a/compiler/rustc_builtin_macros/src/assert/context.rs +++ b/compiler/rustc_builtin_macros/src/assert/context.rs @@ -294,7 +294,7 @@ impl<'cx, 'a> Context<'cx, 'a> { // sync with the `rfc-2011-nicer-assert-messages/all-expr-kinds.rs` test. ExprKind::Assign(_, _, _) | ExprKind::AssignOp(_, _, _) - | ExprKind::Async(_, _) + | ExprKind::Gen(_, _, _) | ExprKind::Await(_, _) | ExprKind::Block(_, _) | ExprKind::Break(_, _) diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 5900c764073..1a85eb8dd79 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -560,6 +560,9 @@ pub fn push_item_name(tcx: TyCtxt<'_>, def_id: DefId, qualified: bool, output: & fn coroutine_kind_label(coroutine_kind: Option<CoroutineKind>) -> &'static str { match coroutine_kind { + Some(CoroutineKind::Gen(CoroutineSource::Block)) => "gen_block", + Some(CoroutineKind::Gen(CoroutineSource::Closure)) => "gen_closure", + Some(CoroutineKind::Gen(CoroutineSource::Fn)) => "gen_fn", Some(CoroutineKind::Async(CoroutineSource::Block)) => "async_block", Some(CoroutineKind::Async(CoroutineSource::Closure)) => "async_closure", Some(CoroutineKind::Async(CoroutineSource::Fn)) => "async_fn", diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 695de54eefa..72100863bb5 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -456,6 +456,8 @@ declare_features! ( (unstable, ffi_returns_twice, "1.34.0", Some(58314), None), /// Allows using `#[repr(align(...))]` on function items (unstable, fn_align, "1.53.0", Some(82232), None), + /// Allows defining gen blocks and `gen fn`. + (unstable, gen_blocks, "CURRENT_RUSTC_VERSION", Some(117078), None), /// Infer generic args for both consts and types. (unstable, generic_arg_infer, "1.55.0", Some(85077), None), /// An extension to the `generic_associated_types` feature, allowing incomplete features. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 17c6352ce24..d88f165b9e5 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1522,6 +1522,9 @@ pub enum CoroutineKind { /// An explicit `async` block or the body of an async function. Async(CoroutineSource), + /// An explicit `gen` block or the body of a `gen` function. + Gen(CoroutineSource), + /// A coroutine literal created via a `yield` inside a closure. Coroutine, } @@ -1538,6 +1541,14 @@ impl fmt::Display for CoroutineKind { k.fmt(f) } CoroutineKind::Coroutine => f.write_str("coroutine"), + CoroutineKind::Gen(k) => { + if f.alternate() { + f.write_str("`gen` ")?; + } else { + f.write_str("gen ")? + } + k.fmt(f) + } } } } @@ -2251,6 +2262,7 @@ impl From<CoroutineKind> for YieldSource { // Guess based on the kind of the current coroutine. CoroutineKind::Coroutine => Self::Yield, CoroutineKind::Async(_) => Self::Await { expr: None }, + CoroutineKind::Gen(_) => Self::Yield, } } } diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs index e7060dac844..14cd3881a06 100644 --- a/compiler/rustc_hir_typeck/src/check.rs +++ b/compiler/rustc_hir_typeck/src/check.rs @@ -58,15 +58,16 @@ pub(super) fn check_fn<'a, 'tcx>( if let Some(kind) = body.coroutine_kind && can_be_coroutine.is_some() { - let yield_ty = if kind == hir::CoroutineKind::Coroutine { - let yield_ty = fcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span, - }); - fcx.require_type_is_sized(yield_ty, span, traits::SizedYieldType); - yield_ty - } else { - Ty::new_unit(tcx) + let yield_ty = match kind { + hir::CoroutineKind::Gen(..) | hir::CoroutineKind::Coroutine => { + let yield_ty = fcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeInference, + span, + }); + fcx.require_type_is_sized(yield_ty, span, traits::SizedYieldType); + yield_ty + } + hir::CoroutineKind::Async(..) => Ty::new_unit(tcx), }; // Resume type defaults to `()` if the coroutine has no argument. diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 47bde21ceb2..b102ddd8d86 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -652,6 +652,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }, ) } + Some(hir::CoroutineKind::Gen(hir::CoroutineSource::Fn)) => { + todo!("gen closures do not exist yet") + } _ => astconv.ty_infer(None, decl.output.span()), }, diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index affa83fa348..c2aa015f4b7 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -148,8 +148,15 @@ impl<O> AssertKind<O> { RemainderByZero(_) => "attempt to calculate the remainder with a divisor of zero", ResumedAfterReturn(CoroutineKind::Coroutine) => "coroutine resumed after completion", ResumedAfterReturn(CoroutineKind::Async(_)) => "`async fn` resumed after completion", + ResumedAfterReturn(CoroutineKind::Gen(_)) => { + "`gen fn` should just keep returning `None` after completion" + } ResumedAfterPanic(CoroutineKind::Coroutine) => "coroutine resumed after panicking", ResumedAfterPanic(CoroutineKind::Async(_)) => "`async fn` resumed after panicking", + ResumedAfterPanic(CoroutineKind::Gen(_)) => { + "`gen fn` should just keep returning `None` after panicking" + } + BoundsCheck { .. } | MisalignedPointerDereference { .. } => { bug!("Unexpected AssertKind") } @@ -236,10 +243,15 @@ impl<O> AssertKind<O> { DivisionByZero(_) => middle_assert_divide_by_zero, RemainderByZero(_) => middle_assert_remainder_by_zero, ResumedAfterReturn(CoroutineKind::Async(_)) => middle_assert_async_resume_after_return, + ResumedAfterReturn(CoroutineKind::Gen(_)) => { + bug!("gen blocks can be resumed after they return and will keep returning `None`") + } ResumedAfterReturn(CoroutineKind::Coroutine) => { middle_assert_coroutine_resume_after_return } ResumedAfterPanic(CoroutineKind::Async(_)) => middle_assert_async_resume_after_panic, + // FIXME(gen_blocks): custom error message for `gen` blocks + ResumedAfterPanic(CoroutineKind::Gen(_)) => middle_assert_async_resume_after_panic, ResumedAfterPanic(CoroutineKind::Coroutine) => { middle_assert_coroutine_resume_after_panic } diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index bc6856bc900..f33421bbaa6 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -144,6 +144,10 @@ pub enum SelectionCandidate<'tcx> { /// generated for an async construct. FutureCandidate, + /// Implementation of an `Iterator` trait by one of the generator types + /// generated for a gen construct. + IteratorCandidate, + /// Implementation of a `Fn`-family trait by one of the anonymous /// types generated for a fn pointer type (e.g., `fn(int) -> int`) FnPointerCandidate { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index a669ff8d961..0b3b6700cec 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -782,6 +782,17 @@ impl<'tcx> TyCtxt<'tcx> { matches!(self.coroutine_kind(def_id), Some(hir::CoroutineKind::Async(_))) } + /// Returns `true` if the node pointed to by `def_id` is a general coroutine that implements `Coroutine`. + /// This means it is neither an `async` or `gen` construct. + pub fn is_general_coroutine(self, def_id: DefId) -> bool { + matches!(self.coroutine_kind(def_id), Some(hir::CoroutineKind::Coroutine)) + } + + /// Returns `true` if the node pointed to by `def_id` is a coroutine for a gen construct. + pub fn coroutine_is_gen(self, def_id: DefId) -> bool { + matches!(self.coroutine_kind(def_id), Some(hir::CoroutineKind::Gen(_))) + } + pub fn stability(self) -> &'tcx stability::Index { self.stability_index(()) } diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index be48c0e8926..d29d1902404 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -749,6 +749,7 @@ impl<'tcx> TyCtxt<'tcx> { DefKind::Coroutine => match self.coroutine_kind(def_id).unwrap() { rustc_hir::CoroutineKind::Async(..) => "async closure", rustc_hir::CoroutineKind::Coroutine => "coroutine", + rustc_hir::CoroutineKind::Gen(..) => "gen closure", }, _ => def_kind.descr(def_id), } @@ -766,6 +767,7 @@ impl<'tcx> TyCtxt<'tcx> { DefKind::Coroutine => match self.coroutine_kind(def_id).unwrap() { rustc_hir::CoroutineKind::Async(..) => "an", rustc_hir::CoroutineKind::Coroutine => "a", + rustc_hir::CoroutineKind::Gen(..) => "a", }, _ => def_kind.article(), } diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index fa56d59dd80..50d244d2831 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -224,7 +224,7 @@ struct SuspensionPoint<'tcx> { struct TransformVisitor<'tcx> { tcx: TyCtxt<'tcx>, - is_async_kind: bool, + coroutine_kind: hir::CoroutineKind, state_adt_ref: AdtDef<'tcx>, state_args: GenericArgsRef<'tcx>, @@ -249,6 +249,47 @@ struct TransformVisitor<'tcx> { } impl<'tcx> TransformVisitor<'tcx> { + fn insert_none_ret_block(&self, body: &mut Body<'tcx>) -> BasicBlock { + let block = BasicBlock::new(body.basic_blocks.len()); + + let source_info = SourceInfo::outermost(body.span); + + let (kind, idx) = self.coroutine_state_adt_and_variant_idx(true); + assert_eq!(self.state_adt_ref.variant(idx).fields.len(), 0); + let statements = vec![Statement { + kind: StatementKind::Assign(Box::new(( + Place::return_place(), + Rvalue::Aggregate(Box::new(kind), IndexVec::new()), + ))), + source_info, + }]; + + body.basic_blocks_mut().push(BasicBlockData { + statements, + terminator: Some(Terminator { source_info, kind: TerminatorKind::Return }), + is_cleanup: false, + }); + + block + } + + fn coroutine_state_adt_and_variant_idx( + &self, + is_return: bool, + ) -> (AggregateKind<'tcx>, VariantIdx) { + let idx = VariantIdx::new(match (is_return, self.coroutine_kind) { + (true, hir::CoroutineKind::Coroutine) => 1, // CoroutineState::Complete + (false, hir::CoroutineKind::Coroutine) => 0, // CoroutineState::Yielded + (true, hir::CoroutineKind::Async(_)) => 0, // Poll::Ready + (false, hir::CoroutineKind::Async(_)) => 1, // Poll::Pending + (true, hir::CoroutineKind::Gen(_)) => 0, // Option::None + (false, hir::CoroutineKind::Gen(_)) => 1, // Option::Some + }); + + let kind = AggregateKind::Adt(self.state_adt_ref.did(), idx, self.state_args, None, None); + (kind, idx) + } + // Make a `CoroutineState` or `Poll` variant assignment. // // `core::ops::CoroutineState` only has single element tuple variants, @@ -261,31 +302,44 @@ impl<'tcx> TransformVisitor<'tcx> { is_return: bool, statements: &mut Vec<Statement<'tcx>>, ) { - let idx = VariantIdx::new(match (is_return, self.is_async_kind) { - (true, false) => 1, // CoroutineState::Complete - (false, false) => 0, // CoroutineState::Yielded - (true, true) => 0, // Poll::Ready - (false, true) => 1, // Poll::Pending - }); + let (kind, idx) = self.coroutine_state_adt_and_variant_idx(is_return); - let kind = AggregateKind::Adt(self.state_adt_ref.did(), idx, self.state_args, None, None); + match self.coroutine_kind { + // `Poll::Pending` + CoroutineKind::Async(_) => { + if !is_return { + assert_eq!(self.state_adt_ref.variant(idx).fields.len(), 0); - // `Poll::Pending` - if self.is_async_kind && idx == VariantIdx::new(1) { - assert_eq!(self.state_adt_ref.variant(idx).fields.len(), 0); + // FIXME(swatinem): assert that `val` is indeed unit? + statements.push(Statement { + kind: StatementKind::Assign(Box::new(( + Place::return_place(), + Rvalue::Aggregate(Box::new(kind), IndexVec::new()), + ))), + source_info, + }); + return; + } + } + // `Option::None` + CoroutineKind::Gen(_) => { + if is_return { + assert_eq!(self.state_adt_ref.variant(idx).fields.len(), 0); - // FIXME(swatinem): assert that `val` is indeed unit? - statements.push(Statement { - kind: StatementKind::Assign(Box::new(( - Place::return_place(), - Rvalue::Aggregate(Box::new(kind), IndexVec::new()), - ))), - source_info, - }); - return; + statements.push(Statement { + kind: StatementKind::Assign(Box::new(( + Place::return_place(), + Rvalue::Aggregate(Box::new(kind), IndexVec::new()), + ))), + source_info, + }); + return; + } + } + CoroutineKind::Coroutine => {} } - // else: `Poll::Ready(x)`, `CoroutineState::Yielded(x)` or `CoroutineState::Complete(x)` + // else: `Poll::Ready(x)`, `CoroutineState::Yielded(x)`, `CoroutineState::Complete(x)`, or `Option::Some(x)` assert_eq!(self.state_adt_ref.variant(idx).fields.len(), 1); statements.push(Statement { @@ -1263,10 +1317,13 @@ fn create_coroutine_resume_function<'tcx>( } if can_return { - cases.insert( - 1, - (RETURNED, insert_panic_block(tcx, body, ResumedAfterReturn(coroutine_kind))), - ); + let block = match coroutine_kind { + CoroutineKind::Async(_) | CoroutineKind::Coroutine => { + insert_panic_block(tcx, body, ResumedAfterReturn(coroutine_kind)) + } + CoroutineKind::Gen(_) => transform.insert_none_ret_block(body), + }; + cases.insert(1, (RETURNED, block)); } insert_switch(body, cases, &transform, TerminatorKind::Unreachable); @@ -1439,18 +1496,28 @@ impl<'tcx> MirPass<'tcx> for StateTransform { }; let is_async_kind = matches!(body.coroutine_kind(), Some(CoroutineKind::Async(_))); - let (state_adt_ref, state_args) = if is_async_kind { - // Compute Poll<return_ty> - let poll_did = tcx.require_lang_item(LangItem::Poll, None); - let poll_adt_ref = tcx.adt_def(poll_did); - let poll_args = tcx.mk_args(&[body.return_ty().into()]); - (poll_adt_ref, poll_args) - } else { - // Compute CoroutineState<yield_ty, return_ty> - let state_did = tcx.require_lang_item(LangItem::CoroutineState, None); - let state_adt_ref = tcx.adt_def(state_did); - let state_args = tcx.mk_args(&[yield_ty.into(), body.return_ty().into()]); - (state_adt_ref, state_args) + let (state_adt_ref, state_args) = match body.coroutine_kind().unwrap() { + CoroutineKind::Async(_) => { + // Compute Poll<return_ty> + let poll_did = tcx.require_lang_item(LangItem::Poll, None); + let poll_adt_ref = tcx.adt_def(poll_did); + let poll_args = tcx.mk_args(&[body.return_ty().into()]); + (poll_adt_ref, poll_args) + } + CoroutineKind::Gen(_) => { + // Compute Option<yield_ty> + let option_did = tcx.require_lang_item(LangItem::Option, None); + let option_adt_ref = tcx.adt_def(option_did); + let option_args = tcx.mk_args(&[body.yield_ty().unwrap().into()]); + (option_adt_ref, option_args) + } + CoroutineKind::Coroutine => { + // Compute CoroutineState<yield_ty, return_ty> + let state_did = tcx.require_lang_item(LangItem::CoroutineState, None); + let state_adt_ref = tcx.adt_def(state_did); + let state_args = tcx.mk_args(&[yield_ty.into(), body.return_ty().into()]); + (state_adt_ref, state_args) + } }; let ret_ty = Ty::new_adt(tcx, state_adt_ref, state_args); @@ -1518,7 +1585,7 @@ impl<'tcx> MirPass<'tcx> for StateTransform { // or Poll::Ready(x) and Poll::Pending respectively depending on `is_async_kind`. let mut transform = TransformVisitor { tcx, - is_async_kind, + coroutine_kind: body.coroutine_kind().unwrap(), state_adt_ref, state_args, remap, diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index e0778f72bfe..9bedd7b8a33 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -278,6 +278,9 @@ parse_found_expr_would_be_stmt = expected expression, found `{$token}` parse_function_body_equals_expr = function body cannot be `= expression;` .suggestion = surround the expression with `{"{"}` and `{"}"}` instead of `=` and `;` +parse_gen_block = `gen` blocks are not yet implemented + .help = only the keyword is reserved for now + parse_generic_args_in_pat_require_turbofish_syntax = generic args in patterns require the turbofish syntax parse_generic_parameters_without_angle_brackets = generic parameters without surrounding angle brackets diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index aeb4fd0a304..c0e94d15da0 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -521,6 +521,14 @@ pub(crate) struct CatchAfterTry { } #[derive(Diagnostic)] +#[diag(parse_gen_block)] +#[help] +pub(crate) struct GenBlock { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] #[diag(parse_comma_after_base_struct)] #[note] pub(crate) struct CommaAfterBaseStruct { diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 5157106f4e2..0b5ec9b59ea 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -9,7 +9,7 @@ use super::{ use crate::errors; use crate::maybe_recover_from_interpolated_ty_qpath; use ast::mut_visit::{noop_visit_expr, MutVisitor}; -use ast::{Path, PathSegment}; +use ast::{GenBlockKind, Path, PathSegment}; use core::mem; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; @@ -1441,14 +1441,20 @@ impl<'a> Parser<'a> { } else if this.token.uninterpolated_span().at_least_rust_2018() { // `Span:.at_least_rust_2018()` is somewhat expensive; don't get it repeatedly. if this.check_keyword(kw::Async) { - if this.is_async_block() { + if this.is_gen_block(kw::Async) { // Check for `async {` and `async move {`. - this.parse_async_block() + this.parse_gen_block() } else { this.parse_expr_closure() } } else if this.eat_keyword(kw::Await) { this.recover_incorrect_await_syntax(lo, this.prev_token.span) + } else if this.token.uninterpolated_span().at_least_rust_2024() { + if this.is_gen_block(kw::Gen) { + this.parse_gen_block() + } else { + this.parse_expr_lit() + } } else { this.parse_expr_lit() } @@ -1848,7 +1854,7 @@ impl<'a> Parser<'a> { let lo = self.prev_token.span; let kind = ExprKind::Yield(self.parse_expr_opt()?); let span = lo.to(self.prev_token.span); - self.sess.gated_spans.gate(sym::coroutines, span); + self.sess.gated_spans.gate(sym::yield_expr, span); let expr = self.mk_expr(span, kind); self.maybe_recover_from_bad_qpath(expr) } @@ -3059,18 +3065,24 @@ impl<'a> Parser<'a> { && self.token.uninterpolated_span().at_least_rust_2018() } - /// Parses an `async move? {...}` expression. - fn parse_async_block(&mut self) -> PResult<'a, P<Expr>> { + /// Parses an `async move? {...}` or `gen move? {...}` expression. + fn parse_gen_block(&mut self) -> PResult<'a, P<Expr>> { let lo = self.token.span; - self.expect_keyword(kw::Async)?; + let kind = if self.eat_keyword(kw::Async) { + GenBlockKind::Async + } else { + assert!(self.eat_keyword(kw::Gen)); + self.sess.gated_spans.gate(sym::gen_blocks, lo.to(self.token.span)); + GenBlockKind::Gen + }; let capture_clause = self.parse_capture_clause()?; let (attrs, body) = self.parse_inner_attrs_and_block()?; - let kind = ExprKind::Async(capture_clause, body); + let kind = ExprKind::Gen(capture_clause, body, kind); Ok(self.mk_expr_with_attrs(lo.to(self.prev_token.span), kind, attrs)) } - fn is_async_block(&self) -> bool { - self.token.is_keyword(kw::Async) + fn is_gen_block(&self, kw: Symbol) -> bool { + self.token.is_keyword(kw) && (( // `async move {` self.is_keyword_ahead(1, &[kw::Move]) @@ -3596,7 +3608,7 @@ impl MutVisitor for CondChecker<'_> { | ExprKind::Match(_, _) | ExprKind::Closure(_) | ExprKind::Block(_, _) - | ExprKind::Async(_, _) + | ExprKind::Gen(_, _, _) | ExprKind::TryBlock(_) | ExprKind::Underscore | ExprKind::Path(_, _) diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 982f601c0d5..4c76ade0139 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -2297,9 +2297,9 @@ impl<'a> Parser<'a> { // `pub` is added in case users got confused with the ordering like `async pub fn`, // only if it wasn't preceded by `default` as `default pub` is invalid. let quals: &[Symbol] = if check_pub { - &[kw::Pub, kw::Const, kw::Async, kw::Unsafe, kw::Extern] + &[kw::Pub, kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Extern] } else { - &[kw::Const, kw::Async, kw::Unsafe, kw::Extern] + &[kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Extern] }; self.check_keyword_case(kw::Fn, case) // Definitely an `fn`. // `$qual fn` or `$qual $qual`: @@ -2353,6 +2353,9 @@ impl<'a> Parser<'a> { let async_start_sp = self.token.span; let asyncness = self.parse_asyncness(case); + let _gen_start_sp = self.token.span; + let genness = self.parse_genness(case); + let unsafe_start_sp = self.token.span; let unsafety = self.parse_unsafety(case); @@ -2368,6 +2371,10 @@ impl<'a> Parser<'a> { } } + if let Gen::Yes { span, .. } = genness { + self.sess.emit_err(errors::GenBlock { span }); + } + if !self.eat_keyword_case(kw::Fn, case) { // It is possible for `expect_one_of` to recover given the contents of // `self.expected_tokens`, therefore, do not use `self.unexpected()` which doesn't diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 41b19ecb63a..59b51954542 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -11,6 +11,7 @@ mod stmt; mod ty; use crate::lexer::UnmatchedDelim; +use ast::Gen; pub use attr_wrapper::AttrWrapper; pub use diagnostics::AttemptLocalParseRecovery; pub(crate) use expr::ForbiddenLetReason; @@ -1128,6 +1129,16 @@ impl<'a> Parser<'a> { } } + /// Parses genness: `gen` or nothing. + fn parse_genness(&mut self, case: Case) -> Gen { + if self.token.span.at_least_rust_2024() && self.eat_keyword_case(kw::Gen, case) { + let span = self.prev_token.uninterpolated_span(); + Gen::Yes { span, closure_id: DUMMY_NODE_ID, return_impl_trait_id: DUMMY_NODE_ID } + } else { + Gen::No + } + } + /// Parses unsafety: `unsafe` or nothing. fn parse_unsafety(&mut self, case: Case) -> Unsafe { if self.eat_keyword_case(kw::Unsafe, case) { diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index 24087a4eabb..f915c1057d6 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -567,10 +567,10 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { (self, e, e.kind, Id::None, ast, Expr, ExprKind), [ Array, ConstBlock, Call, MethodCall, Tup, Binary, Unary, Lit, Cast, Type, Let, - If, While, ForLoop, Loop, Match, Closure, Block, Async, Await, TryBlock, Assign, + If, While, ForLoop, Loop, Match, Closure, Block, Await, TryBlock, Assign, AssignOp, Field, Index, Range, Underscore, Path, AddrOf, Break, Continue, Ret, InlineAsm, FormatArgs, OffsetOf, MacCall, Struct, Repeat, Paren, Try, Yield, Yeet, - Become, IncludedBytes, Err + Become, IncludedBytes, Gen, Err ] ); ast_visit::walk_expr(self, e) diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 356d7f365fe..13df7efe636 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -260,7 +260,7 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { Async::No => closure_def, } } - ExprKind::Async(_, _) => self.create_def(expr.id, DefPathData::ClosureExpr, expr.span), + ExprKind::Gen(_, _, _) => self.create_def(expr.id, DefPathData::ClosureExpr, expr.span), _ => self.parent_def, }; diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index f2fd4b0bf60..1a50bd5ec98 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -1083,7 +1083,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { for rib in ribs { match rib.kind { RibKind::Normal - | RibKind::ClosureOrAsync + | RibKind::FnOrCoroutine | RibKind::Module(..) | RibKind::MacroDefinition(..) | RibKind::ForwardGenericParamBan => { @@ -1156,7 +1156,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { for rib in ribs { let has_generic_params: HasGenericParams = match rib.kind { RibKind::Normal - | RibKind::ClosureOrAsync + | RibKind::FnOrCoroutine | RibKind::Module(..) | RibKind::MacroDefinition(..) | RibKind::InlineAsmSym @@ -1240,7 +1240,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { for rib in ribs { let has_generic_params = match rib.kind { RibKind::Normal - | RibKind::ClosureOrAsync + | RibKind::FnOrCoroutine | RibKind::Module(..) | RibKind::MacroDefinition(..) | RibKind::InlineAsmSym diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 42ca407cddf..3be962dab90 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -177,8 +177,8 @@ pub(crate) enum RibKind<'a> { /// upvars). AssocItem, - /// We passed through a closure. Disallow labels. - ClosureOrAsync, + /// We passed through a function, closure or coroutine signature. Disallow labels. + FnOrCoroutine, /// We passed through an item scope. Disallow upvars. Item(HasGenericParams), @@ -215,7 +215,7 @@ impl RibKind<'_> { pub(crate) fn contains_params(&self) -> bool { match self { RibKind::Normal - | RibKind::ClosureOrAsync + | RibKind::FnOrCoroutine | RibKind::ConstantItem(..) | RibKind::Module(_) | RibKind::MacroDefinition(_) @@ -231,7 +231,7 @@ impl RibKind<'_> { RibKind::Normal | RibKind::MacroDefinition(..) => false, RibKind::AssocItem - | RibKind::ClosureOrAsync + | RibKind::FnOrCoroutine | RibKind::Item(..) | RibKind::ConstantItem(..) | RibKind::Module(..) @@ -924,9 +924,9 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, debug!("(resolving function) entering function"); // Create a value rib for the function. - self.with_rib(ValueNS, RibKind::ClosureOrAsync, |this| { + self.with_rib(ValueNS, RibKind::FnOrCoroutine, |this| { // Create a label rib for the function. - this.with_label_rib(RibKind::ClosureOrAsync, |this| { + this.with_label_rib(RibKind::FnOrCoroutine, |this| { match fn_kind { FnKind::Fn(_, _, sig, _, generics, body) => { this.visit_generics(generics); @@ -4287,7 +4287,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { .. }) => { self.with_rib(ValueNS, RibKind::Normal, |this| { - this.with_label_rib(RibKind::ClosureOrAsync, |this| { + this.with_label_rib(RibKind::FnOrCoroutine, |this| { // Resolve arguments: this.resolve_params(&fn_decl.inputs); // No need to resolve return type -- @@ -4304,7 +4304,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { }) }); } - // For closures, ClosureOrAsyncRibKind is added in visit_fn + // For closures, RibKind::FnOrCoroutine is added in visit_fn ExprKind::Closure(box ast::Closure { binder: ClosureBinder::For { ref generic_params, span }, .. @@ -4321,8 +4321,8 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { ); } ExprKind::Closure(..) => visit::walk_expr(self, expr), - ExprKind::Async(..) => { - self.with_label_rib(RibKind::ClosureOrAsync, |this| visit::walk_expr(this, expr)); + ExprKind::Gen(..) => { + self.with_label_rib(RibKind::FnOrCoroutine, |this| visit::walk_expr(this, expr)); } ExprKind::Repeat(ref elem, ref ct) => { self.visit_expr(elem); diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index eb868913017..5ab5a048ffa 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -879,18 +879,28 @@ impl<'tcx> Stable<'tcx> for mir::AggregateKind<'tcx> { } } +impl<'tcx> Stable<'tcx> for rustc_hir::CoroutineSource { + type T = stable_mir::mir::CoroutineSource; + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { + use rustc_hir::CoroutineSource; + match self { + CoroutineSource::Block => stable_mir::mir::CoroutineSource::Block, + CoroutineSource::Closure => stable_mir::mir::CoroutineSource::Closure, + CoroutineSource::Fn => stable_mir::mir::CoroutineSource::Fn, + } + } +} + impl<'tcx> Stable<'tcx> for rustc_hir::CoroutineKind { type T = stable_mir::mir::CoroutineKind; - fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { - use rustc_hir::{CoroutineKind, CoroutineSource}; + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + use rustc_hir::CoroutineKind; match self { - CoroutineKind::Async(async_gen) => { - let async_gen = match async_gen { - CoroutineSource::Block => stable_mir::mir::CoroutineSource::Block, - CoroutineSource::Closure => stable_mir::mir::CoroutineSource::Closure, - CoroutineSource::Fn => stable_mir::mir::CoroutineSource::Fn, - }; - stable_mir::mir::CoroutineKind::Async(async_gen) + CoroutineKind::Async(source) => { + stable_mir::mir::CoroutineKind::Async(source.stable(tables)) + } + CoroutineKind::Gen(source) => { + stable_mir::mir::CoroutineKind::Gen(source.stable(tables)) } CoroutineKind::Coroutine => stable_mir::mir::CoroutineKind::Coroutine, } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 88d9dab2ba5..3f99d2a4b1f 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -98,6 +98,7 @@ symbols! { Builtin: "builtin", Catch: "catch", Default: "default", + Gen: "gen", MacroRules: "macro_rules", Raw: "raw", Union: "union", @@ -225,6 +226,7 @@ symbols! { IpAddr, IrTyKind, Is, + Item, ItemContext, IterEmpty, IterOnce, @@ -818,6 +820,7 @@ symbols! { future_trait, gdb_script_file, ge, + gen_blocks, gen_future, gen_kill, generator_clone, @@ -1779,6 +1782,7 @@ symbols! { xmm_reg, yeet_desugar_details, yeet_expr, + yield_expr, ymm_reg, zmm_reg, } @@ -2189,8 +2193,9 @@ impl Symbol { self >= kw::Abstract && self <= kw::Yield } - fn is_unused_keyword_conditional(self, edition: impl FnOnce() -> Edition) -> bool { - self == kw::Try && edition() >= Edition::Edition2018 + fn is_unused_keyword_conditional(self, edition: impl Copy + FnOnce() -> Edition) -> bool { + self == kw::Try && edition().at_least_rust_2018() + || self == kw::Gen && edition().at_least_rust_2024() } pub fn is_reserved(self, edition: impl Copy + FnOnce() -> Edition) -> bool { diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index 6562bc86f99..1ab2bb40af7 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -201,7 +201,15 @@ pub(super) trait GoalKind<'tcx>: goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; - /// A coroutine (that doesn't come from an `async` desugaring) is known to + /// A coroutine (that comes from a `gen` desugaring) is known to implement + /// `Iterator<Item = O>`, where `O` is given by the generator's yield type + /// that was computed during type-checking. + fn consider_builtin_iterator_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx>; + + /// A coroutine (that doesn't come from an `async` or `gen` desugaring) is known to /// implement `Coroutine<R, Yield = Y, Return = O>`, given the resume, yield, /// and return types of the coroutine computed during type-checking. fn consider_builtin_coroutine_candidate( @@ -554,6 +562,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { G::consider_builtin_pointee_candidate(self, goal) } else if lang_items.future_trait() == Some(trait_def_id) { G::consider_builtin_future_candidate(self, goal) + } else if lang_items.iterator_trait() == Some(trait_def_id) { + G::consider_builtin_iterator_candidate(self, goal) } else if lang_items.gen_trait() == Some(trait_def_id) { G::consider_builtin_coroutine_candidate(self, goal) } else if lang_items.discriminant_kind_trait() == Some(trait_def_id) { diff --git a/compiler/rustc_trait_selection/src/solve/project_goals/mod.rs b/compiler/rustc_trait_selection/src/solve/project_goals/mod.rs index be631552c57..240141065dc 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals/mod.rs @@ -489,6 +489,37 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ) } + fn consider_builtin_iterator_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + let self_ty = goal.predicate.self_ty(); + let ty::Coroutine(def_id, args, _) = *self_ty.kind() else { + return Err(NoSolution); + }; + + // Coroutines are not Iterators unless they come from `gen` desugaring + let tcx = ecx.tcx(); + if !tcx.coroutine_is_gen(def_id) { + return Err(NoSolution); + } + + let term = args.as_coroutine().yield_ty().into(); + + Self::consider_implied_clause( + ecx, + goal, + ty::ProjectionPredicate { + projection_ty: ty::AliasTy::new(ecx.tcx(), goal.predicate.def_id(), [self_ty]), + term, + } + .to_predicate(tcx), + // Technically, we need to check that the iterator type is Sized, + // but that's already proven by the generator being WF. + [], + ) + } + fn consider_builtin_coroutine_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, @@ -500,7 +531,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { // `async`-desugared coroutines do not implement the coroutine trait let tcx = ecx.tcx(); - if tcx.coroutine_is_async(def_id) { + if !tcx.is_general_coroutine(def_id) { return Err(NoSolution); } @@ -527,7 +558,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { term, } .to_predicate(tcx), - // Technically, we need to check that the future type is Sized, + // Technically, we need to check that the coroutine type is Sized, // but that's already proven by the coroutine being WF. [], ) diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 7c4f6562f10..a0e2ad6e202 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -350,6 +350,30 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } + fn consider_builtin_iterator_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + if goal.predicate.polarity != ty::ImplPolarity::Positive { + return Err(NoSolution); + } + + let ty::Coroutine(def_id, _, _) = *goal.predicate.self_ty().kind() else { + return Err(NoSolution); + }; + + // Coroutines are not iterators unless they come from `gen` desugaring + let tcx = ecx.tcx(); + if !tcx.coroutine_is_gen(def_id) { + return Err(NoSolution); + } + + // Gen coroutines unconditionally implement `Iterator` + // Technically, we need to check that the iterator output type is Sized, + // but that's already proven by the coroutines being WF. + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + fn consider_builtin_coroutine_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, @@ -365,7 +389,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { // `async`-desugared coroutines do not implement the coroutine trait let tcx = ecx.tcx(); - if tcx.coroutine_is_async(def_id) { + if !tcx.is_general_coroutine(def_id) { return Err(NoSolution); } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index aad07d7683a..31da437f2e9 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -2425,6 +2425,21 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { CoroutineKind::Async(CoroutineSource::Closure) => { format!("future created by async closure is not {trait_name}") } + CoroutineKind::Gen(CoroutineSource::Fn) => self + .tcx + .parent(coroutine_did) + .as_local() + .map(|parent_did| hir.local_def_id_to_hir_id(parent_did)) + .and_then(|parent_hir_id| hir.opt_name(parent_hir_id)) + .map(|name| { + format!("iterator returned by `{name}` is not {trait_name}") + })?, + CoroutineKind::Gen(CoroutineSource::Block) => { + format!("iterator created by gen block is not {trait_name}") + } + CoroutineKind::Gen(CoroutineSource::Closure) => { + format!("iterator created by gen closure is not {trait_name}") + } }) }) .unwrap_or_else(|| format!("{future_or_coroutine} is not {trait_name}")); @@ -2931,7 +2946,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } ObligationCauseCode::SizedCoroutineInterior(coroutine_def_id) => { let what = match self.tcx.coroutine_kind(coroutine_def_id) { - None | Some(hir::CoroutineKind::Coroutine) => "yield", + None | Some(hir::CoroutineKind::Coroutine) | Some(hir::CoroutineKind::Gen(_)) => "yield", Some(hir::CoroutineKind::Async(..)) => "await", }; err.note(format!( diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index af27f5cf4cb..a697c6650b2 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -1653,6 +1653,9 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { hir::CoroutineKind::Async(hir::CoroutineSource::Block) => "an async block", hir::CoroutineKind::Async(hir::CoroutineSource::Fn) => "an async function", hir::CoroutineKind::Async(hir::CoroutineSource::Closure) => "an async closure", + hir::CoroutineKind::Gen(hir::CoroutineSource::Block) => "a gen block", + hir::CoroutineKind::Gen(hir::CoroutineSource::Fn) => "a gen function", + hir::CoroutineKind::Gen(hir::CoroutineSource::Closure) => "a gen closure", }) } diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index af30d9121d1..20aa3cec873 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1798,7 +1798,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty()); let lang_items = selcx.tcx().lang_items(); - if [lang_items.gen_trait(), lang_items.future_trait()].contains(&Some(trait_ref.def_id)) + if [lang_items.gen_trait(), lang_items.future_trait(), lang_items.iterator_trait()].contains(&Some(trait_ref.def_id)) || selcx.tcx().fn_trait_kind_from_def_id(trait_ref.def_id).is_some() { true @@ -2015,6 +2015,8 @@ fn confirm_select_candidate<'cx, 'tcx>( confirm_coroutine_candidate(selcx, obligation, data) } else if lang_items.future_trait() == Some(trait_def_id) { confirm_future_candidate(selcx, obligation, data) + } else if lang_items.iterator_trait() == Some(trait_def_id) { + confirm_iterator_candidate(selcx, obligation, data) } else if selcx.tcx().fn_trait_kind_from_def_id(trait_def_id).is_some() { if obligation.predicate.self_ty().is_closure() { confirm_closure_candidate(selcx, obligation, data) @@ -2135,6 +2137,50 @@ fn confirm_future_candidate<'cx, 'tcx>( .with_addl_obligations(obligations) } +fn confirm_iterator_candidate<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + nested: Vec<PredicateObligation<'tcx>>, +) -> Progress<'tcx> { + let ty::Coroutine(_, args, _) = + selcx.infcx.shallow_resolve(obligation.predicate.self_ty()).kind() + else { + unreachable!() + }; + let gen_sig = args.as_coroutine().poly_sig(); + let Normalized { value: gen_sig, obligations } = normalize_with_depth( + selcx, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + gen_sig, + ); + + debug!(?obligation, ?gen_sig, ?obligations, "confirm_future_candidate"); + + let tcx = selcx.tcx(); + let iter_def_id = tcx.require_lang_item(LangItem::Iterator, None); + + let predicate = super::util::iterator_trait_ref_and_outputs( + tcx, + iter_def_id, + obligation.predicate.self_ty(), + gen_sig, + ) + .map_bound(|(trait_ref, yield_ty)| { + debug_assert_eq!(tcx.associated_item(obligation.predicate.def_id).name, sym::Item); + + ty::ProjectionPredicate { + projection_ty: ty::AliasTy::new(tcx, obligation.predicate.def_id, trait_ref.args), + term: yield_ty.into(), + } + }); + + confirm_param_env_candidate(selcx, obligation, predicate, false) + .with_addl_obligations(nested) + .with_addl_obligations(obligations) +} + fn confirm_builtin_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, 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 123881d9bc6..ee8e1bf7675 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -113,6 +113,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.assemble_coroutine_candidates(obligation, &mut candidates); } else if lang_items.future_trait() == Some(def_id) { self.assemble_future_candidates(obligation, &mut candidates); + } else if lang_items.iterator_trait() == Some(def_id) { + self.assemble_iterator_candidates(obligation, &mut candidates); } self.assemble_closure_candidates(obligation, &mut candidates); @@ -210,9 +212,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // type/region parameters. let self_ty = obligation.self_ty().skip_binder(); match self_ty.kind() { - // async constructs get lowered to a special kind of coroutine that + // `async`/`gen` constructs get lowered to a special kind of coroutine that // should *not* `impl Coroutine`. - ty::Coroutine(did, ..) if !self.tcx().coroutine_is_async(*did) => { + ty::Coroutine(did, ..) if self.tcx().is_general_coroutine(*did) => { debug!(?self_ty, ?obligation, "assemble_coroutine_candidates",); candidates.vec.push(CoroutineCandidate); @@ -242,6 +244,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } + fn assemble_iterator_candidates( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + let self_ty = obligation.self_ty().skip_binder(); + if let ty::Coroutine(did, ..) = self_ty.kind() { + // gen constructs get lowered to a special kind of coroutine that + // should directly `impl Iterator`. + if self.tcx().coroutine_is_gen(*did) { + debug!(?self_ty, ?obligation, "assemble_iterator_candidates",); + + candidates.vec.push(IteratorCandidate); + } + } + } + /// Checks for the artificial impl that the compiler will create for an obligation like `X : /// FnMut<..>` where `X` is a closure type. /// diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index fce439f21fa..952184175f4 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -93,6 +93,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ImplSource::Builtin(BuiltinImplSource::Misc, vtable_future) } + IteratorCandidate => { + let vtable_iterator = self.confirm_iterator_candidate(obligation)?; + ImplSource::Builtin(BuiltinImplSource::Misc, vtable_iterator) + } + FnPointerCandidate { is_const } => { let data = self.confirm_fn_pointer_candidate(obligation, is_const)?; ImplSource::Builtin(BuiltinImplSource::Misc, data) @@ -780,6 +785,36 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(nested) } + fn confirm_iterator_candidate( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> { + // Okay to skip binder because the args on coroutine types never + // touch bound regions, they just capture the in-scope + // type/region parameters. + let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); + let ty::Coroutine(coroutine_def_id, args, _) = *self_ty.kind() else { + bug!("closure candidate for non-closure {:?}", obligation); + }; + + debug!(?obligation, ?coroutine_def_id, ?args, "confirm_iterator_candidate"); + + let gen_sig = args.as_coroutine().poly_sig(); + + let trait_ref = super::util::iterator_trait_ref_and_outputs( + self.tcx(), + obligation.predicate.def_id(), + obligation.predicate.no_bound_vars().expect("iterator has no bound vars").self_ty(), + gen_sig, + ) + .map_bound(|(trait_ref, ..)| trait_ref); + + let nested = self.confirm_poly_trait_refs(obligation, trait_ref)?; + debug!(?trait_ref, ?nested, "iterator candidate obligations"); + + Ok(nested) + } + #[instrument(skip(self), level = "debug")] fn confirm_closure_candidate( &mut self, diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 67cb39bc004..cf52e6726a1 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1888,6 +1888,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | ClosureCandidate { .. } | CoroutineCandidate | FutureCandidate + | IteratorCandidate | FnPointerCandidate { .. } | BuiltinObjectCandidate | BuiltinUnsizeCandidate @@ -1916,6 +1917,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | ClosureCandidate { .. } | CoroutineCandidate | FutureCandidate + | IteratorCandidate | FnPointerCandidate { .. } | BuiltinObjectCandidate | BuiltinUnsizeCandidate @@ -1950,6 +1952,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | ClosureCandidate { .. } | CoroutineCandidate | FutureCandidate + | IteratorCandidate | FnPointerCandidate { .. } | BuiltinObjectCandidate | BuiltinUnsizeCandidate @@ -1964,6 +1967,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | ClosureCandidate { .. } | CoroutineCandidate | FutureCandidate + | IteratorCandidate | FnPointerCandidate { .. } | BuiltinObjectCandidate | BuiltinUnsizeCandidate @@ -2070,6 +2074,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | ClosureCandidate { .. } | CoroutineCandidate | FutureCandidate + | IteratorCandidate | FnPointerCandidate { .. } | BuiltinObjectCandidate | BuiltinUnsizeCandidate @@ -2080,6 +2085,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | ClosureCandidate { .. } | CoroutineCandidate | FutureCandidate + | IteratorCandidate | FnPointerCandidate { .. } | BuiltinObjectCandidate | BuiltinUnsizeCandidate diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index 67681fb6ec1..bbde0c82743 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -297,6 +297,17 @@ pub fn future_trait_ref_and_outputs<'tcx>( sig.map_bound(|sig| (trait_ref, sig.return_ty)) } +pub fn iterator_trait_ref_and_outputs<'tcx>( + tcx: TyCtxt<'tcx>, + iterator_def_id: DefId, + self_ty: Ty<'tcx>, + sig: ty::PolyGenSig<'tcx>, +) -> ty::Binder<'tcx, (ty::TraitRef<'tcx>, Ty<'tcx>)> { + assert!(!self_ty.has_escaping_bound_vars()); + let trait_ref = ty::TraitRef::new(tcx, iterator_def_id, [self_ty]); + sig.map_bound(|sig| (trait_ref, sig.yield_ty)) +} + pub fn impl_item_is_final(tcx: TyCtxt<'_>, assoc_item: &ty::AssocItem) -> bool { assoc_item.defaultness(tcx).is_final() && tcx.defaultness(assoc_item.container_id(tcx)).is_final() diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 0e9d79c15c3..c0fe13ac996 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -258,6 +258,19 @@ fn resolve_associated_item<'tcx>( debug_assert!(tcx.defaultness(trait_item_id).has_value()); Some(Instance::new(trait_item_id, rcvr_args)) } + } else if Some(trait_ref.def_id) == lang_items.iterator_trait() { + let ty::Coroutine(coroutine_def_id, args, _) = *rcvr_args.type_at(0).kind() else { + bug!() + }; + if Some(trait_item_id) == tcx.lang_items().next_fn() { + // `Iterator::next` is generated by the compiler. + Some(Instance { def: ty::InstanceDef::Item(coroutine_def_id), args }) + } else { + // All other methods are default methods of the `Iterator` trait. + // (this assumes that `ImplSource::Builtin` is only used for methods on `Iterator`) + debug_assert!(tcx.defaultness(trait_item_id).has_value()); + Some(Instance::new(trait_item_id, rcvr_args)) + } } else if Some(trait_ref.def_id) == lang_items.gen_trait() { let ty::Coroutine(coroutine_def_id, args, _) = *rcvr_args.type_at(0).kind() else { bug!() diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs index 50c37e8f910..9f69e61d6fe 100644 --- a/compiler/stable_mir/src/mir/body.rs +++ b/compiler/stable_mir/src/mir/body.rs @@ -187,6 +187,7 @@ pub enum UnOp { pub enum CoroutineKind { Async(CoroutineSource), Coroutine, + Gen(CoroutineSource), } #[derive(Clone, Debug)] diff --git a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs index d10f10ef87e..7dff37a2b8f 100644 --- a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs +++ b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs @@ -578,7 +578,7 @@ fn ident_difference_expr_with_base_location( | (Assign(_, _, _), Assign(_, _, _)) | (TryBlock(_), TryBlock(_)) | (Await(_, _), Await(_, _)) - | (Async(_, _), Async(_, _)) + | (Gen(_, _, _), Gen(_, _, _)) | (Block(_, _), Block(_, _)) | (Closure(_), Closure(_)) | (Match(_, _), Match(_, _)) diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs index a78ff02021f..a2c61e07b70 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs @@ -211,7 +211,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { && eq_fn_decl(lf, rf) && eq_expr(le, re) }, - (Async(lc, lb), Async(rc, rb)) => lc == rc && eq_block(lb, rb), + (Gen(lc, lb, lk), Gen(rc, rb, rk)) => lc == rc && eq_block(lb, rb) && lk == rk, (Range(lf, lt, ll), Range(rf, rt, rl)) => ll == rl && eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt), (AddrOf(lbk, lm, le), AddrOf(rbk, rm, re)) => lbk == rbk && lm == rm && eq_expr(le, re), (Path(lq, lp), Path(rq, rp)) => both(lq, rq, eq_qself) && eq_path(lp, rp), diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index ae8ee371ffa..836f8cc1916 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -190,7 +190,7 @@ impl<'a> Sugg<'a> { (snip, false) => Sugg::MaybeParen(snip), (snip, true) => Sugg::NonParen(snip), }, - ast::ExprKind::Async(..) + ast::ExprKind::Gen(..) | ast::ExprKind::Block(..) | ast::ExprKind::Break(..) | ast::ExprKind::Call(..) diff --git a/src/tools/rustfmt/src/closures.rs b/src/tools/rustfmt/src/closures.rs index a09146e9592..16b8ce7a916 100644 --- a/src/tools/rustfmt/src/closures.rs +++ b/src/tools/rustfmt/src/closures.rs @@ -188,7 +188,7 @@ fn rewrite_closure_expr( fn allow_multi_line(expr: &ast::Expr) -> bool { match expr.kind { ast::ExprKind::Match(..) - | ast::ExprKind::Async(..) + | ast::ExprKind::Gen(..) | ast::ExprKind::Block(..) | ast::ExprKind::TryBlock(..) | ast::ExprKind::Loop(..) diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs index acde8809329..8c2262fde81 100644 --- a/src/tools/rustfmt/src/expr.rs +++ b/src/tools/rustfmt/src/expr.rs @@ -367,7 +367,7 @@ pub(crate) fn format_expr( )) } } - ast::ExprKind::Async(capture_by, ref block) => { + ast::ExprKind::Gen(capture_by, ref block, ref kind) => { let mover = if capture_by == ast::CaptureBy::Value { "move " } else { @@ -375,7 +375,7 @@ pub(crate) fn format_expr( }; if let rw @ Some(_) = rewrite_single_line_block( context, - format!("async {mover}").as_str(), + format!("{kind} {mover}").as_str(), block, Some(&expr.attrs), None, @@ -386,7 +386,7 @@ pub(crate) fn format_expr( // 6 = `async ` let budget = shape.width.saturating_sub(6); Some(format!( - "async {mover}{}", + "{kind} {mover}{}", rewrite_block( block, Some(&expr.attrs), @@ -1371,7 +1371,7 @@ pub(crate) fn can_be_overflowed_expr( } // Handle always block-like expressions - ast::ExprKind::Async(..) | ast::ExprKind::Block(..) | ast::ExprKind::Closure(..) => true, + ast::ExprKind::Gen(..) | ast::ExprKind::Block(..) | ast::ExprKind::Closure(..) => true, // Handle `[]` and `{}`-like expressions ast::ExprKind::Array(..) | ast::ExprKind::Struct(..) => { diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs index 79a759d68ce..fd49030bf1b 100644 --- a/src/tools/rustfmt/src/utils.rs +++ b/src/tools/rustfmt/src/utils.rs @@ -473,7 +473,7 @@ pub(crate) fn is_block_expr(context: &RewriteContext<'_>, expr: &ast::Expr, repr | ast::ExprKind::If(..) | ast::ExprKind::Block(..) | ast::ExprKind::ConstBlock(..) - | ast::ExprKind::Async(..) + | ast::ExprKind::Gen(..) | ast::ExprKind::Loop(..) | ast::ExprKind::ForLoop(..) | ast::ExprKind::TryBlock(..) diff --git a/tests/ui/coroutine/gen_block.e2024.stderr b/tests/ui/coroutine/gen_block.e2024.stderr new file mode 100644 index 00000000000..f250e2f79c7 --- /dev/null +++ b/tests/ui/coroutine/gen_block.e2024.stderr @@ -0,0 +1,19 @@ +error[E0658]: yield syntax is experimental + --> $DIR/gen_block.rs:15:16 + | +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 + +error[E0282]: type annotations needed + --> $DIR/gen_block.rs:6:17 + | +LL | let x = gen {}; + | ^^ cannot infer type + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0282, E0658. +For more information about an error, try `rustc --explain E0282`. diff --git a/tests/ui/coroutine/gen_block.none.stderr b/tests/ui/coroutine/gen_block.none.stderr new file mode 100644 index 00000000000..012a8308c7f --- /dev/null +++ b/tests/ui/coroutine/gen_block.none.stderr @@ -0,0 +1,49 @@ +error: expected identifier, found reserved keyword `yield` + --> $DIR/gen_block.rs:9:19 + | +LL | let y = gen { yield 42 }; + | --- ^^^^^ expected identifier, found reserved keyword + | | + | while parsing this struct + +error[E0422]: cannot find struct, variant or union type `gen` in this scope + --> $DIR/gen_block.rs:6:13 + | +LL | let x = gen {}; + | ^^^ not found in this scope + +error[E0422]: cannot find struct, variant or union type `gen` in this scope + --> $DIR/gen_block.rs:9:13 + | +LL | let y = gen { yield 42 }; + | ^^^ not found in this scope + +error[E0422]: cannot find struct, variant or union type `gen` in this scope + --> $DIR/gen_block.rs:12:5 + | +LL | gen {}; + | ^^^ not found in this scope + +error[E0658]: yield syntax is experimental + --> $DIR/gen_block.rs:15:16 + | +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 + +error[E0658]: yield syntax is experimental + --> $DIR/gen_block.rs:15:16 + | +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 + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0422, E0658. +For more information about an error, try `rustc --explain E0422`. diff --git a/tests/ui/coroutine/gen_block.rs b/tests/ui/coroutine/gen_block.rs new file mode 100644 index 00000000000..852c7c455a6 --- /dev/null +++ b/tests/ui/coroutine/gen_block.rs @@ -0,0 +1,17 @@ +// revisions: e2024 none +//[e2024] compile-flags: --edition 2024 -Zunstable-options +#![cfg_attr(e2024, feature(gen_blocks))] + +fn main() { + let x = gen {}; + //[none]~^ ERROR: cannot find + //[e2024]~^^ ERROR: type annotations needed + let y = gen { yield 42 }; + //[none]~^ ERROR: found reserved keyword `yield` + //[none]~| ERROR: cannot find + gen {}; + //[none]~^ ERROR: cannot find + + let _ = || yield true; //[none]~ ERROR yield syntax is experimental + //~^ ERROR yield syntax is experimental +} diff --git a/tests/ui/coroutine/gen_block_is_coro.rs b/tests/ui/coroutine/gen_block_is_coro.rs new file mode 100644 index 00000000000..c66ccefba85 --- /dev/null +++ b/tests/ui/coroutine/gen_block_is_coro.rs @@ -0,0 +1,18 @@ +//compile-flags: --edition 2024 -Zunstable-options +#![feature(coroutines, coroutine_trait, gen_blocks)] + +use std::ops::Coroutine; + +fn foo() -> impl Coroutine<Yield = u32, Return = ()> { //~ ERROR: Coroutine` is not satisfied + gen { yield 42 } +} + +fn bar() -> impl Coroutine<Yield = i64, Return = ()> { //~ ERROR: Coroutine` is not satisfied + gen { yield 42 } +} + +fn baz() -> impl Coroutine<Yield = i32, Return = ()> { //~ ERROR: Coroutine` is not satisfied + gen { yield 42 } +} + +fn main() {} diff --git a/tests/ui/coroutine/gen_block_is_coro.stderr b/tests/ui/coroutine/gen_block_is_coro.stderr new file mode 100644 index 00000000000..83a674fa53c --- /dev/null +++ b/tests/ui/coroutine/gen_block_is_coro.stderr @@ -0,0 +1,21 @@ +error[E0277]: the trait bound `{gen block@$DIR/gen_block_is_coro.rs:7:5: 7:21}: Coroutine` is not satisfied + --> $DIR/gen_block_is_coro.rs:6:13 + | +LL | fn foo() -> impl Coroutine<Yield = u32, Return = ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Coroutine` is not implemented for `{gen block@$DIR/gen_block_is_coro.rs:7:5: 7:21}` + +error[E0277]: the trait bound `{gen block@$DIR/gen_block_is_coro.rs:11:5: 11:21}: Coroutine` is not satisfied + --> $DIR/gen_block_is_coro.rs:10:13 + | +LL | fn bar() -> impl Coroutine<Yield = i64, Return = ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Coroutine` is not implemented for `{gen block@$DIR/gen_block_is_coro.rs:11:5: 11:21}` + +error[E0277]: the trait bound `{gen block@$DIR/gen_block_is_coro.rs:15:5: 15:21}: Coroutine` is not satisfied + --> $DIR/gen_block_is_coro.rs:14:13 + | +LL | fn baz() -> impl Coroutine<Yield = i32, Return = ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Coroutine` is not implemented for `{gen block@$DIR/gen_block_is_coro.rs:15:5: 15:21}` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/coroutine/gen_block_is_iter.rs b/tests/ui/coroutine/gen_block_is_iter.rs new file mode 100644 index 00000000000..92625cf7c28 --- /dev/null +++ b/tests/ui/coroutine/gen_block_is_iter.rs @@ -0,0 +1,19 @@ +// revisions: next old +//compile-flags: --edition 2024 -Zunstable-options +//[next] compile-flags: -Ztrait-solver=next +// check-pass +#![feature(gen_blocks)] + +fn foo() -> impl Iterator<Item = u32> { + gen { yield 42 } +} + +fn bar() -> impl Iterator<Item = i64> { + gen { yield 42 } +} + +fn baz() -> impl Iterator<Item = i32> { + gen { yield 42 } +} + +fn main() {} diff --git a/tests/ui/coroutine/gen_block_is_no_future.rs b/tests/ui/coroutine/gen_block_is_no_future.rs new file mode 100644 index 00000000000..94766519738 --- /dev/null +++ b/tests/ui/coroutine/gen_block_is_no_future.rs @@ -0,0 +1,8 @@ +//compile-flags: --edition 2024 -Zunstable-options +#![feature(gen_blocks)] + +fn foo() -> impl std::future::Future { //~ ERROR is not a future + gen { yield 42 } +} + +fn main() {} diff --git a/tests/ui/coroutine/gen_block_is_no_future.stderr b/tests/ui/coroutine/gen_block_is_no_future.stderr new file mode 100644 index 00000000000..db0c3c19b58 --- /dev/null +++ b/tests/ui/coroutine/gen_block_is_no_future.stderr @@ -0,0 +1,12 @@ +error[E0277]: `{gen block@$DIR/gen_block_is_no_future.rs:5:5: 5:21}` is not a future + --> $DIR/gen_block_is_no_future.rs:4:13 + | +LL | fn foo() -> impl std::future::Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^ `{gen block@$DIR/gen_block_is_no_future.rs:5:5: 5:21}` is not a future + | + = help: the trait `Future` is not implemented for `{gen block@$DIR/gen_block_is_no_future.rs:5:5: 5:21}` + = note: {gen block@$DIR/gen_block_is_no_future.rs:5:5: 5:21} must be a future or must implement `IntoFuture` to be awaited + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/coroutine/gen_block_iterate.rs b/tests/ui/coroutine/gen_block_iterate.rs new file mode 100644 index 00000000000..18e1bb88772 --- /dev/null +++ b/tests/ui/coroutine/gen_block_iterate.rs @@ -0,0 +1,35 @@ +// revisions: next old +//compile-flags: --edition 2024 -Zunstable-options +//[next] compile-flags: -Ztrait-solver=next +// run-pass +#![feature(gen_blocks)] + +fn foo() -> impl Iterator<Item = u32> { + gen { yield 42; for x in 3..6 { yield x } } +} + +fn moved() -> impl Iterator<Item = u32> { + let mut x = "foo".to_string(); + gen move { + yield 42; + if x == "foo" { return } + x.clear(); + for x in 3..6 { yield x } + } +} + +fn main() { + let mut iter = foo(); + assert_eq!(iter.next(), Some(42)); + assert_eq!(iter.next(), Some(3)); + assert_eq!(iter.next(), Some(4)); + assert_eq!(iter.next(), Some(5)); + assert_eq!(iter.next(), None); + // `gen` blocks are fused + assert_eq!(iter.next(), None); + + let mut iter = moved(); + assert_eq!(iter.next(), Some(42)); + assert_eq!(iter.next(), None); + +} diff --git a/tests/ui/coroutine/gen_block_move.fixed b/tests/ui/coroutine/gen_block_move.fixed new file mode 100644 index 00000000000..5c6c8062322 --- /dev/null +++ b/tests/ui/coroutine/gen_block_move.fixed @@ -0,0 +1,17 @@ +// compile-flags: --edition 2024 -Zunstable-options +// run-rustfix +#![feature(gen_blocks)] + +fn moved() -> impl Iterator<Item = u32> { + let mut x = "foo".to_string(); + gen move { //~ ERROR: gen block may outlive the current function + yield 42; + if x == "foo" { return } + x.clear(); + for x in 3..6 { yield x } + } +} + +fn main() { + for _ in moved() {} +} diff --git a/tests/ui/coroutine/gen_block_move.rs b/tests/ui/coroutine/gen_block_move.rs new file mode 100644 index 00000000000..abbf8132476 --- /dev/null +++ b/tests/ui/coroutine/gen_block_move.rs @@ -0,0 +1,17 @@ +// compile-flags: --edition 2024 -Zunstable-options +// run-rustfix +#![feature(gen_blocks)] + +fn moved() -> impl Iterator<Item = u32> { + let mut x = "foo".to_string(); + gen { //~ ERROR: gen block may outlive the current function + yield 42; + if x == "foo" { return } + x.clear(); + for x in 3..6 { yield x } + } +} + +fn main() { + for _ in moved() {} +} diff --git a/tests/ui/coroutine/gen_block_move.stderr b/tests/ui/coroutine/gen_block_move.stderr new file mode 100644 index 00000000000..b93ac65f5e7 --- /dev/null +++ b/tests/ui/coroutine/gen_block_move.stderr @@ -0,0 +1,30 @@ +error[E0373]: gen block may outlive the current function, but it borrows `x`, which is owned by the current function + --> $DIR/gen_block_move.rs:7:5 + | +LL | / gen { +LL | | yield 42; +LL | | if x == "foo" { return } +LL | | x.clear(); + | | - `x` is borrowed here +LL | | for x in 3..6 { yield x } +LL | | } + | |_____^ may outlive borrowed value `x` + | +note: gen block is returned here + --> $DIR/gen_block_move.rs:7:5 + | +LL | / gen { +LL | | yield 42; +LL | | if x == "foo" { return } +LL | | x.clear(); +LL | | for x in 3..6 { yield x } +LL | | } + | |_____^ +help: to force the gen block to take ownership of `x` (and any other referenced variables), use the `move` keyword + | +LL | gen move { + | ++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0373`. diff --git a/tests/ui/coroutine/gen_fn.e2024.stderr b/tests/ui/coroutine/gen_fn.e2024.stderr new file mode 100644 index 00000000000..41bd04d9769 --- /dev/null +++ b/tests/ui/coroutine/gen_fn.e2024.stderr @@ -0,0 +1,10 @@ +error: `gen` blocks are not yet implemented + --> $DIR/gen_fn.rs:4:1 + | +LL | gen fn foo() {} + | ^^^ + | + = help: only the keyword is reserved for now + +error: aborting due to previous error + diff --git a/tests/ui/coroutine/gen_fn.none.stderr b/tests/ui/coroutine/gen_fn.none.stderr new file mode 100644 index 00000000000..5e7bd9d8bbf --- /dev/null +++ b/tests/ui/coroutine/gen_fn.none.stderr @@ -0,0 +1,8 @@ +error: expected one of `#`, `async`, `const`, `default`, `extern`, `fn`, `pub`, `unsafe`, or `use`, found `gen` + --> $DIR/gen_fn.rs:4:1 + | +LL | gen fn foo() {} + | ^^^ expected one of 9 possible tokens + +error: aborting due to previous error + diff --git a/tests/ui/coroutine/gen_fn.rs b/tests/ui/coroutine/gen_fn.rs new file mode 100644 index 00000000000..9566660dfb5 --- /dev/null +++ b/tests/ui/coroutine/gen_fn.rs @@ -0,0 +1,8 @@ +// revisions: e2024 none +//[e2024] compile-flags: --edition 2024 -Zunstable-options + +gen fn foo() {} +//[none]~^ ERROR: expected one of `#`, `async`, `const`, `default`, `extern`, `fn`, `pub`, `unsafe`, or `use`, found `gen` +//[e2024]~^^ ERROR: `gen` blocks are not yet implemented + +fn main() {} diff --git a/tests/ui/coroutine/self_referential_gen_block.rs b/tests/ui/coroutine/self_referential_gen_block.rs new file mode 100644 index 00000000000..14daa2e9c35 --- /dev/null +++ b/tests/ui/coroutine/self_referential_gen_block.rs @@ -0,0 +1,17 @@ +// compile-flags: --edition 2024 -Zunstable-options +#![feature(gen_blocks)] +//! This test checks that we don't allow self-referential generators + +fn main() { + let mut x = { + let mut x = gen { + let y = 42; + let z = &y; //~ ERROR: borrow may still be in use when `gen` block yields + yield 43; + panic!("{z}"); + }; + x.next(); + Box::new(x) + }; + x.next(); +} diff --git a/tests/ui/coroutine/self_referential_gen_block.stderr b/tests/ui/coroutine/self_referential_gen_block.stderr new file mode 100644 index 00000000000..586f53df8f2 --- /dev/null +++ b/tests/ui/coroutine/self_referential_gen_block.stderr @@ -0,0 +1,11 @@ +error[E0626]: borrow may still be in use when `gen` block yields + --> $DIR/self_referential_gen_block.rs:9:21 + | +LL | let z = &y; + | ^^ +LL | yield 43; + | -------- possible yield occurs here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0626`. diff --git a/tests/ui/feature-gates/feature-gate-coroutines.stderr b/tests/ui/feature-gates/feature-gate-coroutines.e2024.stderr index dd561643901..2e529236ad8 100644 --- a/tests/ui/feature-gates/feature-gate-coroutines.stderr +++ b/tests/ui/feature-gates/feature-gate-coroutines.e2024.stderr @@ -1,5 +1,5 @@ error[E0658]: yield syntax is experimental - --> $DIR/feature-gate-coroutines.rs:2:5 + --> $DIR/feature-gate-coroutines.rs:5:5 | LL | yield true; | ^^^^^^^^^^ @@ -8,30 +8,21 @@ LL | yield true; = help: add `#![feature(coroutines)]` to the crate attributes to enable error[E0658]: yield syntax is experimental - --> $DIR/feature-gate-coroutines.rs:8:5 + --> $DIR/feature-gate-coroutines.rs:9:16 | -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 - -error[E0658]: yield syntax is experimental - --> $DIR/feature-gate-coroutines.rs:9:5 - | -LL | yield 0; - | ^^^^^^^ +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 error[E0627]: yield expression outside of coroutine literal - --> $DIR/feature-gate-coroutines.rs:2:5 + --> $DIR/feature-gate-coroutines.rs:5:5 | LL | yield true; | ^^^^^^^^^^ -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors Some errors have detailed explanations: E0627, E0658. For more information about an error, try `rustc --explain E0627`. diff --git a/tests/ui/feature-gates/feature-gate-coroutines.none.stderr b/tests/ui/feature-gates/feature-gate-coroutines.none.stderr new file mode 100644 index 00000000000..ab24805e467 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-coroutines.none.stderr @@ -0,0 +1,66 @@ +error[E0658]: yield syntax is experimental + --> $DIR/feature-gate-coroutines.rs:5:5 + | +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 + +error[E0658]: yield syntax is experimental + --> $DIR/feature-gate-coroutines.rs:9:16 + | +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 + +error[E0658]: yield syntax is experimental + --> $DIR/feature-gate-coroutines.rs:16:5 + | +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 + +error[E0658]: yield syntax is experimental + --> $DIR/feature-gate-coroutines.rs:17:5 + | +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 + +error[E0658]: yield syntax is experimental + --> $DIR/feature-gate-coroutines.rs:5:5 + | +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 + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0658]: yield syntax is experimental + --> $DIR/feature-gate-coroutines.rs:9:16 + | +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 + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0627]: yield expression outside of coroutine literal + --> $DIR/feature-gate-coroutines.rs:5:5 + | +LL | yield true; + | ^^^^^^^^^^ + +error: aborting due to 7 previous errors + +Some errors have detailed explanations: E0627, E0658. +For more information about an error, try `rustc --explain E0627`. diff --git a/tests/ui/feature-gates/feature-gate-coroutines.rs b/tests/ui/feature-gates/feature-gate-coroutines.rs index c3c5aec8824..53b58d486a8 100644 --- a/tests/ui/feature-gates/feature-gate-coroutines.rs +++ b/tests/ui/feature-gates/feature-gate-coroutines.rs @@ -1,10 +1,18 @@ +// revisions: e2024 none +//[e2024] compile-flags: --edition 2024 -Zunstable-options + fn main() { yield true; //~ ERROR yield syntax is experimental //~^ ERROR yield expression outside of coroutine literal + //[none]~^^ ERROR yield syntax is experimental + + let _ = || yield true; //~ ERROR yield syntax is experimental + //[none]~^ ERROR yield syntax is experimental } #[cfg(FALSE)] fn foo() { - yield; //~ ERROR yield syntax is experimental - yield 0; //~ ERROR yield syntax is experimental + // Ok in 2024 edition + yield; //[none]~ ERROR yield syntax is experimental + yield 0; //[none]~ ERROR yield syntax is experimental } diff --git a/tests/ui/feature-gates/feature-gate-gen_blocks.e2024.stderr b/tests/ui/feature-gates/feature-gate-gen_blocks.e2024.stderr new file mode 100644 index 00000000000..1462c41e957 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-gen_blocks.e2024.stderr @@ -0,0 +1,28 @@ +error[E0658]: gen blocks are experimental + --> $DIR/feature-gate-gen_blocks.rs:5:5 + | +LL | gen {}; + | ^^^^^ + | + = note: see issue #117078 <https://github.com/rust-lang/rust/issues/117078> for more information + = help: add `#![feature(gen_blocks)]` to the crate attributes to enable + +error[E0658]: gen blocks are experimental + --> $DIR/feature-gate-gen_blocks.rs:13:5 + | +LL | gen {}; + | ^^^^^ + | + = note: see issue #117078 <https://github.com/rust-lang/rust/issues/117078> for more information + = help: add `#![feature(gen_blocks)]` to the crate attributes to enable + +error[E0282]: type annotations needed + --> $DIR/feature-gate-gen_blocks.rs:5:9 + | +LL | gen {}; + | ^^ cannot infer type + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0282, E0658. +For more information about an error, try `rustc --explain E0282`. diff --git a/tests/ui/feature-gates/feature-gate-gen_blocks.none.stderr b/tests/ui/feature-gates/feature-gate-gen_blocks.none.stderr new file mode 100644 index 00000000000..b448c35e846 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-gen_blocks.none.stderr @@ -0,0 +1,9 @@ +error[E0422]: cannot find struct, variant or union type `gen` in this scope + --> $DIR/feature-gate-gen_blocks.rs:5:5 + | +LL | gen {}; + | ^^^ not found in this scope + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0422`. diff --git a/tests/ui/feature-gates/feature-gate-gen_blocks.rs b/tests/ui/feature-gates/feature-gate-gen_blocks.rs new file mode 100644 index 00000000000..e2e1574a36a --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-gen_blocks.rs @@ -0,0 +1,15 @@ +// revisions: e2024 none +//[e2024] compile-flags: --edition 2024 -Zunstable-options + +fn main() { + gen {}; + //[none]~^ ERROR: cannot find struct, variant or union type `gen` + //[e2024]~^^ ERROR: gen blocks are experimental + //[e2024]~| ERROR: type annotations needed +} + +#[cfg(FALSE)] +fn foo() { + gen {}; + //[e2024]~^ ERROR: gen blocks are experimental +} |
