Auto merge of #51580 - cramertj:async-await, r=eddyb

async/await

This PR implements `async`/`await` syntax for `async fn` in Rust 2015 and `async` closures and `async` blocks in Rust 2018 (tracking issue: https://github.com/rust-lang/rust/issues/50547). Limitations: non-`move` async closures with arguments are currently not supported, nor are `async fn` with multiple different input lifetimes. These limitations are not fundamental and will be removed in the future, however I'd like to go ahead and get this PR merged so we can start experimenting with this in combination with futures 0.3.

Based on https://github.com/rust-lang/rust/pull/51414.
cc @petrochenkov for parsing changes.
r? @eddyb
This commit is contained in:
bors 2018-06-23 09:02:45 +00:00
commit 56e8f29dbe
69 changed files with 1796 additions and 538 deletions

View file

@ -19,11 +19,11 @@ use ast::{Constness, Crate};
use ast::Defaultness;
use ast::EnumDef;
use ast::{Expr, ExprKind, RangeLimits};
use ast::{Field, FnDecl};
use ast::{Field, FnDecl, FnHeader};
use ast::{ForeignItem, ForeignItemKind, FunctionRetTy};
use ast::{GenericParam, GenericParamKind};
use ast::GenericArg;
use ast::{Ident, ImplItem, IsAuto, Item, ItemKind};
use ast::{Ident, ImplItem, IsAsync, IsAuto, Item, ItemKind};
use ast::{Label, Lifetime, Lit, LitKind};
use ast::Local;
use ast::MacStmtStyle;
@ -43,7 +43,7 @@ use ast::{BinOpKind, UnOp};
use ast::{RangeEnd, RangeSyntax};
use {ast, attr};
use codemap::{self, CodeMap, Spanned, respan};
use syntax_pos::{self, Span, MultiSpan, BytePos, FileName, DUMMY_SP};
use syntax_pos::{self, Span, MultiSpan, BytePos, FileName, DUMMY_SP, edition::Edition};
use errors::{self, Applicability, DiagnosticBuilder};
use parse::{self, SeqSep, classify, token};
use parse::lexer::TokenAndSpan;
@ -1296,6 +1296,15 @@ impl<'a> Parser<'a> {
})))
}
/// Parse asyncness: `async` or nothing
fn parse_asyncness(&mut self) -> IsAsync {
if self.eat_keyword(keywords::Async) {
IsAsync::Async(ast::DUMMY_NODE_ID)
} else {
IsAsync::NotAsync
}
}
/// Parse unsafety: `unsafe` or nothing.
fn parse_unsafety(&mut self) -> Unsafety {
if self.eat_keyword(keywords::Unsafe) {
@ -1345,7 +1354,7 @@ impl<'a> Parser<'a> {
// trait item macro.
(keywords::Invalid.ident(), ast::TraitItemKind::Macro(mac), ast::Generics::default())
} else {
let (constness, unsafety, abi) = self.parse_fn_front_matter()?;
let (constness, unsafety, asyncness, abi) = self.parse_fn_front_matter()?;
let ident = self.parse_ident()?;
let mut generics = self.parse_generics()?;
@ -1359,10 +1368,13 @@ impl<'a> Parser<'a> {
generics.where_clause = self.parse_where_clause()?;
let sig = ast::MethodSig {
unsafety,
constness,
header: FnHeader {
unsafety,
constness,
abi,
asyncness,
},
decl: d,
abi,
};
let body = match self.token {
@ -2258,6 +2270,15 @@ impl<'a> Parser<'a> {
hi = path.span;
return Ok(self.mk_expr(lo.to(hi), ExprKind::Path(Some(qself), path), attrs));
}
if self.span.edition() >= Edition::Edition2018 &&
self.check_keyword(keywords::Async)
{
if self.is_async_block() { // check for `async {` and `async move {`
return self.parse_async_block(attrs);
} else {
return self.parse_lambda_expr(attrs);
}
}
if self.check_keyword(keywords::Move) || self.check_keyword(keywords::Static) {
return self.parse_lambda_expr(attrs);
}
@ -3258,6 +3279,13 @@ impl<'a> Parser<'a> {
} else {
Movability::Movable
};
let asyncness = if self.span.edition() >= Edition::Edition2018
&& self.eat_keyword(keywords::Async)
{
IsAsync::Async(ast::DUMMY_NODE_ID)
} else {
IsAsync::NotAsync
};
let capture_clause = if self.eat_keyword(keywords::Move) {
CaptureBy::Value
} else {
@ -3280,7 +3308,7 @@ impl<'a> Parser<'a> {
Ok(self.mk_expr(
lo.to(body.span),
ExprKind::Closure(capture_clause, movability, decl, body, lo.to(decl_hi)),
ExprKind::Closure(capture_clause, asyncness, movability, decl, body, lo.to(decl_hi)),
attrs))
}
@ -3358,6 +3386,24 @@ impl<'a> Parser<'a> {
Ok(self.mk_expr(span, ExprKind::Loop(body, opt_label), attrs))
}
/// Parse an `async move {...}` expression
pub fn parse_async_block(&mut self, mut attrs: ThinVec<Attribute>)
-> PResult<'a, P<Expr>>
{
let span_lo = self.span;
self.expect_keyword(keywords::Async)?;
let capture_clause = if self.eat_keyword(keywords::Move) {
CaptureBy::Value
} else {
CaptureBy::Ref
};
let (iattrs, body) = self.parse_inner_attrs_and_block()?;
attrs.extend(iattrs);
Ok(self.mk_expr(
span_lo.to(body.span),
ExprKind::Async(capture_clause, ast::DUMMY_NODE_ID, body), attrs))
}
/// Parse a `do catch {...}` expression (`do catch` token already eaten)
fn parse_catch_expr(&mut self, span_lo: Span, mut attrs: ThinVec<Attribute>)
-> PResult<'a, P<Expr>>
@ -4292,6 +4338,18 @@ impl<'a> Parser<'a> {
})
}
fn is_async_block(&mut self) -> bool {
self.token.is_keyword(keywords::Async) &&
(
( // `async move {`
self.look_ahead(1, |t| t.is_keyword(keywords::Move)) &&
self.look_ahead(2, |t| *t == token::OpenDelim(token::Brace))
) || ( // `async {`
self.look_ahead(1, |t| *t == token::OpenDelim(token::Brace))
)
)
}
fn is_catch_expr(&mut self) -> bool {
self.token.is_keyword(keywords::Do) &&
self.look_ahead(1, |t| t.is_keyword(keywords::Catch)) &&
@ -5358,6 +5416,7 @@ impl<'a> Parser<'a> {
/// Parse an item-position function declaration.
fn parse_item_fn(&mut self,
unsafety: Unsafety,
asyncness: IsAsync,
constness: Spanned<Constness>,
abi: Abi)
-> PResult<'a, ItemInfo> {
@ -5365,7 +5424,8 @@ impl<'a> Parser<'a> {
let decl = self.parse_fn_decl(false)?;
generics.where_clause = self.parse_where_clause()?;
let (inner_attrs, body) = self.parse_inner_attrs_and_block()?;
Ok((ident, ItemKind::Fn(decl, unsafety, constness, abi, generics, body), Some(inner_attrs)))
let header = FnHeader { unsafety, asyncness, constness, abi };
Ok((ident, ItemKind::Fn(decl, header, generics, body), Some(inner_attrs)))
}
/// true if we are looking at `const ID`, false for things like `const fn` etc
@ -5383,10 +5443,18 @@ impl<'a> Parser<'a> {
/// - `const unsafe fn`
/// - `extern fn`
/// - etc
fn parse_fn_front_matter(&mut self) -> PResult<'a, (Spanned<Constness>, Unsafety, Abi)> {
fn parse_fn_front_matter(&mut self)
-> PResult<'a, (
Spanned<Constness>,
Unsafety,
IsAsync,
Abi
)>
{
let is_const_fn = self.eat_keyword(keywords::Const);
let const_span = self.prev_span;
let unsafety = self.parse_unsafety();
let asyncness = self.parse_asyncness();
let (constness, unsafety, abi) = if is_const_fn {
(respan(const_span, Constness::Const), unsafety, Abi::Rust)
} else {
@ -5398,7 +5466,7 @@ impl<'a> Parser<'a> {
(respan(self.prev_span, Constness::NotConst), unsafety, abi)
};
self.expect_keyword(keywords::Fn)?;
Ok((constness, unsafety, abi))
Ok((constness, unsafety, asyncness, abi))
}
/// Parse an impl item.
@ -5533,19 +5601,18 @@ impl<'a> Parser<'a> {
Ok((keywords::Invalid.ident(), vec![], ast::Generics::default(),
ast::ImplItemKind::Macro(mac)))
} else {
let (constness, unsafety, abi) = self.parse_fn_front_matter()?;
let (constness, unsafety, asyncness, abi) = self.parse_fn_front_matter()?;
let ident = self.parse_ident()?;
let mut generics = self.parse_generics()?;
let decl = self.parse_fn_decl_with_self(|p| p.parse_arg())?;
generics.where_clause = self.parse_where_clause()?;
*at_end = true;
let (inner_attrs, body) = self.parse_inner_attrs_and_block()?;
Ok((ident, inner_attrs, generics, ast::ImplItemKind::Method(ast::MethodSig {
abi,
unsafety,
constness,
decl,
}, body)))
let header = ast::FnHeader { abi, unsafety, constness, asyncness };
Ok((ident, inner_attrs, generics, ast::ImplItemKind::Method(
ast::MethodSig { header, decl },
body
)))
}
}
@ -6631,6 +6698,7 @@ impl<'a> Parser<'a> {
let abi = opt_abi.unwrap_or(Abi::C);
let (ident, item_, extra_attrs) =
self.parse_item_fn(Unsafety::Normal,
IsAsync::NotAsync,
respan(fn_span, Constness::NotConst),
abi)?;
let prev_span = self.prev_span;
@ -6674,6 +6742,7 @@ impl<'a> Parser<'a> {
self.bump();
let (ident, item_, extra_attrs) =
self.parse_item_fn(unsafety,
IsAsync::NotAsync,
respan(const_span, Constness::Const),
Abi::Rust)?;
let prev_span = self.prev_span;
@ -6701,6 +6770,34 @@ impl<'a> Parser<'a> {
maybe_append(attrs, extra_attrs));
return Ok(Some(item));
}
// `unsafe async fn` or `async fn`
if (
self.check_keyword(keywords::Unsafe) &&
self.look_ahead(1, |t| t.is_keyword(keywords::Async))
) || (
self.check_keyword(keywords::Async) &&
self.look_ahead(1, |t| t.is_keyword(keywords::Fn))
)
{
// ASYNC FUNCTION ITEM
let unsafety = self.parse_unsafety();
self.expect_keyword(keywords::Async)?;
self.expect_keyword(keywords::Fn)?;
let fn_span = self.prev_span;
let (ident, item_, extra_attrs) =
self.parse_item_fn(unsafety,
IsAsync::Async(ast::DUMMY_NODE_ID),
respan(fn_span, Constness::NotConst),
Abi::Rust)?;
let prev_span = self.prev_span;
let item = self.mk_item(lo.to(prev_span),
ident,
item_,
visibility,
maybe_append(attrs, extra_attrs));
return Ok(Some(item));
}
if self.check_keyword(keywords::Unsafe) &&
(self.look_ahead(1, |t| t.is_keyword(keywords::Trait)) ||
self.look_ahead(1, |t| t.is_keyword(keywords::Auto)))
@ -6746,6 +6843,7 @@ impl<'a> Parser<'a> {
let fn_span = self.prev_span;
let (ident, item_, extra_attrs) =
self.parse_item_fn(Unsafety::Normal,
IsAsync::NotAsync,
respan(fn_span, Constness::NotConst),
Abi::Rust)?;
let prev_span = self.prev_span;
@ -6771,6 +6869,7 @@ impl<'a> Parser<'a> {
let fn_span = self.prev_span;
let (ident, item_, extra_attrs) =
self.parse_item_fn(Unsafety::Unsafe,
IsAsync::NotAsync,
respan(fn_span, Constness::NotConst),
abi)?;
let prev_span = self.prev_span;