]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #51580 - cramertj:async-await, r=eddyb
authorbors <bors@rust-lang.org>
Sat, 23 Jun 2018 09:02:45 +0000 (09:02 +0000)
committerbors <bors@rust-lang.org>
Sat, 23 Jun 2018 09:02:45 +0000 (09:02 +0000)
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

1  2 
src/librustc/middle/resolve_lifetime.rs
src/libsyntax/parse/mod.rs
src/libsyntax/parse/parser.rs

index 025ee0f3d746cc6fc60f022715a4012ab28ada75,c2623f403567e8ee3b0a5645069fc23a6443901f..efb3eecd691f2dc47d3930af098c0b49166ff05b
@@@ -476,7 -476,7 +476,7 @@@ impl<'a, 'tcx> Visitor<'tcx> for Lifeti
  
      fn visit_item(&mut self, item: &'tcx hir::Item) {
          match item.node {
-             hir::ItemFn(ref decl, _, _, _, ref generics, _) => {
+             hir::ItemFn(ref decl, _, ref generics, _) => {
                  self.visit_early_late(None, decl, generics, |this| {
                      intravisit::walk_item(this, item);
                  });
@@@ -1395,7 -1395,7 +1395,7 @@@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx
                  lifetimeuseset
              );
              match lifetimeuseset {
 -                Some(LifetimeUseSet::One(_)) => {
 +                Some(LifetimeUseSet::One(lifetime)) => {
                      let node_id = self.tcx.hir.as_local_node_id(def_id).unwrap();
                      debug!("node id first={:?}", node_id);
                      if let Some((id, span, name)) = match self.tcx.hir.get(node_id) {
                          _ => None,
                      } {
                          debug!("id = {:?} span = {:?} name = {:?}", node_id, span, name);
 -                        self.tcx.struct_span_lint_node(
 +                        let mut err = self.tcx.struct_span_lint_node(
                              lint::builtin::SINGLE_USE_LIFETIMES,
                              id,
                              span,
                              &format!("lifetime parameter `{}` only used once", name),
 -                        ).emit();
 +                        );
 +                        err.span_label(span, "this lifetime...");
 +                        err.span_label(lifetime.span, "...is used only here");
 +                        err.emit();
                      }
                  }
                  Some(LifetimeUseSet::Many) => {
index 1cb127bdcc964737737915481518a55b23253f14,61f88f3a250d3ce567f3c0390a9d9beb0899f520..cce8da1dcbd53f9291aa08bab5d6e032920e9938
@@@ -294,7 -294,7 +294,7 @@@ fn char_lit(lit: &str, diag: Option<(Sp
  
  /// Parse a string representing a string literal into its final form. Does
  /// unescaping.
 -fn str_lit(lit: &str, diag: Option<(Span, &Handler)>) -> String {
 +pub fn str_lit(lit: &str, diag: Option<(Span, &Handler)>) -> String {
      debug!("str_lit: given {}", lit.escape_default());
      let mut res = String::with_capacity(lit.len());
  
@@@ -928,12 -928,15 +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 {
index 69521c17a253bfa5fd44260e7efea0b977e15fa2,0be149d7a034eb158ef57cafd04a2c735df06294..5970f94b97d52f12f4cfc9737033c8703900875a
@@@ -19,11 -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 +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;
@@@ -776,9 -776,6 +776,9 @@@ impl<'a> Parser<'a> 
              err.span_label(self.span, format!("expected identifier, found {}", token_descr));
          } else {
              err.span_label(self.span, "expected identifier");
 +            if self.token == token::Comma && self.look_ahead(1, |t| t.is_ident()) {
 +                err.span_suggestion(self.span, "remove this comma", "".into());
 +            }
          }
          err
      }
      /// Parse a sequence, not including the closing delimiter. The function
      /// f must consume tokens until reaching the next separator or
      /// closing bracket.
 -    fn parse_seq_to_before_end<T, F>(&mut self,
 +    pub fn parse_seq_to_before_end<T, F>(&mut self,
                                           ket: &token::Token,
                                           sep: SeqSep,
                                           f: F)
          })))
      }
  
+     /// 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) {
              // 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()?;
              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 {
          ExprKind::AssignOp(binop, lhs, rhs)
      }
  
 -    fn mk_mac_expr(&mut self, span: Span, m: Mac_, attrs: ThinVec<Attribute>) -> P<Expr> {
 +    pub fn mk_mac_expr(&mut self, span: Span, m: Mac_, attrs: ThinVec<Attribute>) -> P<Expr> {
          P(Expr {
              id: ast::DUMMY_NODE_ID,
              node: ExprKind::Mac(codemap::Spanned {node: m, span: span}),
                      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);
                  }
                  Err(mut e) => {
                      e.span_label(struct_sp, "while parsing this struct");
                      e.emit();
 -                    self.recover_stmt();
 -                    break;
 +
 +                    // If the next token is a comma, then try to parse
 +                    // what comes next as additional fields, rather than
 +                    // bailing out until next `}`.
 +                    if self.token != token::Comma {
 +                        self.recover_stmt();
 +                        break;
 +                    }
                  }
              }
  
          } 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 {
  
          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))
      }
  
          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>>
          })
      }
  
+     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)) &&
                  if macro_legacy_warnings && self.token != token::Semi {
                      self.warn_missing_semicolon();
                  } else {
 -                    self.expect_one_of(&[token::Semi], &[])?;
 +                    self.expect_one_of(&[], &[token::Semi])?;
                  }
              }
              _ => {}
      /// Parse an item-position function declaration.
      fn parse_item_fn(&mut self,
                       unsafety: Unsafety,
+                      asyncness: IsAsync,
                       constness: Spanned<Constness>,
                       abi: Abi)
                       -> PResult<'a, ItemInfo> {
          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
      /// - `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 {
              (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.
 -    crate fn parse_impl_item(&mut self, at_end: &mut bool) -> PResult<'a, ImplItem> {
 +    pub fn parse_impl_item(&mut self, at_end: &mut bool) -> PResult<'a, ImplItem> {
          maybe_whole!(self, NtImplItem, |x| x);
          let attrs = self.parse_outer_attributes()?;
          let (mut item, tokens) = self.collect_tokens(|this| {
              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
+             )))
          }
      }
  
                  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;
                  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;
                                      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)))
              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;
              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;
          })
      }
  
 -    fn parse_optional_str(&mut self) -> Option<(Symbol, ast::StrStyle, Option<ast::Name>)> {
 +    pub fn parse_optional_str(&mut self) -> Option<(Symbol, ast::StrStyle, Option<ast::Name>)> {
          let ret = match self.token {
              token::Literal(token::Str_(s), suf) => (s, ast::StrStyle::Cooked, suf),
              token::Literal(token::StrRaw(s, n), suf) => (s, ast::StrStyle::Raw(n), suf),