diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index e8731cf20f28b..d6c2bfacf66a6 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1311,7 +1311,7 @@ pub struct Closure { pub binder: ClosureBinder, pub capture_clause: CaptureBy, pub constness: Const, - pub asyncness: Async, + pub coro_kind: Option, pub movability: Movability, pub fn_decl: P, pub body: P, @@ -2406,28 +2406,34 @@ pub enum Unsafe { No, } +/// Describes what kind of coroutine markers, if any, a function has. +/// +/// Coroutine markers are things that cause the function to generate a coroutine, such as `async`, +/// which makes the function return `impl Future`, or `gen`, which makes the function return `impl +/// Iterator`. #[derive(Copy, Clone, Encodable, Decodable, Debug)] -pub enum Async { - Yes { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId }, - No, +pub enum CoroutineKind { + /// `async`, which evaluates to `impl Future` + Async { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId }, + /// `gen`, which evaluates to `impl Iterator` + Gen { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId }, } -#[derive(Copy, Clone, Encodable, Decodable, Debug)] -pub enum Gen { - Yes { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId }, - No, -} - -impl Async { +impl CoroutineKind { pub fn is_async(self) -> bool { - matches!(self, Async::Yes { .. }) + matches!(self, CoroutineKind::Async { .. }) + } + + pub fn is_gen(self) -> bool { + matches!(self, CoroutineKind::Gen { .. }) } - /// In this case this is an `async` return, the `NodeId` for the generated `impl Trait` item. - pub fn opt_return_id(self) -> Option<(NodeId, Span)> { + /// In this case this is an `async` or `gen` return, the `NodeId` for the generated `impl Trait` + /// item. + pub fn return_id(self) -> (NodeId, Span) { match self { - Async::Yes { return_impl_trait_id, span, .. } => Some((return_impl_trait_id, span)), - Async::No => None, + CoroutineKind::Async { return_impl_trait_id, span, .. } + | CoroutineKind::Gen { return_impl_trait_id, span, .. } => (return_impl_trait_id, span), } } } @@ -2831,8 +2837,8 @@ impl Extern { pub struct FnHeader { /// The `unsafe` keyword, if any pub unsafety: Unsafe, - /// The `async` keyword, if any - pub asyncness: Async, + /// Whether this is `async`, `gen`, or nothing. + pub coro_kind: Option, /// The `const` keyword, if any pub constness: Const, /// The `extern` keyword and corresponding ABI string, if any @@ -2842,9 +2848,9 @@ pub struct FnHeader { impl FnHeader { /// Does this function header have any qualifiers or is it empty? pub fn has_qualifiers(&self) -> bool { - let Self { unsafety, asyncness, constness, ext } = self; + let Self { unsafety, coro_kind, constness, ext } = self; matches!(unsafety, Unsafe::Yes(_)) - || asyncness.is_async() + || coro_kind.is_some() || matches!(constness, Const::Yes(_)) || !matches!(ext, Extern::None) } @@ -2852,12 +2858,7 @@ impl FnHeader { impl Default for FnHeader { fn default() -> FnHeader { - FnHeader { - unsafety: Unsafe::No, - asyncness: Async::No, - constness: Const::No, - ext: Extern::None, - } + FnHeader { unsafety: Unsafe::No, coro_kind: None, constness: Const::No, ext: Extern::None } } } @@ -3177,7 +3178,7 @@ mod size_asserts { static_assert_size!(Block, 32); static_assert_size!(Expr, 72); static_assert_size!(ExprKind, 40); - static_assert_size!(Fn, 152); + static_assert_size!(Fn, 160); static_assert_size!(ForeignItem, 96); static_assert_size!(ForeignItemKind, 24); static_assert_size!(GenericArg, 24); diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 8ce86bf9ecf52..c6a31fbdbc31c 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -121,8 +121,8 @@ pub trait MutVisitor: Sized { noop_visit_fn_decl(d, self); } - fn visit_asyncness(&mut self, a: &mut Async) { - noop_visit_asyncness(a, self); + fn visit_coro_kind(&mut self, a: &mut CoroutineKind) { + noop_visit_coro_kind(a, self); } fn visit_closure_binder(&mut self, b: &mut ClosureBinder) { @@ -871,13 +871,14 @@ pub fn noop_visit_closure_binder(binder: &mut ClosureBinder, vis: } } -pub fn noop_visit_asyncness(asyncness: &mut Async, vis: &mut T) { - match asyncness { - Async::Yes { span: _, closure_id, return_impl_trait_id } => { +pub fn noop_visit_coro_kind(coro_kind: &mut CoroutineKind, vis: &mut T) { + match coro_kind { + CoroutineKind::Async { span, closure_id, return_impl_trait_id } + | CoroutineKind::Gen { span, closure_id, return_impl_trait_id } => { + vis.visit_span(span); vis.visit_id(closure_id); vis.visit_id(return_impl_trait_id); } - Async::No => {} } } @@ -1170,9 +1171,9 @@ fn visit_const_item( } pub fn noop_visit_fn_header(header: &mut FnHeader, vis: &mut T) { - let FnHeader { unsafety, asyncness, constness, ext: _ } = header; + let FnHeader { unsafety, coro_kind, constness, ext: _ } = header; visit_constness(constness, vis); - vis.visit_asyncness(asyncness); + coro_kind.as_mut().map(|coro_kind| vis.visit_coro_kind(coro_kind)); visit_unsafety(unsafety, vis); } @@ -1406,7 +1407,7 @@ pub fn noop_visit_expr( binder, capture_clause, constness, - asyncness, + coro_kind, movability: _, fn_decl, body, @@ -1415,7 +1416,7 @@ pub fn noop_visit_expr( }) => { vis.visit_closure_binder(binder); visit_constness(constness, vis); - vis.visit_asyncness(asyncness); + coro_kind.as_mut().map(|coro_kind| vis.visit_coro_kind(coro_kind)); vis.visit_capture_by(capture_clause); vis.visit_fn_decl(fn_decl); vis.visit_expr(body); diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 9dbadcb49d3fc..a303d6584f44f 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -861,7 +861,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { ExprKind::Closure(box Closure { binder, capture_clause, - asyncness: _, + coro_kind: _, constness: _, movability: _, fn_decl, diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index eb1a1d150270c..3556ee02bd7b8 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -195,39 +195,39 @@ impl<'hir> LoweringContext<'_, 'hir> { binder, capture_clause, constness, - asyncness, + coro_kind, movability, fn_decl, body, fn_decl_span, fn_arg_span, - }) => { - if let Async::Yes { closure_id, .. } = asyncness { - self.lower_expr_async_closure( - binder, - *capture_clause, - e.id, - hir_id, - *closure_id, - fn_decl, - body, - *fn_decl_span, - *fn_arg_span, - ) - } else { - self.lower_expr_closure( - binder, - *capture_clause, - e.id, - *constness, - *movability, - fn_decl, - body, - *fn_decl_span, - *fn_arg_span, - ) - } - } + }) => match coro_kind { + Some( + CoroutineKind::Async { closure_id, .. } + | CoroutineKind::Gen { closure_id, .. }, + ) => self.lower_expr_async_closure( + binder, + *capture_clause, + e.id, + hir_id, + *closure_id, + fn_decl, + body, + *fn_decl_span, + *fn_arg_span, + ), + None => self.lower_expr_closure( + binder, + *capture_clause, + e.id, + *constness, + *movability, + fn_decl, + body, + *fn_decl_span, + *fn_arg_span, + ), + }, ExprKind::Block(blk, opt_label) => { let opt_label = self.lower_label(*opt_label); hir::ExprKind::Block(self.lower_block(blk, opt_label.is_some()), opt_label) diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index f0f3e2c3c746a..a23a77f45be75 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -206,15 +206,19 @@ impl<'hir> LoweringContext<'_, 'hir> { // `impl Future` here because lower_body // only cares about the input argument patterns in the function // declaration (decl), not the return types. - let asyncness = header.asyncness; - let body_id = - this.lower_maybe_async_body(span, hir_id, decl, asyncness, body.as_deref()); + let coro_kind = header.coro_kind; + let body_id = this.lower_maybe_coroutine_body( + span, + hir_id, + decl, + coro_kind, + body.as_deref(), + ); let itctx = ImplTraitContext::Universal; let (generics, decl) = this.lower_generics(generics, header.constness, id, &itctx, |this| { - let ret_id = asyncness.opt_return_id(); - this.lower_fn_decl(decl, id, *fn_sig_span, FnDeclKind::Fn, ret_id) + this.lower_fn_decl(decl, id, *fn_sig_span, FnDeclKind::Fn, coro_kind) }); let sig = hir::FnSig { decl, @@ -725,27 +729,30 @@ impl<'hir> LoweringContext<'_, 'hir> { (generics, kind, expr.is_some()) } AssocItemKind::Fn(box Fn { sig, generics, body: None, .. }) => { - let asyncness = sig.header.asyncness; let names = self.lower_fn_params_to_names(&sig.decl); let (generics, sig) = self.lower_method_sig( generics, sig, i.id, FnDeclKind::Trait, - asyncness.opt_return_id(), + sig.header.coro_kind, ); (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)), false) } AssocItemKind::Fn(box Fn { sig, generics, body: Some(body), .. }) => { - let asyncness = sig.header.asyncness; - let body_id = - self.lower_maybe_async_body(i.span, hir_id, &sig.decl, asyncness, Some(body)); + let body_id = self.lower_maybe_coroutine_body( + i.span, + hir_id, + &sig.decl, + sig.header.coro_kind, + Some(body), + ); let (generics, sig) = self.lower_method_sig( generics, sig, i.id, FnDeclKind::Trait, - asyncness.opt_return_id(), + sig.header.coro_kind, ); (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)), true) } @@ -834,12 +841,11 @@ impl<'hir> LoweringContext<'_, 'hir> { }, ), AssocItemKind::Fn(box Fn { sig, generics, body, .. }) => { - let asyncness = sig.header.asyncness; - let body_id = self.lower_maybe_async_body( + let body_id = self.lower_maybe_coroutine_body( i.span, hir_id, &sig.decl, - asyncness, + sig.header.coro_kind, body.as_deref(), ); let (generics, sig) = self.lower_method_sig( @@ -847,7 +853,7 @@ impl<'hir> LoweringContext<'_, 'hir> { sig, i.id, if self.is_in_trait_impl { FnDeclKind::Impl } else { FnDeclKind::Inherent }, - asyncness.opt_return_id(), + sig.header.coro_kind, ); (generics, hir::ImplItemKind::Fn(sig, body_id)) @@ -1011,17 +1017,23 @@ impl<'hir> LoweringContext<'_, 'hir> { }) } - fn lower_maybe_async_body( + /// Takes what may be the body of an `async fn` or a `gen fn` and wraps it in an `async {}` or + /// `gen {}` block as appropriate. + fn lower_maybe_coroutine_body( &mut self, span: Span, fn_id: hir::HirId, decl: &FnDecl, - asyncness: Async, + coro_kind: Option, body: Option<&Block>, ) -> hir::BodyId { - let (closure_id, body) = match (asyncness, body) { - (Async::Yes { closure_id, .. }, Some(body)) => (closure_id, body), - _ => return self.lower_fn_body_block(span, decl, body), + let (Some(coro_kind), Some(body)) = (coro_kind, body) else { + return self.lower_fn_body_block(span, decl, body); + }; + let closure_id = match coro_kind { + CoroutineKind::Async { closure_id, .. } | CoroutineKind::Gen { closure_id, .. } => { + closure_id + } }; self.lower_body(|this| { @@ -1163,44 +1175,54 @@ impl<'hir> LoweringContext<'_, 'hir> { parameters.push(new_parameter); } - let async_expr = this.make_async_expr( - CaptureBy::Value { move_kw: rustc_span::DUMMY_SP }, - closure_id, - None, - body.span, - hir::CoroutineSource::Fn, - |this| { - // Create a block from the user's function body: - let user_body = this.lower_block_expr(body); + let mkbody = |this: &mut LoweringContext<'_, 'hir>| { + // Create a block from the user's function body: + let user_body = this.lower_block_expr(body); - // Transform into `drop-temps { }`, an expression: - let desugared_span = - this.mark_span_with_reason(DesugaringKind::Async, user_body.span, None); - let user_body = - this.expr_drop_temps(desugared_span, this.arena.alloc(user_body)); + // Transform into `drop-temps { }`, an expression: + let desugared_span = + this.mark_span_with_reason(DesugaringKind::Async, user_body.span, None); + let user_body = this.expr_drop_temps(desugared_span, this.arena.alloc(user_body)); - // As noted above, create the final block like - // - // ``` - // { - // let $param_pattern = $raw_param; - // ... - // drop-temps { } - // } - // ``` - let body = this.block_all( - desugared_span, - this.arena.alloc_from_iter(statements), - Some(user_body), - ); + // As noted above, create the final block like + // + // ``` + // { + // let $param_pattern = $raw_param; + // ... + // drop-temps { } + // } + // ``` + let body = this.block_all( + desugared_span, + this.arena.alloc_from_iter(statements), + Some(user_body), + ); - this.expr_block(body) - }, - ); + this.expr_block(body) + }; + let coroutine_expr = match coro_kind { + CoroutineKind::Async { .. } => this.make_async_expr( + CaptureBy::Value { move_kw: rustc_span::DUMMY_SP }, + closure_id, + None, + body.span, + hir::CoroutineSource::Fn, + mkbody, + ), + CoroutineKind::Gen { .. } => this.make_gen_expr( + CaptureBy::Value { move_kw: rustc_span::DUMMY_SP }, + closure_id, + None, + body.span, + hir::CoroutineSource::Fn, + mkbody, + ), + }; let hir_id = this.lower_node_id(closure_id); this.maybe_forward_track_caller(body.span, fn_id, hir_id); - let expr = hir::Expr { hir_id, kind: async_expr, span: this.lower_span(body.span) }; + let expr = hir::Expr { hir_id, kind: coroutine_expr, span: this.lower_span(body.span) }; (this.arena.alloc_from_iter(parameters), expr) }) @@ -1212,21 +1234,26 @@ impl<'hir> LoweringContext<'_, 'hir> { sig: &FnSig, id: NodeId, kind: FnDeclKind, - is_async: Option<(NodeId, Span)>, + coro_kind: Option, ) -> (&'hir hir::Generics<'hir>, hir::FnSig<'hir>) { let header = self.lower_fn_header(sig.header); let itctx = ImplTraitContext::Universal; let (generics, decl) = self.lower_generics(generics, sig.header.constness, id, &itctx, |this| { - this.lower_fn_decl(&sig.decl, id, sig.span, kind, is_async) + this.lower_fn_decl(&sig.decl, id, sig.span, kind, coro_kind) }); (generics, hir::FnSig { header, decl, span: self.lower_span(sig.span) }) } fn lower_fn_header(&mut self, h: FnHeader) -> hir::FnHeader { + let asyncness = if let Some(CoroutineKind::Async { span, .. }) = h.coro_kind { + hir::IsAsync::Async(span) + } else { + hir::IsAsync::NotAsync + }; hir::FnHeader { unsafety: self.lower_unsafety(h.unsafety), - asyncness: self.lower_asyncness(h.asyncness), + asyncness: asyncness, constness: self.lower_constness(h.constness), abi: self.lower_extern(h.ext), } @@ -1268,13 +1295,6 @@ impl<'hir> LoweringContext<'_, 'hir> { }); } - fn lower_asyncness(&mut self, a: Async) -> hir::IsAsync { - match a { - Async::Yes { span, .. } => hir::IsAsync::Async(span), - Async::No => hir::IsAsync::NotAsync, - } - } - pub(super) fn lower_constness(&mut self, c: Const) -> hir::Constness { match c { Const::Yes(_) => hir::Constness::Const, diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index aa8ad9784513d..d435082e12190 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1778,13 +1778,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { })) } - // Lowers a function declaration. - // - // `decl`: the unlowered (AST) function declaration. - // `fn_node_id`: `impl Trait` arguments are lowered into generic parameters on the given `NodeId`. - // `make_ret_async`: if `Some`, converts `-> T` into `-> impl Future` in the - // return type. This is used for `async fn` declarations. The `NodeId` is the ID of the - // return type `impl Trait` item, and the `Span` points to the `async` keyword. + /// Lowers a function declaration. + /// + /// `decl`: the unlowered (AST) function declaration. + /// + /// `fn_node_id`: `impl Trait` arguments are lowered into generic parameters on the given + /// `NodeId`. + /// + /// `transform_return_type`: if `Some`, applies some conversion to the return type, such as is + /// needed for `async fn` and `gen fn`. See [`CoroutineKind`] for more details. #[instrument(level = "debug", skip(self))] fn lower_fn_decl( &mut self, @@ -1792,7 +1794,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn_node_id: NodeId, fn_span: Span, kind: FnDeclKind, - make_ret_async: Option<(NodeId, Span)>, + coro: Option, ) -> &'hir hir::FnDecl<'hir> { let c_variadic = decl.c_variadic(); @@ -1821,11 +1823,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.lower_ty_direct(¶m.ty, &itctx) })); - let output = if let Some((ret_id, _span)) = make_ret_async { - let fn_def_id = self.local_def_id(fn_node_id); - self.lower_async_fn_ret_ty(&decl.output, fn_def_id, ret_id, kind, fn_span) - } else { - match &decl.output { + let output = match coro { + Some(coro) => { + let fn_def_id = self.local_def_id(fn_node_id); + self.lower_coroutine_fn_ret_ty(&decl.output, fn_def_id, coro, kind, fn_span) + } + None => match &decl.output { FnRetTy::Ty(ty) => { let context = if kind.return_impl_trait_allowed() { let fn_def_id = self.local_def_id(fn_node_id); @@ -1849,7 +1852,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir::FnRetTy::Return(self.lower_ty(ty, &context)) } FnRetTy::Default(span) => hir::FnRetTy::DefaultReturn(self.lower_span(*span)), - } + }, }; self.arena.alloc(hir::FnDecl { @@ -1888,17 +1891,22 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // `fn_node_id`: `NodeId` of the parent function (used to create child impl trait definition) // `opaque_ty_node_id`: `NodeId` of the opaque `impl Trait` type that should be created #[instrument(level = "debug", skip(self))] - fn lower_async_fn_ret_ty( + fn lower_coroutine_fn_ret_ty( &mut self, output: &FnRetTy, fn_def_id: LocalDefId, - opaque_ty_node_id: NodeId, + coro: CoroutineKind, fn_kind: FnDeclKind, fn_span: Span, ) -> hir::FnRetTy<'hir> { let span = self.lower_span(fn_span); let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::Async, span, None); + let opaque_ty_node_id = match coro { + CoroutineKind::Async { return_impl_trait_id, .. } + | CoroutineKind::Gen { return_impl_trait_id, .. } => return_impl_trait_id, + }; + let captured_lifetimes: Vec<_> = self .resolver .take_extra_lifetime_params(opaque_ty_node_id) @@ -1914,15 +1922,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { span, opaque_ty_span, |this| { - let future_bound = this.lower_async_fn_output_type_to_future_bound( + let bound = this.lower_coroutine_fn_output_type_to_bound( output, + coro, span, ImplTraitContext::ReturnPositionOpaqueTy { origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id), fn_kind, }, ); - arena_vec![this; future_bound] + arena_vec![this; bound] }, ); @@ -1931,9 +1940,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } /// Transforms `-> T` into `Future`. - fn lower_async_fn_output_type_to_future_bound( + fn lower_coroutine_fn_output_type_to_bound( &mut self, output: &FnRetTy, + coro: CoroutineKind, span: Span, nested_impl_trait_context: ImplTraitContext, ) -> hir::GenericBound<'hir> { @@ -1948,17 +1958,21 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { FnRetTy::Default(ret_ty_span) => self.arena.alloc(self.ty_tup(*ret_ty_span, &[])), }; - // "" + // "<$assoc_ty_name = T>" + let (assoc_ty_name, trait_lang_item) = match coro { + CoroutineKind::Async { .. } => (hir::FN_OUTPUT_NAME, hir::LangItem::Future), + CoroutineKind::Gen { .. } => (hir::ITERATOR_ITEM_NAME, hir::LangItem::Iterator), + }; + let future_args = self.arena.alloc(hir::GenericArgs { args: &[], - bindings: arena_vec![self; self.output_ty_binding(span, output_ty)], + bindings: arena_vec![self; self.assoc_ty_binding(assoc_ty_name, span, output_ty)], parenthesized: hir::GenericArgsParentheses::No, span_ext: DUMMY_SP, }); hir::GenericBound::LangItemTrait( - // ::std::future::Future - hir::LangItem::Future, + trait_lang_item, self.lower_span(span), self.next_id(), future_args, diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index db8ca7c3643f2..7ab0805d08667 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -389,7 +389,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { FnRetTy::Default(_) => self.arena.alloc(self.ty_tup(*span, &[])), }; let args = smallvec![GenericArg::Type(self.arena.alloc(self.ty_tup(*inputs_span, inputs)))]; - let binding = self.output_ty_binding(output_ty.span, output_ty); + let binding = self.assoc_ty_binding(hir::FN_OUTPUT_NAME, output_ty.span, output_ty); ( GenericArgsCtor { args, @@ -401,13 +401,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) } - /// An associated type binding `Output = $ty`. - pub(crate) fn output_ty_binding( + /// An associated type binding `$assoc_ty_name = $ty`. + pub(crate) fn assoc_ty_binding( &mut self, + assoc_ty_name: rustc_span::Symbol, span: Span, ty: &'hir hir::Ty<'hir>, ) -> hir::TypeBinding<'hir> { - let ident = Ident::with_dummy_span(hir::FN_OUTPUT_NAME); + let ident = Ident::with_dummy_span(assoc_ty_name); let kind = hir::TypeBindingKind::Equality { term: ty.into() }; let args = arena_vec![self;]; let bindings = arena_vec![self;]; diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 45b5e63bd1b3b..554ed36b814e6 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1268,13 +1268,18 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.check_c_variadic_type(fk); - // Functions cannot both be `const async` + // Functions cannot both be `const async` or `const gen` if let Some(&FnHeader { constness: Const::Yes(cspan), - asyncness: Async::Yes { span: aspan, .. }, + coro_kind: + Some( + CoroutineKind::Async { span: aspan, .. } + | CoroutineKind::Gen { span: aspan, .. }, + ), .. }) = fk.header() { + // FIXME(gen_blocks): Report a different error for `const gen` self.err_handler().emit_err(errors::ConstAndAsync { spans: vec![cspan, aspan], cspan, diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index cbdcc683b566d..1ad28ffbf2bb6 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1490,9 +1490,14 @@ impl<'a> State<'a> { } } - fn print_asyncness(&mut self, asyncness: ast::Async) { - if asyncness.is_async() { - self.word_nbsp("async"); + fn print_coro_kind(&mut self, coro_kind: ast::CoroutineKind) { + match coro_kind { + ast::CoroutineKind::Gen { .. } => { + self.word_nbsp("gen"); + } + ast::CoroutineKind::Async { .. } => { + self.word_nbsp("async"); + } } } @@ -1685,7 +1690,7 @@ impl<'a> State<'a> { fn print_fn_header_info(&mut self, header: ast::FnHeader) { self.print_constness(header.constness); - self.print_asyncness(header.asyncness); + header.coro_kind.map(|coro_kind| self.print_coro_kind(coro_kind)); self.print_unsafety(header.unsafety); match header.ext { diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 45a55e20ca737..0082d6e350e26 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -413,7 +413,7 @@ impl<'a> State<'a> { binder, capture_clause, constness, - asyncness, + coro_kind, movability, fn_decl, body, @@ -423,7 +423,7 @@ impl<'a> State<'a> { self.print_closure_binder(binder); self.print_constness(*constness); self.print_movability(*movability); - self.print_asyncness(*asyncness); + coro_kind.map(|coro_kind| self.print_coro_kind(coro_kind)); self.print_capture_clause(*capture_clause); self.print_fn_params_and_ret(fn_decl, true); diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index bf1e1ebf5ddf4..81433155ecfd0 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -541,10 +541,14 @@ fn check_test_signature( return Err(sd.emit_err(errors::TestBadFn { span: i.span, cause: span, kind: "unsafe" })); } - if let ast::Async::Yes { span, .. } = f.sig.header.asyncness { + if let Some(ast::CoroutineKind::Async { span, .. }) = f.sig.header.coro_kind { return Err(sd.emit_err(errors::TestBadFn { span: i.span, cause: span, kind: "async" })); } + if let Some(ast::CoroutineKind::Gen { span, .. }) = f.sig.header.coro_kind { + return Err(sd.emit_err(errors::TestBadFn { span: i.span, cause: span, kind: "gen" })); + } + // If the termination trait is active, the compiler will check that the output // type implements the `Termination` trait as `libtest` enforces that. let has_output = match &f.sig.decl.output { diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 9a8d0d691f09b..794e11d87d1e4 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -547,7 +547,7 @@ impl<'a> ExtCtxt<'a> { binder: ast::ClosureBinder::NotPresent, capture_clause: ast::CaptureBy::Ref, constness: ast::Const::No, - asyncness: ast::Async::No, + coro_kind: None, movability: ast::Movability::Movable, fn_decl, body, diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 81733d8f64e2c..0d9e174e37aad 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2255,6 +2255,8 @@ pub enum ImplItemKind<'hir> { /// The name of the associated type for `Fn` return types. pub const FN_OUTPUT_NAME: Symbol = sym::Output; +/// The name of the associated type for `Iterator` item types. +pub const ITERATOR_ITEM_NAME: Symbol = sym::Item; /// Bind a type to an associated type (i.e., `A = Foo`). /// diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index cb36d510149f4..f0bb18df48c94 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -651,9 +651,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }, ) } - Some(hir::CoroutineKind::Gen(hir::CoroutineSource::Fn)) => { - todo!("gen closures do not exist yet") - } + // For a `gen {}` block created as a `gen fn` body, we need the return type to be + // (). + Some(hir::CoroutineKind::Gen(hir::CoroutineSource::Fn)) => self.tcx.types.unit, _ => astconv.ty_infer(None, decl.output.span()), }, diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 4cccaeeca8462..7c4f81a4c3970 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -162,7 +162,11 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> // Explicitly check for lints associated with 'closure_id', since // it does not have a corresponding AST node if let ast_visit::FnKind::Fn(_, _, sig, _, _, _) = fk { - if let ast::Async::Yes { closure_id, .. } = sig.header.asyncness { + if let Some( + ast::CoroutineKind::Async { closure_id, .. } + | ast::CoroutineKind::Gen { closure_id, .. }, + ) = sig.header.coro_kind + { self.check_id(closure_id); } } @@ -223,7 +227,11 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> // it does not have a corresponding AST node match e.kind { ast::ExprKind::Closure(box ast::Closure { - asyncness: ast::Async::Yes { closure_id, .. }, + coro_kind: + Some( + ast::CoroutineKind::Async { closure_id, .. } + | ast::CoroutineKind::Gen { closure_id, .. }, + ), .. }) => self.check_id(closure_id), _ => {} diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 1c3c433d8b731..da51d9dbe9f84 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -23,6 +23,8 @@ parse_async_block_in_2015 = `async` blocks are only allowed in Rust 2018 or late parse_async_fn_in_2015 = `async fn` is not permitted in Rust 2015 .label = to use `async fn`, switch to Rust 2018 or later +parse_async_gen_fn = `async gen` functions are not supported + parse_async_move_block_in_2015 = `async move` blocks are only allowed in Rust 2018 or later parse_async_move_order_incorrect = the order of `move` and `async` is incorrect diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 03e047b297dc5..4bd7c99de9ae5 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -562,6 +562,13 @@ pub(crate) struct GenFn { pub span: Span, } +#[derive(Diagnostic)] +#[diag(parse_async_gen_fn)] +pub(crate) struct AsyncGenFn { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(parse_comma_after_base_struct)] #[note] diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index f8444881d1a54..8482824ec4be4 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -10,7 +10,7 @@ use super::{ use crate::errors; use crate::maybe_recover_from_interpolated_ty_qpath; use ast::mut_visit::{noop_visit_expr, MutVisitor}; -use ast::{GenBlockKind, Pat, Path, PathSegment}; +use ast::{CoroutineKind, GenBlockKind, Pat, Path, PathSegment}; use core::mem; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; @@ -21,7 +21,7 @@ use rustc_ast::util::parser::{prec_let_scrutinee_needs_par, AssocOp, Fixity}; use rustc_ast::visit::Visitor; use rustc_ast::{self as ast, AttrStyle, AttrVec, CaptureBy, ExprField, UnOp, DUMMY_NODE_ID}; use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty, TyKind}; -use rustc_ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits}; +use rustc_ast::{Arm, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits}; use rustc_ast::{ClosureBinder, MetaItemLit, StmtKind}; use rustc_ast_pretty::pprust; use rustc_data_structures::stack::ensure_sufficient_stack; @@ -2237,7 +2237,7 @@ impl<'a> Parser<'a> { let asyncness = if self.token.uninterpolated_span().at_least_rust_2018() { self.parse_asyncness(Case::Sensitive) } else { - Async::No + None }; let capture_clause = self.parse_capture_clause()?; @@ -2261,7 +2261,7 @@ impl<'a> Parser<'a> { } }; - if let Async::Yes { span, .. } = asyncness { + if let Some(CoroutineKind::Async { span, .. }) = asyncness { // Feature-gate `async ||` closures. self.sess.gated_spans.gate(sym::async_closure, span); } @@ -2284,7 +2284,7 @@ impl<'a> Parser<'a> { binder, capture_clause, constness, - asyncness, + coro_kind: asyncness, movability, fn_decl, body, diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index a737f37a1040e..8a987767dc4cb 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -11,8 +11,8 @@ use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree}; use rustc_ast::util::case::Case; use rustc_ast::MacCall; use rustc_ast::{self as ast, AttrVec, Attribute, DUMMY_NODE_ID}; -use rustc_ast::{Async, Const, Defaultness, IsAuto, Mutability, Unsafe, UseTree, UseTreeKind}; use rustc_ast::{BindingAnnotation, Block, FnDecl, FnSig, Param, SelfKind}; +use rustc_ast::{Const, Defaultness, IsAuto, Mutability, Unsafe, UseTree, UseTreeKind}; use rustc_ast::{EnumDef, FieldDef, Generics, TraitRef, Ty, TyKind, Variant, VariantData}; use rustc_ast::{FnHeader, ForeignItem, Path, PathSegment, Visibility, VisibilityKind}; use rustc_ast_pretty::pprust; @@ -2401,7 +2401,7 @@ impl<'a> Parser<'a> { let ext_start_sp = self.token.span; let ext = self.parse_extern(case); - if let Async::Yes { span, .. } = asyncness { + if let Some(CoroutineKind::Async { span, .. }) = asyncness { if span.is_rust_2015() { self.sess.emit_err(errors::AsyncFnIn2015 { span, @@ -2410,8 +2410,16 @@ impl<'a> Parser<'a> { } } - if let Gen::Yes { span, .. } = genness { - self.sess.emit_err(errors::GenFn { span }); + if let Some(CoroutineKind::Gen { span, .. }) = genness { + self.sess.gated_spans.gate(sym::gen_blocks, span); + } + + if let ( + Some(CoroutineKind::Async { span: async_span, .. }), + Some(CoroutineKind::Gen { span: gen_span, .. }), + ) = (asyncness, genness) + { + self.sess.emit_err(errors::AsyncGenFn { span: async_span.to(gen_span) }); } if !self.eat_keyword_case(kw::Fn, case) { @@ -2444,13 +2452,18 @@ impl<'a> Parser<'a> { } } else if self.check_keyword(kw::Async) { match asyncness { - Async::Yes { span, .. } => Some(WrongKw::Duplicated(span)), - Async::No => { - recover_asyncness = Async::Yes { + Some(CoroutineKind::Async { span, .. }) => { + Some(WrongKw::Duplicated(span)) + } + Some(CoroutineKind::Gen { .. }) => { + panic!("not sure how to recover here") + } + None => { + recover_asyncness = Some(CoroutineKind::Async { span: self.token.span, closure_id: DUMMY_NODE_ID, return_impl_trait_id: DUMMY_NODE_ID, - }; + }); Some(WrongKw::Misplaced(unsafe_start_sp)) } } @@ -2531,6 +2544,8 @@ impl<'a> Parser<'a> { } } + // FIXME(gen_blocks): add keyword recovery logic for genness + if wrong_kw.is_some() && self.may_recover() && self.look_ahead(1, |tok| tok.is_keyword_case(kw::Fn, case)) @@ -2542,7 +2557,7 @@ impl<'a> Parser<'a> { return Ok(FnHeader { constness: recover_constness, unsafety: recover_unsafety, - asyncness: recover_asyncness, + coro_kind: recover_asyncness, ext, }); } @@ -2552,7 +2567,13 @@ impl<'a> Parser<'a> { } } - Ok(FnHeader { constness, unsafety, asyncness, ext }) + let coro_kind = match asyncness { + Some(CoroutineKind::Async { .. }) => asyncness, + Some(CoroutineKind::Gen { .. }) => unreachable!("asycness cannot be Gen"), + None => genness, + }; + + Ok(FnHeader { constness, unsafety, coro_kind, ext }) } /// Parses the parameter list and result type of a function declaration. diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index c680a950584dd..2816386cbad9f 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -11,7 +11,6 @@ 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; @@ -25,9 +24,10 @@ use rustc_ast::tokenstream::{AttributesData, DelimSpan, Spacing}; use rustc_ast::tokenstream::{TokenStream, TokenTree, TokenTreeCursor}; use rustc_ast::util::case::Case; use rustc_ast::AttrId; +use rustc_ast::CoroutineKind; use rustc_ast::DUMMY_NODE_ID; use rustc_ast::{self as ast, AnonConst, Const, DelimArgs, Extern}; -use rustc_ast::{Async, AttrArgs, AttrArgsEq, Expr, ExprKind, Mutability, StrLit}; +use rustc_ast::{AttrArgs, AttrArgsEq, Expr, ExprKind, Mutability, StrLit}; use rustc_ast::{HasAttrs, HasTokens, Unsafe, Visibility, VisibilityKind}; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; @@ -1125,22 +1125,30 @@ impl<'a> Parser<'a> { } /// Parses asyncness: `async` or nothing. - fn parse_asyncness(&mut self, case: Case) -> Async { + fn parse_asyncness(&mut self, case: Case) -> Option { if self.eat_keyword_case(kw::Async, case) { let span = self.prev_token.uninterpolated_span(); - Async::Yes { span, closure_id: DUMMY_NODE_ID, return_impl_trait_id: DUMMY_NODE_ID } + Some(CoroutineKind::Async { + span, + closure_id: DUMMY_NODE_ID, + return_impl_trait_id: DUMMY_NODE_ID, + }) } else { - Async::No + None } } /// Parses genness: `gen` or nothing. - fn parse_genness(&mut self, case: Case) -> Gen { + fn parse_genness(&mut self, case: Case) -> Option { 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 } + Some(CoroutineKind::Gen { + span, + closure_id: DUMMY_NODE_ID, + return_impl_trait_id: DUMMY_NODE_ID, + }) } else { - Gen::No + None } } diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index b1a57c3dfd97c..068a99db4ae1a 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -596,7 +596,7 @@ impl<'a> Parser<'a> { tokens: None, }; let span_start = self.token.span; - let ast::FnHeader { ext, unsafety, constness, asyncness } = + let ast::FnHeader { ext, unsafety, constness, coro_kind } = self.parse_fn_front_matter(&inherited_vis, Case::Sensitive)?; if self.may_recover() && self.token.kind == TokenKind::Lt { self.recover_fn_ptr_with_generics(lo, &mut params, param_insertion_point)?; @@ -609,9 +609,10 @@ impl<'a> Parser<'a> { // cover it. self.sess.emit_err(FnPointerCannotBeConst { span: whole_span, qualifier: span }); } - if let ast::Async::Yes { span, .. } = asyncness { + if let Some(ast::CoroutineKind::Async { span, .. }) = coro_kind { self.sess.emit_err(FnPointerCannotBeAsync { span: whole_span, qualifier: span }); } + // FIXME(gen_blocks): emit a similar error for `gen fn()` let decl_span = span_start.to(self.token.span); Ok(TyKind::BareFn(P(BareFnTy { ext, unsafety, generic_params: params, decl, decl_span }))) } diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 647c92785e10d..ab5d3b368eb8a 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -156,7 +156,10 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { fn visit_fn(&mut self, fn_kind: FnKind<'a>, span: Span, _: NodeId) { if let FnKind::Fn(_, _, sig, _, generics, body) = fn_kind { - if let Async::Yes { closure_id, .. } = sig.header.asyncness { + if let Some( + CoroutineKind::Async { closure_id, .. } | CoroutineKind::Gen { closure_id, .. }, + ) = sig.header.coro_kind + { self.visit_generics(generics); // For async functions, we need to create their inner defs inside of a @@ -281,11 +284,12 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { // Async closures desugar to closures inside of closures, so // we must create two defs. let closure_def = self.create_def(expr.id, kw::Empty, DefKind::Closure, expr.span); - match closure.asyncness { - Async::Yes { closure_id, .. } => { - self.create_def(closure_id, kw::Empty, DefKind::Closure, expr.span) - } - Async::No => closure_def, + match closure.coro_kind { + Some( + CoroutineKind::Async { closure_id, .. } + | CoroutineKind::Gen { closure_id, .. }, + ) => self.create_def(closure_id, kw::Empty, DefKind::Closure, expr.span), + None => closure_def, } } ExprKind::Gen(_, _, _) => { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index f20d582903085..ad14f5e5225f6 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -916,8 +916,10 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, &sig.decl.output, ); - if let Some((async_node_id, _)) = sig.header.asyncness.opt_return_id() { - this.record_lifetime_params_for_impl_trait(async_node_id); + if let Some((coro_node_id, _)) = + sig.header.coro_kind.map(|coro_kind| coro_kind.return_id()) + { + this.record_lifetime_params_for_impl_trait(coro_node_id); } }, ); @@ -940,12 +942,13 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, this.visit_generics(generics); let declaration = &sig.decl; - let async_node_id = sig.header.asyncness.opt_return_id(); + let coro_node_id = + sig.header.coro_kind.map(|coro_kind| coro_kind.return_id()); this.with_lifetime_rib( LifetimeRibKind::AnonymousCreateParameter { binder: fn_id, - report_in_path: async_node_id.is_some(), + report_in_path: coro_node_id.is_some(), }, |this| { this.resolve_fn_signature( @@ -958,7 +961,7 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, &declaration.output, ); - if let Some((async_node_id, _)) = async_node_id { + if let Some((async_node_id, _)) = coro_node_id { this.record_lifetime_params_for_impl_trait(async_node_id); } }, @@ -4288,8 +4291,10 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { // `async |x| ...` gets desugared to `|x| async {...}`, so we need to // resolve the arguments within the proper scopes so that usages of them inside the // closure are detected as upvars rather than normal closure arg usages. + // + // Similarly, `gen |x| ...` gets desugared to `|x| gen {...}`, so we handle that too. ExprKind::Closure(box ast::Closure { - asyncness: Async::Yes { .. }, + coro_kind: Some(_), ref fn_decl, ref body, .. diff --git a/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs b/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs index e50e83834c1ab..640d4a069ec78 100644 --- a/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs +++ b/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs @@ -3,7 +3,7 @@ use std::{io, thread}; use crate::doc::{NEEDLESS_DOCTEST_MAIN, TEST_ATTR_IN_DOCTEST}; use clippy_utils::diagnostics::span_lint; -use rustc_ast::{Async, Fn, FnRetTy, Item, ItemKind}; +use rustc_ast::{CoroutineKind, Fn, FnRetTy, Item, ItemKind}; use rustc_data_structures::sync::Lrc; use rustc_errors::emitter::EmitterWriter; use rustc_errors::Handler; @@ -69,7 +69,7 @@ pub fn check( if !ignore { get_test_spans(&item, &mut test_attr_spans); } - let is_async = matches!(sig.header.asyncness, Async::Yes { .. }); + let is_async = matches!(sig.header.coro_kind, Some(CoroutineKind::Async { .. })); let returns_nothing = match &sig.decl.output { FnRetTy::Default(..) => true, FnRetTy::Ty(ty) if ty.kind.is_unit() => true, diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs index a2c61e07b70ad..12403bbff3c92 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs @@ -188,7 +188,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { Closure(box ast::Closure { binder: lb, capture_clause: lc, - asyncness: la, + coro_kind: la, movability: lm, fn_decl: lf, body: le, @@ -197,7 +197,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { Closure(box ast::Closure { binder: rb, capture_clause: rc, - asyncness: ra, + coro_kind: ra, movability: rm, fn_decl: rf, body: re, @@ -206,7 +206,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { ) => { eq_closure_binder(lb, rb) && lc == rc - && la.is_async() == ra.is_async() + && la.map_or(false, |la| la.is_async()) == ra.map_or(false, |ra| ra.is_async()) && lm == rm && eq_fn_decl(lf, rf) && eq_expr(le, re) @@ -563,9 +563,18 @@ pub fn eq_fn_sig(l: &FnSig, r: &FnSig) -> bool { eq_fn_decl(&l.decl, &r.decl) && eq_fn_header(&l.header, &r.header) } +fn eq_opt_coro_kind(l: Option, r: Option) -> bool { + match (l, r) { + (Some(CoroutineKind::Async { .. }), Some(CoroutineKind::Async { .. })) + | (Some(CoroutineKind::Gen { .. }), Some(CoroutineKind::Gen { .. })) => true, + (None, None) => true, + _ => false, + } +} + pub fn eq_fn_header(l: &FnHeader, r: &FnHeader) -> bool { matches!(l.unsafety, Unsafe::No) == matches!(r.unsafety, Unsafe::No) - && l.asyncness.is_async() == r.asyncness.is_async() + && eq_opt_coro_kind(l.coro_kind, r.coro_kind) && matches!(l.constness, Const::No) == matches!(r.constness, Const::No) && eq_ext(&l.ext, &r.ext) } diff --git a/src/tools/rustfmt/src/closures.rs b/src/tools/rustfmt/src/closures.rs index 8a4089a56f094..c1ce87eadcb99 100644 --- a/src/tools/rustfmt/src/closures.rs +++ b/src/tools/rustfmt/src/closures.rs @@ -29,7 +29,7 @@ pub(crate) fn rewrite_closure( binder: &ast::ClosureBinder, constness: ast::Const, capture: ast::CaptureBy, - is_async: &ast::Async, + coro_kind: &Option, movability: ast::Movability, fn_decl: &ast::FnDecl, body: &ast::Expr, @@ -40,7 +40,7 @@ pub(crate) fn rewrite_closure( debug!("rewrite_closure {:?}", body); let (prefix, extra_offset) = rewrite_closure_fn_decl( - binder, constness, capture, is_async, movability, fn_decl, body, span, context, shape, + binder, constness, capture, coro_kind, movability, fn_decl, body, span, context, shape, )?; // 1 = space between `|...|` and body. let body_shape = shape.offset_left(extra_offset)?; @@ -233,7 +233,7 @@ fn rewrite_closure_fn_decl( binder: &ast::ClosureBinder, constness: ast::Const, capture: ast::CaptureBy, - asyncness: &ast::Async, + coro_kind: &Option, movability: ast::Movability, fn_decl: &ast::FnDecl, body: &ast::Expr, @@ -263,7 +263,11 @@ fn rewrite_closure_fn_decl( } else { "" }; - let is_async = if asyncness.is_async() { "async " } else { "" }; + let coro = match coro_kind { + Some(ast::CoroutineKind::Async { .. }) => "async ", + Some(ast::CoroutineKind::Gen { .. }) => "gen ", + None => "", + }; let mover = if matches!(capture, ast::CaptureBy::Value { .. }) { "move " } else { @@ -272,7 +276,7 @@ fn rewrite_closure_fn_decl( // 4 = "|| {".len(), which is overconservative when the closure consists of // a single expression. let nested_shape = shape - .shrink_left(binder.len() + const_.len() + immovable.len() + is_async.len() + mover.len())? + .shrink_left(binder.len() + const_.len() + immovable.len() + coro.len() + mover.len())? .sub_width(4)?; // 1 = | @@ -310,7 +314,7 @@ fn rewrite_closure_fn_decl( .tactic(tactic) .preserve_newline(true); let list_str = write_list(&item_vec, &fmt)?; - let mut prefix = format!("{binder}{const_}{immovable}{is_async}{mover}|{list_str}|"); + let mut prefix = format!("{binder}{const_}{immovable}{coro}{mover}|{list_str}|"); if !ret_str.is_empty() { if prefix.contains('\n') { @@ -339,7 +343,7 @@ pub(crate) fn rewrite_last_closure( ref binder, constness, capture_clause, - ref asyncness, + ref coro_kind, movability, ref fn_decl, ref body, @@ -360,7 +364,7 @@ pub(crate) fn rewrite_last_closure( binder, constness, capture_clause, - asyncness, + coro_kind, movability, fn_decl, body, diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs index 60e0e007b1d11..4515c27be374a 100644 --- a/src/tools/rustfmt/src/expr.rs +++ b/src/tools/rustfmt/src/expr.rs @@ -212,7 +212,7 @@ pub(crate) fn format_expr( &cl.binder, cl.constness, cl.capture_clause, - &cl.asyncness, + &cl.coro_kind, cl.movability, &cl.fn_decl, &cl.body, diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs index edb5a5b629a8f..4dff65f8cd0a6 100644 --- a/src/tools/rustfmt/src/items.rs +++ b/src/tools/rustfmt/src/items.rs @@ -287,7 +287,7 @@ pub(crate) struct FnSig<'a> { decl: &'a ast::FnDecl, generics: &'a ast::Generics, ext: ast::Extern, - is_async: Cow<'a, ast::Async>, + coro_kind: Cow<'a, Option>, constness: ast::Const, defaultness: ast::Defaultness, unsafety: ast::Unsafe, @@ -302,7 +302,7 @@ impl<'a> FnSig<'a> { ) -> FnSig<'a> { FnSig { unsafety: method_sig.header.unsafety, - is_async: Cow::Borrowed(&method_sig.header.asyncness), + coro_kind: Cow::Borrowed(&method_sig.header.coro_kind), constness: method_sig.header.constness, defaultness: ast::Defaultness::Final, ext: method_sig.header.ext, @@ -328,7 +328,7 @@ impl<'a> FnSig<'a> { generics, ext: fn_sig.header.ext, constness: fn_sig.header.constness, - is_async: Cow::Borrowed(&fn_sig.header.asyncness), + coro_kind: Cow::Borrowed(&fn_sig.header.coro_kind), defaultness, unsafety: fn_sig.header.unsafety, visibility: vis, @@ -343,7 +343,8 @@ impl<'a> FnSig<'a> { result.push_str(&*format_visibility(context, self.visibility)); result.push_str(format_defaultness(self.defaultness)); result.push_str(format_constness(self.constness)); - result.push_str(format_async(&self.is_async)); + self.coro_kind + .map(|coro_kind| result.push_str(format_coro(&coro_kind))); result.push_str(format_unsafety(self.unsafety)); result.push_str(&format_extern( self.ext, diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs index fd49030bf1b4a..5805e417c04ad 100644 --- a/src/tools/rustfmt/src/utils.rs +++ b/src/tools/rustfmt/src/utils.rs @@ -75,10 +75,10 @@ pub(crate) fn format_visibility( } #[inline] -pub(crate) fn format_async(is_async: &ast::Async) -> &'static str { - match is_async { - ast::Async::Yes { .. } => "async ", - ast::Async::No => "", +pub(crate) fn format_coro(coro_kind: &ast::CoroutineKind) -> &'static str { + match coro_kind { + ast::CoroutineKind::Async { .. } => "async ", + ast::CoroutineKind::Gen { .. } => "gen ", } } diff --git a/tests/ui-fulldeps/pprust-expr-roundtrip.rs b/tests/ui-fulldeps/pprust-expr-roundtrip.rs index 685a029dcb226..9e581620ec1b4 100644 --- a/tests/ui-fulldeps/pprust-expr-roundtrip.rs +++ b/tests/ui-fulldeps/pprust-expr-roundtrip.rs @@ -132,7 +132,7 @@ fn iter_exprs(depth: usize, f: &mut dyn FnMut(P)) { binder: ClosureBinder::NotPresent, capture_clause: CaptureBy::Value { move_kw: DUMMY_SP }, constness: Const::No, - asyncness: Async::No, + coro_kind: None, movability: Movability::Movable, fn_decl: decl.clone(), body: e, diff --git a/tests/ui/coroutine/async_gen_fn.rs b/tests/ui/coroutine/async_gen_fn.rs new file mode 100644 index 0000000000000..f8860e07f6cc8 --- /dev/null +++ b/tests/ui/coroutine/async_gen_fn.rs @@ -0,0 +1,11 @@ +// edition: 2024 +// compile-flags: -Zunstable-options +#![feature(gen_blocks)] + +// async generators are not yet supported, so this test makes sure they make some kind of reasonable +// error. + +async gen fn foo() {} +//~^ `async gen` functions are not supported + +fn main() {} diff --git a/tests/ui/coroutine/async_gen_fn.stderr b/tests/ui/coroutine/async_gen_fn.stderr new file mode 100644 index 0000000000000..6857ebe6c7901 --- /dev/null +++ b/tests/ui/coroutine/async_gen_fn.stderr @@ -0,0 +1,8 @@ +error: `async gen` functions are not supported + --> $DIR/async_gen_fn.rs:8:1 + | +LL | async gen fn foo() {} + | ^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/coroutine/gen_fn.e2024.stderr b/tests/ui/coroutine/gen_fn.e2024.stderr index 928c16f57a2ec..9ad890af3e1e7 100644 --- a/tests/ui/coroutine/gen_fn.e2024.stderr +++ b/tests/ui/coroutine/gen_fn.e2024.stderr @@ -1,10 +1,12 @@ -error: `gen` functions are not yet implemented +error[E0658]: gen blocks are experimental --> $DIR/gen_fn.rs:4:1 | LL | gen fn foo() {} | ^^^ | - = help: for now you can use `gen {}` blocks and return `impl Iterator` instead + = note: see issue #117078 for more information + = help: add `#![feature(gen_blocks)]` to the crate attributes to enable error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/coroutine/gen_fn.rs b/tests/ui/coroutine/gen_fn.rs index da515f263b00a..e06629c5dfe54 100644 --- a/tests/ui/coroutine/gen_fn.rs +++ b/tests/ui/coroutine/gen_fn.rs @@ -3,6 +3,6 @@ gen fn foo() {} //[none]~^ ERROR: expected one of `#`, `async`, `const`, `default`, `extern`, `fn`, `pub`, `unsafe`, or `use`, found `gen` -//[e2024]~^^ ERROR: `gen` functions are not yet implemented +//[e2024]~^^ ERROR: gen blocks are experimental fn main() {} diff --git a/tests/ui/coroutine/gen_fn_iter.rs b/tests/ui/coroutine/gen_fn_iter.rs new file mode 100644 index 0000000000000..da01bc96ef453 --- /dev/null +++ b/tests/ui/coroutine/gen_fn_iter.rs @@ -0,0 +1,20 @@ +// edition: 2024 +// compile-flags: -Zunstable-options +// run-pass +#![feature(gen_blocks)] + +// make sure that a ridiculously simple gen fn works as an iterator. + +gen fn foo() -> i32 { + yield 1; + yield 2; + yield 3; +} + +fn main() { + let mut iter = foo(); + assert_eq!(iter.next(), Some(1)); + assert_eq!(iter.next(), Some(2)); + assert_eq!(iter.next(), Some(3)); + assert_eq!(iter.next(), None); +} diff --git a/tests/ui/coroutine/gen_fn_lifetime_capture.rs b/tests/ui/coroutine/gen_fn_lifetime_capture.rs new file mode 100644 index 0000000000000..b6a4d71e6cce1 --- /dev/null +++ b/tests/ui/coroutine/gen_fn_lifetime_capture.rs @@ -0,0 +1,19 @@ +// edition: 2024 +// compile-flags: -Zunstable-options +// check-pass +#![feature(gen_blocks)] + +// make sure gen fn captures lifetimes in its signature + +gen fn foo<'a, 'b>(x: &'a i32, y: &'b i32, z: &'b i32) -> &'b i32 { + yield y; + yield z; +} + +fn main() { + let z = 3; + let mut iter = foo(&1, &2, &z); + assert_eq!(iter.next(), Some(&2)); + assert_eq!(iter.next(), Some(&3)); + assert_eq!(iter.next(), None); +}