diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 1435957a5c142..09153c144ae80 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 + + 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 } diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs index ed86ef705649b..766909f5723b6 100644 --- a/src/librustc/hir/intravisit.rs +++ b/src/librustc/hir/intravisit.rs @@ -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::*; @@ -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]), @@ -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, diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 6291e0eb11372..d6da2fce69a74 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -168,6 +168,7 @@ pub trait Resolver { span: Span, crate_root: Option<&str>, components: &[&str], + params: Option>, is_value: bool, ) -> hir::Path; } @@ -449,7 +450,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 +458,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 +502,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 +841,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, &["future", "from_generator"], None, 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 +1108,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 +1173,8 @@ 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, - ) - }) + self.lower_existential_impl_trait( + span, fn_def_id, |this| this.lower_param_bounds(bounds, itctx)) } ImplTraitContext::Universal(def_id) => { let def_node_id = self.next_id().node_id; @@ -1281,6 +1238,94 @@ impl<'a> LoweringContext<'a> { }) } + fn lower_existential_impl_trait( + &mut self, + span: Span, + fn_def_id: DefId, + lower_bounds: impl FnOnce(&mut LoweringContext) -> hir::GenericBounds, + ) -> 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: keywords::Invalid.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 +1874,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 +1915,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 +1930,231 @@ 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_generic_args(&mut self, span: Span, parameters: &'v hir::GenericArgs) { + // 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_generic_args(self, span, parameters); + self.collect_elided_lifetimes = old_collect_elided_lifetimes; + } else { + hir::intravisit::walk_generic_args(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::GenericParamKind::Lifetime { .. } = param.kind { + // Introduce lifetimes one at a time so that we can handle + // cases like `fn foo<'d>() -> impl for<'a, 'b: 'a, 'c: 'b + 'd>` + let lt_name = hir::LifetimeName::Param(param.name); + self.currently_bound_lifetimes.push(lt_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; + } + } + hir::LifetimeName::Param(_) => lifetime.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), + E0725, + "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 \ + with 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), + E0726, + "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 span = match output { + FunctionRetTy::Ty(ty) => ty.span, + FunctionRetTy::Default(span) => *span, + }; + + let impl_trait_ty = self.lower_existential_impl_trait( + span, fn_def_id, |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 future_params = P(hir::GenericArgs { + args: 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, + }); + + let future_path = + this.std_path(span, &["future", "Future"], Some(future_params), false); + + let mut bounds = vec![ + hir::GenericBound::Trait( + 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::GenericBound::Outlives( + 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, @@ -2284,25 +2568,40 @@ impl<'a> LoweringContext<'a> { let value = self.lower_body(None, |this| this.lower_expr(e)); hir::ItemConst(self.lower_ty(t, ImplTraitContext::Disallowed), value) } - ItemKind::Fn(ref decl, unsafety, constness, abi, ref generics, ref body) => { + 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 don't need to change the return type from `T` to + // `impl Future` here because lower_body + // only cares about the input argument patterns in the function + // declaration (decl), 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( fn_decl, - this.lower_unsafety(unsafety), - this.lower_constness(constness), - abi, + this.lower_fn_header(header), generics, body_id, ) @@ -2620,7 +2919,7 @@ impl<'a> LoweringContext<'a> { AnonymousLifetimeMode::PassThrough, |this| { hir::TraitItemKind::Method( - this.lower_method_sig(sig, trait_item_def_id, false), + this.lower_method_sig(sig, trait_item_def_id, false, false), hir::TraitMethod::Required(names), ) }, @@ -2638,7 +2937,7 @@ impl<'a> LoweringContext<'a> { AnonymousLifetimeMode::PassThrough, |this| { hir::TraitItemKind::Method( - this.lower_method_sig(sig, trait_item_def_id, false), + this.lower_method_sig(sig, trait_item_def_id, false, false), hir::TraitMethod::Provided(body_id), ) }, @@ -2709,8 +3008,18 @@ impl<'a> LoweringContext<'a> { } ImplItemKind::Method(ref sig, ref body) => { let body_id = self.lower_body(Some(&sig.decl), |this| { - let body = this.lower_block(body, false); - this.expr_block(body, ThinVec::new()) + if let IsAsync::Async(async_node_id) = sig.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 impl_trait_return_allow = !self.is_in_trait_impl; @@ -2724,6 +3033,7 @@ impl<'a> LoweringContext<'a> { sig, impl_item_def_id, impl_trait_return_allow, + sig.header.asyncness.is_async(), ), body_id, ) @@ -2865,7 +3175,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), ) }, @@ -2889,12 +3199,11 @@ impl<'a> LoweringContext<'a> { sig: &MethodSig, fn_def_id: DefId, impl_trait_return_allow: bool, + is_async: bool, ) -> hir::MethodSig { hir::MethodSig { - abi: sig.abi, - unsafety: self.lower_unsafety(sig.unsafety), - constness: self.lower_constness(sig.constness), - decl: self.lower_fn_decl(&sig.decl, Some(fn_def_id), impl_trait_return_allow), + header: self.lower_fn_header(sig.header), + decl: self.lower_fn_decl(&sig.decl, Some(fn_def_id), impl_trait_return_allow, is_async), } } @@ -2905,6 +3214,15 @@ impl<'a> LoweringContext<'a> { } } + fn lower_fn_header(&mut self, h: FnHeader) -> hir::FnHeader { + hir::FnHeader { + unsafety: self.lower_unsafety(h.unsafety), + asyncness: self.lower_asyncness(h.asyncness), + constness: self.lower_constness(h.constness), + abi: h.abi, + } + } + fn lower_unsafety(&mut self, u: Unsafety) -> hir::Unsafety { match u { Unsafety::Unsafe => hir::Unsafety::Unsafe, @@ -2919,6 +3237,13 @@ impl<'a> LoweringContext<'a> { } } + fn lower_asyncness(&mut self, a: IsAsync) -> hir::IsAsync { + match a { + IsAsync::Async(_) => hir::IsAsync::Async, + IsAsync::NotAsync => hir::IsAsync::NotAsync, + } + } + fn lower_unop(&mut self, u: UnOp) -> hir::UnOp { match u { UnOp::Deref => hir::UnDeref, @@ -3206,46 +3531,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(); + E0727, + "`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) => { @@ -3272,7 +3652,7 @@ impl<'a> LoweringContext<'a> { let id = self.next_id(); let e1 = self.lower_expr(e1); let e2 = self.lower_expr(e2); - let ty_path = P(self.std_path(span, &["ops", "RangeInclusive"], false)); + let ty_path = P(self.std_path(span, &["ops", "RangeInclusive"], None, false)); let ty = self.ty_path(id, span, hir::QPath::Resolved(None, ty_path)); let new_seg = P(hir::PathSegment::from_name(Symbol::intern("new"))); let new_path = hir::QPath::TypeRelative(ty, new_seg); @@ -3312,7 +3692,7 @@ impl<'a> LoweringContext<'a> { let struct_path = iter::once("ops") .chain(iter::once(path)) .collect::>(); - let struct_path = self.std_path(unstable_span, &struct_path, is_unit); + let struct_path = self.std_path(unstable_span, &struct_path, None, is_unit); let struct_path = hir::QPath::Resolved(None, P(struct_path)); let LoweredNodeId { node_id, hir_id } = self.lower_node_id(e.id); @@ -3589,7 +3969,7 @@ impl<'a> LoweringContext<'a> { let iter = P(self.expr_ident(head_sp, iter, iter_pat.id)); let ref_mut_iter = self.expr_mut_addr_of(head_sp, iter); let next_path = &["iter", "Iterator", "next"]; - let next_path = P(self.expr_std_path(head_sp, next_path, ThinVec::new())); + let next_path = P(self.expr_std_path(head_sp, next_path, None, ThinVec::new())); let next_expr = P(self.expr_call(head_sp, next_path, hir_vec![ref_mut_iter])); let arms = hir_vec![pat_arm, break_arm]; @@ -3647,7 +4027,8 @@ impl<'a> LoweringContext<'a> { // `match ::std::iter::IntoIterator::into_iter() { ... }` let into_iter_expr = { let into_iter_path = &["iter", "IntoIterator", "into_iter"]; - let into_iter = P(self.expr_std_path(head_sp, into_iter_path, ThinVec::new())); + let into_iter = P(self.expr_std_path( + head_sp, into_iter_path, None, ThinVec::new())); P(self.expr_call(head_sp, into_iter, hir_vec![head])) }; @@ -3693,7 +4074,8 @@ impl<'a> LoweringContext<'a> { let sub_expr = self.lower_expr(sub_expr); let path = &["ops", "Try", "into_result"]; - let path = P(self.expr_std_path(unstable_span, path, ThinVec::new())); + let path = P(self.expr_std_path( + unstable_span, path, None, ThinVec::new())); P(self.expr_call(e.span, path, hir_vec![sub_expr])) }; @@ -3732,7 +4114,8 @@ impl<'a> LoweringContext<'a> { let err_local = self.pat_ident(e.span, err_ident); let from_expr = { let path = &["convert", "From", "from"]; - let from = P(self.expr_std_path(e.span, path, ThinVec::new())); + let from = P(self.expr_std_path( + e.span, path, None, ThinVec::new())); let err_expr = self.expr_ident(e.span, err_ident, err_local.id); self.expr_call(e.span, from, hir_vec![err_expr]) @@ -3972,9 +4355,10 @@ impl<'a> LoweringContext<'a> { &mut self, span: Span, components: &[&str], + params: Option>, attrs: ThinVec, ) -> hir::Expr { - let path = self.std_path(span, components, true); + let path = self.std_path(span, components, params, true); self.expr( span, hir::ExprPath(hir::QPath::Resolved(None, P(path))), @@ -4099,7 +4483,7 @@ impl<'a> LoweringContext<'a> { components: &[&str], subpats: hir::HirVec>, ) -> P { - let path = self.std_path(span, components, true); + let path = self.std_path(span, components, None, true); let qpath = hir::QPath::Resolved(None, P(path)); let pt = if subpats.is_empty() { hir::PatKind::Path(qpath) @@ -4146,9 +4530,15 @@ impl<'a> LoweringContext<'a> { /// Given suffix ["b","c","d"], returns path `::std::b::c::d` when /// `fld.cx.use_std`, and `::core::b::c::d` otherwise. /// The path is also resolved according to `is_value`. - fn std_path(&mut self, span: Span, components: &[&str], is_value: bool) -> hir::Path { + fn std_path( + &mut self, + span: Span, + components: &[&str], + params: Option>, + is_value: bool + ) -> hir::Path { self.resolver - .resolve_str_path(span, self.crate_root, components, is_value) + .resolve_str_path(span, self.crate_root, components, params, is_value) } fn ty_path(&mut self, id: LoweredNodeId, span: Span, qpath: hir::QPath) -> P { @@ -4280,7 +4670,7 @@ impl<'a> LoweringContext<'a> { unstable_span: Span, ) -> P { let path = &["ops", "Try", method]; - let from_err = P(self.expr_std_path(unstable_span, path, + let from_err = P(self.expr_std_path(unstable_span, path, None, ThinVec::new())); P(self.expr_call(e.span, from_err, hir_vec![e])) } diff --git a/src/librustc/hir/map/blocks.rs b/src/librustc/hir/map/blocks.rs index 362c0bf07f73b..f665ced3dbcf1 100644 --- a/src/librustc/hir/map/blocks.rs +++ b/src/librustc/hir/map/blocks.rs @@ -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; @@ -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, @@ -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) @@ -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"), }, diff --git a/src/librustc/hir/map/def_collector.rs b/src/librustc/hir/map/def_collector.rs index 8aa5dd4ad80fd..7c71401c8b2e8 100644 --- a/src/librustc/hir/map/def_collector.rs +++ b/src/librustc/hir/map/def_collector.rs @@ -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 { @@ -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()), @@ -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()), @@ -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; diff --git a/src/librustc/hir/map/mod.rs b/src/librustc/hir/map/mod.rs index c2c8c7a391b9c..1e03381861b9b 100644 --- a/src/librustc/hir/map/mod.rs +++ b/src/librustc/hir/map/mod.rs @@ -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, } } @@ -210,7 +210,7 @@ impl<'hir> MapEntry<'hir> { match item.node { ItemConst(_, body) | ItemStatic(.., body) | - ItemFn(_, _, _, _, _, body) => Some(body), + ItemFn(_, _, _, body) => Some(body), _ => None, } } diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index f6876113c1176..5e41c51abd98c 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -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, @@ -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, } @@ -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, @@ -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. @@ -2031,7 +2043,7 @@ pub enum Item_ { /// A `const` item ItemConst(P, BodyId), /// A function declaration - ItemFn(P, Unsafety, Constness, Abi, Generics, BodyId), + ItemFn(P, FnHeader, Generics, BodyId), /// A module ItemMod(Mod), /// An external module @@ -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) | diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs index 14f780fab7f2e..51a7504efcfcb 100644 --- a/src/librustc/hir/print.rs +++ b/src/librustc/hir/print.rs @@ -459,9 +459,12 @@ impl<'a> State<'a> { hir::ForeignItemFn(ref decl, ref arg_names, ref generics) => { self.head("")?; self.print_fn(decl, - hir::Unsafety::Normal, - hir::Constness::NotConst, - Abi::Rust, + hir::FnHeader { + unsafety: hir::Unsafety::Normal, + constness: hir::Constness::NotConst, + abi: Abi::Rust, + asyncness: hir::IsAsync::NotAsync, + }, Some(item.name), generics, &item.vis, @@ -598,12 +601,10 @@ impl<'a> State<'a> { self.s.word(";")?; self.end()?; // end the outer cbox } - hir::ItemFn(ref decl, unsafety, constness, abi, ref typarams, body) => { + hir::ItemFn(ref decl, header, ref typarams, body) => { self.head("")?; self.print_fn(decl, - unsafety, - constness, - abi, + header, Some(item.name), typarams, &item.vis, @@ -935,9 +936,7 @@ impl<'a> State<'a> { body_id: Option) -> io::Result<()> { self.print_fn(&m.decl, - m.unsafety, - m.constness, - m.abi, + m.header, Some(name), generics, vis, @@ -1986,16 +1985,14 @@ impl<'a> State<'a> { pub fn print_fn(&mut self, decl: &hir::FnDecl, - unsafety: hir::Unsafety, - constness: hir::Constness, - abi: Abi, + header: hir::FnHeader, name: Option, generics: &hir::Generics, vis: &hir::Visibility, arg_names: &[Spanned], body_id: Option) -> 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()?; @@ -2260,9 +2257,12 @@ impl<'a> State<'a> { span: syntax_pos::DUMMY_SP, }; self.print_fn(decl, - unsafety, - hir::Constness::NotConst, - abi, + hir::FnHeader { + unsafety, + abi, + constness: hir::Constness::NotConst, + asyncness: hir::IsAsync::NotAsync, + }, name, &generics, &hir::Inherited, @@ -2333,22 +2333,26 @@ impl<'a> State<'a> { } pub fn print_fn_header_info(&mut self, - unsafety: hir::Unsafety, - constness: hir::Constness, - abi: Abi, + header: hir::FnHeader, vis: &hir::Visibility) -> io::Result<()> { self.s.word(&visibility_qualified(vis, ""))?; - self.print_unsafety(unsafety)?; - match constness { + match header.constness { hir::Constness::NotConst => {} hir::Constness::Const => self.word_nbsp("const")?, } - if abi != Abi::Rust { + match header.asyncness { + hir::IsAsync::NotAsync => {} + hir::IsAsync::Async => self.word_nbsp("async")?, + } + + self.print_unsafety(header.unsafety)?; + + 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") diff --git a/src/librustc/ich/impls_hir.rs b/src/librustc/ich/impls_hir.rs index 882194ae64e79..b1429247ab071 100644 --- a/src/librustc/ich/impls_hir.rs +++ b/src/librustc/ich/impls_hir.rs @@ -272,9 +272,7 @@ impl_stable_hash_for!(struct hir::MutTy { }); impl_stable_hash_for!(struct hir::MethodSig { - unsafety, - constness, - abi, + header, decl }); @@ -285,6 +283,13 @@ impl_stable_hash_for!(struct hir::TypeBinding { span }); +impl_stable_hash_for!(struct hir::FnHeader { + unsafety, + constness, + asyncness, + abi +}); + impl<'a> HashStable> for hir::Ty { fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, @@ -897,7 +902,7 @@ impl_stable_hash_for!(enum hir::Item_ { ItemUse(path, use_kind), ItemStatic(ty, mutability, body_id), ItemConst(ty, body_id), - ItemFn(fn_decl, unsafety, constness, abi, generics, body_id), + ItemFn(fn_decl, header, generics, body_id), ItemMod(module), ItemForeignMod(foreign_mod), ItemGlobalAsm(global_asm), @@ -1100,6 +1105,10 @@ impl_stable_hash_for!(enum hir::Unsafety { Normal }); +impl_stable_hash_for!(enum hir::IsAsync { + Async, + NotAsync +}); impl_stable_hash_for!(enum hir::Constness { Const, diff --git a/src/librustc/ich/impls_syntax.rs b/src/librustc/ich/impls_syntax.rs index 7b14831cf95c0..0f4603be39d32 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/middle/reachable.rs b/src/librustc/middle/reachable.rs index 7f81af7e46c7d..95829346ede8f 100644 --- a/src/librustc/middle/reachable.rs +++ b/src/librustc/middle/reachable.rs @@ -238,8 +238,8 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> { // If we are building an executable, only explicitly extern // types need to be exported. if let hir_map::NodeItem(item) = *node { - let reachable = if let hir::ItemFn(.., abi, _, _) = item.node { - abi != Abi::Rust + let reachable = if let hir::ItemFn(_, header, ..) = item.node { + header.abi != Abi::Rust } else { false }; diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index 025ee0f3d746c..efb3eecd691f2 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -476,7 +476,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { 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); }); diff --git a/src/librustc_allocator/expand.rs b/src/librustc_allocator/expand.rs index ec0676259ef20..78406e88c7572 100644 --- a/src/librustc_allocator/expand.rs +++ b/src/librustc_allocator/expand.rs @@ -10,12 +10,11 @@ use rustc::middle::allocator::AllocatorKind; use rustc_errors; -use rustc_target::spec::abi::Abi; use syntax::ast::{Attribute, Crate, LitKind, StrStyle}; -use syntax::ast::{Arg, Constness, Generics, Mac, Mutability, Ty, Unsafety}; +use syntax::ast::{Arg, FnHeader, Generics, Mac, Mutability, Ty, Unsafety}; use syntax::ast::{self, Expr, Ident, Item, ItemKind, TyKind, VisibilityKind}; use syntax::attr; -use syntax::codemap::{dummy_spanned, respan}; +use syntax::codemap::respan; use syntax::codemap::{ExpnInfo, MacroAttribute, NameAndSpan}; use syntax::ext::base::ExtCtxt; use syntax::ext::base::Resolver; @@ -152,9 +151,10 @@ impl<'a> AllocFnFactory<'a> { let (output_ty, output_expr) = self.ret_ty(&method.output, result); let kind = ItemKind::Fn( self.cx.fn_decl(abi_args, ast::FunctionRetTy::Ty(output_ty)), - Unsafety::Unsafe, - dummy_spanned(Constness::NotConst), - Abi::Rust, + FnHeader { + unsafety: Unsafety::Unsafe, + ..FnHeader::default() + }, Generics::default(), self.cx.block_expr(output_expr), ); diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs index 67720e61e91af..38ecbf5ca8ada 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -712,8 +712,8 @@ impl<'a> fold::Folder for ReplaceBodyWithLoop<'a> { fn fold_item_kind(&mut self, i: ast::ItemKind) -> ast::ItemKind { let is_const = match i { ast::ItemKind::Static(..) | ast::ItemKind::Const(..) => true, - ast::ItemKind::Fn(ref decl, _, ref constness, _, _, _) => - constness.node == ast::Constness::Const || Self::should_ignore_fn(decl), + ast::ItemKind::Fn(ref decl, ref header, _, _) => + header.constness.node == ast::Constness::Const || Self::should_ignore_fn(decl), _ => false, }; self.run(is_const, |s| fold::noop_fold_item_kind(i, s)) @@ -722,8 +722,8 @@ impl<'a> fold::Folder for ReplaceBodyWithLoop<'a> { fn fold_trait_item(&mut self, i: ast::TraitItem) -> SmallVector { let is_const = match i.node { ast::TraitItemKind::Const(..) => true, - ast::TraitItemKind::Method(ast::MethodSig { ref decl, ref constness, .. }, _) => - constness.node == ast::Constness::Const || Self::should_ignore_fn(decl), + ast::TraitItemKind::Method(ast::MethodSig { ref decl, ref header, .. }, _) => + header.constness.node == ast::Constness::Const || Self::should_ignore_fn(decl), _ => false, }; self.run(is_const, |s| fold::noop_fold_trait_item(i, s)) @@ -732,8 +732,8 @@ impl<'a> fold::Folder for ReplaceBodyWithLoop<'a> { fn fold_impl_item(&mut self, i: ast::ImplItem) -> SmallVector { let is_const = match i.node { ast::ImplItemKind::Const(..) => true, - ast::ImplItemKind::Method(ast::MethodSig { ref decl, ref constness, .. }, _) => - constness.node == ast::Constness::Const || Self::should_ignore_fn(decl), + ast::ImplItemKind::Method(ast::MethodSig { ref decl, ref header, .. }, _) => + header.constness.node == ast::Constness::Const || Self::should_ignore_fn(decl), _ => false, }; self.run(is_const, |s| fold::noop_fold_impl_item(i, s)) diff --git a/src/librustc_lint/bad_style.rs b/src/librustc_lint/bad_style.rs index 8f8fe04fd8e01..1b5361187f36a 100644 --- a/src/librustc_lint/bad_style.rs +++ b/src/librustc_lint/bad_style.rs @@ -284,9 +284,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonSnakeCase { _ => (), } } - FnKind::ItemFn(name, _, _, _, abi, _, attrs) => { + FnKind::ItemFn(name, _, header, _, attrs) => { // Skip foreign-ABI #[no_mangle] functions (Issue #31924) - if abi != Abi::Rust && attr::find_by_name(attrs, "no_mangle").is_some() { + if header.abi != Abi::Rust && attr::find_by_name(attrs, "no_mangle").is_some() { return; } self.check_snake_case(cx, "function", &name.as_str(), Some(span)) diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 941fabe26a6ed..d6120ab207924 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -259,12 +259,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnsafeCode { span: Span, _: ast::NodeId) { match fk { - FnKind::ItemFn(_, _, hir::Unsafety::Unsafe, ..) => { + FnKind::ItemFn(_, _, hir::FnHeader { unsafety: hir::Unsafety::Unsafe, .. }, ..) => { self.report_unsafe(cx, span, "declaration of an `unsafe` function") } FnKind::Method(_, sig, ..) => { - if sig.unsafety == hir::Unsafety::Unsafe { + if sig.header.unsafety == hir::Unsafety::Unsafe { self.report_unsafe(cx, span, "implementation of an `unsafe` method") } } @@ -275,7 +275,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnsafeCode { fn check_trait_item(&mut self, cx: &LateContext, item: &hir::TraitItem) { if let hir::TraitItemKind::Method(ref sig, hir::TraitMethod::Required(_)) = item.node { - if sig.unsafety == hir::Unsafety::Unsafe { + if sig.header.unsafety == hir::Unsafety::Unsafe { self.report_unsafe(cx, item.span, "declaration of an `unsafe` method") } } diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index cbe9615c69397..ce270006a9d0a 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -917,7 +917,7 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> { ty::AssociatedKind::Method => { let fn_data = if let hir::ImplItemKind::Method(ref sig, body) = ast_item.node { FnData { - constness: sig.constness, + constness: sig.header.constness, arg_names: self.encode_fn_arg_names_for_body(body), sig: self.lazy(&tcx.fn_sig(def_id)), } @@ -941,7 +941,7 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> { let needs_inline = (generics.requires_monomorphization(self.tcx) || tcx.codegen_fn_attrs(def_id).requests_inline()) && !self.metadata_output_only(); - let is_const_fn = sig.constness == hir::Constness::Const; + let is_const_fn = sig.header.constness == hir::Constness::Const; let always_encode_mir = self.tcx.sess.opts.debugging_opts.always_encode_mir; needs_inline || is_const_fn || always_encode_mir }, @@ -1045,9 +1045,9 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> { self.encode_rendered_const_for_body(body_id) ) } - hir::ItemFn(_, _, constness, .., body) => { + hir::ItemFn(_, header, .., body) => { let data = FnData { - constness, + constness: header.constness, arg_names: self.encode_fn_arg_names_for_body(body), sig: self.lazy(&tcx.fn_sig(def_id)), }; @@ -1235,7 +1235,7 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> { self.encode_optimized_mir(def_id) } hir::ItemConst(..) => self.encode_optimized_mir(def_id), - hir::ItemFn(_, _, constness, ..) => { + hir::ItemFn(_, header, ..) => { let generics = tcx.generics_of(def_id); let has_types = generics.params.iter().any(|param| match param.kind { ty::GenericParamDefKind::Type { .. } => true, @@ -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 || 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_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs index 5f8f9acae8385..00e064fbb4717 100644 --- a/src/librustc_mir/transform/check_unsafety.rs +++ b/src/librustc_mir/transform/check_unsafety.rs @@ -380,10 +380,10 @@ fn is_enclosed(tcx: TyCtxt, if used_unsafe.contains(&parent_id) { Some(("block".to_string(), parent_id)) } else if let Some(hir::map::NodeItem(&hir::Item { - node: hir::ItemFn(_, fn_unsafety, _, _, _, _), + node: hir::ItemFn(_, header, _, _), .. })) = tcx.hir.find(parent_id) { - match fn_unsafety { + match header.unsafety { hir::Unsafety::Unsafe => Some(("fn".to_string(), parent_id)), hir::Unsafety::Normal => None, } diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index fc54d323b0f96..25187032fb473 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -87,6 +87,13 @@ impl<'a> AstValidator<'a> { } } + fn check_trait_fn_not_async(&self, span: Span, asyncness: IsAsync) { + if asyncness.is_async() { + struct_span_err!(self.session, span, E0706, + "trait fns cannot be declared `async`").emit() + } + } + fn check_trait_fn_not_const(&self, constness: Spanned) { match constness.node { Constness::Const => { @@ -257,7 +264,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> { for impl_item in impl_items { self.invalid_visibility(&impl_item.vis, None); if let ImplItemKind::Method(ref sig, _) = impl_item.node { - self.check_trait_fn_not_const(sig.constness); + self.check_trait_fn_not_const(sig.header.constness); + self.check_trait_fn_not_async(impl_item.span, sig.header.asyncness); } } } @@ -309,7 +317,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.no_questions_in_bounds(bounds, "supertraits", true); for trait_item in trait_items { if let TraitItemKind::Method(ref sig, ref block) = trait_item.node { - self.check_trait_fn_not_const(sig.constness); + self.check_trait_fn_not_async(trait_item.span, sig.header.asyncness); + self.check_trait_fn_not_const(sig.header.constness); if block.is_none() { self.check_decl_no_pat(&sig.decl, |span, mut_ident| { if mut_ident { diff --git a/src/librustc_passes/diagnostics.rs b/src/librustc_passes/diagnostics.rs index d031694d85308..f1ec3371c3b9a 100644 --- a/src/librustc_passes/diagnostics.rs +++ b/src/librustc_passes/diagnostics.rs @@ -310,4 +310,5 @@ register_diagnostics! { E0666, // nested `impl Trait` is illegal E0667, // `impl Trait` in projections E0696, // `continue` pointing to a labeled block + E0706, // `async fn` in trait } diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index e311701ac0589..792edf4d12b1d 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}; use syntax::ast::{FnDecl, ForeignItem, ForeignItemKind, GenericParamKind, Generics}; use syntax::ast::{Item, ItemKind, ImplItem, ImplItemKind}; use syntax::ast::{Label, Local, Mutability, Pat, PatKind, Path}; @@ -746,15 +746,17 @@ impl<'a, 'tcx> Visitor<'tcx> for Resolver<'a> { function_kind: FnKind<'tcx>, declaration: &'tcx FnDecl, _: Span, - node_id: NodeId) { - let rib_kind = match function_kind { - FnKind::ItemFn(..) => { - ItemRibKind - } - FnKind::Method(_, _, _, _) => { - TraitOrImplItemRibKind - } - FnKind::Closure(_) => ClosureRibKind(node_id), + node_id: NodeId) + { + let (rib_kind, asyncness) = match function_kind { + FnKind::ItemFn(_, ref header, ..) => + (ItemRibKind, header.asyncness), + FnKind::Method(_, ref sig, _, _) => + (TraitOrImplItemRibKind, sig.header.asyncness), + FnKind::Closure(_) => + // Async closures aren't resolved through `visit_fn`-- they're + // processed separately + (ClosureRibKind(node_id), IsAsync::NotAsync), }; // Create a value rib for the function. @@ -774,7 +776,13 @@ impl<'a, 'tcx> Visitor<'tcx> for Resolver<'a> { } visit::walk_fn_ret_ty(self, &declaration.output); - // Resolve the function body. + // Resolve the function body, potentially inside the body of an async closure + if let IsAsync::Async(async_closure_id) = asyncness { + let rib_kind = ClosureRibKind(async_closure_id); + self.ribs[ValueNS].push(Rib::new(rib_kind)); + self.label_ribs.push(Rib::new(rib_kind)); + } + match function_kind { FnKind::ItemFn(.., body) | FnKind::Method(.., body) => { @@ -785,6 +793,12 @@ impl<'a, 'tcx> Visitor<'tcx> for Resolver<'a> { } }; + // Leave the body of the async closure + if asyncness.is_async() { + self.label_ribs.pop(); + self.ribs[ValueNS].pop(); + } + debug!("(resolving function) leaving function"); self.label_ribs.pop(); @@ -1475,14 +1489,34 @@ impl<'a> hir::lowering::Resolver for Resolver<'a> { |resolver, span, error| resolve_error(resolver, span, error)) } - fn resolve_str_path(&mut self, span: Span, crate_root: Option<&str>, - components: &[&str], is_value: bool) -> hir::Path { + fn resolve_str_path( + &mut self, + span: Span, + crate_root: Option<&str>, + components: &[&str], + args: Option>, + is_value: bool + ) -> hir::Path { + let mut segments = iter::once(keywords::CrateRoot.name()) + .chain( + crate_root.into_iter() + .chain(components.iter().cloned()) + .map(Symbol::intern) + ).map(hir::PathSegment::from_name).collect::>(); + + if let Some(args) = args { + let name = segments.last().unwrap().name; + *segments.last_mut().unwrap() = hir::PathSegment { + name, + args: Some(args), + infer_types: true, + }; + } + let mut path = hir::Path { span, def: Def::Err, - segments: iter::once(keywords::CrateRoot.name()).chain({ - crate_root.into_iter().chain(components.iter().cloned()).map(Symbol::intern) - }).map(hir::PathSegment::from_name).collect(), + segments: segments.into(), }; self.resolve_hir_path(&mut path, is_value); @@ -2058,9 +2092,9 @@ impl<'a> Resolver<'a> { 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) => @@ -2374,7 +2408,7 @@ impl<'a> Resolver<'a> { visit::walk_impl_item(this, impl_item) ); } - ImplItemKind::Method(_, _) => { + ImplItemKind::Method(..) => { // If this is a trait impl, ensure the method // exists in trait this.check_trait_item(impl_item.ident, @@ -3888,6 +3922,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 7da5b1668b3d8..262c0e40abc54 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 7f2f0b0c83716..9f2ca20276cdc 100644 --- a/src/librustc_save_analysis/sig.rs +++ b/src/librustc_save_analysis/sig.rs @@ -380,17 +380,20 @@ impl Sig for ast::Item { Ok(extend_sig(ty, text, defs, vec![])) } - ast::ItemKind::Fn(ref decl, unsafety, constness, abi, ref generics, _) => { + ast::ItemKind::Fn(ref decl, header, ref generics, _) => { let mut text = String::new(); - if constness.node == ast::Constness::Const { + if header.constness.node == ast::Constness::Const { text.push_str("const "); } - if unsafety == ast::Unsafety::Unsafe { + if header.asyncness.is_async() { + text.push_str("async "); + } + if header.unsafety == ast::Unsafety::Unsafe { text.push_str("unsafe "); } - if abi != ::rustc_target::spec::abi::Abi::Rust { + if header.abi != ::rustc_target::spec::abi::Abi::Rust { text.push_str("extern"); - text.push_str(&abi.to_string()); + text.push_str(&header.abi.to_string()); text.push(' '); } text.push_str("fn "); @@ -914,15 +917,18 @@ fn make_method_signature( ) -> Result { // FIXME code dup with function signature let mut text = String::new(); - if m.constness.node == ast::Constness::Const { + if m.header.constness.node == ast::Constness::Const { text.push_str("const "); } - if m.unsafety == ast::Unsafety::Unsafe { + if m.header.asyncness.is_async() { + text.push_str("async "); + } + if m.header.unsafety == ast::Unsafety::Unsafe { text.push_str("unsafe "); } - if m.abi != ::rustc_target::spec::abi::Abi::Rust { + if m.header.abi != ::rustc_target::spec::abi::Abi::Rust { text.push_str("extern"); - text.push_str(&m.abi.to_string()); + text.push_str(&m.header.abi.to_string()); text.push(' '); } text.push_str("fn "); diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 366420cfcabb1..f149a9fe57140 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/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 4c849cad187ab..824346dab94fc 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -1181,13 +1181,13 @@ fn fn_sig<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let icx = ItemCtxt::new(tcx, def_id); match tcx.hir.get(node_id) { - NodeTraitItem(&hir::TraitItem { node: TraitItemKind::Method(ref sig, _), .. }) | - NodeImplItem(&hir::ImplItem { node: ImplItemKind::Method(ref sig, _), .. }) => { - AstConv::ty_of_fn(&icx, sig.unsafety, sig.abi, &sig.decl) + NodeTraitItem(hir::TraitItem { node: TraitItemKind::Method(sig, _), .. }) | + NodeImplItem(hir::ImplItem { node: ImplItemKind::Method(sig, _), .. }) => { + AstConv::ty_of_fn(&icx, sig.header.unsafety, sig.header.abi, &sig.decl) } - NodeItem(&hir::Item { node: ItemFn(ref decl, unsafety, _, abi, _, _), .. }) => { - AstConv::ty_of_fn(&icx, unsafety, abi, decl) + NodeItem(hir::Item { node: ItemFn(decl, header, _, _), .. }) => { + AstConv::ty_of_fn(&icx, header.unsafety, header.abi, decl) } NodeForeignItem(&hir::ForeignItem { node: ForeignItemFn(ref fn_decl, _, _), .. }) => { diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 114cb0e455d9a..ad7389db729ec 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -198,9 +198,12 @@ fn build_external_function(cx: &DocContext, did: DefId) -> clean::Function { clean::Function { decl: (did, sig).clean(cx), generics: (cx.tcx.generics_of(did), &predicates).clean(cx), - unsafety: sig.unsafety(), - constness, - abi: sig.abi(), + header: hir::FnHeader { + unsafety: sig.unsafety(), + abi: sig.abi(), + constness, + asyncness: hir::IsAsync::NotAsync, + } } } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 0979c3d85587d..057e7f3ab8433 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -18,8 +18,8 @@ pub use self::SelfTy::*; pub use self::FunctionRetTy::*; pub use self::Visibility::{Public, Inherited}; -use syntax; use rustc_target::spec::abi::Abi; +use syntax; use syntax::ast::{self, AttrStyle, NodeId, Ident}; use syntax::attr; use syntax::codemap::{dummy_spanned, Spanned}; @@ -2074,10 +2074,8 @@ impl<'a, 'tcx> Clean for (&'a ty::Generics, #[derive(Clone, RustcEncodable, RustcDecodable, Debug)] pub struct Method { pub generics: Generics, - pub unsafety: hir::Unsafety, - pub constness: hir::Constness, pub decl: FnDecl, - pub abi: Abi, + pub header: hir::FnHeader, } impl<'a> Clean for (&'a hir::MethodSig, &'a hir::Generics, hir::BodyId) { @@ -2088,28 +2086,23 @@ impl<'a> Clean for (&'a hir::MethodSig, &'a hir::Generics, hir::BodyId) Method { decl, generics, - unsafety: self.0.unsafety, - constness: self.0.constness, - abi: self.0.abi + header: self.0.header, } } } #[derive(Clone, RustcEncodable, RustcDecodable, Debug)] pub struct TyMethod { - pub unsafety: hir::Unsafety, + pub header: hir::FnHeader, pub decl: FnDecl, pub generics: Generics, - pub abi: Abi, } #[derive(Clone, RustcEncodable, RustcDecodable, Debug)] pub struct Function { pub decl: FnDecl, pub generics: Generics, - pub unsafety: hir::Unsafety, - pub constness: hir::Constness, - pub abi: Abi, + pub header: hir::FnHeader, } impl Clean for doctree::Function { @@ -2128,9 +2121,7 @@ impl Clean for doctree::Function { inner: FunctionItem(Function { decl, generics, - unsafety: self.unsafety, - constness: self.constness, - abi: self.abi, + header: self.header, }), } } @@ -2359,10 +2350,9 @@ impl Clean for hir::TraitItem { (self.generics.clean(cx), (&*sig.decl, &names[..]).clean(cx)) }); TyMethodItem(TyMethod { - unsafety: sig.unsafety.clone(), + header: sig.header, decl, generics, - abi: sig.abi }) } hir::TraitItemKind::Type(ref bounds, ref default) => { @@ -2461,18 +2451,25 @@ impl<'tcx> Clean for ty::AssociatedItem { hir::Constness::NotConst }; MethodItem(Method { - unsafety: sig.unsafety(), generics, decl, - abi: sig.abi(), - constness, + header: hir::FnHeader { + unsafety: sig.unsafety(), + abi: sig.abi(), + constness, + asyncness: hir::IsAsync::NotAsync, + } }) } else { TyMethodItem(TyMethod { - unsafety: sig.unsafety(), generics, decl, - abi: sig.abi(), + header: hir::FnHeader { + unsafety: sig.unsafety(), + abi: sig.abi(), + constness: hir::Constness::NotConst, + asyncness: hir::IsAsync::NotAsync, + } }) } } @@ -3697,9 +3694,9 @@ impl Clean for hir::BareFnTy { }); BareFunctionDecl { unsafety: self.unsafety, + abi: self.abi, decl, generic_params, - abi: self.abi, } } } @@ -3994,7 +3991,7 @@ impl Clean> for hir::ForeignMod { let mut items = self.items.clean(cx); for item in &mut items { if let ForeignFunctionItem(ref mut f) = item.inner { - f.abi = self.abi; + f.header.abi = self.abi; } } items @@ -4011,9 +4008,12 @@ impl Clean for hir::ForeignItem { ForeignFunctionItem(Function { decl, generics, - unsafety: hir::Unsafety::Unsafe, - abi: Abi::Rust, - constness: hir::Constness::NotConst, + header: hir::FnHeader { + unsafety: hir::Unsafety::Unsafe, + abi: Abi::Rust, + constness: hir::Constness::NotConst, + asyncness: hir::IsAsync::NotAsync, + }, }) } hir::ForeignItemStatic(ref ty, mutbl) => { diff --git a/src/librustdoc/doctree.rs b/src/librustdoc/doctree.rs index 16d14bc56d695..0807db2997626 100644 --- a/src/librustdoc/doctree.rs +++ b/src/librustdoc/doctree.rs @@ -13,7 +13,6 @@ pub use self::StructType::*; pub use self::TypeBound::*; -use rustc_target::spec::abi; use syntax::ast; use syntax::ast::{Name, NodeId}; use syntax::attr; @@ -149,11 +148,9 @@ pub struct Function { pub vis: hir::Visibility, pub stab: Option, pub depr: Option, - pub unsafety: hir::Unsafety, - pub constness: hir::Constness, + pub header: hir::FnHeader, pub whence: Span, pub generics: hir::Generics, - pub abi: abi::Abi, pub body: hir::BodyId, } diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 3d360f2f344b6..2377354b85f6b 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -39,6 +39,10 @@ pub struct UnsafetySpace(pub hir::Unsafety); /// with a space after it. #[derive(Copy, Clone)] pub struct ConstnessSpace(pub hir::Constness); +/// Similarly to VisSpace, this structure is used to render a function asyncness +/// with a space after it. +#[derive(Copy, Clone)] +pub struct AsyncSpace(pub hir::IsAsync); /// Similar to VisSpace, but used for mutability #[derive(Copy, Clone)] pub struct MutableSpace(pub clean::Mutability); @@ -962,6 +966,15 @@ impl fmt::Display for ConstnessSpace { } } +impl fmt::Display for AsyncSpace { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.0 { + hir::IsAsync::Async => write!(f, "async "), + hir::IsAsync::NotAsync => Ok(()), + } + } +} + impl fmt::Display for clean::Import { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 180591b353260..f7be4f58dfb57 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -62,13 +62,12 @@ use rustc::middle::stability; use rustc::hir; use rustc::util::nodemap::{FxHashMap, FxHashSet}; use rustc_data_structures::flock; -use rustc_target::spec::abi; use clean::{self, AttributesExt, GetDefId, SelfTy, Mutability}; use doctree; use fold::DocFolder; use html::escape::Escape; -use html::format::{ConstnessSpace}; +use html::format::{AsyncSpace, ConstnessSpace}; use html::format::{GenericBounds, WhereClause, href, AbiSpace}; use html::format::{VisSpace, Method, UnsafetySpace, MutableSpace}; use html::format::fmt_impl_for_trait_page; @@ -2405,7 +2404,7 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context, let unsafety_flag = match myitem.inner { clean::FunctionItem(ref func) | clean::ForeignFunctionItem(ref func) - if func.unsafety == hir::Unsafety::Unsafe => { + if func.header.unsafety == hir::Unsafety::Unsafe => { "âš " } _ => "", @@ -2575,21 +2574,24 @@ fn item_static(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, fn item_function(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, f: &clean::Function) -> fmt::Result { - let name_len = format!("{}{}{}{:#}fn {}{:#}", + let name_len = format!("{}{}{}{}{:#}fn {}{:#}", VisSpace(&it.visibility), - ConstnessSpace(f.constness), - UnsafetySpace(f.unsafety), - AbiSpace(f.abi), + ConstnessSpace(f.header.constness), + UnsafetySpace(f.header.unsafety), + AsyncSpace(f.header.asyncness), + AbiSpace(f.header.abi), it.name.as_ref().unwrap(), f.generics).len(); write!(w, "{}
", render_spotlight_traits(it)?)?;
     render_attributes(w, it)?;
     write!(w,
-           "{vis}{constness}{unsafety}{abi}fn {name}{generics}{decl}{where_clause}
", + "{vis}{constness}{unsafety}{asyncness}{abi}fn \ + {name}{generics}{decl}{where_clause}", vis = VisSpace(&it.visibility), - constness = ConstnessSpace(f.constness), - unsafety = UnsafetySpace(f.unsafety), - abi = AbiSpace(f.abi), + constness = ConstnessSpace(f.header.constness), + unsafety = UnsafetySpace(f.header.unsafety), + asyncness = AsyncSpace(f.header.asyncness), + abi = AbiSpace(f.header.abi), name = it.name.as_ref().unwrap(), generics = f.generics, where_clause = WhereClause { gens: &f.generics, indent: 0, end_newline: true }, @@ -2999,9 +3001,7 @@ fn render_assoc_item(w: &mut fmt::Formatter, parent: ItemType) -> fmt::Result { fn method(w: &mut fmt::Formatter, meth: &clean::Item, - unsafety: hir::Unsafety, - constness: hir::Constness, - abi: abi::Abi, + header: hir::FnHeader, g: &clean::Generics, d: &clean::FnDecl, link: AssocItemLink, @@ -3024,11 +3024,12 @@ fn render_assoc_item(w: &mut fmt::Formatter, href(did).map(|p| format!("{}#{}.{}", p.0, ty, name)).unwrap_or(anchor) } }; - let mut head_len = format!("{}{}{}{:#}fn {}{:#}", + let mut head_len = format!("{}{}{}{}{:#}fn {}{:#}", VisSpace(&meth.visibility), - ConstnessSpace(constness), - UnsafetySpace(unsafety), - AbiSpace(abi), + ConstnessSpace(header.constness), + UnsafetySpace(header.unsafety), + AsyncSpace(header.asyncness), + AbiSpace(header.abi), name, *g).len(); let (indent, end_newline) = if parent == ItemType::Trait { @@ -3038,12 +3039,13 @@ fn render_assoc_item(w: &mut fmt::Formatter, (0, true) }; render_attributes(w, meth)?; - write!(w, "{}{}{}{}fn {name}\ + write!(w, "{}{}{}{}{}fn {name}\ {generics}{decl}{where_clause}", VisSpace(&meth.visibility), - ConstnessSpace(constness), - UnsafetySpace(unsafety), - AbiSpace(abi), + ConstnessSpace(header.constness), + UnsafetySpace(header.unsafety), + AsyncSpace(header.asyncness), + AbiSpace(header.abi), href = href, name = name, generics = *g, @@ -3061,12 +3063,10 @@ fn render_assoc_item(w: &mut fmt::Formatter, match item.inner { clean::StrippedItem(..) => Ok(()), clean::TyMethodItem(ref m) => { - method(w, item, m.unsafety, hir::Constness::NotConst, - m.abi, &m.generics, &m.decl, link, parent) + method(w, item, m.header, &m.generics, &m.decl, link, parent) } clean::MethodItem(ref m) => { - method(w, item, m.unsafety, m.constness, - m.abi, &m.generics, &m.decl, link, parent) + method(w, item, m.header, &m.generics, &m.decl, link, parent) } clean::AssociatedConstItem(ref ty, ref default) => { assoc_const(w, item, ty, default.as_ref(), link) diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 10b1a2e1e0b67..6bf1931e468de 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -13,7 +13,6 @@ use std::mem; -use rustc_target::spec::abi; use syntax::ast; use syntax::attr; use syntax_pos::Span; @@ -172,9 +171,7 @@ impl<'a, 'tcx, 'rcx> RustdocVisitor<'a, 'tcx, 'rcx> { pub fn visit_fn(&mut self, item: &hir::Item, name: ast::Name, fd: &hir::FnDecl, - unsafety: &hir::Unsafety, - constness: hir::Constness, - abi: &abi::Abi, + header: hir::FnHeader, gen: &hir::Generics, body: hir::BodyId) -> Function { debug!("Visiting fn"); @@ -188,9 +185,7 @@ impl<'a, 'tcx, 'rcx> RustdocVisitor<'a, 'tcx, 'rcx> { name, whence: item.span, generics: gen.clone(), - unsafety: *unsafety, - constness, - abi: *abi, + header, body, } } @@ -463,9 +458,8 @@ impl<'a, 'tcx, 'rcx> RustdocVisitor<'a, 'tcx, 'rcx> { om.structs.push(self.visit_variant_data(item, name, sd, gen)), hir::ItemUnion(ref sd, ref gen) => om.unions.push(self.visit_union_data(item, name, sd, gen)), - hir::ItemFn(ref fd, ref unsafety, constness, ref abi, ref gen, body) => - om.fns.push(self.visit_fn(item, name, &**fd, unsafety, - constness, abi, gen, body)), + hir::ItemFn(ref fd, header, ref gen, body) => + om.fns.push(self.visit_fn(item, name, &**fd, header, gen, body)), hir::ItemTy(ref ty, ref gen) => { let t = Typedef { ty: ty.clone(), diff --git a/src/libstd/future.rs b/src/libstd/future.rs new file mode 100644 index 0000000000000..2da775fdc949e --- /dev/null +++ b/src/libstd/future.rs @@ -0,0 +1,116 @@ +// Copyright 2018 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. + +//! Asynchronous values. + +use core::cell::Cell; +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}; + +#[doc(inline)] +pub use core::future::*; + +/// 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 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 { + set_task_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")] +/// Sets the thread-local task context used by async/await futures. +pub fn set_task_cx(cx: &mut task::Context, f: F) -> R +where + F: FnOnce() -> R +{ + let old_cx = TLS_CX.with(|tls_cx| { + tls_cx.replace(NonNull::new( + cx + as *mut task::Context + as *mut () + as *mut task::Context<'static> + )) + }); + let _reset_cx = SetOnDrop(old_cx); + f() +} + +#[unstable(feature = "gen_future", issue = "50547")] +/// Retrieves the thread-local task context used by async/await futures. +/// +/// This function acquires exclusive access to the task context. +/// +/// Panics if no task has been set or if the task context has already been +/// retrived by a surrounding call to get_task_cx. +pub fn get_task_cx(f: F) -> R +where + F: FnOnce(&mut task::Context) -> R +{ + let cx_ptr = TLS_CX.with(|tls_cx| { + // Clear the entry so that nested `with_get_cx` calls + // will fail or set their own value. + tls_cx.replace(None) + }); + 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()) } +} + +#[unstable(feature = "gen_future", issue = "50547")] +/// Polls a future in the current thread-local task context. +pub fn poll_in_task_cx(f: &mut PinMut) -> Poll +where + F: Future +{ + get_task_cx(|cx| f.reborrow().poll(cx)) +} diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index a6061e96ae587..caad924ea5b5f 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)] @@ -462,22 +463,6 @@ pub use core::u128; #[stable(feature = "core_hint", since = "1.27.0")] pub use core::hint; -#[unstable(feature = "futures_api", - reason = "futures in libcore are unstable", - issue = "50547")] -pub mod task { - //! Types and Traits for working with asynchronous tasks. - #[doc(inline)] - pub use core::task::*; - #[doc(inline)] - pub use alloc_crate::task::*; -} - -#[unstable(feature = "futures_api", - reason = "futures in libcore are unstable", - issue = "50547")] -pub use core::future; - pub mod f32; pub mod f64; @@ -499,6 +484,22 @@ pub mod process; pub mod sync; pub mod time; +#[unstable(feature = "futures_api", + reason = "futures in libcore are unstable", + issue = "50547")] +pub mod task { + //! Types and Traits for working with asynchronous tasks. + #[doc(inline)] + pub use core::task::*; + #[doc(inline)] + pub use alloc_crate::task::*; +} + +#[unstable(feature = "futures_api", + reason = "futures in libcore are unstable", + issue = "50547")] +pub mod future; + // Platform-abstraction modules #[macro_use] mod sys_common; diff --git a/src/libstd/macros.rs b/src/libstd/macros.rs index 8da70f5717e71..a856e7736fbaa 100644 --- a/src/libstd/macros.rs +++ b/src/libstd/macros.rs @@ -213,6 +213,25 @@ macro_rules! eprintln { ($fmt:expr, $($arg:tt)*) => (eprint!(concat!($fmt, "\n"), $($arg)*)); } +#[macro_export] +#[unstable(feature = "await_macro", issue = "50547")] +#[allow_internal_unstable] +#[allow_internal_unsafe] +macro_rules! await { + ($e:expr) => { { + let mut pinned = $e; + let mut pinned = unsafe { $crate::mem::PinMut::new_unchecked(&mut pinned) }; + loop { + match $crate::future::poll_in_task_cx(&mut pinned) { + // FIXME(cramertj) prior to stabilizing await, we have to ensure that this + // can't be used to create a generator on stable via `|| await!()`. + $crate::task::Poll::Pending => yield, + $crate::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/libsyntax/ast.rs b/src/libsyntax/ast.rs index c6de2c4da39cf..a57a9b95e5f77 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, P, Span), + Closure(CaptureBy, IsAsync, Movability, P, P, Span), /// A block (`'label: { ... }`) Block(P, Option