From d64e577fa3220a7a26dbfd54d503240f5856cc2a Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Mon, 18 Jun 2018 21:18:10 -0700 Subject: [PATCH] Async methods --- src/librustc/hir/lowering.rs | 22 ++++-- src/librustc/hir/map/def_collector.rs | 52 +++++++++---- src/librustc_passes/ast_validation.rs | 8 ++ src/librustc_passes/diagnostics.rs | 1 + src/librustc_resolve/lib.rs | 77 ++++++------------- src/libsyntax/parse/parser.rs | 29 +++++-- src/test/run-pass/async-await.rs | 10 ++- .../ui/async-fn-multiple-lifetimes.stderr | 2 +- 8 files changed, 122 insertions(+), 79 deletions(-) diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 2deb0b53e6bb5..e694dc64ab3ce 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -2932,7 +2932,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), ) }, @@ -2950,7 +2950,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), ) }, @@ -3021,8 +3021,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; @@ -3036,6 +3046,7 @@ impl<'a> LoweringContext<'a> { sig, impl_item_def_id, impl_trait_return_allow, + sig.header.asyncness.is_async(), ), body_id, ) @@ -3201,10 +3212,11 @@ impl<'a> LoweringContext<'a> { sig: &MethodSig, fn_def_id: DefId, impl_trait_return_allow: bool, + is_async: bool, ) -> 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, false), + decl: self.lower_fn_decl(&sig.decl, Some(fn_def_id), impl_trait_return_allow, is_async), } } diff --git a/src/librustc/hir/map/def_collector.rs b/src/librustc/hir/map/def_collector.rs index 335e38bbe7e22..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 { @@ -100,19 +121,13 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> { 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); - }) - }); + 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(..) => @@ -212,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()), diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index 8111caed5c59a..7cdcdf4f0d109 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 => { @@ -309,6 +316,7 @@ 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_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| { 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 d3c1929159414..26e6e80ce1861 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -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(); @@ -2054,47 +2068,6 @@ 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) | @@ -2415,7 +2388,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, diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index c2abb79a93abd..e59efcd68575f 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1293,6 +1293,15 @@ impl<'a> Parser<'a> { }))) } + /// Parse asyncness: `async` or nothing + fn parse_asyncness(&mut self) -> IsAsync { + if self.eat_keyword(keywords::Async) { + IsAsync::Async(ast::DUMMY_NODE_ID) + } else { + IsAsync::NotAsync + } + } + /// Parse unsafety: `unsafe` or nothing. fn parse_unsafety(&mut self) -> Unsafety { if self.eat_keyword(keywords::Unsafe) { @@ -1342,7 +1351,7 @@ impl<'a> Parser<'a> { // trait item macro. (keywords::Invalid.ident(), ast::TraitItemKind::Macro(mac), ast::Generics::default()) } else { - let (constness, unsafety, abi) = self.parse_fn_front_matter()?; + let (constness, unsafety, asyncness, abi) = self.parse_fn_front_matter()?; let ident = self.parse_ident()?; let mut generics = self.parse_generics()?; @@ -1360,7 +1369,7 @@ impl<'a> Parser<'a> { unsafety, constness, abi, - asyncness: IsAsync::NotAsync, + asyncness, }, decl: d, }; @@ -5425,10 +5434,18 @@ impl<'a> Parser<'a> { /// - `const unsafe fn` /// - `extern fn` /// - etc - fn parse_fn_front_matter(&mut self) -> PResult<'a, (Spanned, Unsafety, Abi)> { + fn parse_fn_front_matter(&mut self) + -> PResult<'a, ( + Spanned, + Unsafety, + IsAsync, + Abi + )> + { let is_const_fn = self.eat_keyword(keywords::Const); let const_span = self.prev_span; let unsafety = self.parse_unsafety(); + let asyncness = self.parse_asyncness(); let (constness, unsafety, abi) = if is_const_fn { (respan(const_span, Constness::Const), unsafety, Abi::Rust) } else { @@ -5440,7 +5457,7 @@ impl<'a> Parser<'a> { (respan(self.prev_span, Constness::NotConst), unsafety, abi) }; self.expect_keyword(keywords::Fn)?; - Ok((constness, unsafety, abi)) + Ok((constness, unsafety, asyncness, abi)) } /// Parse an impl item. @@ -5575,14 +5592,14 @@ impl<'a> Parser<'a> { Ok((keywords::Invalid.ident(), vec![], ast::Generics::default(), ast::ImplItemKind::Macro(mac))) } else { - let (constness, unsafety, abi) = self.parse_fn_front_matter()?; + let (constness, unsafety, asyncness, abi) = self.parse_fn_front_matter()?; let ident = self.parse_ident()?; let mut generics = self.parse_generics()?; let decl = self.parse_fn_decl_with_self(|p| p.parse_arg())?; generics.where_clause = self.parse_where_clause()?; *at_end = true; let (inner_attrs, body) = self.parse_inner_attrs_and_block()?; - let header = ast::FnHeader { abi, unsafety, constness, asyncness: IsAsync::NotAsync }; + let header = ast::FnHeader { abi, unsafety, constness, asyncness }; Ok((ident, inner_attrs, generics, ast::ImplItemKind::Method( ast::MethodSig { header, decl }, body diff --git a/src/test/run-pass/async-await.rs b/src/test/run-pass/async-await.rs index bc8b8a152fb2b..817db4bb79ec3 100644 --- a/src/test/run-pass/async-await.rs +++ b/src/test/run-pass/async-await.rs @@ -104,10 +104,16 @@ unsafe async fn unsafe_async_fn(x: u8) -> u8 { x } -struct Foo { +struct Foo; + +trait Bar { + fn foo() {} +} + +impl Foo { async fn async_method(x: u8) -> u8 { unsafe { - await!(unsafe_async_fn()) + await!(unsafe_async_fn(x)) } } } diff --git a/src/test/ui/async-fn-multiple-lifetimes.stderr b/src/test/ui/async-fn-multiple-lifetimes.stderr index 227dd4483f2bf..085b9499a416b 100644 --- a/src/test/ui/async-fn-multiple-lifetimes.stderr +++ b/src/test/ui/async-fn-multiple-lifetimes.stderr @@ -6,7 +6,7 @@ LL | async fn multiple_named_lifetimes<'a, 'b>(_: &'a u8, _: &'b u8) {} | | | first lifetime here | - = help: `async fn` can only accept borrowed values identical lifetimes + = help: `async fn` can only accept borrowed values with identical lifetimes error[E0704]: multiple elided lifetimes used in arguments of `async fn` --> $DIR/async-fn-multiple-lifetimes.rs:26:39