Auto merge of #118420 - compiler-errors:async-gen, r=eholk

Introduce support for `async gen` blocks

I'm delighted to demonstrate that `async gen` block are not very difficult to support. They're simply coroutines that yield `Poll<Option<T>>` and return `()`.

**This PR is WIP and in draft mode for now** -- I'm mostly putting it up to show folks that it's possible. This PR needs a lang-team experiment associated with it or possible an RFC, since I don't think it falls under the jurisdiction of the `gen` RFC that was recently authored by oli (https://github.com/rust-lang/rfcs/pull/3513, https://github.com/rust-lang/rust/issues/117078).

### Technical note on the pre-generator-transform yield type:

The reason that the underlying coroutines yield `Poll<Option<T>>` and not `Poll<T>` (which would make more sense, IMO, for the pre-transformed coroutine), is because the `TransformVisitor` that is used to turn coroutines into built-in state machine functions would have to destructure and reconstruct the latter into the former, which requires at least inserting a new basic block (for a `switchInt` terminator, to match on the `Poll` discriminant).

This does mean that the desugaring (at the `rustc_ast_lowering` level) of `async gen` blocks is a bit more involved. However, since we already need to intercept both `.await` and `yield` operators, I don't consider it much of a technical burden.

r? `@ghost`
This commit is contained in:
bors 2023-12-08 19:13:57 +00:00
commit f967532a47
61 changed files with 1120 additions and 357 deletions

View File

@ -1329,7 +1329,7 @@ pub struct Closure {
pub binder: ClosureBinder,
pub capture_clause: CaptureBy,
pub constness: Const,
pub coro_kind: Option<CoroutineKind>,
pub coroutine_kind: Option<CoroutineKind>,
pub movability: Movability,
pub fn_decl: P<FnDecl>,
pub body: P<Expr>,
@ -1534,6 +1534,7 @@ pub enum ExprKind {
pub enum GenBlockKind {
Async,
Gen,
AsyncGen,
}
impl fmt::Display for GenBlockKind {
@ -1547,6 +1548,7 @@ impl GenBlockKind {
match self {
GenBlockKind::Async => "async",
GenBlockKind::Gen => "gen",
GenBlockKind::AsyncGen => "async gen",
}
}
}
@ -2431,10 +2433,12 @@ pub enum Unsafe {
/// Iterator`.
#[derive(Copy, Clone, Encodable, Decodable, Debug)]
pub enum CoroutineKind {
/// `async`, which evaluates to `impl Future`
/// `async`, which returns an `impl Future`
Async { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId },
/// `gen`, which evaluates to `impl Iterator`
/// `gen`, which returns an `impl Iterator`
Gen { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId },
/// `async gen`, which returns an `impl AsyncIterator`
AsyncGen { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId },
}
impl CoroutineKind {
@ -2451,7 +2455,10 @@ impl CoroutineKind {
pub fn return_id(self) -> (NodeId, Span) {
match self {
CoroutineKind::Async { return_impl_trait_id, span, .. }
| CoroutineKind::Gen { return_impl_trait_id, span, .. } => (return_impl_trait_id, span),
| CoroutineKind::Gen { return_impl_trait_id, span, .. }
| CoroutineKind::AsyncGen { return_impl_trait_id, span, .. } => {
(return_impl_trait_id, span)
}
}
}
}
@ -2856,7 +2863,7 @@ pub struct FnHeader {
/// The `unsafe` keyword, if any
pub unsafety: Unsafe,
/// Whether this is `async`, `gen`, or nothing.
pub coro_kind: Option<CoroutineKind>,
pub coroutine_kind: Option<CoroutineKind>,
/// The `const` keyword, if any
pub constness: Const,
/// The `extern` keyword and corresponding ABI string, if any
@ -2866,9 +2873,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, coro_kind, constness, ext } = self;
let Self { unsafety, coroutine_kind, constness, ext } = self;
matches!(unsafety, Unsafe::Yes(_))
|| coro_kind.is_some()
|| coroutine_kind.is_some()
|| matches!(constness, Const::Yes(_))
|| !matches!(ext, Extern::None)
}
@ -2876,7 +2883,12 @@ impl FnHeader {
impl Default for FnHeader {
fn default() -> FnHeader {
FnHeader { unsafety: Unsafe::No, coro_kind: None, constness: Const::No, ext: Extern::None }
FnHeader {
unsafety: Unsafe::No,
coroutine_kind: None,
constness: Const::No,
ext: Extern::None,
}
}
}

View File

@ -121,8 +121,8 @@ pub trait MutVisitor: Sized {
noop_visit_fn_decl(d, self);
}
fn visit_coro_kind(&mut self, a: &mut CoroutineKind) {
noop_visit_coro_kind(a, self);
fn visit_coroutine_kind(&mut self, a: &mut CoroutineKind) {
noop_visit_coroutine_kind(a, self);
}
fn visit_closure_binder(&mut self, b: &mut ClosureBinder) {
@ -871,10 +871,11 @@ pub fn noop_visit_closure_binder<T: MutVisitor>(binder: &mut ClosureBinder, vis:
}
}
pub fn noop_visit_coro_kind<T: MutVisitor>(coro_kind: &mut CoroutineKind, vis: &mut T) {
match coro_kind {
pub fn noop_visit_coroutine_kind<T: MutVisitor>(coroutine_kind: &mut CoroutineKind, vis: &mut T) {
match coroutine_kind {
CoroutineKind::Async { span, closure_id, return_impl_trait_id }
| CoroutineKind::Gen { span, closure_id, return_impl_trait_id } => {
| CoroutineKind::Gen { span, closure_id, return_impl_trait_id }
| CoroutineKind::AsyncGen { span, closure_id, return_impl_trait_id } => {
vis.visit_span(span);
vis.visit_id(closure_id);
vis.visit_id(return_impl_trait_id);
@ -1171,9 +1172,9 @@ fn visit_const_item<T: MutVisitor>(
}
pub fn noop_visit_fn_header<T: MutVisitor>(header: &mut FnHeader, vis: &mut T) {
let FnHeader { unsafety, coro_kind, constness, ext: _ } = header;
let FnHeader { unsafety, coroutine_kind, constness, ext: _ } = header;
visit_constness(constness, vis);
coro_kind.as_mut().map(|coro_kind| vis.visit_coro_kind(coro_kind));
coroutine_kind.as_mut().map(|coroutine_kind| vis.visit_coroutine_kind(coroutine_kind));
visit_unsafety(unsafety, vis);
}
@ -1407,7 +1408,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
binder,
capture_clause,
constness,
coro_kind,
coroutine_kind,
movability: _,
fn_decl,
body,
@ -1416,7 +1417,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
}) => {
vis.visit_closure_binder(binder);
visit_constness(constness, vis);
coro_kind.as_mut().map(|coro_kind| vis.visit_coro_kind(coro_kind));
coroutine_kind.as_mut().map(|coroutine_kind| vis.visit_coroutine_kind(coroutine_kind));
vis.visit_capture_by(capture_clause);
vis.visit_fn_decl(fn_decl);
vis.visit_expr(body);

View File

@ -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,
coro_kind: _,
coroutine_kind: _,
constness: _,
movability: _,
fn_decl,

View File

@ -14,6 +14,7 @@ use rustc_ast::*;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_middle::span_bug;
use rustc_session::errors::report_lit_error;
use rustc_span::source_map::{respan, Spanned};
use rustc_span::symbol::{kw, sym, Ident, Symbol};
@ -196,22 +197,19 @@ impl<'hir> LoweringContext<'_, 'hir> {
binder,
capture_clause,
constness,
coro_kind,
coroutine_kind,
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(
}) => match coroutine_kind {
Some(coroutine_kind) => self.lower_expr_coroutine_closure(
binder,
*capture_clause,
e.id,
hir_id,
*closure_id,
*coroutine_kind,
fn_decl,
body,
*fn_decl_span,
@ -325,6 +323,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir::CoroutineSource::Block,
|this| this.with_new_scopes(e.span, |this| this.lower_block_expr(block)),
),
ExprKind::Gen(capture_clause, block, GenBlockKind::AsyncGen) => self
.make_async_gen_expr(
*capture_clause,
e.id,
None,
e.span,
hir::CoroutineSource::Block,
|this| this.with_new_scopes(e.span, |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.span_delayed_bug(e.span, "lowered ExprKind::Err"),
@ -736,6 +743,87 @@ impl<'hir> LoweringContext<'_, 'hir> {
}))
}
/// Lower a `async gen` construct to a generator that implements `AsyncIterator`.
///
/// This results in:
///
/// ```text
/// static move? |_task_context| -> () {
/// <body>
/// }
/// ```
pub(super) fn make_async_gen_expr(
&mut self,
capture_clause: CaptureBy,
closure_node_id: NodeId,
_yield_ty: Option<hir::FnRetTy<'hir>>,
span: Span,
async_coroutine_source: hir::CoroutineSource,
body: impl FnOnce(&mut Self) -> hir::Expr<'hir>,
) -> hir::ExprKind<'hir> {
let output = hir::FnRetTy::DefaultReturn(self.lower_span(span));
// Resume argument type: `ResumeTy`
let unstable_span = self.mark_span_with_reason(
DesugaringKind::Async,
span,
Some(self.allow_gen_future.clone()),
);
let resume_ty = hir::QPath::LangItem(hir::LangItem::ResumeTy, unstable_span);
let input_ty = hir::Ty {
hir_id: self.next_id(),
kind: hir::TyKind::Path(resume_ty),
span: unstable_span,
};
// The closure/coroutine `FnDecl` takes a single (resume) argument of type `input_ty`.
let fn_decl = self.arena.alloc(hir::FnDecl {
inputs: arena_vec![self; input_ty],
output,
c_variadic: false,
implicit_self: hir::ImplicitSelfKind::None,
lifetime_elision_allowed: false,
});
// Lower the argument pattern/ident. The ident is used again in the `.await` lowering.
let (pat, task_context_hid) = self.pat_ident_binding_mode(
span,
Ident::with_dummy_span(sym::_task_context),
hir::BindingAnnotation::MUT,
);
let param = hir::Param {
hir_id: self.next_id(),
pat,
ty_span: self.lower_span(span),
span: self.lower_span(span),
};
let params = arena_vec![self; param];
let body = self.lower_body(move |this| {
this.coroutine_kind = Some(hir::CoroutineKind::AsyncGen(async_coroutine_source));
let old_ctx = this.task_context;
this.task_context = Some(task_context_hid);
let res = body(this);
this.task_context = old_ctx;
(params, res)
});
// `static |_task_context| -> <ret_ty> { 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(hir::Movability::Static),
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(
@ -785,15 +873,18 @@ impl<'hir> LoweringContext<'_, 'hir> {
/// ```
fn lower_expr_await(&mut self, await_kw_span: Span, expr: &Expr) -> hir::ExprKind<'hir> {
let full_span = expr.span.to(await_kw_span);
match self.coroutine_kind {
Some(hir::CoroutineKind::Async(_)) => {}
let is_async_gen = match self.coroutine_kind {
Some(hir::CoroutineKind::Async(_)) => false,
Some(hir::CoroutineKind::AsyncGen(_)) => true,
Some(hir::CoroutineKind::Coroutine) | Some(hir::CoroutineKind::Gen(_)) | None => {
return hir::ExprKind::Err(self.tcx.sess.emit_err(AwaitOnlyInAsyncFnAndBlocks {
await_kw_span,
item_span: self.current_item,
}));
}
}
};
let span = self.mark_span_with_reason(DesugaringKind::Await, await_kw_span, None);
let gen_future_span = self.mark_span_with_reason(
DesugaringKind::Await,
@ -882,12 +973,19 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.stmt_expr(span, match_expr)
};
// task_context = yield ();
// Depending on `async` of `async gen`:
// async - task_context = yield ();
// async gen - task_context = yield ASYNC_GEN_PENDING;
let yield_stmt = {
let unit = self.expr_unit(span);
let yielded = if is_async_gen {
self.arena.alloc(self.expr_lang_item_path(span, hir::LangItem::AsyncGenPending))
} else {
self.expr_unit(span)
};
let yield_expr = self.expr(
span,
hir::ExprKind::Yield(unit, hir::YieldSource::Await { expr: Some(expr_hir_id) }),
hir::ExprKind::Yield(yielded, hir::YieldSource::Await { expr: Some(expr_hir_id) }),
);
let yield_expr = self.arena.alloc(yield_expr);
@ -997,7 +1095,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
Some(movability)
}
Some(hir::CoroutineKind::Gen(_)) | Some(hir::CoroutineKind::Async(_)) => {
Some(
hir::CoroutineKind::Gen(_)
| hir::CoroutineKind::Async(_)
| hir::CoroutineKind::AsyncGen(_),
) => {
panic!("non-`async`/`gen` closure body turned `async`/`gen` during lowering");
}
None => {
@ -1024,18 +1126,22 @@ impl<'hir> LoweringContext<'_, 'hir> {
(binder, params)
}
fn lower_expr_async_closure(
fn lower_expr_coroutine_closure(
&mut self,
binder: &ClosureBinder,
capture_clause: CaptureBy,
closure_id: NodeId,
closure_hir_id: hir::HirId,
inner_closure_id: NodeId,
coroutine_kind: CoroutineKind,
decl: &FnDecl,
body: &Expr,
fn_decl_span: Span,
fn_arg_span: Span,
) -> hir::ExprKind<'hir> {
let CoroutineKind::Async { closure_id: inner_closure_id, .. } = coroutine_kind else {
span_bug!(fn_decl_span, "`async gen` and `gen` closures are not supported, yet");
};
if let &ClosureBinder::For { span, .. } = binder {
self.tcx.sess.emit_err(NotSupportedForLifetimeBinderAsyncClosure { span });
}
@ -1504,8 +1610,9 @@ 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::Gen(_)) => {}
let is_async_gen = match self.coroutine_kind {
Some(hir::CoroutineKind::Gen(_)) => false,
Some(hir::CoroutineKind::AsyncGen(_)) => true,
Some(hir::CoroutineKind::Async(_)) => {
return hir::ExprKind::Err(
self.tcx.sess.emit_err(AsyncCoroutinesNotSupported { span }),
@ -1521,14 +1628,24 @@ impl<'hir> LoweringContext<'_, 'hir> {
)
.emit();
}
self.coroutine_kind = Some(hir::CoroutineKind::Coroutine)
self.coroutine_kind = Some(hir::CoroutineKind::Coroutine);
false
}
}
};
let expr =
let mut yielded =
opt_expr.as_ref().map(|x| self.lower_expr(x)).unwrap_or_else(|| self.expr_unit(span));
hir::ExprKind::Yield(expr, hir::YieldSource::Yield)
if is_async_gen {
// yield async_gen_ready($expr);
yielded = self.expr_call_lang_item_fn(
span,
hir::LangItem::AsyncGenReady,
std::slice::from_ref(yielded),
);
}
hir::ExprKind::Yield(yielded, hir::YieldSource::Yield)
}
/// Desugar `ExprForLoop` from: `[opt_ident]: for <pat> in <head> <body>` into:

View File

@ -206,19 +206,25 @@ impl<'hir> LoweringContext<'_, 'hir> {
// `impl Future<Output = T>` here because lower_body
// only cares about the input argument patterns in the function
// declaration (decl), not the return types.
let coro_kind = header.coro_kind;
let coroutine_kind = header.coroutine_kind;
let body_id = this.lower_maybe_coroutine_body(
span,
hir_id,
decl,
coro_kind,
coroutine_kind,
body.as_deref(),
);
let itctx = ImplTraitContext::Universal;
let (generics, decl) =
this.lower_generics(generics, header.constness, id, &itctx, |this| {
this.lower_fn_decl(decl, id, *fn_sig_span, FnDeclKind::Fn, coro_kind)
this.lower_fn_decl(
decl,
id,
*fn_sig_span,
FnDeclKind::Fn,
coroutine_kind,
)
});
let sig = hir::FnSig {
decl,
@ -734,7 +740,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
sig,
i.id,
FnDeclKind::Trait,
sig.header.coro_kind,
sig.header.coroutine_kind,
);
(generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)), false)
}
@ -743,7 +749,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
i.span,
hir_id,
&sig.decl,
sig.header.coro_kind,
sig.header.coroutine_kind,
Some(body),
);
let (generics, sig) = self.lower_method_sig(
@ -751,7 +757,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
sig,
i.id,
FnDeclKind::Trait,
sig.header.coro_kind,
sig.header.coroutine_kind,
);
(generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)), true)
}
@ -844,7 +850,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
i.span,
hir_id,
&sig.decl,
sig.header.coro_kind,
sig.header.coroutine_kind,
body.as_deref(),
);
let (generics, sig) = self.lower_method_sig(
@ -852,7 +858,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
sig,
i.id,
if self.is_in_trait_impl { FnDeclKind::Impl } else { FnDeclKind::Inherent },
sig.header.coro_kind,
sig.header.coroutine_kind,
);
(generics, hir::ImplItemKind::Fn(sig, body_id))
@ -1023,17 +1029,16 @@ impl<'hir> LoweringContext<'_, 'hir> {
span: Span,
fn_id: hir::HirId,
decl: &FnDecl,
coro_kind: Option<CoroutineKind>,
coroutine_kind: Option<CoroutineKind>,
body: Option<&Block>,
) -> hir::BodyId {
let (Some(coro_kind), Some(body)) = (coro_kind, body) else {
let (Some(coroutine_kind), Some(body)) = (coroutine_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
}
};
// FIXME(gen_blocks): Introduce `closure_id` method and remove ALL destructuring.
let (CoroutineKind::Async { closure_id, .. }
| CoroutineKind::Gen { closure_id, .. }
| CoroutineKind::AsyncGen { closure_id, .. }) = coroutine_kind;
self.lower_body(|this| {
let mut parameters: Vec<hir::Param<'_>> = Vec::new();
@ -1200,7 +1205,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
this.expr_block(body)
};
let coroutine_expr = match coro_kind {
// FIXME(gen_blocks): Consider unifying the `make_*_expr` functions.
let coroutine_expr = match coroutine_kind {
CoroutineKind::Async { .. } => this.make_async_expr(
CaptureBy::Value { move_kw: rustc_span::DUMMY_SP },
closure_id,
@ -1217,6 +1223,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir::CoroutineSource::Fn,
mkbody,
),
CoroutineKind::AsyncGen { .. } => this.make_async_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);
@ -1233,19 +1247,19 @@ impl<'hir> LoweringContext<'_, 'hir> {
sig: &FnSig,
id: NodeId,
kind: FnDeclKind,
coro_kind: Option<CoroutineKind>,
coroutine_kind: Option<CoroutineKind>,
) -> (&'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, coro_kind)
this.lower_fn_decl(&sig.decl, id, sig.span, kind, coroutine_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 {
let asyncness = if let Some(CoroutineKind::Async { span, .. }) = h.coroutine_kind {
hir::IsAsync::Async(span)
} else {
hir::IsAsync::NotAsync

View File

@ -132,6 +132,7 @@ struct LoweringContext<'a, 'hir> {
allow_try_trait: Lrc<[Symbol]>,
allow_gen_future: Lrc<[Symbol]>,
allow_async_iterator: Lrc<[Symbol]>,
/// Mapping from generics `def_id`s to TAIT generics `def_id`s.
/// For each captured lifetime (e.g., 'a), we create a new lifetime parameter that is a generic
@ -176,6 +177,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
} else {
[sym::gen_future].into()
},
// FIXME(gen_blocks): how does `closure_track_caller`
allow_async_iterator: [sym::gen_future, sym::async_iterator].into(),
generics_def_id_map: Default::default(),
host_param_id: None,
}
@ -1900,13 +1903,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
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 (opaque_ty_node_id, allowed_features) = match coro {
CoroutineKind::Async { return_impl_trait_id, .. } => (return_impl_trait_id, None),
CoroutineKind::Gen { return_impl_trait_id, .. } => (return_impl_trait_id, None),
CoroutineKind::AsyncGen { return_impl_trait_id, .. } => {
(return_impl_trait_id, Some(self.allow_async_iterator.clone()))
}
};
let opaque_ty_span =
self.mark_span_with_reason(DesugaringKind::Async, span, allowed_features);
let captured_lifetimes: Vec<_> = self
.resolver
.take_extra_lifetime_params(opaque_ty_node_id)
@ -1925,7 +1933,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let bound = this.lower_coroutine_fn_output_type_to_bound(
output,
coro,
span,
opaque_ty_span,
ImplTraitContext::ReturnPositionOpaqueTy {
origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id),
fn_kind,
@ -1944,7 +1952,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
&mut self,
output: &FnRetTy,
coro: CoroutineKind,
span: Span,
opaque_ty_span: Span,
nested_impl_trait_context: ImplTraitContext,
) -> hir::GenericBound<'hir> {
// Compute the `T` in `Future<Output = T>` from the return type.
@ -1960,20 +1968,21 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// "<$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),
CoroutineKind::Async { .. } => (sym::Output, hir::LangItem::Future),
CoroutineKind::Gen { .. } => (sym::Item, hir::LangItem::Iterator),
CoroutineKind::AsyncGen { .. } => (sym::Item, hir::LangItem::AsyncIterator),
};
let future_args = self.arena.alloc(hir::GenericArgs {
args: &[],
bindings: arena_vec![self; self.assoc_ty_binding(assoc_ty_name, span, output_ty)],
bindings: arena_vec![self; self.assoc_ty_binding(assoc_ty_name, opaque_ty_span, output_ty)],
parenthesized: hir::GenericArgsParentheses::No,
span_ext: DUMMY_SP,
});
hir::GenericBound::LangItemTrait(
trait_lang_item,
self.lower_span(span),
opaque_ty_span,
self.next_id(),
future_args,
)

View File

@ -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.assoc_ty_binding(hir::FN_OUTPUT_NAME, output_ty.span, output_ty);
let binding = self.assoc_ty_binding(sym::Output, output_ty.span, output_ty);
(
GenericArgsCtor {
args,

View File

@ -1271,14 +1271,15 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
// Functions cannot both be `const async` or `const gen`
if let Some(&FnHeader {
constness: Const::Yes(cspan),
coro_kind:
Some(
CoroutineKind::Async { span: aspan, .. }
| CoroutineKind::Gen { span: aspan, .. },
),
coroutine_kind: Some(coro_kind),
..
}) = fk.header()
{
let aspan = match coro_kind {
CoroutineKind::Async { span: aspan, .. }
| CoroutineKind::Gen { span: aspan, .. }
| CoroutineKind::AsyncGen { span: aspan, .. } => aspan,
};
// FIXME(gen_blocks): Report a different error for `const gen`
self.err_handler().emit_err(errors::ConstAndAsync {
spans: vec![cspan, aspan],

View File

@ -1490,14 +1490,18 @@ impl<'a> State<'a> {
}
}
fn print_coro_kind(&mut self, coro_kind: ast::CoroutineKind) {
match coro_kind {
fn print_coroutine_kind(&mut self, coroutine_kind: ast::CoroutineKind) {
match coroutine_kind {
ast::CoroutineKind::Gen { .. } => {
self.word_nbsp("gen");
}
ast::CoroutineKind::Async { .. } => {
self.word_nbsp("async");
}
ast::CoroutineKind::AsyncGen { .. } => {
self.word_nbsp("async");
self.word_nbsp("gen");
}
}
}
@ -1690,7 +1694,7 @@ impl<'a> State<'a> {
fn print_fn_header_info(&mut self, header: ast::FnHeader) {
self.print_constness(header.constness);
header.coro_kind.map(|coro_kind| self.print_coro_kind(coro_kind));
header.coroutine_kind.map(|coroutine_kind| self.print_coroutine_kind(coroutine_kind));
self.print_unsafety(header.unsafety);
match header.ext {

View File

@ -413,7 +413,7 @@ impl<'a> State<'a> {
binder,
capture_clause,
constness,
coro_kind,
coroutine_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);
coro_kind.map(|coro_kind| self.print_coro_kind(coro_kind));
coroutine_kind.map(|coroutine_kind| self.print_coroutine_kind(coroutine_kind));
self.print_capture_clause(*capture_clause);
self.print_fn_params_and_ret(fn_decl, true);

View File

@ -2517,12 +2517,23 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
CoroutineKind::Gen(kind) => match kind {
CoroutineSource::Block => "gen block",
CoroutineSource::Closure => "gen closure",
_ => bug!("gen block/closure expected, but gen function found."),
CoroutineSource::Fn => {
bug!("gen block/closure expected, but gen function found.")
}
},
CoroutineKind::AsyncGen(kind) => match kind {
CoroutineSource::Block => "async gen block",
CoroutineSource::Closure => "async gen closure",
CoroutineSource::Fn => {
bug!("gen block/closure expected, but gen function found.")
}
},
CoroutineKind::Async(async_kind) => match async_kind {
CoroutineSource::Block => "async block",
CoroutineSource::Closure => "async closure",
_ => bug!("async block/closure expected, but async function found."),
CoroutineSource::Fn => {
bug!("async block/closure expected, but async function found.")
}
},
CoroutineKind::Coroutine => "coroutine",
},

View File

@ -684,7 +684,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
hir::FnRetTy::Return(hir_ty) => (fn_decl.output.span(), Some(hir_ty)),
};
let mir_description = match hir.body(body).coroutine_kind {
Some(hir::CoroutineKind::Async(gen)) => match gen {
Some(hir::CoroutineKind::Async(src)) => match src {
hir::CoroutineSource::Block => " of async block",
hir::CoroutineSource::Closure => " of async closure",
hir::CoroutineSource::Fn => {
@ -701,7 +701,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
" of async function"
}
},
Some(hir::CoroutineKind::Gen(gen)) => match gen {
Some(hir::CoroutineKind::Gen(src)) => match src {
hir::CoroutineSource::Block => " of gen block",
hir::CoroutineSource::Closure => " of gen closure",
hir::CoroutineSource::Fn => {
@ -715,6 +715,21 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
" of gen function"
}
},
Some(hir::CoroutineKind::AsyncGen(src)) => match src {
hir::CoroutineSource::Block => " of async gen block",
hir::CoroutineSource::Closure => " of async 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 async gen fn should be in fn")
.output;
span = output.span();
" of async gen function"
}
},
Some(hir::CoroutineKind::Coroutine) => " of coroutine",
None => " of closure",
};

View File

@ -541,12 +541,30 @@ fn check_test_signature(
return Err(sd.emit_err(errors::TestBadFn { span: i.span, cause: span, kind: "unsafe" }));
}
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 let Some(coro_kind) = f.sig.header.coroutine_kind {
match coro_kind {
ast::CoroutineKind::Async { span, .. } => {
return Err(sd.emit_err(errors::TestBadFn {
span: i.span,
cause: span,
kind: "async",
}));
}
ast::CoroutineKind::Gen { span, .. } => {
return Err(sd.emit_err(errors::TestBadFn {
span: i.span,
cause: span,
kind: "gen",
}));
}
ast::CoroutineKind::AsyncGen { span, .. } => {
return Err(sd.emit_err(errors::TestBadFn {
span: i.span,
cause: span,
kind: "async gen",
}));
}
}
}
// If the termination trait is active, the compiler will check that the output

View File

@ -566,6 +566,9 @@ fn coroutine_kind_label(coroutine_kind: Option<CoroutineKind>) -> &'static str {
Some(CoroutineKind::Async(CoroutineSource::Block)) => "async_block",
Some(CoroutineKind::Async(CoroutineSource::Closure)) => "async_closure",
Some(CoroutineKind::Async(CoroutineSource::Fn)) => "async_fn",
Some(CoroutineKind::AsyncGen(CoroutineSource::Block)) => "async_gen_block",
Some(CoroutineKind::AsyncGen(CoroutineSource::Closure)) => "async_gen_closure",
Some(CoroutineKind::AsyncGen(CoroutineSource::Fn)) => "async_gen_fn",
Some(CoroutineKind::Coroutine) => "coroutine",
None => "closure",
}

View File

@ -43,7 +43,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let local = mir::Local::from_usize(local);
let expected_ty = self.monomorphize(self.mir.local_decls[local].ty);
if expected_ty != op.layout.ty {
warn!("Unexpected initial operand type. See the issues/114858");
warn!(
"Unexpected initial operand type: expected {expected_ty:?}, found {:?}.\
See <https://github.com/rust-lang/rust/issues/114858>.",
op.layout.ty
);
}
}
}

View File

@ -547,7 +547,7 @@ impl<'a> ExtCtxt<'a> {
binder: ast::ClosureBinder::NotPresent,
capture_clause: ast::CaptureBy::Ref,
constness: ast::Const::No,
coro_kind: None,
coroutine_kind: None,
movability: ast::Movability::Movable,
fn_decl,
body,

View File

@ -1356,12 +1356,16 @@ impl<'hir> Body<'hir> {
/// The type of source expression that caused this coroutine to be created.
#[derive(Clone, PartialEq, Eq, Debug, Copy, Hash, HashStable_Generic, Encodable, Decodable)]
pub enum CoroutineKind {
/// An explicit `async` block or the body of an async function.
/// 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),
/// An explicit `async gen` block or the body of an `async gen` function,
/// which is able to both `yield` and `.await`.
AsyncGen(CoroutineSource),
/// A coroutine literal created via a `yield` inside a closure.
Coroutine,
}
@ -1386,6 +1390,14 @@ impl fmt::Display for CoroutineKind {
}
k.fmt(f)
}
CoroutineKind::AsyncGen(k) => {
if f.alternate() {
f.write_str("`async gen` ")?;
} else {
f.write_str("async gen ")?
}
k.fmt(f)
}
}
}
}
@ -2081,17 +2093,6 @@ impl fmt::Display for YieldSource {
}
}
impl From<CoroutineKind> for YieldSource {
fn from(kind: CoroutineKind) -> Self {
match kind {
// Guess based on the kind of the current coroutine.
CoroutineKind::Coroutine => Self::Yield,
CoroutineKind::Async(_) => Self::Await { expr: None },
CoroutineKind::Gen(_) => Self::Yield,
}
}
}
// N.B., if you change this, you'll probably want to change the corresponding
// type structure in middle/ty.rs as well.
#[derive(Debug, Clone, Copy, HashStable_Generic)]
@ -2271,11 +2272,6 @@ pub enum ImplItemKind<'hir> {
Type(&'hir Ty<'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`).
///
/// Bindings like `A: Debug` are represented as a special type `A =

View File

@ -212,6 +212,7 @@ language_item_table! {
Iterator, sym::iterator, iterator_trait, Target::Trait, GenericRequirement::Exact(0);
Future, sym::future_trait, future_trait, Target::Trait, GenericRequirement::Exact(0);
AsyncIterator, sym::async_iterator, async_iterator_trait, Target::Trait, GenericRequirement::Exact(0);
CoroutineState, sym::coroutine_state, coroutine_state, Target::Enum, GenericRequirement::None;
Coroutine, sym::coroutine, coroutine_trait, Target::Trait, GenericRequirement::Minimum(1);
Unpin, sym::unpin, unpin_trait, Target::Trait, GenericRequirement::None;
@ -294,6 +295,10 @@ language_item_table! {
PollReady, sym::Ready, poll_ready_variant, Target::Variant, GenericRequirement::None;
PollPending, sym::Pending, poll_pending_variant, Target::Variant, GenericRequirement::None;
AsyncGenReady, sym::AsyncGenReady, async_gen_ready, Target::Method(MethodKind::Inherent), GenericRequirement::Exact(1);
AsyncGenPending, sym::AsyncGenPending, async_gen_pending, Target::AssocConst, GenericRequirement::Exact(1);
AsyncGenFinished, sym::AsyncGenFinished, async_gen_finished, Target::AssocConst, GenericRequirement::Exact(1);
// FIXME(swatinem): the following lang items are used for async lowering and
// should become obsolete eventually.
ResumeTy, sym::ResumeTy, resume_ty, Target::Struct, GenericRequirement::None;

View File

@ -67,6 +67,28 @@ pub(super) fn check_fn<'a, 'tcx>(
fcx.require_type_is_sized(yield_ty, span, traits::SizedYieldType);
yield_ty
}
// HACK(-Ztrait-solver=next): In the *old* trait solver, we must eagerly
// guide inference on the yield type so that we can handle `AsyncIterator`
// in this block in projection correctly. In the new trait solver, it is
// not a problem.
hir::CoroutineKind::AsyncGen(..) => {
let yield_ty = fcx.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::TypeInference,
span,
});
fcx.require_type_is_sized(yield_ty, span, traits::SizedYieldType);
Ty::new_adt(
tcx,
tcx.adt_def(tcx.require_lang_item(hir::LangItem::Poll, Some(span))),
tcx.mk_args(&[Ty::new_adt(
tcx,
tcx.adt_def(tcx.require_lang_item(hir::LangItem::Option, Some(span))),
tcx.mk_args(&[yield_ty.into()]),
)
.into()]),
)
}
hir::CoroutineKind::Async(..) => Ty::new_unit(tcx),
};

View File

@ -763,6 +763,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let args = self.fresh_args_for_item(span, def_id);
let ty = item_ty.instantiate(self.tcx, args);
self.write_args(hir_id, args);
self.write_resolution(hir_id, Ok((def_kind, def_id)));
let code = match lang_item {

View File

@ -162,11 +162,10 @@ 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 Some(
ast::CoroutineKind::Async { closure_id, .. }
| ast::CoroutineKind::Gen { closure_id, .. },
) = sig.header.coro_kind
{
if let Some(coro_kind) = sig.header.coroutine_kind {
let (ast::CoroutineKind::Async { closure_id, .. }
| ast::CoroutineKind::Gen { closure_id, .. }
| ast::CoroutineKind::AsyncGen { closure_id, .. }) = coro_kind;
self.check_id(closure_id);
}
}
@ -227,13 +226,13 @@ 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 {
coro_kind:
Some(
ast::CoroutineKind::Async { closure_id, .. }
| ast::CoroutineKind::Gen { closure_id, .. },
),
..
}) => self.check_id(closure_id),
coroutine_kind: Some(coro_kind), ..
}) => {
let (ast::CoroutineKind::Async { closure_id, .. }
| ast::CoroutineKind::Gen { closure_id, .. }
| ast::CoroutineKind::AsyncGen { closure_id, .. }) = coro_kind;
self.check_id(closure_id);
}
_ => {}
}
lint_callback!(self, check_expr_post, e);

View File

@ -150,11 +150,17 @@ 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::AsyncGen(_)) => {
"`async gen 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::AsyncGen(_)) => {
"`async gen fn` resumed after panicking"
}
ResumedAfterPanic(CoroutineKind::Gen(_)) => {
"`gen fn` should just keep returning `None` after panicking"
}
@ -245,6 +251,7 @@ 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::AsyncGen(_)) => todo!(),
ResumedAfterReturn(CoroutineKind::Gen(_)) => {
bug!("gen blocks can be resumed after they return and will keep returning `None`")
}
@ -252,6 +259,7 @@ impl<O> AssertKind<O> {
middle_assert_coroutine_resume_after_return
}
ResumedAfterPanic(CoroutineKind::Async(_)) => middle_assert_async_resume_after_panic,
ResumedAfterPanic(CoroutineKind::AsyncGen(_)) => todo!(),
ResumedAfterPanic(CoroutineKind::Gen(_)) => middle_assert_gen_resume_after_panic,
ResumedAfterPanic(CoroutineKind::Coroutine) => {
middle_assert_coroutine_resume_after_panic

View File

@ -144,10 +144,14 @@ 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.
/// Implementation of an `Iterator` trait by one of the coroutine types
/// generated for a `gen` construct.
IteratorCandidate,
/// Implementation of an `AsyncIterator` trait by one of the coroutine types
/// generated for a `async gen` construct.
AsyncIteratorCandidate,
/// Implementation of a `Fn`-family trait by one of the anonymous
/// types generated for a fn pointer type (e.g., `fn(int) -> int`)
FnPointerCandidate {

View File

@ -825,11 +825,16 @@ impl<'tcx> TyCtxt<'tcx> {
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.
/// 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(_)))
}
/// Returns `true` if the node pointed to by `def_id` is a coroutine for a `async gen` construct.
pub fn coroutine_is_async_gen(self, def_id: DefId) -> bool {
matches!(self.coroutine_kind(def_id), Some(hir::CoroutineKind::AsyncGen(_)))
}
pub fn stability(self) -> &'tcx stability::Index {
self.stability_index(())
}

View File

@ -732,6 +732,7 @@ impl<'tcx> TyCtxt<'tcx> {
DefKind::Closure if let Some(coroutine_kind) = self.coroutine_kind(def_id) => {
match coroutine_kind {
rustc_hir::CoroutineKind::Async(..) => "async closure",
rustc_hir::CoroutineKind::AsyncGen(..) => "async gen closure",
rustc_hir::CoroutineKind::Coroutine => "coroutine",
rustc_hir::CoroutineKind::Gen(..) => "gen closure",
}
@ -752,6 +753,7 @@ impl<'tcx> TyCtxt<'tcx> {
DefKind::Closure if let Some(coroutine_kind) = self.coroutine_kind(def_id) => {
match coroutine_kind {
rustc_hir::CoroutineKind::Async(..) => "an",
rustc_hir::CoroutineKind::AsyncGen(..) => "an",
rustc_hir::CoroutineKind::Coroutine => "a",
rustc_hir::CoroutineKind::Gen(..) => "a",
}

View File

@ -66,9 +66,9 @@ use rustc_index::{Idx, IndexVec};
use rustc_middle::mir::dump_mir;
use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor};
use rustc_middle::mir::*;
use rustc_middle::ty::CoroutineArgs;
use rustc_middle::ty::InstanceDef;
use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
use rustc_middle::ty::{CoroutineArgs, GenericArgsRef};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_mir_dataflow::impls::{
MaybeBorrowedLocals, MaybeLiveLocals, MaybeRequiresStorage, MaybeStorageLive,
};
@ -225,8 +225,6 @@ struct SuspensionPoint<'tcx> {
struct TransformVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
coroutine_kind: hir::CoroutineKind,
state_adt_ref: AdtDef<'tcx>,
state_args: GenericArgsRef<'tcx>,
// The type of the discriminant in the coroutine struct
discr_ty: Ty<'tcx>,
@ -245,21 +243,34 @@ struct TransformVisitor<'tcx> {
always_live_locals: BitSet<Local>,
// The original RETURN_PLACE local
new_ret_local: Local,
old_ret_local: Local,
old_yield_ty: Ty<'tcx>,
old_ret_ty: Ty<'tcx>,
}
impl<'tcx> TransformVisitor<'tcx> {
fn insert_none_ret_block(&self, body: &mut Body<'tcx>) -> BasicBlock {
assert!(matches!(self.coroutine_kind, CoroutineKind::Gen(_)));
let block = BasicBlock::new(body.basic_blocks.len());
let source_info = SourceInfo::outermost(body.span);
let option_def_id = self.tcx.require_lang_item(LangItem::Option, None);
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()),
Rvalue::Aggregate(
Box::new(AggregateKind::Adt(
option_def_id,
VariantIdx::from_usize(0),
self.tcx.mk_args(&[self.old_yield_ty.into()]),
None,
None,
)),
IndexVec::new(),
),
))),
source_info,
}];
@ -273,23 +284,6 @@ impl<'tcx> TransformVisitor<'tcx> {
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,
@ -302,51 +296,119 @@ impl<'tcx> TransformVisitor<'tcx> {
is_return: bool,
statements: &mut Vec<Statement<'tcx>>,
) {
let (kind, idx) = self.coroutine_state_adt_and_variant_idx(is_return);
match self.coroutine_kind {
// `Poll::Pending`
let rvalue = match self.coroutine_kind {
CoroutineKind::Async(_) => {
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;
}
}
// `Option::None`
CoroutineKind::Gen(_) => {
let poll_def_id = self.tcx.require_lang_item(LangItem::Poll, None);
let args = self.tcx.mk_args(&[self.old_ret_ty.into()]);
if is_return {
assert_eq!(self.state_adt_ref.variant(idx).fields.len(), 0);
statements.push(Statement {
kind: StatementKind::Assign(Box::new((
Place::return_place(),
Rvalue::Aggregate(Box::new(kind), IndexVec::new()),
))),
source_info,
});
return;
// Poll::Ready(val)
Rvalue::Aggregate(
Box::new(AggregateKind::Adt(
poll_def_id,
VariantIdx::from_usize(0),
args,
None,
None,
)),
IndexVec::from_raw(vec![val]),
)
} else {
// Poll::Pending
Rvalue::Aggregate(
Box::new(AggregateKind::Adt(
poll_def_id,
VariantIdx::from_usize(1),
args,
None,
None,
)),
IndexVec::new(),
)
}
}
CoroutineKind::Coroutine => {}
}
// 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);
CoroutineKind::Gen(_) => {
let option_def_id = self.tcx.require_lang_item(LangItem::Option, None);
let args = self.tcx.mk_args(&[self.old_yield_ty.into()]);
if is_return {
// None
Rvalue::Aggregate(
Box::new(AggregateKind::Adt(
option_def_id,
VariantIdx::from_usize(0),
args,
None,
None,
)),
IndexVec::new(),
)
} else {
// Some(val)
Rvalue::Aggregate(
Box::new(AggregateKind::Adt(
option_def_id,
VariantIdx::from_usize(1),
args,
None,
None,
)),
IndexVec::from_raw(vec![val]),
)
}
}
CoroutineKind::AsyncGen(_) => {
if is_return {
let ty::Adt(_poll_adt, args) = *self.old_yield_ty.kind() else { bug!() };
let ty::Adt(_option_adt, args) = *args.type_at(0).kind() else { bug!() };
let yield_ty = args.type_at(0);
Rvalue::Use(Operand::Constant(Box::new(ConstOperand {
span: source_info.span,
const_: Const::Unevaluated(
UnevaluatedConst::new(
self.tcx.require_lang_item(LangItem::AsyncGenFinished, None),
self.tcx.mk_args(&[yield_ty.into()]),
),
self.old_yield_ty,
),
user_ty: None,
})))
} else {
Rvalue::Use(val)
}
}
CoroutineKind::Coroutine => {
let coroutine_state_def_id =
self.tcx.require_lang_item(LangItem::CoroutineState, None);
let args = self.tcx.mk_args(&[self.old_yield_ty.into(), self.old_ret_ty.into()]);
if is_return {
// CoroutineState::Complete(val)
Rvalue::Aggregate(
Box::new(AggregateKind::Adt(
coroutine_state_def_id,
VariantIdx::from_usize(1),
args,
None,
None,
)),
IndexVec::from_raw(vec![val]),
)
} else {
// CoroutineState::Yielded(val)
Rvalue::Aggregate(
Box::new(AggregateKind::Adt(
coroutine_state_def_id,
VariantIdx::from_usize(0),
args,
None,
None,
)),
IndexVec::from_raw(vec![val]),
)
}
}
};
statements.push(Statement {
kind: StatementKind::Assign(Box::new((
Place::return_place(),
Rvalue::Aggregate(Box::new(kind), [val].into()),
))),
kind: StatementKind::Assign(Box::new((Place::return_place(), rvalue))),
source_info,
});
}
@ -420,7 +482,7 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> {
let ret_val = match data.terminator().kind {
TerminatorKind::Return => {
Some((true, None, Operand::Move(Place::from(self.new_ret_local)), None))
Some((true, None, Operand::Move(Place::from(self.old_ret_local)), None))
}
TerminatorKind::Yield { ref value, resume, resume_arg, drop } => {
Some((false, Some((resume, resume_arg)), value.clone(), drop))
@ -1331,7 +1393,8 @@ fn create_coroutine_resume_function<'tcx>(
if can_return {
let block = match coroutine_kind {
CoroutineKind::Async(_) | CoroutineKind::Coroutine => {
// FIXME(gen_blocks): Should `async gen` yield `None` when resumed once again?
CoroutineKind::Async(_) | CoroutineKind::AsyncGen(_) | CoroutineKind::Coroutine => {
insert_panic_block(tcx, body, ResumedAfterReturn(coroutine_kind))
}
CoroutineKind::Gen(_) => transform.insert_none_ret_block(body),
@ -1493,10 +1556,11 @@ pub(crate) fn mir_coroutine_witnesses<'tcx>(
impl<'tcx> MirPass<'tcx> for StateTransform {
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let Some(yield_ty) = body.yield_ty() else {
let Some(old_yield_ty) = body.yield_ty() else {
// This only applies to coroutines
return;
};
let old_ret_ty = body.return_ty();
assert!(body.coroutine_drop().is_none());
@ -1519,38 +1583,42 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
};
let is_async_kind = matches!(body.coroutine_kind(), Some(CoroutineKind::Async(_)));
let is_async_gen_kind = matches!(body.coroutine_kind(), Some(CoroutineKind::AsyncGen(_)));
let is_gen_kind = matches!(body.coroutine_kind(), Some(CoroutineKind::Gen(_)));
let (state_adt_ref, state_args) = match body.coroutine_kind().unwrap() {
let new_ret_ty = 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)
let poll_args = tcx.mk_args(&[old_ret_ty.into()]);
Ty::new_adt(tcx, 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)
let option_args = tcx.mk_args(&[old_yield_ty.into()]);
Ty::new_adt(tcx, option_adt_ref, option_args)
}
CoroutineKind::AsyncGen(_) => {
// The yield ty is already `Poll<Option<yield_ty>>`
old_yield_ty
}
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 state_args = tcx.mk_args(&[old_yield_ty.into(), old_ret_ty.into()]);
Ty::new_adt(tcx, state_adt_ref, state_args)
}
};
let ret_ty = Ty::new_adt(tcx, state_adt_ref, state_args);
// We rename RETURN_PLACE which has type mir.return_ty to new_ret_local
// We rename RETURN_PLACE which has type mir.return_ty to old_ret_local
// RETURN_PLACE then is a fresh unused local with type ret_ty.
let new_ret_local = replace_local(RETURN_PLACE, ret_ty, body, tcx);
let old_ret_local = replace_local(RETURN_PLACE, new_ret_ty, body, tcx);
// Replace all occurrences of `ResumeTy` with `&mut Context<'_>` within async bodies.
if is_async_kind {
if is_async_kind || is_async_gen_kind {
transform_async_context(tcx, body);
}
@ -1564,9 +1632,10 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
} else {
body.local_decls[resume_local].ty
};
let new_resume_local = replace_local(resume_local, resume_ty, body, tcx);
let old_resume_local = replace_local(resume_local, resume_ty, body, tcx);
// When first entering the coroutine, move the resume argument into its new local.
// When first entering the coroutine, move the resume argument into its old local
// (which is now a generator interior).
let source_info = SourceInfo::outermost(body.span);
let stmts = &mut body.basic_blocks_mut()[START_BLOCK].statements;
stmts.insert(
@ -1574,7 +1643,7 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
Statement {
source_info,
kind: StatementKind::Assign(Box::new((
new_resume_local.into(),
old_resume_local.into(),
Rvalue::Use(Operand::Move(resume_local.into())),
))),
},
@ -1610,14 +1679,14 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
let mut transform = TransformVisitor {
tcx,
coroutine_kind: body.coroutine_kind().unwrap(),
state_adt_ref,
state_args,
remap,
storage_liveness,
always_live_locals,
suspension_points: Vec::new(),
new_ret_local,
old_ret_local,
discr_ty,
old_ret_ty,
old_yield_ty,
};
transform.visit_body(body);

View File

@ -23,8 +23,6 @@ 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

View File

@ -562,13 +562,6 @@ 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]

View File

@ -1442,20 +1442,21 @@ 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_gen_block(kw::Async) {
// Check for `async {` and `async move {`.
// FIXME(gen_blocks): Parse `gen async` and suggest swap
if this.is_gen_block(kw::Async, 0) {
// Check for `async {` and `async move {`,
// or `async gen {` and `async gen move {`.
this.parse_gen_block()
} else {
this.parse_expr_closure()
}
} else if this.eat_keyword(kw::Await) {
} else if this.token.uninterpolated_span().at_least_rust_2024()
&& (this.is_gen_block(kw::Gen, 0)
|| (this.check_keyword(kw::Async) && this.is_gen_block(kw::Gen, 1)))
{
this.parse_gen_block()
} else if this.eat_keyword_noexpect(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()
}
@ -2234,8 +2235,8 @@ impl<'a> Parser<'a> {
let movability =
if self.eat_keyword(kw::Static) { Movability::Static } else { Movability::Movable };
let asyncness = if self.token.uninterpolated_span().at_least_rust_2018() {
self.parse_asyncness(Case::Sensitive)
let coroutine_kind = if self.token.uninterpolated_span().at_least_rust_2018() {
self.parse_coroutine_kind(Case::Sensitive)
} else {
None
};
@ -2261,9 +2262,17 @@ impl<'a> Parser<'a> {
}
};
if let Some(CoroutineKind::Async { span, .. }) = asyncness {
// Feature-gate `async ||` closures.
self.sess.gated_spans.gate(sym::async_closure, span);
match coroutine_kind {
Some(CoroutineKind::Async { span, .. }) => {
// Feature-gate `async ||` closures.
self.sess.gated_spans.gate(sym::async_closure, span);
}
Some(CoroutineKind::Gen { span, .. }) | Some(CoroutineKind::AsyncGen { span, .. }) => {
// Feature-gate `gen ||` and `async gen ||` closures.
// FIXME(gen_blocks): This perhaps should be a different gate.
self.sess.gated_spans.gate(sym::gen_blocks, span);
}
None => {}
}
if self.token.kind == TokenKind::Semi
@ -2284,7 +2293,7 @@ impl<'a> Parser<'a> {
binder,
capture_clause,
constness,
coro_kind: asyncness,
coroutine_kind,
movability,
fn_decl,
body,
@ -3207,7 +3216,7 @@ impl<'a> Parser<'a> {
fn parse_gen_block(&mut self) -> PResult<'a, P<Expr>> {
let lo = self.token.span;
let kind = if self.eat_keyword(kw::Async) {
GenBlockKind::Async
if self.eat_keyword(kw::Gen) { GenBlockKind::AsyncGen } else { GenBlockKind::Async }
} else {
assert!(self.eat_keyword(kw::Gen));
self.sess.gated_spans.gate(sym::gen_blocks, lo.to(self.token.span));
@ -3219,22 +3228,26 @@ impl<'a> Parser<'a> {
Ok(self.mk_expr_with_attrs(lo.to(self.prev_token.span), kind, attrs))
}
fn is_gen_block(&self, kw: Symbol) -> bool {
self.token.is_keyword(kw)
fn is_gen_block(&self, kw: Symbol, lookahead: usize) -> bool {
self.is_keyword_ahead(lookahead, &[kw])
&& ((
// `async move {`
self.is_keyword_ahead(1, &[kw::Move])
&& self.look_ahead(2, |t| {
self.is_keyword_ahead(lookahead + 1, &[kw::Move])
&& self.look_ahead(lookahead + 2, |t| {
*t == token::OpenDelim(Delimiter::Brace) || t.is_whole_block()
})
) || (
// `async {`
self.look_ahead(1, |t| {
self.look_ahead(lookahead + 1, |t| {
*t == token::OpenDelim(Delimiter::Brace) || t.is_whole_block()
})
))
}
pub(super) fn is_async_gen_block(&self) -> bool {
self.token.is_keyword(kw::Async) && self.is_gen_block(kw::Gen, 1)
}
fn is_certainly_not_a_block(&self) -> bool {
self.look_ahead(1, |t| t.is_ident())
&& (

View File

@ -2359,8 +2359,10 @@ impl<'a> Parser<'a> {
|| case == Case::Insensitive
&& t.is_non_raw_ident_where(|i| quals.iter().any(|qual| qual.as_str() == i.name.as_str().to_lowercase()))
)
// Rule out unsafe extern block.
&& !self.is_unsafe_foreign_mod())
// Rule out `unsafe extern {`.
&& !self.is_unsafe_foreign_mod()
// Rule out `async gen {` and `async gen move {`
&& !self.is_async_gen_block())
})
// `extern ABI fn`
|| self.check_keyword_case(kw::Extern, case)
@ -2392,10 +2394,7 @@ impl<'a> Parser<'a> {
let constness = self.parse_constness(case);
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 coroutine_kind = self.parse_coroutine_kind(case);
let unsafe_start_sp = self.token.span;
let unsafety = self.parse_unsafety(case);
@ -2403,7 +2402,7 @@ impl<'a> Parser<'a> {
let ext_start_sp = self.token.span;
let ext = self.parse_extern(case);
if let Some(CoroutineKind::Async { span, .. }) = asyncness {
if let Some(CoroutineKind::Async { span, .. }) = coroutine_kind {
if span.is_rust_2015() {
self.sess.emit_err(errors::AsyncFnIn2015 {
span,
@ -2412,16 +2411,11 @@ impl<'a> Parser<'a> {
}
}
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) });
match coroutine_kind {
Some(CoroutineKind::Gen { span, .. }) | Some(CoroutineKind::AsyncGen { span, .. }) => {
self.sess.gated_spans.gate(sym::gen_blocks, span);
}
Some(CoroutineKind::Async { .. }) | None => {}
}
if !self.eat_keyword_case(kw::Fn, case) {
@ -2440,7 +2434,7 @@ impl<'a> Parser<'a> {
// We may be able to recover
let mut recover_constness = constness;
let mut recover_asyncness = asyncness;
let mut recover_coroutine_kind = coroutine_kind;
let mut recover_unsafety = unsafety;
// This will allow the machine fix to directly place the keyword in the correct place or to indicate
// that the keyword is already present and the second instance should be removed.
@ -2453,15 +2447,24 @@ impl<'a> Parser<'a> {
}
}
} else if self.check_keyword(kw::Async) {
match asyncness {
match coroutine_kind {
Some(CoroutineKind::Async { span, .. }) => {
Some(WrongKw::Duplicated(span))
}
Some(CoroutineKind::AsyncGen { span, .. }) => {
Some(WrongKw::Duplicated(span))
}
Some(CoroutineKind::Gen { .. }) => {
panic!("not sure how to recover here")
recover_coroutine_kind = Some(CoroutineKind::AsyncGen {
span: self.token.span,
closure_id: DUMMY_NODE_ID,
return_impl_trait_id: DUMMY_NODE_ID,
});
// FIXME(gen_blocks): This span is wrong, didn't want to think about it.
Some(WrongKw::Misplaced(unsafe_start_sp))
}
None => {
recover_asyncness = Some(CoroutineKind::Async {
recover_coroutine_kind = Some(CoroutineKind::Async {
span: self.token.span,
closure_id: DUMMY_NODE_ID,
return_impl_trait_id: DUMMY_NODE_ID,
@ -2559,7 +2562,7 @@ impl<'a> Parser<'a> {
return Ok(FnHeader {
constness: recover_constness,
unsafety: recover_unsafety,
coro_kind: recover_asyncness,
coroutine_kind: recover_coroutine_kind,
ext,
});
}
@ -2569,13 +2572,7 @@ impl<'a> Parser<'a> {
}
}
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 })
Ok(FnHeader { constness, unsafety, coroutine_kind, ext })
}
/// Parses the parameter list and result type of a function declaration.

View File

@ -1125,23 +1125,30 @@ impl<'a> Parser<'a> {
}
/// Parses asyncness: `async` or nothing.
fn parse_asyncness(&mut self, case: Case) -> Option<CoroutineKind> {
fn parse_coroutine_kind(&mut self, case: Case) -> Option<CoroutineKind> {
let span = self.token.uninterpolated_span();
if self.eat_keyword_case(kw::Async, case) {
let span = self.prev_token.uninterpolated_span();
Some(CoroutineKind::Async {
span,
closure_id: DUMMY_NODE_ID,
return_impl_trait_id: DUMMY_NODE_ID,
})
} else {
None
}
}
/// Parses genness: `gen` or nothing.
fn parse_genness(&mut self, case: Case) -> Option<CoroutineKind> {
if self.token.span.at_least_rust_2024() && self.eat_keyword_case(kw::Gen, case) {
let span = self.prev_token.uninterpolated_span();
// FIXME(gen_blocks): Do we want to unconditionally parse `gen` and then
// error if edition <= 2024, like we do with async and edition <= 2018?
if self.token.uninterpolated_span().at_least_rust_2024()
&& self.eat_keyword_case(kw::Gen, case)
{
let gen_span = self.prev_token.uninterpolated_span();
Some(CoroutineKind::AsyncGen {
span: span.to(gen_span),
closure_id: DUMMY_NODE_ID,
return_impl_trait_id: DUMMY_NODE_ID,
})
} else {
Some(CoroutineKind::Async {
span,
closure_id: DUMMY_NODE_ID,
return_impl_trait_id: DUMMY_NODE_ID,
})
}
} else if self.token.uninterpolated_span().at_least_rust_2024()
&& self.eat_keyword_case(kw::Gen, case)
{
Some(CoroutineKind::Gen {
span,
closure_id: DUMMY_NODE_ID,

View File

@ -598,7 +598,7 @@ impl<'a> Parser<'a> {
tokens: None,
};
let span_start = self.token.span;
let ast::FnHeader { ext, unsafety, constness, coro_kind } =
let ast::FnHeader { ext, unsafety, constness, coroutine_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)?;
@ -611,7 +611,7 @@ impl<'a> Parser<'a> {
// cover it.
self.sess.emit_err(FnPointerCannotBeConst { span: whole_span, qualifier: span });
}
if let Some(ast::CoroutineKind::Async { span, .. }) = coro_kind {
if let Some(ast::CoroutineKind::Async { span, .. }) = coroutine_kind {
self.sess.emit_err(FnPointerCannotBeAsync { span: whole_span, qualifier: span });
}
// FIXME(gen_blocks): emit a similar error for `gen fn()`

View File

@ -156,29 +156,33 @@ 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 Some(
CoroutineKind::Async { closure_id, .. } | CoroutineKind::Gen { closure_id, .. },
) = sig.header.coro_kind
{
self.visit_generics(generics);
match sig.header.coroutine_kind {
Some(
CoroutineKind::Async { closure_id, .. }
| CoroutineKind::Gen { closure_id, .. }
| CoroutineKind::AsyncGen { closure_id, .. },
) => {
self.visit_generics(generics);
// For async functions, we need to create their inner defs inside of a
// closure to match their desugared representation. Besides that,
// we must mirror everything that `visit::walk_fn` below does.
self.visit_fn_header(&sig.header);
for param in &sig.decl.inputs {
self.visit_param(param);
// For async functions, we need to create their inner defs inside of a
// closure to match their desugared representation. Besides that,
// we must mirror everything that `visit::walk_fn` below does.
self.visit_fn_header(&sig.header);
for param in &sig.decl.inputs {
self.visit_param(param);
}
self.visit_fn_ret_ty(&sig.decl.output);
// If this async fn has no body (i.e. it's an async fn signature in a trait)
// then the closure_def will never be used, and we should avoid generating a
// def-id for it.
if let Some(body) = body {
let closure_def =
self.create_def(closure_id, kw::Empty, DefKind::Closure, span);
self.with_parent(closure_def, |this| this.visit_block(body));
}
return;
}
self.visit_fn_ret_ty(&sig.decl.output);
// If this async fn has no body (i.e. it's an async fn signature in a trait)
// then the closure_def will never be used, and we should avoid generating a
// def-id for it.
if let Some(body) = body {
let closure_def =
self.create_def(closure_id, kw::Empty, DefKind::Closure, span);
self.with_parent(closure_def, |this| this.visit_block(body));
}
return;
None => {}
}
}
@ -284,10 +288,11 @@ 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.coro_kind {
match closure.coroutine_kind {
Some(
CoroutineKind::Async { closure_id, .. }
| CoroutineKind::Gen { closure_id, .. },
| CoroutineKind::Gen { closure_id, .. }
| CoroutineKind::AsyncGen { closure_id, .. },
) => self.create_def(closure_id, kw::Empty, DefKind::Closure, expr.span),
None => closure_def,
}

View File

@ -916,8 +916,10 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast,
&sig.decl.output,
);
if let Some((coro_node_id, _)) =
sig.header.coro_kind.map(|coro_kind| coro_kind.return_id())
if let Some((coro_node_id, _)) = sig
.header
.coroutine_kind
.map(|coroutine_kind| coroutine_kind.return_id())
{
this.record_lifetime_params_for_impl_trait(coro_node_id);
}
@ -942,8 +944,10 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast,
this.visit_generics(generics);
let declaration = &sig.decl;
let coro_node_id =
sig.header.coro_kind.map(|coro_kind| coro_kind.return_id());
let coro_node_id = sig
.header
.coroutine_kind
.map(|coroutine_kind| coroutine_kind.return_id());
this.with_lifetime_rib(
LifetimeRibKind::AnonymousCreateParameter {
@ -4294,7 +4298,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
//
// Similarly, `gen |x| ...` gets desugared to `|x| gen {...}`, so we handle that too.
ExprKind::Closure(box ast::Closure {
coro_kind: Some(_),
coroutine_kind: Some(_),
ref fn_decl,
ref body,
..

View File

@ -56,6 +56,7 @@ impl<'tcx> Stable<'tcx> for rustc_hir::CoroutineKind {
stable_mir::mir::CoroutineKind::Gen(source.stable(tables))
}
CoroutineKind::Coroutine => stable_mir::mir::CoroutineKind::Coroutine,
CoroutineKind::AsyncGen(_) => todo!(),
}
}
}

View File

@ -139,6 +139,9 @@ symbols! {
AssertParamIsClone,
AssertParamIsCopy,
AssertParamIsEq,
AsyncGenFinished,
AsyncGenPending,
AsyncGenReady,
AtomicBool,
AtomicI128,
AtomicI16,
@ -423,6 +426,7 @@ symbols! {
async_closure,
async_fn_in_trait,
async_fn_track_caller,
async_iterator,
atomic,
atomic_mod,
atomics,
@ -1200,6 +1204,7 @@ symbols! {
pointer,
pointer_like,
poll,
poll_next,
post_dash_lto: "post-lto",
powerpc_target_feature,
powf32,

View File

@ -207,6 +207,11 @@ pub(super) trait GoalKind<'tcx>:
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
fn consider_builtin_async_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.
@ -565,6 +570,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
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.async_iterator_trait() == Some(trait_def_id) {
G::consider_builtin_async_iterator_candidate(self, goal)
} else if lang_items.coroutine_trait() == Some(trait_def_id) {
G::consider_builtin_coroutine_candidate(self, goal)
} else if lang_items.discriminant_kind_trait() == Some(trait_def_id) {

View File

@ -510,6 +510,40 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
)
}
fn consider_builtin_async_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 AsyncIterators unless they come from `gen` desugaring
let tcx = ecx.tcx();
if !tcx.coroutine_is_async_gen(def_id) {
return Err(NoSolution);
}
ecx.probe_misc_candidate("builtin AsyncIterator kind").enter(|ecx| {
// Take `AsyncIterator<Item = I>` and turn it into the corresponding
// coroutine yield ty `Poll<Option<I>>`.
let expected_ty = Ty::new_adt(
tcx,
tcx.adt_def(tcx.require_lang_item(LangItem::Poll, None)),
tcx.mk_args(&[Ty::new_adt(
tcx,
tcx.adt_def(tcx.require_lang_item(LangItem::Option, None)),
tcx.mk_args(&[goal.predicate.term.into()]),
)
.into()]),
);
let yield_ty = args.as_coroutine().yield_ty();
ecx.eq(goal.param_env, expected_ty, yield_ty)?;
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
})
}
fn consider_builtin_coroutine_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,

View File

@ -370,6 +370,30 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
fn consider_builtin_async_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_async_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>,

View File

@ -2587,6 +2587,23 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
CoroutineKind::Async(CoroutineSource::Closure) => {
format!("future created by async closure is not {trait_name}")
}
CoroutineKind::AsyncGen(CoroutineSource::Fn) => self
.tcx
.parent(coroutine_did)
.as_local()
.map(|parent_did| self.tcx.local_def_id_to_hir_id(parent_did))
.and_then(|parent_hir_id| hir.opt_name(parent_hir_id))
.map(|name| {
format!("async iterator returned by `{name}` is not {trait_name}")
})?,
CoroutineKind::AsyncGen(CoroutineSource::Block) => {
format!("async iterator created by async gen block is not {trait_name}")
}
CoroutineKind::AsyncGen(CoroutineSource::Closure) => {
format!(
"async iterator created by async gen closure is not {trait_name}"
)
}
CoroutineKind::Gen(CoroutineSource::Fn) => self
.tcx
.parent(coroutine_did)
@ -3127,7 +3144,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let what = match self.tcx.coroutine_kind(coroutine_def_id) {
None
| Some(hir::CoroutineKind::Coroutine)
| Some(hir::CoroutineKind::Gen(_)) => "yield",
| Some(hir::CoroutineKind::Gen(_))
// FIXME(gen_blocks): This could be yield or await...
| Some(hir::CoroutineKind::AsyncGen(_)) => "yield",
Some(hir::CoroutineKind::Async(..)) => "await",
};
err.note(format!(

View File

@ -1921,6 +1921,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::AsyncGen(hir::CoroutineSource::Block) => "an async gen block",
hir::CoroutineKind::AsyncGen(hir::CoroutineSource::Fn) => "an async gen function",
hir::CoroutineKind::AsyncGen(hir::CoroutineSource::Closure) => "an async gen 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",

View File

@ -1823,11 +1823,18 @@ 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.coroutine_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()
if [
lang_items.coroutine_trait(),
lang_items.future_trait(),
lang_items.iterator_trait(),
lang_items.async_iterator_trait(),
lang_items.fn_trait(),
lang_items.fn_mut_trait(),
lang_items.fn_once_trait(),
].contains(&Some(trait_ref.def_id))
{
true
} else if lang_items.discriminant_kind_trait() == Some(trait_ref.def_id) {
}else if lang_items.discriminant_kind_trait() == Some(trait_ref.def_id) {
match self_ty.kind() {
ty::Bool
| ty::Char
@ -2042,6 +2049,8 @@ fn confirm_select_candidate<'cx, 'tcx>(
confirm_future_candidate(selcx, obligation, data)
} else if lang_items.iterator_trait() == Some(trait_def_id) {
confirm_iterator_candidate(selcx, obligation, data)
} else if lang_items.async_iterator_trait() == Some(trait_def_id) {
confirm_async_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)
@ -2203,6 +2212,57 @@ fn confirm_iterator_candidate<'cx, 'tcx>(
.with_addl_obligations(obligations)
}
fn confirm_async_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().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_async_iterator_candidate");
let tcx = selcx.tcx();
let iter_def_id = tcx.require_lang_item(LangItem::AsyncIterator, None);
let (trait_ref, yield_ty) = super::util::async_iterator_trait_ref_and_outputs(
tcx,
iter_def_id,
obligation.predicate.self_ty(),
gen_sig,
);
debug_assert_eq!(tcx.associated_item(obligation.predicate.def_id).name, sym::Item);
let ty::Adt(_poll_adt, args) = *yield_ty.kind() else {
bug!();
};
let ty::Adt(_option_adt, args) = *args.type_at(0).kind() else {
bug!();
};
let item_ty = args.type_at(0);
let predicate = ty::ProjectionPredicate {
projection_ty: ty::AliasTy::new(tcx, obligation.predicate.def_id, trait_ref.args),
term: item_ty.into(),
};
confirm_param_env_candidate(selcx, obligation, ty::Binder::dummy(predicate), false)
.with_addl_obligations(nested)
.with_addl_obligations(obligations)
}
fn confirm_builtin_candidate<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTyObligation<'tcx>,

View File

@ -112,6 +112,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
self.assemble_future_candidates(obligation, &mut candidates);
} else if lang_items.iterator_trait() == Some(def_id) {
self.assemble_iterator_candidates(obligation, &mut candidates);
} else if lang_items.async_iterator_trait() == Some(def_id) {
self.assemble_async_iterator_candidates(obligation, &mut candidates);
}
self.assemble_closure_candidates(obligation, &mut candidates);
@ -258,6 +260,34 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
fn assemble_async_iterator_candidates(
&mut self,
obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
let self_ty = obligation.self_ty().skip_binder();
if let ty::Coroutine(did, args, _) = *self_ty.kind() {
// gen constructs get lowered to a special kind of coroutine that
// should directly `impl AsyncIterator`.
if self.tcx().coroutine_is_async_gen(did) {
debug!(?self_ty, ?obligation, "assemble_iterator_candidates",);
// Can only confirm this candidate if we have constrained
// the `Yield` type to at least `Poll<Option<?0>>`..
let ty::Adt(_poll_def, args) = *args.as_coroutine().yield_ty().kind() else {
candidates.ambiguous = true;
return;
};
let ty::Adt(_option_def, _) = *args.type_at(0).kind() else {
candidates.ambiguous = true;
return;
};
candidates.vec.push(AsyncIteratorCandidate);
}
}
}
/// Checks for the artificial impl that the compiler will create for an obligation like `X :
/// FnMut<..>` where `X` is a closure type.
///

View File

@ -98,6 +98,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
ImplSource::Builtin(BuiltinImplSource::Misc, vtable_iterator)
}
AsyncIteratorCandidate => {
let vtable_iterator = self.confirm_async_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)
@ -813,6 +818,35 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
Ok(nested)
}
fn confirm_async_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_async_iterator_candidate");
let gen_sig = args.as_coroutine().sig();
let (trait_ref, _) = super::util::async_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,
);
let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?;
debug!(?trait_ref, ?nested, "iterator candidate obligations");
Ok(nested)
}
#[instrument(skip(self), level = "debug")]
fn confirm_closure_candidate(
&mut self,

View File

@ -1875,6 +1875,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
| CoroutineCandidate
| FutureCandidate
| IteratorCandidate
| AsyncIteratorCandidate
| FnPointerCandidate { .. }
| BuiltinObjectCandidate
| BuiltinUnsizeCandidate
@ -1904,6 +1905,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
| CoroutineCandidate
| FutureCandidate
| IteratorCandidate
| AsyncIteratorCandidate
| FnPointerCandidate { .. }
| BuiltinObjectCandidate
| BuiltinUnsizeCandidate
@ -1939,6 +1941,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
| CoroutineCandidate
| FutureCandidate
| IteratorCandidate
| AsyncIteratorCandidate
| FnPointerCandidate { .. }
| BuiltinObjectCandidate
| BuiltinUnsizeCandidate
@ -1954,6 +1957,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
| CoroutineCandidate
| FutureCandidate
| IteratorCandidate
| AsyncIteratorCandidate
| FnPointerCandidate { .. }
| BuiltinObjectCandidate
| BuiltinUnsizeCandidate
@ -2061,6 +2065,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
| CoroutineCandidate
| FutureCandidate
| IteratorCandidate
| AsyncIteratorCandidate
| FnPointerCandidate { .. }
| BuiltinObjectCandidate
| BuiltinUnsizeCandidate
@ -2072,6 +2077,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
| CoroutineCandidate
| FutureCandidate
| IteratorCandidate
| AsyncIteratorCandidate
| FnPointerCandidate { .. }
| BuiltinObjectCandidate
| BuiltinUnsizeCandidate

View File

@ -308,6 +308,17 @@ pub fn iterator_trait_ref_and_outputs<'tcx>(
(trait_ref, sig.yield_ty)
}
pub fn async_iterator_trait_ref_and_outputs<'tcx>(
tcx: TyCtxt<'tcx>,
async_iterator_def_id: DefId,
self_ty: Ty<'tcx>,
sig: ty::GenSig<'tcx>,
) -> (ty::TraitRef<'tcx>, Ty<'tcx>) {
assert!(!self_ty.has_escaping_bound_vars());
let trait_ref = ty::TraitRef::new(tcx, async_iterator_def_id, [self_ty]);
(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()

View File

@ -119,9 +119,9 @@ fn fn_sig_for_fn_abi<'tcx>(
// unlike for all other coroutine kinds.
env_ty
}
hir::CoroutineKind::Async(_) | hir::CoroutineKind::Coroutine => {
Ty::new_adt(tcx, pin_adt_ref, pin_args)
}
hir::CoroutineKind::Async(_)
| hir::CoroutineKind::AsyncGen(_)
| hir::CoroutineKind::Coroutine => Ty::new_adt(tcx, pin_adt_ref, pin_args),
};
// The `FnSig` and the `ret_ty` here is for a coroutines main
@ -168,6 +168,30 @@ fn fn_sig_for_fn_abi<'tcx>(
(None, ret_ty)
}
hir::CoroutineKind::AsyncGen(_) => {
// The signature should be
// `AsyncIterator::poll_next(_, &mut Context<'_>) -> Poll<Option<Output>>`
assert_eq!(sig.return_ty, tcx.types.unit);
// Yield type is already `Poll<Option<yield_ty>>`
let ret_ty = sig.yield_ty;
// We have to replace the `ResumeTy` that is used for type and borrow checking
// with `&mut Context<'_>` which is used in codegen.
#[cfg(debug_assertions)]
{
if let ty::Adt(resume_ty_adt, _) = sig.resume_ty.kind() {
let expected_adt =
tcx.adt_def(tcx.require_lang_item(LangItem::ResumeTy, None));
assert_eq!(*resume_ty_adt, expected_adt);
} else {
panic!("expected `ResumeTy`, found `{:?}`", sig.resume_ty);
};
}
let context_mut_ref = Ty::new_task_context(tcx);
(Some(context_mut_ref), ret_ty)
}
hir::CoroutineKind::Coroutine => {
// The signature should be `Coroutine::resume(_, Resume) -> CoroutineState<Yield, Return>`
let state_did = tcx.require_lang_item(LangItem::CoroutineState, None);

View File

@ -271,6 +271,21 @@ 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.async_iterator_trait() {
let ty::Coroutine(coroutine_def_id, args, _) = *rcvr_args.type_at(0).kind() else {
bug!()
};
if cfg!(debug_assertions) && tcx.item_name(trait_item_id) != sym::poll_next {
span_bug!(
tcx.def_span(coroutine_def_id),
"no definition for `{trait_ref}::{}` for built-in coroutine type",
tcx.item_name(trait_item_id)
)
}
// `AsyncIterator::poll_next` is generated by the compiler.
Some(Instance { def: ty::InstanceDef::Item(coroutine_def_id), args })
} else if Some(trait_ref.def_id) == lang_items.coroutine_trait() {
let ty::Coroutine(coroutine_def_id, args, _) = *rcvr_args.type_at(0).kind() else {
bug!()

View File

@ -13,6 +13,7 @@ use crate::task::{Context, Poll};
#[unstable(feature = "async_iterator", issue = "79024")]
#[must_use = "async iterators do nothing unless polled"]
#[doc(alias = "Stream")]
#[cfg_attr(not(bootstrap), lang = "async_iterator")]
pub trait AsyncIterator {
/// The type of items yielded by the async iterator.
type Item;
@ -109,3 +110,27 @@ where
(**self).size_hint()
}
}
#[unstable(feature = "async_gen_internals", issue = "none")]
impl<T> Poll<Option<T>> {
/// A helper function for internal desugaring -- produces `Ready(Some(t))`,
/// which corresponds to the async iterator yielding a value.
#[unstable(feature = "async_gen_internals", issue = "none")]
#[cfg_attr(not(bootstrap), lang = "AsyncGenReady")]
pub fn async_gen_ready(t: T) -> Self {
Poll::Ready(Some(t))
}
/// A helper constant for internal desugaring -- produces `Pending`,
/// which corresponds to the async iterator pending on an `.await`.
#[unstable(feature = "async_gen_internals", issue = "none")]
#[cfg_attr(not(bootstrap), lang = "AsyncGenPending")]
// FIXME(gen_blocks): This probably could be deduplicated.
pub const PENDING: Self = Poll::Pending;
/// A helper constant for internal desugaring -- produces `Ready(None)`,
/// which corresponds to the async iterator finishing its iteration.
#[unstable(feature = "async_gen_internals", issue = "none")]
#[cfg_attr(not(bootstrap), lang = "AsyncGenFinished")]
pub const FINISHED: Self = Poll::Ready(None);
}

View File

@ -69,7 +69,7 @@ pub fn check(
if !ignore {
get_test_spans(&item, &mut test_attr_spans);
}
let is_async = matches!(sig.header.coro_kind, Some(CoroutineKind::Async { .. }));
let is_async = matches!(sig.header.coroutine_kind, Some(CoroutineKind::Async { .. }));
let returns_nothing = match &sig.decl.output {
FnRetTy::Default(..) => true,
FnRetTy::Ty(ty) if ty.kind.is_unit() => true,

View File

@ -188,7 +188,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
Closure(box ast::Closure {
binder: lb,
capture_clause: lc,
coro_kind: la,
coroutine_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,
coro_kind: ra,
coroutine_kind: ra,
movability: rm,
fn_decl: rf,
body: re,
@ -563,10 +563,11 @@ 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<CoroutineKind>, r: Option<CoroutineKind>) -> bool {
fn eq_opt_coroutine_kind(l: Option<CoroutineKind>, r: Option<CoroutineKind>) -> bool {
match (l, r) {
(Some(CoroutineKind::Async { .. }), Some(CoroutineKind::Async { .. }))
| (Some(CoroutineKind::Gen { .. }), Some(CoroutineKind::Gen { .. })) => true,
| (Some(CoroutineKind::Gen { .. }), Some(CoroutineKind::Gen { .. }))
| (Some(CoroutineKind::AsyncGen { .. }), Some(CoroutineKind::AsyncGen { .. })) => true,
(None, None) => true,
_ => false,
}
@ -574,7 +575,7 @@ fn eq_opt_coro_kind(l: Option<CoroutineKind>, r: Option<CoroutineKind>) -> bool
pub fn eq_fn_header(l: &FnHeader, r: &FnHeader) -> bool {
matches!(l.unsafety, Unsafe::No) == matches!(r.unsafety, Unsafe::No)
&& eq_opt_coro_kind(l.coro_kind, r.coro_kind)
&& eq_opt_coroutine_kind(l.coroutine_kind, r.coroutine_kind)
&& matches!(l.constness, Const::No) == matches!(r.constness, Const::No)
&& eq_ext(&l.ext, &r.ext)
}

View File

@ -29,7 +29,7 @@ pub(crate) fn rewrite_closure(
binder: &ast::ClosureBinder,
constness: ast::Const,
capture: ast::CaptureBy,
coro_kind: &Option<ast::CoroutineKind>,
coroutine_kind: &Option<ast::CoroutineKind>,
movability: ast::Movability,
fn_decl: &ast::FnDecl,
body: &ast::Expr,
@ -40,7 +40,16 @@ pub(crate) fn rewrite_closure(
debug!("rewrite_closure {:?}", body);
let (prefix, extra_offset) = rewrite_closure_fn_decl(
binder, constness, capture, coro_kind, movability, fn_decl, body, span, context, shape,
binder,
constness,
capture,
coroutine_kind,
movability,
fn_decl,
body,
span,
context,
shape,
)?;
// 1 = space between `|...|` and body.
let body_shape = shape.offset_left(extra_offset)?;
@ -233,7 +242,7 @@ fn rewrite_closure_fn_decl(
binder: &ast::ClosureBinder,
constness: ast::Const,
capture: ast::CaptureBy,
coro_kind: &Option<ast::CoroutineKind>,
coroutine_kind: &Option<ast::CoroutineKind>,
movability: ast::Movability,
fn_decl: &ast::FnDecl,
body: &ast::Expr,
@ -263,9 +272,10 @@ fn rewrite_closure_fn_decl(
} else {
""
};
let coro = match coro_kind {
let coro = match coroutine_kind {
Some(ast::CoroutineKind::Async { .. }) => "async ",
Some(ast::CoroutineKind::Gen { .. }) => "gen ",
Some(ast::CoroutineKind::AsyncGen { .. }) => "async gen ",
None => "",
};
let mover = if matches!(capture, ast::CaptureBy::Value { .. }) {
@ -343,7 +353,7 @@ pub(crate) fn rewrite_last_closure(
ref binder,
constness,
capture_clause,
ref coro_kind,
ref coroutine_kind,
movability,
ref fn_decl,
ref body,
@ -364,7 +374,7 @@ pub(crate) fn rewrite_last_closure(
binder,
constness,
capture_clause,
coro_kind,
coroutine_kind,
movability,
fn_decl,
body,

View File

@ -212,7 +212,7 @@ pub(crate) fn format_expr(
&cl.binder,
cl.constness,
cl.capture_clause,
&cl.coro_kind,
&cl.coroutine_kind,
cl.movability,
&cl.fn_decl,
&cl.body,

View File

@ -287,7 +287,7 @@ pub(crate) struct FnSig<'a> {
decl: &'a ast::FnDecl,
generics: &'a ast::Generics,
ext: ast::Extern,
coro_kind: Cow<'a, Option<ast::CoroutineKind>>,
coroutine_kind: Cow<'a, Option<ast::CoroutineKind>>,
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,
coro_kind: Cow::Borrowed(&method_sig.header.coro_kind),
coroutine_kind: Cow::Borrowed(&method_sig.header.coroutine_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,
coro_kind: Cow::Borrowed(&fn_sig.header.coro_kind),
coroutine_kind: Cow::Borrowed(&fn_sig.header.coroutine_kind),
defaultness,
unsafety: fn_sig.header.unsafety,
visibility: vis,
@ -343,8 +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));
self.coro_kind
.map(|coro_kind| result.push_str(format_coro(&coro_kind)));
self.coroutine_kind
.map(|coroutine_kind| result.push_str(format_coro(&coroutine_kind)));
result.push_str(format_unsafety(self.unsafety));
result.push_str(&format_extern(
self.ext,

View File

@ -75,10 +75,11 @@ pub(crate) fn format_visibility(
}
#[inline]
pub(crate) fn format_coro(coro_kind: &ast::CoroutineKind) -> &'static str {
match coro_kind {
pub(crate) fn format_coro(coroutine_kind: &ast::CoroutineKind) -> &'static str {
match coroutine_kind {
ast::CoroutineKind::Async { .. } => "async ",
ast::CoroutineKind::Gen { .. } => "gen ",
ast::CoroutineKind::AsyncGen { .. } => "async gen ",
}
}

View File

@ -132,7 +132,7 @@ fn iter_exprs(depth: usize, f: &mut dyn FnMut(P<Expr>)) {
binder: ClosureBinder::NotPresent,
capture_clause: CaptureBy::Value { move_kw: DUMMY_SP },
constness: Const::No,
coro_kind: None,
coroutine_kind: None,
movability: Movability::Movable,
fn_decl: decl.clone(),
body: e,

View File

@ -0,0 +1,12 @@
error[E0658]: gen blocks are experimental
--> $DIR/async_gen_fn.rs:4:1
|
LL | async gen fn foo() {}
| ^^^^^^^^^
|
= 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: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0658`.

View File

@ -0,0 +1,18 @@
error[E0670]: `async fn` is not permitted in Rust 2015
--> $DIR/async_gen_fn.rs:4:1
|
LL | async gen fn foo() {}
| ^^^^^ to use `async fn`, switch to Rust 2018 or later
|
= help: pass `--edition 2021` to `rustc`
= note: for more on editions, read https://doc.rust-lang.org/edition-guide
error: expected one of `extern`, `fn`, or `unsafe`, found `gen`
--> $DIR/async_gen_fn.rs:4:7
|
LL | async gen fn foo() {}
| ^^^ expected one of `extern`, `fn`, or `unsafe`
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0670`.

View File

@ -1,11 +1,9 @@
// 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.
// revisions: e2024 none
//[e2024] compile-flags: --edition 2024 -Zunstable-options
async gen fn foo() {}
//~^ `async gen` functions are not supported
//[none]~^ ERROR: `async fn` is not permitted in Rust 2015
//[none]~| ERROR: expected one of `extern`, `fn`, or `unsafe`, found `gen`
//[e2024]~^^^ ERROR: gen blocks are experimental
fn main() {}

View File

@ -1,8 +0,0 @@
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

View File

@ -0,0 +1,96 @@
// edition: 2024
// compile-flags: -Zunstable-options
// run-pass
#![feature(gen_blocks, async_iterator)]
// make sure that a ridiculously simple async gen fn works as an iterator.
async fn pause() {
// this doesn't actually do anything, lol
}
async fn one() -> i32 {
1
}
async fn two() -> i32 {
2
}
async gen fn foo() -> i32 {
yield one().await;
pause().await;
yield two().await;
pause().await;
yield 3;
pause().await;
}
async fn async_main() {
let mut iter = std::pin::pin!(foo());
assert_eq!(iter.next().await, Some(1));
assert_eq!(iter.as_mut().next().await, Some(2));
assert_eq!(iter.as_mut().next().await, Some(3));
assert_eq!(iter.as_mut().next().await, None);
}
// ------------------------------------------------------------------------- //
// Implementation Details Below...
use std::pin::Pin;
use std::task::*;
use std::async_iter::AsyncIterator;
use std::future::Future;
trait AsyncIterExt {
fn next(&mut self) -> Next<'_, Self>;
}
impl<T> AsyncIterExt for T {
fn next(&mut self) -> Next<'_, Self> {
Next { s: self }
}
}
struct Next<'s, S: ?Sized> {
s: &'s mut S,
}
impl<'s, S: AsyncIterator> Future for Next<'s, S> where S: Unpin {
type Output = Option<S::Item>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<S::Item>> {
Pin::new(&mut *self.s).poll_next(cx)
}
}
pub fn noop_waker() -> Waker {
let raw = RawWaker::new(std::ptr::null(), &NOOP_WAKER_VTABLE);
// SAFETY: the contracts for RawWaker and RawWakerVTable are upheld
unsafe { Waker::from_raw(raw) }
}
const NOOP_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(noop_clone, noop, noop, noop);
unsafe fn noop_clone(_p: *const ()) -> RawWaker {
RawWaker::new(std::ptr::null(), &NOOP_WAKER_VTABLE)
}
unsafe fn noop(_p: *const ()) {}
fn main() {
let mut fut = async_main();
// Poll loop, just to test the future...
let waker = noop_waker();
let ctx = &mut Context::from_waker(&waker);
loop {
match unsafe { Pin::new_unchecked(&mut fut).poll(ctx) } {
Poll::Pending => {}
Poll::Ready(()) => break,
}
}
}