Skip to content

Commit

Permalink
Auto merge of #51580 - cramertj:async-await, r=eddyb
Browse files Browse the repository at this point in the history
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: #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 #51414.
cc @petrochenkov for parsing changes.
r? @eddyb
  • Loading branch information
bors committed Jun 23, 2018
2 parents 2ea922a + 30c17cc commit 56e8f29
Show file tree
Hide file tree
Showing 69 changed files with 1,796 additions and 538 deletions.
7 changes: 6 additions & 1 deletion src/librustc/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2131,5 +2131,10 @@ register_diagnostics! {
E0657, // `impl Trait` can only capture lifetimes bound at the fn level
E0687, // in-band lifetimes cannot be used in `fn`/`Fn` syntax
E0688, // in-band lifetimes cannot be mixed with explicit lifetime binders
E0697, // closures cannot be static

E0906, // closures cannot be static

E0725, // multiple different lifetimes used in arguments of `async fn`
E0726, // multiple elided lifetimes used in arguments of `async fn`
E0727, // `async` non-`move` closures with arguments are not currently supported
}
11 changes: 4 additions & 7 deletions src/librustc/hir/intravisit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@
//! This order consistency is required in a few places in rustc, for
//! example generator inference, and possibly also HIR borrowck.
use rustc_target::spec::abi::Abi;
use syntax::ast::{NodeId, CRATE_NODE_ID, Ident, Name, Attribute};
use syntax_pos::Span;
use hir::*;
Expand All @@ -54,8 +53,8 @@ use std::u32;

#[derive(Copy, Clone, PartialEq, Eq)]
pub enum FnKind<'a> {
/// fn foo() or extern "Abi" fn foo()
ItemFn(Name, &'a Generics, Unsafety, Constness, Abi, &'a Visibility, &'a [Attribute]),
/// #[xxx] pub async/const/extern "Abi" fn foo()
ItemFn(Name, &'a Generics, FnHeader, &'a Visibility, &'a [Attribute]),

/// fn foo(&self)
Method(Name, &'a MethodSig, Option<&'a Visibility>, &'a [Attribute]),
Expand Down Expand Up @@ -479,12 +478,10 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item) {
visitor.visit_ty(typ);
visitor.visit_nested_body(body);
}
ItemFn(ref declaration, unsafety, constness, abi, ref generics, body_id) => {
ItemFn(ref declaration, header, ref generics, body_id) => {
visitor.visit_fn(FnKind::ItemFn(item.name,
generics,
unsafety,
constness,
abi,
header,
&item.vis,
&item.attrs),
declaration,
Expand Down
734 changes: 562 additions & 172 deletions src/librustc/hir/lowering.rs

Large diffs are not rendered by default.

39 changes: 17 additions & 22 deletions src/librustc/hir/map/blocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ use hir as ast;
use hir::map::{self, Node};
use hir::{Expr, FnDecl};
use hir::intravisit::FnKind;
use rustc_target::spec::abi;
use syntax::ast::{Attribute, Name, NodeId};
use syntax_pos::Span;

Expand Down Expand Up @@ -105,9 +104,7 @@ impl<'a> Code<'a> {
struct ItemFnParts<'a> {
name: Name,
decl: &'a ast::FnDecl,
unsafety: ast::Unsafety,
constness: ast::Constness,
abi: abi::Abi,
header: ast::FnHeader,
vis: &'a ast::Visibility,
generics: &'a ast::Generics,
body: ast::BodyId,
Expand Down Expand Up @@ -183,31 +180,31 @@ impl<'a> FnLikeNode<'a> {

pub fn constness(self) -> ast::Constness {
match self.kind() {
FnKind::ItemFn(_, _, _, constness, ..) => {
constness
}
FnKind::Method(_, m, ..) => {
m.constness
}
FnKind::ItemFn(_, _, header, ..) => header.constness,
FnKind::Method(_, m, ..) => m.header.constness,
_ => ast::Constness::NotConst
}
}

pub fn asyncness(self) -> ast::IsAsync {
match self.kind() {
FnKind::ItemFn(_, _, header, ..) => header.asyncness,
FnKind::Method(_, m, ..) => m.header.asyncness,
_ => ast::IsAsync::NotAsync
}
}

pub fn unsafety(self) -> ast::Unsafety {
match self.kind() {
FnKind::ItemFn(_, _, unsafety, ..) => {
unsafety
}
FnKind::Method(_, m, ..) => {
m.unsafety
}
FnKind::ItemFn(_, _, header, ..) => header.unsafety,
FnKind::Method(_, m, ..) => m.header.unsafety,
_ => ast::Unsafety::Normal
}
}

pub fn kind(self) -> FnKind<'a> {
let item = |p: ItemFnParts<'a>| -> FnKind<'a> {
FnKind::ItemFn(p.name, p.generics, p.unsafety, p.constness, p.abi, p.vis, p.attrs)
FnKind::ItemFn(p.name, p.generics, p.header, p.vis, p.attrs)
};
let closure = |c: ClosureParts<'a>| {
FnKind::Closure(c.attrs)
Expand All @@ -232,19 +229,17 @@ impl<'a> FnLikeNode<'a> {
{
match self.node {
map::NodeItem(i) => match i.node {
ast::ItemFn(ref decl, unsafety, constness, abi, ref generics, block) =>
ast::ItemFn(ref decl, header, ref generics, block) =>
item_fn(ItemFnParts {
id: i.id,
name: i.name,
decl: &decl,
unsafety,
body: block,
generics,
abi,
vis: &i.vis,
constness,
span: i.span,
attrs: &i.attrs,
header,
generics,
}),
_ => bug!("item FnLikeNode that is not fn-like"),
},
Expand Down
66 changes: 62 additions & 4 deletions src/librustc/hir/map/def_collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,27 @@ impl<'a> DefCollector<'a> {
self.parent_def = parent;
}

fn visit_async_fn(
&mut self,
id: NodeId,
async_node_id: NodeId,
name: Name,
span: Span,
visit_fn: impl FnOnce(&mut DefCollector<'a>)
) {
// For async functions, we need to create their inner defs inside of a
// closure to match their desugared representation.
let fn_def_data = DefPathData::ValueNs(name.as_interned_str());
let fn_def = self.create_def(id, fn_def_data, ITEM_LIKE_SPACE, span);
return self.with_parent(fn_def, |this| {
let closure_def = this.create_def(async_node_id,
DefPathData::ClosureExpr,
REGULAR_SPACE,
span);
this.with_parent(closure_def, visit_fn)
})
}

fn visit_macro_invoc(&mut self, id: NodeId) {
if let Some(ref mut visit) = self.visit_macro_invoc {
visit(MacroInvocationData {
Expand All @@ -99,6 +120,15 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
ItemKind::Mod(..) if i.ident == keywords::Invalid.ident() => {
return visit::walk_item(self, i);
}
ItemKind::Fn(_, FnHeader { asyncness: IsAsync::Async(async_node_id), .. }, ..) => {
return self.visit_async_fn(
i.id,
async_node_id,
i.ident.name,
i.span,
|this| visit::walk_item(this, i)
)
}
ItemKind::Mod(..) => DefPathData::Module(i.ident.name.as_interned_str()),
ItemKind::Static(..) | ItemKind::Const(..) | ItemKind::Fn(..) =>
DefPathData::ValueNs(i.ident.name.as_interned_str()),
Expand Down Expand Up @@ -197,6 +227,17 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {

fn visit_impl_item(&mut self, ii: &'a ImplItem) {
let def_data = match ii.node {
ImplItemKind::Method(MethodSig {
header: FnHeader { asyncness: IsAsync::Async(async_node_id), .. }, ..
}, ..) => {
return self.visit_async_fn(
ii.id,
async_node_id,
ii.ident.name,
ii.span,
|this| visit::walk_impl_item(this, ii)
)
}
ImplItemKind::Method(..) | ImplItemKind::Const(..) =>
DefPathData::ValueNs(ii.ident.name.as_interned_str()),
ImplItemKind::Type(..) => DefPathData::AssocTypeInImpl(ii.ident.name.as_interned_str()),
Expand Down Expand Up @@ -227,15 +268,32 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {

match expr.node {
ExprKind::Mac(..) => return self.visit_macro_invoc(expr.id),
ExprKind::Closure(..) => {
let def = self.create_def(expr.id,
ExprKind::Closure(_, asyncness, ..) => {
let closure_def = self.create_def(expr.id,
DefPathData::ClosureExpr,
REGULAR_SPACE,
expr.span);
self.parent_def = Some(def);
self.parent_def = Some(closure_def);

// Async closures desugar to closures inside of closures, so
// we must create two defs.
if let IsAsync::Async(async_id) = asyncness {
let async_def = self.create_def(async_id,
DefPathData::ClosureExpr,
REGULAR_SPACE,
expr.span);
self.parent_def = Some(async_def);
}
}
ExprKind::Async(_, async_id, _) => {
let async_def = self.create_def(async_id,
DefPathData::ClosureExpr,
REGULAR_SPACE,
expr.span);
self.parent_def = Some(async_def);
}
_ => {}
}
};

visit::walk_expr(self, expr);
self.parent_def = parent_def;
Expand Down
4 changes: 2 additions & 2 deletions src/librustc/hir/map/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ impl<'hir> MapEntry<'hir> {
match self {
EntryItem(_, _, ref item) => {
match item.node {
ItemFn(ref fn_decl, _, _, _, _, _) => Some(&fn_decl),
ItemFn(ref fn_decl, _, _, _) => Some(&fn_decl),
_ => None,
}
}
Expand Down Expand Up @@ -210,7 +210,7 @@ impl<'hir> MapEntry<'hir> {
match item.node {
ItemConst(_, body) |
ItemStatic(.., body) |
ItemFn(_, _, _, _, _, body) => Some(body),
ItemFn(_, _, _, body) => Some(body),
_ => None,
}
}
Expand Down
26 changes: 19 additions & 7 deletions src/librustc/hir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ impl LifetimeName {
}
}

fn is_elided(&self) -> bool {
pub fn is_elided(&self) -> bool {
use self::LifetimeName::*;
match self {
Implicit | Underscore => true,
Expand Down Expand Up @@ -1506,9 +1506,7 @@ pub struct MutTy {
/// Represents a method's signature in a trait declaration or implementation.
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub struct MethodSig {
pub unsafety: Unsafety,
pub constness: Constness,
pub abi: Abi,
pub header: FnHeader,
pub decl: P<FnDecl>,
}

Expand Down Expand Up @@ -1736,7 +1734,13 @@ pub enum IsAuto {
No
}

#[derive(Copy, Clone, PartialEq, Eq,PartialOrd, Ord, RustcEncodable, RustcDecodable, Hash, Debug)]
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, RustcEncodable, RustcDecodable, Hash, Debug)]
pub enum IsAsync {
Async,
NotAsync,
}

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, RustcEncodable, RustcDecodable, Hash, Debug)]
pub enum Unsafety {
Unsafe,
Normal,
Expand Down Expand Up @@ -2012,6 +2016,14 @@ pub struct Item {
pub span: Span,
}

#[derive(Clone, Copy, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub struct FnHeader {
pub unsafety: Unsafety,
pub constness: Constness,
pub asyncness: IsAsync,
pub abi: Abi,
}

#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub enum Item_ {
/// An `extern crate` item, with optional *original* crate name if the crate was renamed.
Expand All @@ -2031,7 +2043,7 @@ pub enum Item_ {
/// A `const` item
ItemConst(P<Ty>, BodyId),
/// A function declaration
ItemFn(P<FnDecl>, Unsafety, Constness, Abi, Generics, BodyId),
ItemFn(P<FnDecl>, FnHeader, Generics, BodyId),
/// A module
ItemMod(Mod),
/// An external module
Expand Down Expand Up @@ -2096,7 +2108,7 @@ impl Item_ {

pub fn generics(&self) -> Option<&Generics> {
Some(match *self {
ItemFn(_, _, _, _, ref generics, _) |
ItemFn(_, _, ref generics, _) |
ItemTy(_, ref generics) |
ItemEnum(_, ref generics) |
ItemStruct(_, ref generics) |
Expand Down
Loading

0 comments on commit 56e8f29

Please sign in to comment.