Auto merge of #118847 - eholk:for-await, r=compiler-errors

Add support for `for await` loops

This adds support for `for await` loops. This includes parsing, desugaring in AST->HIR lowering, and adding some support functions to the library.

Given a loop like:
```rust
for await i in iter {
    ...
}
```
this is desugared to something like:
```rust
let mut iter = iter.into_async_iter();
while let Some(i) = loop {
    match core::pin::Pin::new(&mut iter).poll_next(cx) {
        Poll::Ready(i) => break i,
        Poll::Pending => yield,
    }
} {
    ...
}
```

This PR also adds a basic `IntoAsyncIterator` trait. This is partly for symmetry with the way `Iterator` and `IntoIterator` work. The other reason is that for async iterators it's helpful to have a place apart from the data structure being iterated over to store state. `IntoAsyncIterator` gives us a good place to do this.

I've gated this feature behind `async_for_loop` and opened #118898 as the feature tracking issue.

r? `@compiler-errors`
This commit is contained in:
bors 2023-12-22 14:17:10 +00:00
commit 208dd2032b
34 changed files with 367 additions and 79 deletions

View file

@ -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::{CoroutineKind, GenBlockKind, Pat, Path, PathSegment};
use ast::{CoroutineKind, ForLoopKind, GenBlockKind, Pat, Path, PathSegment};
use core::mem;
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter, Token, TokenKind};
@ -1801,7 +1801,7 @@ impl<'a> Parser<'a> {
&& matches!(
expr.kind,
ExprKind::While(_, _, None)
| ExprKind::ForLoop(_, _, _, None)
| ExprKind::ForLoop { label: None, .. }
| ExprKind::Loop(_, None, _)
| ExprKind::Block(_, None)
)
@ -2685,8 +2685,17 @@ impl<'a> Parser<'a> {
Ok((pat, expr))
}
/// Parses `for <src_pat> in <src_expr> <src_loop_block>` (`for` token already eaten).
/// Parses `for await? <src_pat> in <src_expr> <src_loop_block>` (`for` token already eaten).
fn parse_expr_for(&mut self, opt_label: Option<Label>, lo: Span) -> PResult<'a, P<Expr>> {
let is_await =
self.token.uninterpolated_span().at_least_rust_2018() && self.eat_keyword(kw::Await);
if is_await {
self.sess.gated_spans.gate(sym::async_for_loop, self.prev_token.span);
}
let kind = if is_await { ForLoopKind::ForAwait } else { ForLoopKind::For };
let (pat, expr) = self.parse_for_head()?;
// Recover from missing expression in `for` loop
if matches!(expr.kind, ExprKind::Block(..))
@ -2699,13 +2708,13 @@ impl<'a> Parser<'a> {
let block = self.mk_block(thin_vec![], BlockCheckMode::Default, self.prev_token.span);
return Ok(self.mk_expr(
lo.to(self.prev_token.span),
ExprKind::ForLoop(pat, err_expr, block, opt_label),
ExprKind::ForLoop { pat, iter: err_expr, body: block, label: opt_label, kind },
));
}
let (attrs, loop_block) = self.parse_inner_attrs_and_block()?;
let kind = ExprKind::ForLoop(pat, expr, loop_block, opt_label);
let kind = ExprKind::ForLoop { pat, iter: expr, body: loop_block, label: opt_label, kind };
self.recover_loop_else("for", lo)?;
@ -3800,7 +3809,7 @@ impl MutVisitor for CondChecker<'_> {
| ExprKind::Lit(_)
| ExprKind::If(_, _, _)
| ExprKind::While(_, _, _)
| ExprKind::ForLoop(_, _, _, _)
| ExprKind::ForLoop { .. }
| ExprKind::Loop(_, _, _)
| ExprKind::Match(_, _)
| ExprKind::Closure(_)