From 18ff7d091a07706b87c131bf3efc226993916f88 Mon Sep 17 00:00:00 2001 From: Without Boats Date: Wed, 16 May 2018 22:55:18 -0700 Subject: Parse async fn header. This is gated on edition 2018 & the `async_await` feature gate. The parser will accept `async fn` and `async unsafe fn` as fn items. Along the same lines as `const fn`, only `async unsafe fn` is permitted, not `unsafe async fn`.The parser will not accept `async` functions as trait methods. To do a little code clean up, four fields of the function type struct have been merged into the new `FnHeader` struct: constness, asyncness, unsafety, and ABI. Also, a small bug in HIR printing is fixed: it previously printed `const unsafe fn` as `unsafe const fn`, which is grammatically incorrect. --- src/libsyntax/parse/mod.rs | 13 ++++++----- src/libsyntax/parse/parser.rs | 50 ++++++++++++++++++++++++++++++++----------- 2 files changed, 46 insertions(+), 17 deletions(-) (limited to 'src/libsyntax/parse') diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index 0050434d42e..61f88f3a250 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 3955ccb4c42..d897aaadf43 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; @@ -1356,10 +1356,13 @@ impl<'a> Parser<'a> { generics.where_clause = self.parse_where_clause()?; let sig = ast::MethodSig { - unsafety, - constness, + header: FnHeader { + unsafety, + constness, + abi, + asyncness: IsAsync::NotAsync, + }, decl: d, - abi, }; let body = match self.token { @@ -5349,6 +5352,7 @@ impl<'a> Parser<'a> { /// Parse an item-position function declaration. fn parse_item_fn(&mut self, unsafety: Unsafety, + asyncness: IsAsync, constness: Spanned, abi: Abi) -> PResult<'a, ItemInfo> { @@ -5356,7 +5360,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 @@ -5531,12 +5536,11 @@ impl<'a> Parser<'a> { 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: IsAsync::NotAsync }; + Ok((ident, inner_attrs, generics, ast::ImplItemKind::Method( + ast::MethodSig { header, decl }, + body + ))) } } @@ -6622,6 +6626,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; @@ -6665,6 +6670,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; @@ -6692,6 +6698,24 @@ impl<'a> Parser<'a> { maybe_append(attrs, extra_attrs)); return Ok(Some(item)); } + if self.eat_keyword(keywords::Async) { + // ASYNC FUNCTION ITEM + let unsafety = self.parse_unsafety(); + self.expect_keyword(keywords::Fn)?; + let fn_span = self.prev_span; + let (ident, item_, extra_attrs) = + self.parse_item_fn(unsafety, + IsAsync::Async, + 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))) @@ -6737,6 +6761,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; @@ -6762,6 +6787,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; -- cgit 1.4.1-3-g733a5 From cf844b547dbec1f23982fca8e07ec65800ed5d6d Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Wed, 6 Jun 2018 15:50:59 -0700 Subject: async await desugaring and tests --- src/librustc/diagnostics.rs | 7 +- src/librustc/hir/lowering.rs | 671 ++++++++++++++++----- src/librustc/hir/map/def_collector.rs | 40 +- src/librustc/hir/mod.rs | 10 + src/librustc/ich/impls_syntax.rs | 1 + src/librustc_metadata/encoder.rs | 5 +- src/librustc_resolve/lib.rs | 90 ++- src/librustc_save_analysis/dump_visitor.rs | 2 +- src/librustc_save_analysis/sig.rs | 4 +- src/librustc_typeck/check/mod.rs | 2 +- src/librustdoc/html/render.rs | 3 +- src/libstd/lib.rs | 4 +- src/libstd/macros.rs | 20 + src/libstd/raw.rs | 110 ++++ src/libsyntax/ast.rs | 24 +- src/libsyntax/ext/build.rs | 2 + src/libsyntax/feature_gate.rs | 12 +- src/libsyntax/fold.rs | 25 +- src/libsyntax/parse/parser.rs | 58 +- src/libsyntax/print/pprust.rs | 24 +- src/libsyntax/ptr.rs | 10 + src/libsyntax/test.rs | 12 +- src/libsyntax/util/parser.rs | 2 + src/libsyntax/visit.rs | 7 +- src/libsyntax_pos/hygiene.rs | 2 + src/test/run-pass/async-await.rs | 133 ++++ src/test/ui/async-fn-multiple-lifetimes.rs | 30 + src/test/ui/async-fn-multiple-lifetimes.stderr | 32 + src/test/ui/edition-keywords-2018-2015-parsing.rs | 2 +- .../ui/edition-keywords-2018-2015-parsing.stderr | 8 +- src/test/ui/edition-keywords-2018-2018-parsing.rs | 2 +- .../ui/edition-keywords-2018-2018-parsing.stderr | 8 +- .../ui/feature-gate-async-await-2015-edition.rs | 20 + .../feature-gate-async-await-2015-edition.stderr | 24 + src/test/ui/feature-gate-async-await.rs | 19 + src/test/ui/feature-gate-async-await.stderr | 27 + src/test/ui/no-args-non-move-async-closure.rs | 18 + src/test/ui/no-args-non-move-async-closure.stderr | 11 + 38 files changed, 1282 insertions(+), 199 deletions(-) create mode 100644 src/libstd/raw.rs create mode 100644 src/test/run-pass/async-await.rs create mode 100644 src/test/ui/async-fn-multiple-lifetimes.rs create mode 100644 src/test/ui/async-fn-multiple-lifetimes.stderr create mode 100644 src/test/ui/feature-gate-async-await-2015-edition.rs create mode 100644 src/test/ui/feature-gate-async-await-2015-edition.stderr create mode 100644 src/test/ui/feature-gate-async-await.rs create mode 100644 src/test/ui/feature-gate-async-await.stderr create mode 100644 src/test/ui/no-args-non-move-async-closure.rs create mode 100644 src/test/ui/no-args-non-move-async-closure.stderr (limited to 'src/libsyntax/parse') diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 1435957a5c1..6e6b15bdff7 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -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 + + E0703, // multiple different lifetimes used in arguments of `async fn` + E0704, // multiple elided lifetimes used in arguments of `async fn` + E0705, // `async` non-`move` closures with arguments are not currently supported } diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 16405b602be..d0e6a5e2973 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -449,7 +449,7 @@ impl<'a> LoweringContext<'a> { } } - fn allocate_hir_id_counter(&mut self, owner: NodeId, debug: &T) { + fn allocate_hir_id_counter(&mut self, owner: NodeId, debug: &T) -> LoweredNodeId { if self.item_local_id_counters.insert(owner, 0).is_some() { bug!( "Tried to allocate item_local_id_counter for {:?} twice", @@ -457,7 +457,7 @@ impl<'a> LoweringContext<'a> { ); } // Always allocate the first HirId for the owner itself - self.lower_node_id_with_owner(owner, owner); + self.lower_node_id_with_owner(owner, owner) } fn lower_node_id_generic(&mut self, ast_node_id: NodeId, alloc_hir_id: F) -> LoweredNodeId @@ -501,7 +501,7 @@ impl<'a> LoweringContext<'a> { { let counter = self.item_local_id_counters .insert(owner, HIR_ID_COUNTER_LOCKED) - .unwrap(); + .unwrap_or_else(|| panic!("No item_local_id_counters entry for {:?}", owner)); let def_index = self.resolver.definitions().opt_def_index(owner).unwrap(); self.current_hir_id_owner.push((def_index, counter)); let ret = f(self); @@ -840,6 +840,46 @@ impl<'a> LoweringContext<'a> { result } + fn make_async_expr( + &mut self, + capture_clause: CaptureBy, + closure_node_id: NodeId, + ret_ty: Option<&Ty>, + body: impl FnOnce(&mut LoweringContext) -> hir::Expr, + ) -> hir::Expr_ { + let prev_is_generator = mem::replace(&mut self.is_generator, true); + let body_expr = body(self); + let span = body_expr.span; + let output = match ret_ty { + Some(ty) => FunctionRetTy::Ty(P(ty.clone())), + None => FunctionRetTy::Default(span), + }; + let decl = FnDecl { + inputs: vec![], + output, + variadic: false + }; + let body_id = self.record_body(body_expr, Some(&decl)); + self.is_generator = prev_is_generator; + + let capture_clause = self.lower_capture_clause(capture_clause); + let closure_hir_id = self.lower_node_id(closure_node_id).hir_id; + let decl = self.lower_fn_decl(&decl, None, /* impl trait allowed */ false, false); + let generator = hir::Expr { + id: closure_node_id, + hir_id: closure_hir_id, + node: hir::ExprClosure(capture_clause, decl, body_id, span, + Some(hir::GeneratorMovability::Static)), + span, + attrs: ThinVec::new(), + }; + + let unstable_span = self.allow_internal_unstable(CompilerDesugaringKind::Async, span); + let gen_future = self.expr_std_path( + unstable_span, &["raw", "future_from_generator"], ThinVec::new()); + hir::ExprCall(P(gen_future), hir_vec![generator]) + } + fn lower_body(&mut self, decl: Option<&FnDecl>, f: F) -> hir::BodyId where F: FnOnce(&mut LoweringContext) -> hir::Expr, @@ -1067,7 +1107,7 @@ impl<'a> LoweringContext<'a> { ), unsafety: this.lower_unsafety(f.unsafety), abi: f.abi, - decl: this.lower_fn_decl(&f.decl, None, false), + decl: this.lower_fn_decl(&f.decl, None, false, false), arg_names: this.lower_fn_args_to_names(&f.decl), })) }, @@ -1132,92 +1172,10 @@ impl<'a> LoweringContext<'a> { let span = t.span; match itctx { ImplTraitContext::Existential(fn_def_id) => { - - // We need to manually repeat the code of `next_id` because the lowering - // needs to happen while the owner_id is pointing to the item itself, - // because items are their own owners - let exist_ty_node_id = self.sess.next_node_id(); - - // Make sure we know that some funky desugaring has been going on here. - // This is a first: there is code in other places like for loop - // desugaring that explicitly states that we don't want to track that. - // Not tracking it makes lints in rustc and clippy very fragile as - // frequently opened issues show. - let exist_ty_span = self.allow_internal_unstable( - CompilerDesugaringKind::ExistentialReturnType, - t.span, - ); - - // Pull a new definition from the ether - let exist_ty_def_index = self - .resolver - .definitions() - .create_def_with_parent( - fn_def_id.index, - exist_ty_node_id, - DefPathData::ExistentialImplTrait, - DefIndexAddressSpace::High, - Mark::root(), - exist_ty_span, - ); - - // the `t` is just for printing debug messages - self.allocate_hir_id_counter(exist_ty_node_id, t); - - let hir_bounds = self.with_hir_id_owner(exist_ty_node_id, |lctx| { - lctx.lower_param_bounds(bounds, itctx) - }); - - let (lifetimes, lifetime_defs) = self.lifetimes_from_impl_trait_bounds( - exist_ty_node_id, - exist_ty_def_index, - &hir_bounds, - ); - - self.with_hir_id_owner(exist_ty_node_id, |lctx| { - let exist_ty_item_kind = hir::ItemExistential(hir::ExistTy { - generics: hir::Generics { - params: lifetime_defs, - where_clause: hir::WhereClause { - id: lctx.next_id().node_id, - predicates: Vec::new().into(), - }, - span, - }, - bounds: hir_bounds, - impl_trait_fn: Some(fn_def_id), - }); - let exist_ty_id = lctx.lower_node_id(exist_ty_node_id); - // Generate an `existential type Foo: Trait;` declaration - trace!("creating existential type with id {:#?}", exist_ty_id); - // Set the name to `impl Bound1 + Bound2` - let exist_ty_name = Symbol::intern(&pprust::ty_to_string(t)); - - trace!("exist ty def index: {:#?}", exist_ty_def_index); - let exist_ty_item = hir::Item { - id: exist_ty_id.node_id, - hir_id: exist_ty_id.hir_id, - name: exist_ty_name, - attrs: Default::default(), - node: exist_ty_item_kind, - vis: hir::Visibility::Inherited, - span: exist_ty_span, - }; - - // Insert the item into the global list. This usually happens - // automatically for all AST items. But this existential type item - // does not actually exist in the AST. - lctx.items.insert(exist_ty_id.node_id, exist_ty_item); - - // `impl Trait` now just becomes `Foo<'a, 'b, ..>` - hir::TyImplTraitExistential( - hir::ItemId { - id: exist_ty_id.node_id - }, - DefId::local(exist_ty_def_index), - lifetimes, - ) - }) + // Set the name to `impl Bound1 + Bound2` + let exist_ty_name = Symbol::intern(&pprust::ty_to_string(t)); + self.lower_existential_impl_trait( + span, fn_def_id, exist_ty_name, |this| this.lower_bounds(bounds, itctx)) } ImplTraitContext::Universal(def_id) => { let def_node_id = self.next_id().node_id; @@ -1281,6 +1239,95 @@ impl<'a> LoweringContext<'a> { }) } + fn lower_existential_impl_trait( + &mut self, + span: Span, + fn_def_id: DefId, + exist_ty_name: Name, + lower_bounds: impl FnOnce(&mut LoweringContext) -> hir::TyParamBounds, + ) -> hir::Ty_ { + // We need to manually repeat the code of `next_id` because the lowering + // needs to happen while the owner_id is pointing to the item itself, + // because items are their own owners + let exist_ty_node_id = self.sess.next_node_id(); + + // Make sure we know that some funky desugaring has been going on here. + // This is a first: there is code in other places like for loop + // desugaring that explicitly states that we don't want to track that. + // Not tracking it makes lints in rustc and clippy very fragile as + // frequently opened issues show. + let exist_ty_span = self.allow_internal_unstable( + CompilerDesugaringKind::ExistentialReturnType, + span, + ); + + // Pull a new definition from the ether + let exist_ty_def_index = self + .resolver + .definitions() + .create_def_with_parent( + fn_def_id.index, + exist_ty_node_id, + DefPathData::ExistentialImplTrait, + DefIndexAddressSpace::High, + Mark::root(), + exist_ty_span, + ); + + self.allocate_hir_id_counter(exist_ty_node_id, &"existential impl trait"); + + let hir_bounds = self.with_hir_id_owner(exist_ty_node_id, lower_bounds); + + let (lifetimes, lifetime_defs) = self.lifetimes_from_impl_trait_bounds( + exist_ty_node_id, + exist_ty_def_index, + &hir_bounds, + ); + + self.with_hir_id_owner(exist_ty_node_id, |lctx| { + let exist_ty_item_kind = hir::ItemExistential(hir::ExistTy { + generics: hir::Generics { + params: lifetime_defs, + where_clause: hir::WhereClause { + id: lctx.next_id().node_id, + predicates: Vec::new().into(), + }, + span, + }, + bounds: hir_bounds, + impl_trait_fn: Some(fn_def_id), + }); + let exist_ty_id = lctx.lower_node_id(exist_ty_node_id); + // Generate an `existential type Foo: Trait;` declaration + trace!("creating existential type with id {:#?}", exist_ty_id); + + trace!("exist ty def index: {:#?}", exist_ty_def_index); + let exist_ty_item = hir::Item { + id: exist_ty_id.node_id, + hir_id: exist_ty_id.hir_id, + name: exist_ty_name, + attrs: Default::default(), + node: exist_ty_item_kind, + vis: hir::Visibility::Inherited, + span: exist_ty_span, + }; + + // Insert the item into the global list. This usually happens + // automatically for all AST items. But this existential type item + // does not actually exist in the AST. + lctx.items.insert(exist_ty_id.node_id, exist_ty_item); + + // `impl Trait` now just becomes `Foo<'a, 'b, ..>` + hir::TyImplTraitExistential( + hir::ItemId { + id: exist_ty_id.node_id + }, + DefId::local(exist_ty_def_index), + lifetimes, + ) + }) + } + fn lifetimes_from_impl_trait_bounds( &mut self, exist_ty_id: NodeId, @@ -1829,31 +1876,40 @@ impl<'a> LoweringContext<'a> { .collect() } + // Lowers a function declaration. + // + // decl: the unlowered (ast) function declaration. + // fn_def_id: if `Some`, impl Trait arguments are lowered into generic parameters on the + // given DefId, otherwise impl Trait is disallowed. Must be `Some` if + // make_ret_async is true. + // impl_trait_return_allow: determines whether impl Trait can be used in return position. + // This guards against trait declarations and implementations where impl Trait is + // disallowed. + // make_ret_async: if enabled, converts `-> T` into `-> impl Future` in the + // return type. This is used for `async fn` declarations. fn lower_fn_decl( &mut self, decl: &FnDecl, fn_def_id: Option, impl_trait_return_allow: bool, + make_ret_async: bool, ) -> P { - // NOTE: The two last parameters here have to do with impl Trait. If fn_def_id is Some, - // then impl Trait arguments are lowered into generic parameters on the given - // fn_def_id, otherwise impl Trait is disallowed. (for now) - // - // Furthermore, if impl_trait_return_allow is true, then impl Trait may be used in - // return positions as well. This guards against trait declarations and their impls - // where impl Trait is disallowed. (again for now) - P(hir::FnDecl { - inputs: decl.inputs - .iter() - .map(|arg| { - if let Some(def_id) = fn_def_id { - self.lower_ty(&arg.ty, ImplTraitContext::Universal(def_id)) - } else { - self.lower_ty(&arg.ty, ImplTraitContext::Disallowed) - } - }) - .collect(), - output: match decl.output { + let inputs = decl.inputs + .iter() + .map(|arg| { + if let Some(def_id) = fn_def_id { + self.lower_ty(&arg.ty, ImplTraitContext::Universal(def_id)) + } else { + self.lower_ty(&arg.ty, ImplTraitContext::Disallowed) + } + }) + .collect::>(); + + let output = if make_ret_async { + self.lower_async_fn_ret_ty( + &inputs, &decl.output, fn_def_id.expect("make_ret_async but no fn_def_id")) + } else { + match decl.output { FunctionRetTy::Ty(ref ty) => match fn_def_id { Some(def_id) if impl_trait_return_allow => { hir::Return(self.lower_ty(ty, ImplTraitContext::Existential(def_id))) @@ -1861,7 +1917,12 @@ impl<'a> LoweringContext<'a> { _ => hir::Return(self.lower_ty(ty, ImplTraitContext::Disallowed)), }, FunctionRetTy::Default(span) => hir::DefaultReturn(span), - }, + } + }; + + P(hir::FnDecl { + inputs, + output, variadic: decl.variadic, has_implicit_self: decl.inputs.get(0).map_or(false, |arg| match arg.ty.node { TyKind::ImplicitSelf => true, @@ -1871,6 +1932,243 @@ impl<'a> LoweringContext<'a> { }) } + // Transform `-> T` into `-> impl Future` for `async fn` + // + // fn_span: the span of the async function declaration. Used for error reporting. + // inputs: lowered types of arguments to the function. Used to collect lifetimes. + // output: unlowered output type (`T` in `-> T`) + // fn_def_id: DefId of the parent function. Used to create child impl trait definition. + fn lower_async_fn_ret_ty( + &mut self, + inputs: &[P], + output: &FunctionRetTy, + fn_def_id: DefId, + ) -> hir::FunctionRetTy { + // Get lifetimes used in the input arguments to the function. Our output type must also + // have the same lifetime. FIXME(cramertj) multiple different lifetimes are not allowed + // because `impl Trait + 'a + 'b` doesn't allow for capture `'a` and `'b` where neither + // is a subset of the other. We really want some new lifetime that is a subset of all input + // lifetimes, but that doesn't exist at the moment. + + struct AsyncFnLifetimeCollector<'r, 'a: 'r> { + context: &'r mut LoweringContext<'a>, + // Lifetimes bound by HRTB + currently_bound_lifetimes: Vec, + // Whether to count elided lifetimes. + // Disabled inside of `Fn` or `fn` syntax. + collect_elided_lifetimes: bool, + // The lifetime found. + // Multiple different or elided lifetimes cannot appear in async fn for now. + output_lifetime: Option<(hir::LifetimeName, Span)>, + } + + impl<'r, 'a: 'r, 'v> hir::intravisit::Visitor<'v> for AsyncFnLifetimeCollector<'r, 'a> { + fn nested_visit_map<'this>( + &'this mut self, + ) -> hir::intravisit::NestedVisitorMap<'this, 'v> { + hir::intravisit::NestedVisitorMap::None + } + + fn visit_path_parameters(&mut self, span: Span, parameters: &'v hir::PathParameters) { + // Don't collect elided lifetimes used inside of `Fn()` syntax. + if parameters.parenthesized { + let old_collect_elided_lifetimes = self.collect_elided_lifetimes; + self.collect_elided_lifetimes = false; + hir::intravisit::walk_path_parameters(self, span, parameters); + self.collect_elided_lifetimes = old_collect_elided_lifetimes; + } else { + hir::intravisit::walk_path_parameters(self, span, parameters); + } + } + + fn visit_ty(&mut self, t: &'v hir::Ty) { + // Don't collect elided lifetimes used inside of `fn()` syntax + if let &hir::Ty_::TyBareFn(_) = &t.node { + let old_collect_elided_lifetimes = self.collect_elided_lifetimes; + self.collect_elided_lifetimes = false; + + // Record the "stack height" of `for<'a>` lifetime bindings + // to be able to later fully undo their introduction. + let old_len = self.currently_bound_lifetimes.len(); + hir::intravisit::walk_ty(self, t); + self.currently_bound_lifetimes.truncate(old_len); + + self.collect_elided_lifetimes = old_collect_elided_lifetimes; + } else { + hir::intravisit::walk_ty(self, t); + } + } + + fn visit_poly_trait_ref( + &mut self, + trait_ref: &'v hir::PolyTraitRef, + modifier: hir::TraitBoundModifier, + ) { + // Record the "stack height" of `for<'a>` lifetime bindings + // to be able to later fully undo their introduction. + let old_len = self.currently_bound_lifetimes.len(); + hir::intravisit::walk_poly_trait_ref(self, trait_ref, modifier); + self.currently_bound_lifetimes.truncate(old_len); + } + + fn visit_generic_param(&mut self, param: &'v hir::GenericParam) { + // Record the introduction of 'a in `for<'a> ...` + if let hir::GenericParam::Lifetime(ref lt_def) = *param { + // Introduce lifetimes one at a time so that we can handle + // cases like `fn foo<'d>() -> impl for<'a, 'b: 'a, 'c: 'b + 'd>` + self.currently_bound_lifetimes.push(lt_def.lifetime.name); + } + + hir::intravisit::walk_generic_param(self, param); + } + + fn visit_lifetime(&mut self, lifetime: &'v hir::Lifetime) { + let name = match lifetime.name { + hir::LifetimeName::Implicit | hir::LifetimeName::Underscore => { + if self.collect_elided_lifetimes { + // Use `'_` for both implicit and underscore lifetimes in + // `abstract type Foo<'_>: SomeTrait<'_>;` + hir::LifetimeName::Underscore + } else { + return; + } + } + name @ hir::LifetimeName::Fresh(_) => name, + name @ hir::LifetimeName::Name(_) => name, + hir::LifetimeName::Static => return, + }; + + if !self.currently_bound_lifetimes.contains(&name) { + if let Some((current_lt_name, current_lt_span)) = self.output_lifetime { + // We don't currently have a reliable way to desugar `async fn` with + // multiple potentially unrelated input lifetimes into + // `-> impl Trait + 'lt`, so we report an error in this case. + if current_lt_name != name { + struct_span_err!( + self.context.sess, + current_lt_span.between(lifetime.span), + E0703, + "multiple different lifetimes used in arguments of `async fn`", + ) + .span_label(current_lt_span, "first lifetime here") + .span_label(lifetime.span, "different lifetime here") + .help("`async fn` can only accept borrowed values \ + identical lifetimes") + .emit() + } else if current_lt_name.is_elided() && name.is_elided() { + struct_span_err!( + self.context.sess, + current_lt_span.between(lifetime.span), + E0704, + "multiple elided lifetimes used in arguments of `async fn`", + ) + .span_label(current_lt_span, "first lifetime here") + .span_label(lifetime.span, "different lifetime here") + .help("consider giving these arguments named lifetimes") + .emit() + } + } else { + self.output_lifetime = Some((name, lifetime.span)); + } + } + } + } + + let bound_lifetime = { + let mut lifetime_collector = AsyncFnLifetimeCollector { + context: self, + currently_bound_lifetimes: Vec::new(), + collect_elided_lifetimes: true, + output_lifetime: None, + }; + + for arg in inputs { + hir::intravisit::walk_ty(&mut lifetime_collector, arg); + } + lifetime_collector.output_lifetime + }; + + let output_ty_name_owned; + let (output_ty_name, span) = match output { + FunctionRetTy::Ty(ty) => { + output_ty_name_owned = pprust::ty_to_string(ty); + (&*output_ty_name_owned, ty.span) + }, + FunctionRetTy::Default(span) => ("()", *span), + }; + + // FIXME(cramertj) add lifetimes (see FIXME below) to the name + let exist_ty_name = Symbol::intern(&format!("impl Future", output_ty_name)); + let impl_trait_ty = self.lower_existential_impl_trait( + span, fn_def_id, exist_ty_name, |this| { + let output_ty = match output { + FunctionRetTy::Ty(ty) => + this.lower_ty(ty, ImplTraitContext::Existential(fn_def_id)), + FunctionRetTy::Default(span) => { + let LoweredNodeId { node_id, hir_id } = this.next_id(); + P(hir::Ty { + id: node_id, + hir_id: hir_id, + node: hir::TyTup(hir_vec![]), + span: *span, + }) + } + }; + + let hir::Path { def, segments, .. } = this.std_path(span, &["future", "Future"], false); + let future_path = hir::Path { + segments: segments.map_slice(|mut v| { + v.last_mut().unwrap().parameters = Some(P(hir::PathParameters { + lifetimes: hir_vec![], + types: hir_vec![], + bindings: hir_vec![hir::TypeBinding { + name: Symbol::intern(FN_OUTPUT_NAME), + ty: output_ty, + id: this.next_id().node_id, + span, + }], + parenthesized: false, + })); + v + }), + def, span + }; + + // FIXME(cramertj) collect input lifetimes to function and add them to + // the output `impl Trait` type here. + let mut bounds = vec![ + hir::TyParamBound::TraitTyParamBound( + hir::PolyTraitRef { + trait_ref: hir::TraitRef { + path: future_path, + ref_id: this.next_id().node_id, + }, + bound_generic_params: hir_vec![], + span, + }, + hir::TraitBoundModifier::None + ), + ]; + + if let Some((name, span)) = bound_lifetime { + bounds.push(hir::RegionTyParamBound( + hir::Lifetime { id: this.next_id().node_id, name, span })); + } + + hir::HirVec::from(bounds) + }); + + let LoweredNodeId { node_id, hir_id } = self.next_id(); + let impl_trait_ty = P(hir::Ty { + id: node_id, + node: impl_trait_ty, + span, + hir_id, + }); + + hir::FunctionRetTy::Return(impl_trait_ty) + } + fn lower_param_bound( &mut self, tpb: &GenericBound, @@ -2286,16 +2584,32 @@ impl<'a> LoweringContext<'a> { } ItemKind::Fn(ref decl, header, ref generics, ref body) => { let fn_def_id = self.resolver.definitions().local_def_id(id); + self.with_new_scopes(|this| { + // Note: we can use non-async decl here because lower_body + // only cares about the input argument patterns, + // not the return types. let body_id = this.lower_body(Some(decl), |this| { - let body = this.lower_block(body, false); - this.expr_block(body, ThinVec::new()) + if let IsAsync::Async(async_node_id) = header.asyncness { + let async_expr = this.make_async_expr( + CaptureBy::Value, async_node_id, None, + |this| { + let body = this.lower_block(body, false); + this.expr_block(body, ThinVec::new()) + }); + this.expr(body.span, async_expr, ThinVec::new()) + } else { + let body = this.lower_block(body, false); + this.expr_block(body, ThinVec::new()) + } }); + let (generics, fn_decl) = this.add_in_band_defs( generics, fn_def_id, AnonymousLifetimeMode::PassThrough, - |this| this.lower_fn_decl(decl, Some(fn_def_id), true), + |this| this.lower_fn_decl( + decl, Some(fn_def_id), true, header.asyncness.is_async()) ); hir::ItemFn( @@ -2863,7 +3177,7 @@ impl<'a> LoweringContext<'a> { |this| { ( // Disallow impl Trait in foreign items - this.lower_fn_decl(fdec, None, false), + this.lower_fn_decl(fdec, None, false, false), this.lower_fn_args_to_names(fdec), ) }, @@ -2890,7 +3204,7 @@ impl<'a> LoweringContext<'a> { ) -> hir::MethodSig { hir::MethodSig { header: self.lower_fn_header(sig.header), - decl: self.lower_fn_decl(&sig.decl, Some(fn_def_id), impl_trait_return_allow), + decl: self.lower_fn_decl(&sig.decl, Some(fn_def_id), impl_trait_return_allow, false), } } @@ -2926,7 +3240,7 @@ impl<'a> LoweringContext<'a> { fn lower_asyncness(&mut self, a: IsAsync) -> hir::IsAsync { match a { - IsAsync::Async => hir::IsAsync::Async, + IsAsync::Async(_) => hir::IsAsync::Async, IsAsync::NotAsync => hir::IsAsync::NotAsync, } } @@ -3218,46 +3532,101 @@ impl<'a> LoweringContext<'a> { arms.iter().map(|x| self.lower_arm(x)).collect(), hir::MatchSource::Normal, ), - ExprKind::Closure(capture_clause, movability, ref decl, ref body, fn_decl_span) => { + ExprKind::Async(capture_clause, closure_node_id, ref block) => { + self.make_async_expr(capture_clause, closure_node_id, None, |this| { + this.with_new_scopes(|this| { + let block = this.lower_block(block, false); + this.expr_block(block, ThinVec::new()) + }) + }) + }, + ExprKind::Closure( + capture_clause, asyncness, movability, ref decl, ref body, fn_decl_span) => + { self.with_new_scopes(|this| { - let mut is_generator = false; - let body_id = this.lower_body(Some(decl), |this| { - let e = this.lower_expr(body); - is_generator = this.is_generator; - e - }); - let generator_option = if is_generator { - if !decl.inputs.is_empty() { - span_err!( + if let IsAsync::Async(async_closure_node_id) = asyncness { + // FIXME(cramertj) allow `async` non-`move` closures with + if capture_clause == CaptureBy::Ref && + !decl.inputs.is_empty() + { + struct_span_err!( this.sess, fn_decl_span, - E0628, - "generators cannot have explicit arguments" - ); - this.sess.abort_if_errors(); + E0705, + "`async` non-`move` closures with arguments \ + are not currently supported", + ) + .help("consider using `let` statements to manually capture \ + variables by reference before entering an \ + `async move` closure") + .emit(); } - Some(match movability { - Movability::Movable => hir::GeneratorMovability::Movable, - Movability::Static => hir::GeneratorMovability::Static, - }) + + // Transform `async |x: u8| -> X { ... }` into + // `|x: u8| future_from_generator(|| -> X { ... })` + let outer_decl = FnDecl { + inputs: decl.inputs.clone(), + output: FunctionRetTy::Default(fn_decl_span), + variadic: false, + }; + let body_id = this.lower_body(Some(&outer_decl), |this| { + let async_ret_ty = if let FunctionRetTy::Ty(ty) = &decl.output { + Some(&**ty) + } else { None }; + let async_body = this.make_async_expr( + capture_clause, async_closure_node_id, async_ret_ty, + |this| { + this.with_new_scopes(|this| this.lower_expr(body)) + }); + this.expr(fn_decl_span, async_body, ThinVec::new()) + }); + hir::ExprClosure( + this.lower_capture_clause(capture_clause), + this.lower_fn_decl(&outer_decl, None, false, false), + body_id, + fn_decl_span, + None, + ) } else { - if movability == Movability::Static { - span_err!( - this.sess, - fn_decl_span, - E0697, - "closures cannot be static" - ); - } - None - }; - hir::ExprClosure( - this.lower_capture_clause(capture_clause), - this.lower_fn_decl(decl, None, false), - body_id, - fn_decl_span, - generator_option, - ) + let mut is_generator = false; + let body_id = this.lower_body(Some(decl), |this| { + let e = this.lower_expr(body); + is_generator = this.is_generator; + e + }); + let generator_option = if is_generator { + if !decl.inputs.is_empty() { + span_err!( + this.sess, + fn_decl_span, + E0628, + "generators cannot have explicit arguments" + ); + this.sess.abort_if_errors(); + } + Some(match movability { + Movability::Movable => hir::GeneratorMovability::Movable, + Movability::Static => hir::GeneratorMovability::Static, + }) + } else { + if movability == Movability::Static { + span_err!( + this.sess, + fn_decl_span, + E0906, + "closures cannot be static" + ); + } + None + }; + hir::ExprClosure( + this.lower_capture_clause(capture_clause), + this.lower_fn_decl(decl, None, false, false), + body_id, + fn_decl_span, + generator_option, + ) + } }) } ExprKind::Block(ref blk, opt_label) => { diff --git a/src/librustc/hir/map/def_collector.rs b/src/librustc/hir/map/def_collector.rs index 8aa5dd4ad80..335e38bbe7e 100644 --- a/src/librustc/hir/map/def_collector.rs +++ b/src/librustc/hir/map/def_collector.rs @@ -99,6 +99,21 @@ 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), .. }, ..) => { + // 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(i.ident.name.as_interned_str()); + let fn_def = self.create_def(i.id, fn_def_data, ITEM_LIKE_SPACE, i.span); + return self.with_parent(fn_def, |this| { + let closure_def = this.create_def(async_node_id, + DefPathData::ClosureExpr, + REGULAR_SPACE, + i.span); + this.with_parent(closure_def, |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()), @@ -227,15 +242,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; diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 9479234cf11..bd6bef29c29 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -245,6 +245,16 @@ pub enum LifetimeName { } impl LifetimeName { + pub fn is_elided(self) -> bool { + match self { + LifetimeName::Implicit + | LifetimeName::Underscore => true, + LifetimeName::Fresh(_) + | LifetimeName::Static + | LifetimeName::Name(_) => false, + } + } + pub fn name(&self) -> Name { use self::LifetimeName::*; match *self { diff --git a/src/librustc/ich/impls_syntax.rs b/src/librustc/ich/impls_syntax.rs index 7b14831cf95..0f4603be39d 100644 --- a/src/librustc/ich/impls_syntax.rs +++ b/src/librustc/ich/impls_syntax.rs @@ -409,6 +409,7 @@ impl_stable_hash_for!(enum ::syntax_pos::hygiene::ExpnFormat { }); impl_stable_hash_for!(enum ::syntax_pos::hygiene::CompilerDesugaringKind { + Async, DotFill, QuestionMark, ExistentialReturnType, diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index 7ed3abe66c2..ce270006a9d 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -1245,7 +1245,10 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> { (has_types || tcx.codegen_fn_attrs(def_id).requests_inline()) && !self.metadata_output_only(); let always_encode_mir = self.tcx.sess.opts.debugging_opts.always_encode_mir; - if needs_inline || header.constness == hir::Constness::Const || always_encode_mir { + if needs_inline + || header.constness == hir::Constness::Const + || always_encode_mir + { self.encode_optimized_mir(def_id) } else { None diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index e311701ac05..d3c19291594 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -55,7 +55,7 @@ use syntax::util::lev_distance::find_best_match_for_name; use syntax::visit::{self, FnKind, Visitor}; use syntax::attr; -use syntax::ast::{Arm, BindingMode, Block, Crate, Expr, ExprKind}; +use syntax::ast::{Arm, IsAsync, BindingMode, Block, Crate, Expr, ExprKind, FnHeader}; use syntax::ast::{FnDecl, ForeignItem, ForeignItemKind, GenericParamKind, Generics}; use syntax::ast::{Item, ItemKind, ImplItem, ImplItemKind}; use syntax::ast::{Label, Local, Mutability, Pat, PatKind, Path}; @@ -2054,13 +2054,54 @@ impl<'a> Resolver<'a> { self.check_proc_macro_attrs(&item.attrs); match item.node { + ItemKind::Fn(ref declaration, + FnHeader { asyncness: IsAsync::Async(async_closure_id), .. }, + ref generics, + ref body) => { + // Async functions are desugared from `async fn foo() { .. }` + // to `fn foo() { future_from_generator(move || ... ) }`, + // so we have to visit the body inside the closure scope + self.with_type_parameter_rib(HasTypeParameters(generics, ItemRibKind), |this| { + this.visit_vis(&item.vis); + this.visit_ident(item.ident); + this.visit_generics(generics); + let rib_kind = ItemRibKind; + this.ribs[ValueNS].push(Rib::new(rib_kind)); + this.label_ribs.push(Rib::new(rib_kind)); + let mut bindings_list = FxHashMap(); + for argument in &declaration.inputs { + this.resolve_pattern( + &argument.pat, PatternSource::FnParam, &mut bindings_list); + this.visit_ty(&*argument.ty); + } + visit::walk_fn_ret_ty(this, &declaration.output); + + // Now resolve the inner closure + { + let rib_kind = ClosureRibKind(async_closure_id); + this.ribs[ValueNS].push(Rib::new(rib_kind)); + this.label_ribs.push(Rib::new(rib_kind)); + // No need to resolve either arguments nor return type, + // as this closure has neither + + // Resolve the body + this.visit_block(body); + this.label_ribs.pop(); + this.ribs[ValueNS].pop(); + } + this.label_ribs.pop(); + this.ribs[ValueNS].pop(); + + walk_list!(this, visit_attribute, &item.attrs); + }) + } ItemKind::Enum(_, ref generics) | ItemKind::Ty(_, ref generics) | ItemKind::Struct(_, ref generics) | ItemKind::Union(_, ref generics) | - ItemKind::Fn(.., ref generics, _) => { + ItemKind::Fn(_, _, ref generics, _) => { self.with_type_parameter_rib(HasTypeParameters(generics, ItemRibKind), - |this| visit::walk_item(this, item)); + |this| visit::walk_item(this, item)); } ItemKind::Impl(.., ref generics, ref opt_trait_ref, ref self_type, ref impl_items) => @@ -3888,6 +3929,49 @@ impl<'a> Resolver<'a> { visit::walk_expr(self, expr); self.current_type_ascription.pop(); } + // Resolve the body of async exprs inside the async closure to which they desugar + ExprKind::Async(_, async_closure_id, ref block) => { + let rib_kind = ClosureRibKind(async_closure_id); + self.ribs[ValueNS].push(Rib::new(rib_kind)); + self.label_ribs.push(Rib::new(rib_kind)); + self.visit_block(&block); + self.label_ribs.pop(); + self.ribs[ValueNS].pop(); + } + // `async |x| ...` gets desugared to `|x| future_from_generator(|| ...)`, so we need to + // resolve the arguments within the proper scopes so that usages of them inside the + // closure are detected as upvars rather than normal closure arg usages. + ExprKind::Closure( + _, IsAsync::Async(inner_closure_id), _, ref fn_decl, ref body, _span) => + { + let rib_kind = ClosureRibKind(expr.id); + self.ribs[ValueNS].push(Rib::new(rib_kind)); + self.label_ribs.push(Rib::new(rib_kind)); + // Resolve arguments: + let mut bindings_list = FxHashMap(); + for argument in &fn_decl.inputs { + self.resolve_pattern(&argument.pat, PatternSource::FnParam, &mut bindings_list); + self.visit_ty(&argument.ty); + } + // No need to resolve return type-- the outer closure return type is + // FunctionRetTy::Default + + // Now resolve the inner closure + { + let rib_kind = ClosureRibKind(inner_closure_id); + self.ribs[ValueNS].push(Rib::new(rib_kind)); + self.label_ribs.push(Rib::new(rib_kind)); + // No need to resolve arguments: the inner closure has none. + // Resolve the return type: + visit::walk_fn_ret_ty(self, &fn_decl.output); + // Resolve the body + self.visit_expr(body); + self.label_ribs.pop(); + self.ribs[ValueNS].pop(); + } + self.label_ribs.pop(); + self.ribs[ValueNS].pop(); + } _ => { visit::walk_expr(self, expr); } diff --git a/src/librustc_save_analysis/dump_visitor.rs b/src/librustc_save_analysis/dump_visitor.rs index 7da5b1668b3..262c0e40abc 100644 --- a/src/librustc_save_analysis/dump_visitor.rs +++ b/src/librustc_save_analysis/dump_visitor.rs @@ -1555,7 +1555,7 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> Visitor<'l> for DumpVisitor<'l, 'tc } } } - ast::ExprKind::Closure(_, _, ref decl, ref body, _fn_decl_span) => { + ast::ExprKind::Closure(_, _, _, ref decl, ref body, _fn_decl_span) => { let mut id = String::from("$"); id.push_str(&ex.id.to_string()); diff --git a/src/librustc_save_analysis/sig.rs b/src/librustc_save_analysis/sig.rs index b532aaf90df..9f2ca20276c 100644 --- a/src/librustc_save_analysis/sig.rs +++ b/src/librustc_save_analysis/sig.rs @@ -385,7 +385,7 @@ impl Sig for ast::Item { if header.constness.node == ast::Constness::Const { text.push_str("const "); } - if header.asyncness == ast::IsAsync::Async { + if header.asyncness.is_async() { text.push_str("async "); } if header.unsafety == ast::Unsafety::Unsafe { @@ -920,7 +920,7 @@ fn make_method_signature( if m.header.constness.node == ast::Constness::Const { text.push_str("const "); } - if m.header.asyncness == ast::IsAsync::Async { + if m.header.asyncness.is_async() { text.push_str("async "); } if m.header.unsafety == ast::Unsafety::Unsafe { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 366420cfcab..f149a9fe571 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1164,7 +1164,7 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>, } if let Node::NodeItem(item) = fcx.tcx.hir.get(fn_id) { - if let Item_::ItemFn(_, _, _, _, ref generics, _) = item.node { + if let Item_::ItemFn(_, _, ref generics, _) = item.node { if !generics.params.is_empty() { fcx.tcx.sess.span_err( span, diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 5989694116c..a0f29f5918e 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -2585,7 +2585,8 @@ fn item_function(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, write!(w, "{}
", render_spotlight_traits(it)?)?;
     render_attributes(w, it)?;
     write!(w,
-           "{vis}{constness}{asyncness}{unsafety}{abi}fn {name}{generics}{decl}{where_clause}
", + "{vis}{constness}{asyncness}{unsafety}{abi}fn \ + {name}{generics}{decl}{where_clause}", vis = VisSpace(&it.visibility), constness = ConstnessSpace(f.header.constness), asyncness = AsyncSpace(f.header.asyncness), diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index a6061e96ae5..c74cd3feca3 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -263,6 +263,7 @@ #![feature(fn_traits)] #![feature(fnbox)] #![feature(futures_api)] +#![feature(generator_trait)] #![feature(hashmap_internals)] #![feature(int_error_internals)] #![feature(integer_atomics)] @@ -410,8 +411,6 @@ pub use core::ops; #[stable(feature = "rust1", since = "1.0.0")] pub use core::ptr; #[stable(feature = "rust1", since = "1.0.0")] -pub use core::raw; -#[stable(feature = "rust1", since = "1.0.0")] pub use core::result; #[stable(feature = "rust1", since = "1.0.0")] pub use core::option; @@ -496,6 +495,7 @@ pub mod os; pub mod panic; pub mod path; pub mod process; +pub mod raw; pub mod sync; pub mod time; diff --git a/src/libstd/macros.rs b/src/libstd/macros.rs index 8da70f5717e..812b0b9fdda 100644 --- a/src/libstd/macros.rs +++ b/src/libstd/macros.rs @@ -213,6 +213,26 @@ macro_rules! eprintln { ($fmt:expr, $($arg:tt)*) => (eprint!(concat!($fmt, "\n"), $($arg)*)); } +#[macro_export] +#[unstable(feature = "await_macro", issue = "50547")] +#[allow_internal_unstable] +macro_rules! await { + ($e:expr) => { { + let mut pinned = $e; + let mut pinned = unsafe { ::core::mem::PinMut::new_unchecked(&mut pinned) }; + loop { + match ::std::raw::with_get_cx(|cx| + ::core::future::Future::poll(pinned.reborrow(), cx)) + { + // FIXME(cramertj) prior to stabilizing await, we have to ensure that this + // can't be used to create a generator on stable via `|| await!()`. + ::core::task::Poll::Pending => yield, + ::core::task::Poll::Ready(x) => break x, + } + } + } } +} + /// A macro to select an event from a number of receivers. /// /// This macro is used to wait for the first event to occur on a number of diff --git a/src/libstd/raw.rs b/src/libstd/raw.rs new file mode 100644 index 00000000000..62fd42c4de7 --- /dev/null +++ b/src/libstd/raw.rs @@ -0,0 +1,110 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(missing_docs)] +#![unstable(feature = "raw", issue = "27751")] + +//! Contains struct definitions for the layout of compiler built-in types. +//! +//! They can be used as targets of transmutes in unsafe code for manipulating +//! the raw representations directly. +//! +//! Their definition should always match the ABI defined in `rustc::back::abi`. + +use core::cell::Cell; +use core::future::Future; +use core::marker::Unpin; +use core::mem::PinMut; +use core::option::Option; +use core::ptr::NonNull; +use core::task::{self, Poll}; +use core::ops::{Drop, Generator, GeneratorState}; + +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::raw::*; + +/// Wrap a future in a generator. +/// +/// This function returns a `GenFuture` underneath, but hides it in `impl Trait` to give +/// better error messages (`impl Future` rather than `GenFuture<[closure.....]>`). +#[unstable(feature = "gen_future", issue = "50547")] +pub fn future_from_generator>(x: T) -> impl Future { + GenFuture(x) +} + +/// A wrapper around generators used to implement `Future` for `async`/`await` code. +#[unstable(feature = "gen_future", issue = "50547")] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] +struct GenFuture>(T); + +// We rely on the fact that async/await futures are immovable in order to create +// self-referential borrows in the underlying generator. +impl> !Unpin for GenFuture {} + +#[unstable(feature = "gen_future", issue = "50547")] +impl> Future for GenFuture { + type Output = T::Return; + fn poll(self: PinMut, cx: &mut task::Context) -> Poll { + with_set_cx(cx, || match unsafe { PinMut::get_mut(self).0.resume() } { + GeneratorState::Yielded(()) => Poll::Pending, + GeneratorState::Complete(x) => Poll::Ready(x), + }) + } +} + +thread_local! { + static TLS_CX: Cell>>> = Cell::new(None); +} + +struct SetOnDrop(Option>>); + +impl Drop for SetOnDrop { + fn drop(&mut self) { + TLS_CX.with(|tls_cx| { + tls_cx.set(self.0.take()); + }); + } +} + +#[unstable(feature = "gen_future", issue = "50547")] +pub fn with_set_cx(cx: &mut task::Context, f: F) -> R +where + F: FnOnce() -> R +{ + let old_cx = TLS_CX.with(|tls_cx| { + let old_cx = tls_cx.get(); + tls_cx.set(NonNull::new( + cx as *mut task::Context as *mut () as *mut task::Context<'static>)); + old_cx + }); + let _reset_cx = SetOnDrop(old_cx); + let res = f(); + res +} + +#[unstable(feature = "gen_future", issue = "50547")] +pub fn with_get_cx(f: F) -> R +where + F: FnOnce(&mut task::Context) -> R +{ + let cx_ptr = TLS_CX.with(|tls_cx| { + let cx_ptr = tls_cx.get(); + // Clear the entry so that nested `with_get_cx` calls + // will fail or set their own value. + tls_cx.set(None); + cx_ptr + }); + let _reset_cx = SetOnDrop(cx_ptr); + + let mut cx_ptr = cx_ptr.expect( + "TLS task::Context not set. This is a rustc bug. \ + Please file an issue on https://github.com/rust-lang/rust."); + unsafe { f(cx_ptr.as_mut()) } +} diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 55c21c64c83..a57a9b95e5f 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -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, P, Span), + Closure(CaptureBy, IsAsync, Movability, P, P, Span), /// A block (`'label: { ... }`) Block(P, Option