diff options
| author | bors <bors@rust-lang.org> | 2018-06-23 09:02:45 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2018-06-23 09:02:45 +0000 |
| commit | 56e8f29dbe89f2109cacc8eb5e92ea3de32eefb9 (patch) | |
| tree | 8630b3d600677d35ce9c956fbfe12923b57e302d /src/libsyntax | |
| parent | 2ea922a96d676ff84cd78421d314e6aac305b5e9 (diff) | |
| parent | 30c17ccbffdbb88020e6ed42d89cc214fc3e6e5f (diff) | |
| download | rust-56e8f29dbe89f2109cacc8eb5e92ea3de32eefb9.tar.gz rust-56e8f29dbe89f2109cacc8eb5e92ea3de32eefb9.zip | |
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
Diffstat (limited to 'src/libsyntax')
| -rw-r--r-- | src/libsyntax/ast.rs | 59 | ||||
| -rw-r--r-- | src/libsyntax/ext/build.rs | 11 | ||||
| -rw-r--r-- | src/libsyntax/feature_gate.rs | 49 | ||||
| -rw-r--r-- | src/libsyntax/fold.rs | 31 | ||||
| -rw-r--r-- | src/libsyntax/parse/mod.rs | 13 | ||||
| -rw-r--r-- | src/libsyntax/parse/parser.rs | 135 | ||||
| -rw-r--r-- | src/libsyntax/print/pprust.rs | 79 | ||||
| -rw-r--r-- | src/libsyntax/test.rs | 52 | ||||
| -rw-r--r-- | src/libsyntax/util/parser.rs | 2 | ||||
| -rw-r--r-- | src/libsyntax/visit.rs | 17 |
10 files changed, 330 insertions, 118 deletions
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index c6de2c4da39..a57a9b95e5f 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -17,7 +17,7 @@ pub use util::ThinVec; pub use util::parser::ExprPrecedence; use syntax_pos::{Span, DUMMY_SP}; -use codemap::{respan, Spanned}; +use codemap::{dummy_spanned, respan, Spanned}; use rustc_target::spec::abi::Abi; use ext::hygiene::{Mark, SyntaxContext}; use print::pprust; @@ -987,6 +987,7 @@ impl Expr { ExprKind::Closure(..) => ExprPrecedence::Closure, ExprKind::Block(..) => ExprPrecedence::Block, ExprKind::Catch(..) => ExprPrecedence::Catch, + ExprKind::Async(..) => ExprPrecedence::Async, ExprKind::Assign(..) => ExprPrecedence::Assign, ExprKind::AssignOp(..) => ExprPrecedence::AssignOp, ExprKind::Field(..) => ExprPrecedence::Field, @@ -1094,9 +1095,18 @@ pub enum ExprKind { /// A closure (for example, `move |a, b, c| a + b + c`) /// /// The final span is the span of the argument block `|...|` - Closure(CaptureBy, Movability, P<FnDecl>, P<Expr>, Span), + Closure(CaptureBy, IsAsync, Movability, P<FnDecl>, P<Expr>, Span), /// A block (`'label: { ... }`) Block(P<Block>, Option<Label>), + /// An async block (`async move { ... }`) + /// + /// The `NodeId` is the `NodeId` for the closure that results from + /// desugaring an async block, just like the NodeId field in the + /// `IsAsync` enum. This is necessary in order to create a def for the + /// closure which can be used as a parent of any child defs. Defs + /// created during lowering cannot be made the parent of any other + /// preexisting defs. + Async(CaptureBy, NodeId, P<Block>), /// A catch block (`catch { ... }`) Catch(P<Block>), @@ -1325,9 +1335,7 @@ pub struct MutTy { /// or in an implementation. #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] pub struct MethodSig { - pub unsafety: Unsafety, - pub constness: Spanned<Constness>, - pub abi: Abi, + pub header: FnHeader, pub decl: P<FnDecl>, } @@ -1709,6 +1717,22 @@ pub enum Unsafety { } #[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] +pub enum IsAsync { + Async(NodeId), + NotAsync, +} + +impl IsAsync { + pub fn is_async(self) -> bool { + if let IsAsync::Async(_) = self { + true + } else { + false + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] pub enum Constness { Const, NotConst, @@ -2009,6 +2033,29 @@ pub struct Item { pub tokens: Option<TokenStream>, } +/// A function header +/// +/// All the information between the visibility & the name of the function is +/// included in this struct (e.g. `async unsafe fn` or `const extern "C" fn`) +#[derive(Clone, Copy, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] +pub struct FnHeader { + pub unsafety: Unsafety, + pub asyncness: IsAsync, + pub constness: Spanned<Constness>, + pub abi: Abi, +} + +impl Default for FnHeader { + fn default() -> FnHeader { + FnHeader { + unsafety: Unsafety::Normal, + asyncness: IsAsync::NotAsync, + constness: dummy_spanned(Constness::NotConst), + abi: Abi::Rust, + } + } +} + #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] pub enum ItemKind { /// An `extern crate` item, with optional *original* crate name if the crate was renamed. @@ -2030,7 +2077,7 @@ pub enum ItemKind { /// A function declaration (`fn` or `pub fn`). /// /// E.g. `fn foo(bar: usize) -> usize { .. }` - Fn(P<FnDecl>, Unsafety, Spanned<Constness>, Abi, Generics, P<Block>), + Fn(P<FnDecl>, FnHeader, Generics, P<Block>), /// A module declaration (`mod` or `pub mod`). /// /// E.g. `mod foo;` or `mod foo { .. }` diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 9044cab05d6..61356507665 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -915,6 +915,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> { fn_decl_span: Span) // span of the `|...|` part -> P<ast::Expr> { self.expr(span, ast::ExprKind::Closure(ast::CaptureBy::Ref, + ast::IsAsync::NotAsync, ast::Movability::Movable, fn_decl, body, @@ -935,6 +936,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> { // the entire lambda body. Probably we should extend the API // here, but that's not entirely clear. self.expr(span, ast::ExprKind::Closure(ast::CaptureBy::Ref, + ast::IsAsync::NotAsync, ast::Movability::Movable, fn_decl, body, @@ -1008,9 +1010,12 @@ impl<'a> AstBuilder for ExtCtxt<'a> { name, Vec::new(), ast::ItemKind::Fn(self.fn_decl(inputs, ast::FunctionRetTy::Ty(output)), - ast::Unsafety::Normal, - dummy_spanned(ast::Constness::NotConst), - Abi::Rust, + ast::FnHeader { + unsafety: ast::Unsafety::Normal, + asyncness: ast::IsAsync::NotAsync, + constness: dummy_spanned(ast::Constness::NotConst), + abi: Abi::Rust, + }, generics, body)) } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index be4cf197be4..ccb2f51f964 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -29,7 +29,6 @@ use rustc_target::spec::abi::Abi; use ast::{self, NodeId, PatKind, RangeEnd}; use attr; use edition::{ALL_EDITIONS, Edition}; -use codemap::Spanned; use syntax_pos::{Span, DUMMY_SP}; use errors::{DiagnosticBuilder, Handler, FatalError}; use visit::{self, FnKind, Visitor}; @@ -309,7 +308,7 @@ declare_features! ( // Declarative macros 2.0 (`macro`). (active, decl_macro, "1.17.0", Some(39412), None), - // Allows #[link(kind="static-nobundle"...] + // Allows #[link(kind="static-nobundle"...)] (active, static_nobundle, "1.16.0", Some(37403), None), // `extern "msp430-interrupt" fn()` @@ -468,12 +467,14 @@ declare_features! ( // 'a: { break 'a; } (active, label_break_value, "1.28.0", Some(48594), None), - // #[panic_implementation] (active, panic_implementation, "1.28.0", Some(44489), None), // #[doc(keyword = "...")] (active, doc_keyword, "1.28.0", Some(51315), None), + + // Allows async and await syntax + (active, async_await, "1.28.0", Some(50547), None), ); declare_features! ( @@ -1721,6 +1722,12 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { "labels on blocks are unstable"); } } + ast::ExprKind::Closure(_, ast::IsAsync::Async(_), ..) => { + gate_feature_post!(&self, async_await, e.span, "async closures are unstable"); + } + ast::ExprKind::Async(..) => { + gate_feature_post!(&self, async_await, e.span, "async blocks are unstable"); + } _ => {} } visit::walk_expr(self, e); @@ -1760,20 +1767,24 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { fn_decl: &'a ast::FnDecl, span: Span, _node_id: NodeId) { - // check for const fn declarations - if let FnKind::ItemFn(_, _, Spanned { node: ast::Constness::Const, .. }, _, _, _) = - fn_kind { - gate_feature_post!(&self, const_fn, span, "const fn is unstable"); - } - // stability of const fn methods are covered in - // visit_trait_item and visit_impl_item below; this is - // because default methods don't pass through this - // point. - match fn_kind { - FnKind::ItemFn(_, _, _, abi, _, _) | - FnKind::Method(_, &ast::MethodSig { abi, .. }, _, _) => { - self.check_abi(abi, span); + FnKind::ItemFn(_, header, _, _) => { + // check for const fn and async fn declarations + if header.asyncness.is_async() { + gate_feature_post!(&self, async_await, span, "async fn is unstable"); + } + if header.constness.node == ast::Constness::Const { + gate_feature_post!(&self, const_fn, span, "const fn is unstable"); + } + // stability of const fn methods are covered in + // visit_trait_item and visit_impl_item below; this is + // because default methods don't pass through this + // point. + + self.check_abi(header.abi, span); + } + FnKind::Method(_, sig, _, _) => { + self.check_abi(sig.header.abi, span); } _ => {} } @@ -1784,9 +1795,9 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { match ti.node { ast::TraitItemKind::Method(ref sig, ref block) => { if block.is_none() { - self.check_abi(sig.abi, ti.span); + self.check_abi(sig.header.abi, ti.span); } - if sig.constness.node == ast::Constness::Const { + if sig.header.constness.node == ast::Constness::Const { gate_feature_post!(&self, const_fn, ti.span, "const fn is unstable"); } } @@ -1820,7 +1831,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { match ii.node { ast::ImplItemKind::Method(ref sig, _) => { - if sig.constness.node == ast::Constness::Const { + if sig.header.constness.node == ast::Constness::Const { gate_feature_post!(&self, const_fn, ii.span, "const fn is unstable"); } } diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 93248fe3bfa..ffea713f4ec 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -76,6 +76,10 @@ pub trait Folder : Sized { noop_fold_item_simple(i, self) } + fn fold_fn_header(&mut self, header: FnHeader) -> FnHeader { + noop_fold_fn_header(header, self) + } + fn fold_struct_field(&mut self, sf: StructField) -> StructField { noop_fold_struct_field(sf, self) } @@ -881,11 +885,12 @@ pub fn noop_fold_item_kind<T: Folder>(i: ItemKind, folder: &mut T) -> ItemKind { ItemKind::Const(t, e) => { ItemKind::Const(folder.fold_ty(t), folder.fold_expr(e)) } - ItemKind::Fn(decl, unsafety, constness, abi, generics, body) => { + ItemKind::Fn(decl, header, generics, body) => { let generics = folder.fold_generics(generics); + let header = folder.fold_fn_header(header); let decl = folder.fold_fn_decl(decl); let body = folder.fold_block(body); - ItemKind::Fn(decl, unsafety, constness, abi, generics, body) + ItemKind::Fn(decl, header, generics, body) } ItemKind::Mod(m) => ItemKind::Mod(folder.fold_mod(m)), ItemKind::ForeignMod(nm) => ItemKind::ForeignMod(folder.fold_foreign_mod(nm)), @@ -990,6 +995,14 @@ pub fn noop_fold_impl_item<T: Folder>(i: ImplItem, folder: &mut T) }) } +pub fn noop_fold_fn_header<T: Folder>(mut header: FnHeader, folder: &mut T) -> FnHeader { + header.asyncness = match header.asyncness { + IsAsync::Async(node_id) => IsAsync::Async(folder.new_id(node_id)), + IsAsync::NotAsync => IsAsync::NotAsync, + }; + header +} + pub fn noop_fold_mod<T: Folder>(Mod {inner, items}: Mod, folder: &mut T) -> Mod { Mod { inner: folder.new_span(inner), @@ -1082,9 +1095,7 @@ pub fn noop_fold_foreign_item_simple<T: Folder>(ni: ForeignItem, folder: &mut T) pub fn noop_fold_method_sig<T: Folder>(sig: MethodSig, folder: &mut T) -> MethodSig { MethodSig { - abi: sig.abi, - unsafety: sig.unsafety, - constness: sig.constness, + header: folder.fold_fn_header(sig.header), decl: folder.fold_fn_decl(sig.decl) } } @@ -1237,8 +1248,13 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span, attrs}: Expr, folder: &mu ExprKind::Match(folder.fold_expr(expr), arms.move_map(|x| folder.fold_arm(x))) } - ExprKind::Closure(capture_clause, movability, decl, body, span) => { + ExprKind::Closure(capture_clause, asyncness, movability, decl, body, span) => { + let asyncness = match asyncness { + IsAsync::Async(node_id) => IsAsync::Async(folder.new_id(node_id)), + IsAsync::NotAsync => IsAsync::NotAsync, + }; ExprKind::Closure(capture_clause, + asyncness, movability, folder.fold_fn_decl(decl), folder.fold_expr(body), @@ -1248,6 +1264,9 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span, attrs}: Expr, folder: &mu ExprKind::Block(folder.fold_block(blk), opt_label.map(|label| folder.fold_label(label))) } + ExprKind::Async(capture_clause, node_id, body) => { + ExprKind::Async(capture_clause, folder.new_id(node_id), folder.fold_block(body)) + } ExprKind::Assign(el, er) => { ExprKind::Assign(folder.fold_expr(el), folder.fold_expr(er)) } diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index 1cb127bdcc9..cce8da1dcbd 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -928,12 +928,15 @@ mod tests { output: ast::FunctionRetTy::Default(sp(15, 15)), variadic: false }), - ast::Unsafety::Normal, - Spanned { - span: sp(0,2), - node: ast::Constness::NotConst, + ast::FnHeader { + unsafety: ast::Unsafety::Normal, + asyncness: ast::IsAsync::NotAsync, + constness: Spanned { + span: sp(0,2), + node: ast::Constness::NotConst, + }, + abi: Abi::Rust, }, - Abi::Rust, ast::Generics{ params: Vec::new(), where_clause: ast::WhereClause { diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 69521c17a25..5970f94b97d 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -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; diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 7a55919f422..ac8088507dd 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -373,14 +373,13 @@ pub fn vis_to_string(v: &ast::Visibility) -> String { } pub fn fun_to_string(decl: &ast::FnDecl, - unsafety: ast::Unsafety, - constness: ast::Constness, + header: ast::FnHeader, name: ast::Ident, generics: &ast::Generics) -> String { to_string(|s| { s.head("")?; - s.print_fn(decl, unsafety, constness, Abi::Rust, Some(name), + s.print_fn(decl, header, Some(name), generics, &codemap::dummy_spanned(ast::VisibilityKind::Inherited))?; s.end()?; // Close the head box s.end() // Close the outer box @@ -1118,9 +1117,8 @@ impl<'a> State<'a> { match item.node { ast::ForeignItemKind::Fn(ref decl, ref generics) => { self.head("")?; - self.print_fn(decl, ast::Unsafety::Normal, - ast::Constness::NotConst, - Abi::Rust, Some(item.ident), + self.print_fn(decl, ast::FnHeader::default(), + Some(item.ident), generics, &item.vis)?; self.end()?; // end head-ibox self.s.word(";")?; @@ -1249,13 +1247,11 @@ impl<'a> State<'a> { self.s.word(";")?; self.end()?; // end the outer cbox } - ast::ItemKind::Fn(ref decl, unsafety, constness, abi, ref typarams, ref body) => { + ast::ItemKind::Fn(ref decl, header, ref typarams, ref body) => { self.head("")?; self.print_fn( decl, - unsafety, - constness.node, - abi, + header, Some(item.ident), typarams, &item.vis @@ -1582,9 +1578,7 @@ impl<'a> State<'a> { vis: &ast::Visibility) -> io::Result<()> { self.print_fn(&m.decl, - m.unsafety, - m.constness.node, - m.abi, + m.header, Some(ident), &generics, vis) @@ -2177,8 +2171,10 @@ impl<'a> State<'a> { } self.bclose_(expr.span, INDENT_UNIT)?; } - ast::ExprKind::Closure(capture_clause, movability, ref decl, ref body, _) => { + ast::ExprKind::Closure( + capture_clause, asyncness, movability, ref decl, ref body, _) => { self.print_movability(movability)?; + self.print_asyncness(asyncness)?; self.print_capture_clause(capture_clause)?; self.print_fn_block_args(decl)?; @@ -2202,6 +2198,12 @@ impl<'a> State<'a> { self.ibox(0)?; self.print_block_with_attrs(blk, attrs)?; } + ast::ExprKind::Async(capture_clause, _, ref blk) => { + self.word_nbsp("async")?; + self.print_capture_clause(capture_clause)?; + self.s.space()?; + self.print_block_with_attrs(blk, attrs)?; + } ast::ExprKind::Assign(ref lhs, ref rhs) => { let prec = AssocOp::Assign.precedence() as i8; self.print_expr_maybe_paren(lhs, prec + 1)?; @@ -2740,13 +2742,11 @@ impl<'a> State<'a> { pub fn print_fn(&mut self, decl: &ast::FnDecl, - unsafety: ast::Unsafety, - constness: ast::Constness, - abi: abi::Abi, + header: ast::FnHeader, name: Option<ast::Ident>, generics: &ast::Generics, vis: &ast::Visibility) -> io::Result<()> { - self.print_fn_header_info(unsafety, constness, abi, vis)?; + self.print_fn_header_info(header, vis)?; if let Some(name) = name { self.nbsp()?; @@ -2800,6 +2800,14 @@ impl<'a> State<'a> { } } + pub fn print_asyncness(&mut self, asyncness: ast::IsAsync) + -> io::Result<()> { + if asyncness.is_async() { + self.word_nbsp("async")?; + } + Ok(()) + } + pub fn print_capture_clause(&mut self, capture_clause: ast::CaptureBy) -> io::Result<()> { match capture_clause { @@ -3058,9 +3066,7 @@ impl<'a> State<'a> { span: syntax_pos::DUMMY_SP, }; self.print_fn(decl, - unsafety, - ast::Constness::NotConst, - abi, + ast::FnHeader { unsafety, abi, ..ast::FnHeader::default() }, name, &generics, &codemap::dummy_spanned(ast::VisibilityKind::Inherited))?; @@ -3123,22 +3129,21 @@ impl<'a> State<'a> { } pub fn print_fn_header_info(&mut self, - unsafety: ast::Unsafety, - constness: ast::Constness, - abi: Abi, + header: ast::FnHeader, vis: &ast::Visibility) -> io::Result<()> { self.s.word(&visibility_qualified(vis, ""))?; - match constness { + match header.constness.node { ast::Constness::NotConst => {} ast::Constness::Const => self.word_nbsp("const")? } - self.print_unsafety(unsafety)?; + self.print_asyncness(header.asyncness)?; + self.print_unsafety(header.unsafety)?; - if abi != Abi::Rust { + if header.abi != Abi::Rust { self.word_nbsp("extern")?; - self.word_nbsp(&abi.to_string())?; + self.word_nbsp(&header.abi.to_string())?; } self.s.word("fn") @@ -3181,10 +3186,20 @@ mod tests { variadic: false }; let generics = ast::Generics::default(); - assert_eq!(fun_to_string(&decl, ast::Unsafety::Normal, - ast::Constness::NotConst, - abba_ident, &generics), - "fn abba()"); + assert_eq!( + fun_to_string( + &decl, + ast::FnHeader { + unsafety: ast::Unsafety::Normal, + constness: codemap::dummy_spanned(ast::Constness::NotConst), + asyncness: ast::IsAsync::NotAsync, + abi: Abi::Rust, + }, + abba_ident, + &generics + ), + "fn abba()" + ); }) } diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index f896fa351b0..77225585141 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -124,24 +124,36 @@ impl<'a> fold::Folder for TestHarnessGenerator<'a> { if is_test_fn(&self.cx, &i) || is_bench_fn(&self.cx, &i) { match i.node { - ast::ItemKind::Fn(_, ast::Unsafety::Unsafe, _, _, _, _) => { - let diag = self.cx.span_diagnostic; - diag.span_fatal(i.span, "unsafe functions cannot be used for tests").raise(); - } - _ => { - debug!("this is a test function"); - let test = Test { - span: i.span, - path: self.cx.path.clone(), - bench: is_bench_fn(&self.cx, &i), - ignore: is_ignored(&i), - should_panic: should_panic(&i, &self.cx), - allow_fail: is_allowed_fail(&i), - }; - self.cx.testfns.push(test); - self.tests.push(i.ident); + ast::ItemKind::Fn(_, header, _, _) => { + if header.unsafety == ast::Unsafety::Unsafe { + let diag = self.cx.span_diagnostic; + diag.span_fatal( + i.span, + "unsafe functions cannot be used for tests" + ).raise(); + } + if header.asyncness.is_async() { + let diag = self.cx.span_diagnostic; + diag.span_fatal( + i.span, + "async functions cannot be used for tests" + ).raise(); + } } + _ => {}, } + + debug!("this is a test function"); + let test = Test { + span: i.span, + path: self.cx.path.clone(), + bench: is_bench_fn(&self.cx, &i), + ignore: is_ignored(&i), + should_panic: should_panic(&i, &self.cx), + allow_fail: is_allowed_fail(&i), + }; + self.cx.testfns.push(test); + self.tests.push(i.ident); } let mut item = i.into_inner(); @@ -338,7 +350,7 @@ fn is_test_fn(cx: &TestCtxt, i: &ast::Item) -> bool { fn has_test_signature(_cx: &TestCtxt, i: &ast::Item) -> HasTestSignature { let has_should_panic_attr = attr::contains_name(&i.attrs, "should_panic"); match i.node { - ast::ItemKind::Fn(ref decl, _, _, _, ref generics, _) => { + ast::ItemKind::Fn(ref decl, _, ref generics, _) => { // If the termination trait is active, the compiler will check that the output // type implements the `Termination` trait as `libtest` enforces that. let has_output = match decl.output { @@ -396,7 +408,7 @@ fn is_bench_fn(cx: &TestCtxt, i: &ast::Item) -> bool { fn has_bench_signature(_cx: &TestCtxt, i: &ast::Item) -> bool { match i.node { - ast::ItemKind::Fn(ref decl, _, _, _, _, _) => { + ast::ItemKind::Fn(ref decl, _, _, _) => { // NB: inadequate check, but we're running // well before resolve, can't get too deep. decl.inputs.len() == 1 @@ -537,9 +549,7 @@ fn mk_main(cx: &mut TestCtxt) -> P<ast::Item> { let main_ret_ty = ecx.ty(sp, ast::TyKind::Tup(vec![])); let main_body = ecx.block(sp, vec![call_test_main]); let main = ast::ItemKind::Fn(ecx.fn_decl(vec![], ast::FunctionRetTy::Ty(main_ret_ty)), - ast::Unsafety::Normal, - dummy_spanned(ast::Constness::NotConst), - ::rustc_target::spec::abi::Abi::Rust, + ast::FnHeader::default(), ast::Generics::default(), main_body); P(ast::Item { diff --git a/src/libsyntax/util/parser.rs b/src/libsyntax/util/parser.rs index 51b535275d6..15d910b33b0 100644 --- a/src/libsyntax/util/parser.rs +++ b/src/libsyntax/util/parser.rs @@ -277,6 +277,7 @@ pub enum ExprPrecedence { Block, Catch, Struct, + Async, } impl PartialOrd for ExprPrecedence { @@ -346,6 +347,7 @@ impl ExprPrecedence { ExprPrecedence::Match | ExprPrecedence::Block | ExprPrecedence::Catch | + ExprPrecedence::Async | ExprPrecedence::Struct => PREC_PAREN, } } diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 613f1a4f113..41e3ad9d4f4 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -23,17 +23,15 @@ //! instance, a walker looking for item names in a module will miss all of //! those that are created by the expansion of a macro. -use rustc_target::spec::abi::Abi; use ast::*; use syntax_pos::Span; -use codemap::Spanned; use parse::token::Token; use tokenstream::{TokenTree, TokenStream}; #[derive(Copy, Clone, PartialEq, Eq)] pub enum FnKind<'a> { /// fn foo() or extern "Abi" fn foo() - ItemFn(Ident, Unsafety, Spanned<Constness>, Abi, &'a Visibility, &'a Block), + ItemFn(Ident, FnHeader, &'a Visibility, &'a Block), /// fn foo(&self) Method(Ident, &'a MethodSig, Option<&'a Visibility>, &'a Block), @@ -235,10 +233,10 @@ pub fn walk_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a Item) { visitor.visit_ty(typ); visitor.visit_expr(expr); } - ItemKind::Fn(ref declaration, unsafety, constness, abi, ref generics, ref body) => { + ItemKind::Fn(ref declaration, header, ref generics, ref body) => { visitor.visit_generics(generics); - visitor.visit_fn(FnKind::ItemFn(item.ident, unsafety, - constness, abi, &item.vis, body), + visitor.visit_fn(FnKind::ItemFn(item.ident, header, + &item.vis, body), declaration, item.span, item.id) @@ -544,7 +542,7 @@ pub fn walk_fn<'a, V>(visitor: &mut V, kind: FnKind<'a>, declaration: &'a FnDecl where V: Visitor<'a>, { match kind { - FnKind::ItemFn(_, _, _, _, _, body) => { + FnKind::ItemFn(_, _, _, body) => { walk_fn_decl(visitor, declaration); visitor.visit_block(body); } @@ -737,7 +735,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { visitor.visit_expr(subexpression); walk_list!(visitor, visit_arm, arms); } - ExprKind::Closure(_, _, ref function_declaration, ref body, _decl_span) => { + ExprKind::Closure(_, _, _, ref function_declaration, ref body, _decl_span) => { visitor.visit_fn(FnKind::Closure(body), function_declaration, expression.span, @@ -747,6 +745,9 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { walk_list!(visitor, visit_label, opt_label); visitor.visit_block(block); } + ExprKind::Async(_, _, ref body) => { + visitor.visit_block(body); + } ExprKind::Assign(ref left_hand_expression, ref right_hand_expression) => { visitor.visit_expr(left_hand_expression); visitor.visit_expr(right_hand_expression); |
