From fe8760cb848d45f5c83b41e689878b893b74e45d Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Thu, 18 Apr 2019 12:55:23 -0700 Subject: [PATCH] Implement built-in await syntax Adds support for .await under the existing async_await feature gate. Moves macro-like await! syntax to the await_macro feature gate. Removes support for `await` as a non-keyword under the `async_await` feature. --- src/librustc/error_codes.rs | 2 + src/librustc/hir/lowering.rs | 306 ++++++++++++++++-- src/librustc/hir/mod.rs | 4 + src/librustc/ich/impls_syntax.rs | 1 + src/librustc_lint/builtin.rs | 43 +-- src/librustc_mir/hair/pattern/check_match.rs | 6 +- src/libsyntax/ast.rs | 13 + src/libsyntax/feature_gate.rs | 18 ++ src/libsyntax/mut_visit.rs | 1 + src/libsyntax/parse/parser.rs | 17 + src/libsyntax/parse/token.rs | 5 + src/libsyntax/print/pprust.rs | 12 + src/libsyntax/util/parser.rs | 3 + src/libsyntax/visit.rs | 1 + src/libsyntax_pos/hygiene.rs | 2 + src/libsyntax_pos/symbol.rs | 1 + src/test/run-pass/async-await.rs | 36 +-- src/test/run-pass/await-macro.rs | 199 ++++++++++++ src/test/run-pass/issue-55809.rs | 10 +- ...015-edition-error-in-non-macro-position.rs | 36 +++ ...edition-error-in-non-macro-position.stderr | 88 +++++ ...5-edition-no-warnings-with-feature-gate.rs | 16 - ...018-edition-error-in-non-macro-position.rs | 27 ++ ...edition-error-in-non-macro-position.stderr | 80 +++++ .../ui/await-keyword/2018-edition-error.rs | 11 +- .../await-keyword/2018-edition-error.stderr | 46 ++- ...2018-edition-no-error-with-feature-gate.rs | 16 - .../ui/await-keyword/post_expansion_error.rs | 2 +- .../await-keyword/post_expansion_error.stderr | 6 +- src/test/ui/feature-gate/await-macro.rs | 12 + src/test/ui/feature-gate/await-macro.stderr | 12 + .../ui/generator/unresolved_type_param.rs | 11 +- .../ui/generator/unresolved_type_param.stderr | 13 +- src/test/ui/issues/issue-51719.rs | 11 + src/test/ui/issues/issue-51719.stderr | 8 + src/test/ui/issues/issue-51751.rs | 13 + src/test/ui/issues/issue-51751.stderr | 8 + 37 files changed, 931 insertions(+), 165 deletions(-) create mode 100644 src/test/run-pass/await-macro.rs create mode 100644 src/test/ui/await-keyword/2015-edition-error-in-non-macro-position.rs create mode 100644 src/test/ui/await-keyword/2015-edition-error-in-non-macro-position.stderr delete mode 100644 src/test/ui/await-keyword/2015-edition-no-warnings-with-feature-gate.rs create mode 100644 src/test/ui/await-keyword/2018-edition-error-in-non-macro-position.rs create mode 100644 src/test/ui/await-keyword/2018-edition-error-in-non-macro-position.stderr delete mode 100644 src/test/ui/await-keyword/2018-edition-no-error-with-feature-gate.rs create mode 100644 src/test/ui/feature-gate/await-macro.rs create mode 100644 src/test/ui/feature-gate/await-macro.stderr create mode 100644 src/test/ui/issues/issue-51719.rs create mode 100644 src/test/ui/issues/issue-51719.stderr create mode 100644 src/test/ui/issues/issue-51751.rs create mode 100644 src/test/ui/issues/issue-51751.stderr diff --git a/src/librustc/error_codes.rs b/src/librustc/error_codes.rs index fd089fc688e32..a1bfd417566ad 100644 --- a/src/librustc/error_codes.rs +++ b/src/librustc/error_codes.rs @@ -2205,4 +2205,6 @@ register_diagnostics! { E0711, // a feature has been declared with conflicting stability attributes // E0702, // replaced with a generic attribute input check E0726, // non-explicit (not `'_`) elided lifetime in unsupported position + E0727, // `async` generators are not yet supported + E0728, // `await` must be in an `async` function or block } diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 36e4195c989c0..20e016b8b5b1e 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -95,6 +95,7 @@ pub struct LoweringContext<'a> { modules: BTreeMap, is_generator: bool, + is_async_body: bool, catch_scopes: Vec, loop_scopes: Vec, @@ -248,6 +249,7 @@ pub fn lower_crate( item_local_id_counters: Default::default(), node_id_to_hir_id: IndexVec::new(), is_generator: false, + is_async_body: false, is_in_trait_impl: false, lifetimes_to_define: Vec::new(), is_collecting_in_band_lifetimes: false, @@ -801,8 +803,17 @@ impl<'a> LoweringContext<'a> { } fn record_body(&mut self, value: hir::Expr, arguments: HirVec) -> hir::BodyId { + if self.is_generator && self.is_async_body { + span_err!( + self.sess, + value.span, + E0727, + "`async` generators are not yet supported", + ); + self.sess.abort_if_errors(); + } let body = hir::Body { - is_generator: self.is_generator, + is_generator: self.is_generator || self.is_async_body, arguments, value, }; @@ -1124,7 +1135,8 @@ impl<'a> LoweringContext<'a> { span: Span, body: impl FnOnce(&mut LoweringContext<'_>) -> hir::Expr, ) -> hir::ExprKind { - let prev_is_generator = mem::replace(&mut self.is_generator, true); + let prev_is_generator = mem::replace(&mut self.is_generator, false); + let prev_is_async_body = mem::replace(&mut self.is_async_body, true); let output = match ret_ty { Some(ty) => FunctionRetTy::Ty(P(ty.clone())), None => FunctionRetTy::Default(span), @@ -1140,6 +1152,7 @@ impl<'a> LoweringContext<'a> { let body_expr = body(self); let body_id = self.record_body(body_expr, arguments); self.is_generator = prev_is_generator; + self.is_async_body = prev_is_async_body; let capture_clause = self.lower_capture_clause(capture_clause); let decl = self.lower_fn_decl(&decl, None, /* impl trait allowed */ false, None); @@ -1167,11 +1180,13 @@ impl<'a> LoweringContext<'a> { where F: FnOnce(&mut LoweringContext<'_>) -> hir::Expr, { - let prev = mem::replace(&mut self.is_generator, false); + let prev_generator = mem::replace(&mut self.is_generator, false); + let prev_async = mem::replace(&mut self.is_async_body, false); let arguments = self.lower_args(decl); let result = f(self); let r = self.record_body(result, arguments); - self.is_generator = prev; + self.is_generator = prev_generator; + self.is_async_body = prev_async; return r; } @@ -4205,6 +4220,7 @@ impl<'a> LoweringContext<'a> { }) }) } + ExprKind::Await(_origin, ref expr) => self.lower_await(e.span, expr), ExprKind::Closure( capture_clause, ref asyncness, movability, ref decl, ref body, fn_decl_span ) => { @@ -4326,12 +4342,13 @@ 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(e.span, &["ops", "RangeInclusive"], None, false)); - let ty = P(self.ty_path(id, e.span, hir::QPath::Resolved(None, ty_path))); - let new_seg = P(hir::PathSegment::from_ident(Ident::from_str("new"))); - let new_path = hir::QPath::TypeRelative(ty, new_seg); - let new = P(self.expr(e.span, hir::ExprKind::Path(new_path), ThinVec::new())); - hir::ExprKind::Call(new, hir_vec![e1, e2]) + self.expr_call_std_assoc_fn( + id, + e.span, + &["ops", "RangeInclusive"], + "new", + hir_vec![e1, e2], + ) } ExprKind::Range(ref e1, ref e2, lims) => { use syntax::ast::RangeLimits::*; @@ -4468,9 +4485,7 @@ impl<'a> LoweringContext<'a> { let expr = opt_expr .as_ref() .map(|x| self.lower_expr(x)) - .unwrap_or_else(|| - self.expr(e.span, hir::ExprKind::Tup(hir_vec![]), ThinVec::new()) - ); + .unwrap_or_else(|| self.expr_unit(e.span)); hir::ExprKind::Yield(P(expr)) } @@ -4503,7 +4518,7 @@ impl<'a> LoweringContext<'a> { let body = if let Some(else_expr) = wildcard_arm { P(self.lower_expr(else_expr)) } else { - self.expr_tuple(e.span, hir_vec![]) + P(self.expr_tuple(e.span, hir_vec![])) }; arms.push(self.arm(hir_vec![wildcard_pattern], body)); } @@ -4651,8 +4666,11 @@ impl<'a> LoweringContext<'a> { let iter = P(self.expr_ident(head_sp, iter, iter_pat_nid)); 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, None, ThinVec::new())); - let next_expr = P(self.expr_call(head_sp, next_path, hir_vec![ref_mut_iter])); + let next_expr = P(self.expr_call_std_path( + head_sp, + next_path, + hir_vec![ref_mut_iter], + )); let arms = hir_vec![pat_arm, break_arm]; P(self.expr( @@ -4723,9 +4741,11 @@ 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, None, ThinVec::new())); - P(self.expr_call(head_sp, into_iter, hir_vec![head])) + P(self.expr_call_std_path( + head_sp, + into_iter_path, + hir_vec![head], + )) }; let match_expr = P(self.expr_match( @@ -4778,9 +4798,11 @@ 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, None, ThinVec::new())); - P(self.expr_call(e.span, path, hir_vec![sub_expr])) + P(self.expr_call_std_path( + unstable_span, + path, + hir_vec![sub_expr], + )) }; // `#[allow(unreachable_code)]` @@ -4817,12 +4839,9 @@ impl<'a> LoweringContext<'a> { let err_ident = self.str_to_ident("err"); let (err_local, err_local_nid) = self.pat_ident(try_span, err_ident); let from_expr = { - let path = &["convert", "From", "from"]; - let from = P(self.expr_std_path( - try_span, path, None, ThinVec::new())); + let from_path = &["convert", "From", "from"]; let err_expr = self.expr_ident(try_span, err_ident, err_local_nid); - - self.expr_call(try_span, from, hir_vec![err_expr]) + self.expr_call_std_path(try_span, from_path, hir_vec![err_expr]) }; let from_err_expr = self.wrap_in_try_constructor("from_error", from_expr, unstable_span); @@ -5056,6 +5075,42 @@ impl<'a> LoweringContext<'a> { self.expr(span, hir::ExprKind::Call(e, args), ThinVec::new()) } + // Note: associated functions must use `expr_call_std_path`. + fn expr_call_std_path( + &mut self, + span: Span, + path_components: &[&str], + args: hir::HirVec, + ) -> hir::Expr { + let path = P(self.expr_std_path(span, path_components, None, ThinVec::new())); + self.expr_call(span, path, args) + } + + // Create an expression calling an associated function of an std type. + // + // Associated functions cannot be resolved through the normal `std_path` function, + // as they are resolved differently and so cannot use `expr_call_std_path`. + // + // This function accepts the path component (`ty_path_components`) separately from + // the name of the associated function (`assoc_fn_name`) in order to facilitate + // separate resolution of the type and creation of a path referring to its associated + // function. + fn expr_call_std_assoc_fn( + &mut self, + ty_path_id: hir::HirId, + span: Span, + ty_path_components: &[&str], + assoc_fn_name: &str, + args: hir::HirVec, + ) -> hir::ExprKind { + let ty_path = P(self.std_path(span, ty_path_components, None, false)); + let ty = P(self.ty_path(ty_path_id, span, hir::QPath::Resolved(None, ty_path))); + let fn_seg = P(hir::PathSegment::from_ident(Ident::from_str(assoc_fn_name))); + let fn_path = hir::QPath::TypeRelative(ty, fn_seg); + let fn_expr = P(self.expr(span, hir::ExprKind::Path(fn_path), ThinVec::new())); + hir::ExprKind::Call(fn_expr, args) + } + fn expr_ident(&mut self, span: Span, ident: Ident, binding: hir::HirId) -> hir::Expr { self.expr_ident_with_attrs(span, ident, binding, ThinVec::new()) } @@ -5127,8 +5182,12 @@ impl<'a> LoweringContext<'a> { self.expr(b.span, hir::ExprKind::Block(b, None), attrs) } - fn expr_tuple(&mut self, sp: Span, exprs: hir::HirVec) -> P { - P(self.expr(sp, hir::ExprKind::Tup(exprs), ThinVec::new())) + fn expr_unit(&mut self, sp: Span) -> hir::Expr { + self.expr_tuple(sp, hir_vec![]) + } + + fn expr_tuple(&mut self, sp: Span, exprs: hir::HirVec) -> hir::Expr { + self.expr(sp, hir::ExprKind::Tup(exprs), ThinVec::new()) } fn expr(&mut self, span: Span, node: hir::ExprKind, attrs: ThinVec) -> hir::Expr { @@ -5184,6 +5243,23 @@ impl<'a> LoweringContext<'a> { } } + fn expr_unsafe(&mut self, expr: P) -> hir::Expr { + let hir_id = self.next_id(); + let span = expr.span; + self.expr( + span, + hir::ExprKind::Block(P(hir::Block { + stmts: hir_vec![], + expr: Some(expr), + hir_id, + rules: hir::UnsafeBlock(hir::CompilerGenerated), + span, + targeted_by_break: false, + }), None), + ThinVec::new(), + ) + } + fn pat_ok(&mut self, span: Span, pat: P) -> P { self.pat_std_enum(span, &["result", "Result", "Ok"], hir_vec![pat]) } @@ -5258,13 +5334,12 @@ impl<'a> LoweringContext<'a> { span: Span, components: &[&str], params: Option>, - is_value: bool + is_value: bool, ) -> hir::Path { let mut path = self.resolver .resolve_str_path(span, self.crate_root, components, is_value); path.segments.last_mut().unwrap().args = params; - for seg in path.segments.iter_mut() { if seg.hir_id.is_some() { seg.hir_id = Some(self.next_id()); @@ -5465,6 +5540,175 @@ impl<'a> LoweringContext<'a> { ThinVec::new())); P(self.expr_call(e.span, from_err, hir_vec![e])) } + + fn lower_await( + &mut self, + await_span: Span, + expr: &ast::Expr, + ) -> hir::ExprKind { + // to: + // + // { + // let mut pinned = ; + // loop { + // match ::std::future::poll_with_tls_context(unsafe { + // ::std::pin::Pin::new_unchecked(&mut pinned) + // }) { + // ::std::task::Poll::Ready(x) => break x, + // ::std::task::Poll::Pending => {}, + // } + // yield (); + // } + // } + if !self.is_async_body { + span_err!( + self.sess, + await_span, + E0728, + "`await` is only allowed inside `async` functions and blocks" + ); + self.sess.abort_if_errors(); + } + let span = self.mark_span_with_reason( + CompilerDesugaringKind::Await, + await_span, + None, + ); + let gen_future_span = self.mark_span_with_reason( + CompilerDesugaringKind::Await, + await_span, + Some(vec![Symbol::intern("gen_future")].into()), + ); + + // let mut pinned = ; + let expr = P(self.lower_expr(expr)); + let pinned_ident = self.str_to_ident("pinned"); + let (pinned_pat, pinned_pat_hid) = self.pat_ident_binding_mode( + span, + pinned_ident, + hir::BindingAnnotation::Mutable, + ); + let pinned_let = self.stmt_let_pat( + span, + Some(expr), + pinned_pat, + hir::LocalSource::AwaitDesugar, + ); + + // ::std::future::poll_with_tls_context(unsafe { + // ::std::pin::Pin::new_unchecked(&mut pinned) + // })` + let poll_expr = { + let pinned = P(self.expr_ident(span, pinned_ident, pinned_pat_hid)); + let ref_mut_pinned = self.expr_mut_addr_of(span, pinned); + let pin_ty_id = self.next_id(); + let new_unchecked_expr_kind = self.expr_call_std_assoc_fn( + pin_ty_id, + span, + &["pin", "Pin"], + "new_unchecked", + hir_vec![ref_mut_pinned], + ); + let new_unchecked = P(self.expr(span, new_unchecked_expr_kind, ThinVec::new())); + let unsafe_expr = self.expr_unsafe(new_unchecked); + P(self.expr_call_std_path( + gen_future_span, + &["future", "poll_with_tls_context"], + hir_vec![unsafe_expr], + )) + }; + + // `::std::task::Poll::Ready(x) => break x` + let loop_node_id = self.sess.next_node_id(); + let loop_hir_id = self.lower_node_id(loop_node_id); + let ready_arm = { + let x_ident = self.str_to_ident("x"); + let (x_pat, x_pat_hid) = self.pat_ident(span, x_ident); + let x_expr = P(self.expr_ident(span, x_ident, x_pat_hid)); + let ready_pat = self.pat_std_enum( + span, + &["task", "Poll", "Ready"], + hir_vec![x_pat], + ); + let break_x = self.with_loop_scope(loop_node_id, |this| { + let expr_break = hir::ExprKind::Break( + this.lower_loop_destination(None), + Some(x_expr), + ); + P(this.expr(await_span, expr_break, ThinVec::new())) + }); + self.arm(hir_vec![ready_pat], break_x) + }; + + // `::std::task::Poll::Pending => {}` + let pending_arm = { + let pending_pat = self.pat_std_enum( + span, + &["task", "Poll", "Pending"], + hir_vec![], + ); + let empty_block = P(hir::Block { + stmts: hir_vec![], + expr: None, + hir_id: self.next_id(), + rules: hir::DefaultBlock, + span, + targeted_by_break: false, + }); + let empty_block = P(self.expr_block(empty_block, ThinVec::new())); + self.arm(hir_vec![pending_pat], empty_block) + }; + + let match_stmt = { + let match_expr = P(self.expr_match( + span, + poll_expr, + hir_vec![ready_arm, pending_arm], + hir::MatchSource::AwaitDesugar, + )); + hir::Stmt { + hir_id: self.next_id(), + node: hir::StmtKind::Expr(match_expr), + span, + } + }; + + let yield_stmt = { + let unit = self.expr_unit(span); + let yield_expr = P(self.expr( + span, + hir::ExprKind::Yield(P(unit)), + ThinVec::new(), + )); + hir::Stmt { + hir_id: self.next_id(), + node: hir::StmtKind::Expr(yield_expr), + span, + } + }; + + let loop_block = P(self.block_all( + span, + hir_vec![match_stmt, yield_stmt], + None, + )); + + let loop_expr = P(hir::Expr { + hir_id: loop_hir_id, + node: hir::ExprKind::Loop( + loop_block, + None, + hir::LoopSource::Loop, + ), + span, + attrs: ThinVec::new(), + }); + + hir::ExprKind::Block( + P(self.block_all(span, hir_vec![pinned_let], Some(loop_expr))), + None, + ) + } } fn body_ids(bodies: &BTreeMap) -> Vec { diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 1e357e1341710..01de7917e6e23 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -1606,6 +1606,8 @@ pub enum LocalSource { /// } /// ``` AsyncFn, + /// A desugared `.await`. + AwaitDesugar, } /// Hints at the original code for a `match _ { .. }`. @@ -1624,6 +1626,8 @@ pub enum MatchSource { ForLoopDesugar, /// A desugared `?` operator. TryDesugar, + /// A desugared `.await`. + AwaitDesugar, } /// The loop type that yielded an `ExprKind::Loop`. diff --git a/src/librustc/ich/impls_syntax.rs b/src/librustc/ich/impls_syntax.rs index 40cce8e77c0e0..90dd5099cbfd6 100644 --- a/src/librustc/ich/impls_syntax.rs +++ b/src/librustc/ich/impls_syntax.rs @@ -396,6 +396,7 @@ impl_stable_hash_for!(enum ::syntax_pos::hygiene::ExpnFormat { impl_stable_hash_for!(enum ::syntax_pos::hygiene::CompilerDesugaringKind { Async, + Await, QuestionMark, ExistentialReturnType, ForLoop, diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index f70429c22b68c..f7a89271ec55f 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -46,7 +46,6 @@ use syntax::symbol::{Symbol, keywords}; use syntax::errors::{Applicability, DiagnosticBuilder}; use syntax::print::pprust::expr_to_string; use syntax::visit::FnKind; -use syntax::struct_span_err; use rustc::hir::{self, GenericParamKind, PatKind}; @@ -1438,15 +1437,10 @@ impl KeywordIdents { UnderMacro(under_macro): UnderMacro, ident: ast::Ident) { - let ident_str = &ident.as_str()[..]; - let cur_edition = cx.sess.edition(); - let is_raw_ident = |ident: ast::Ident| { - cx.sess.parse_sess.raw_identifier_spans.borrow().contains(&ident.span) - }; - let next_edition = match cur_edition { + let next_edition = match cx.sess.edition() { Edition::Edition2015 => { - match ident_str { - "async" | "try" => Edition::Edition2018, + match &ident.as_str()[..] { + "async" | "await" | "try" => Edition::Edition2018, // rust-lang/rust#56327: Conservatively do not // attempt to report occurrences of `dyn` within @@ -1462,43 +1456,16 @@ impl KeywordIdents { // an identifier. "dyn" if !under_macro => Edition::Edition2018, - // Only issue warnings for `await` if the `async_await` - // feature isn't being used. Otherwise, users need - // to keep using `await` for the macro exposed by std. - "await" if !cx.sess.features_untracked().async_await => Edition::Edition2018, _ => return, } } // There are no new keywords yet for the 2018 edition and beyond. - // However, `await` is a "false" keyword in the 2018 edition, - // and can only be used if the `async_await` feature is enabled. - // Otherwise, we emit an error. - _ => { - if "await" == ident_str - && !cx.sess.features_untracked().async_await - && !is_raw_ident(ident) - { - let mut err = struct_span_err!( - cx.sess, - ident.span, - E0721, - "`await` is a keyword in the {} edition", cur_edition, - ); - err.span_suggestion( - ident.span, - "you can use a raw identifier to stay compatible", - "r#await".to_string(), - Applicability::MachineApplicable, - ); - err.emit(); - } - return - }, + _ => return, }; // don't lint `r#foo` - if is_raw_ident(ident) { + if cx.sess.parse_sess.raw_identifier_spans.borrow().contains(&ident.span) { return; } diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index ed183acc93b74..1a7266859ad9f 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -77,6 +77,7 @@ impl<'a, 'tcx> Visitor<'tcx> for MatchVisitor<'a, 'tcx> { hir::LocalSource::Normal => "local binding", hir::LocalSource::ForLoopDesugar => "`for` loop binding", hir::LocalSource::AsyncFn => "async fn binding", + hir::LocalSource::AwaitDesugar => "`await` future binding", }); // Check legality of move bindings and `@` patterns. @@ -412,8 +413,9 @@ fn check_arms<'a, 'tcx>( err.emit(); } - // Unreachable patterns in try expressions occur when one of the arms - // are an uninhabited type. Which is OK. + // Unreachable patterns in try and await expressions occur when one of + // the arms are an uninhabited type. Which is OK. + hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {} } } diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 33b8c76bb531a..af2302d24f521 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -1065,6 +1065,7 @@ impl Expr { ExprKind::Block(..) => ExprPrecedence::Block, ExprKind::TryBlock(..) => ExprPrecedence::TryBlock, ExprKind::Async(..) => ExprPrecedence::Async, + ExprKind::Await(..) => ExprPrecedence::Await, ExprKind::Assign(..) => ExprPrecedence::Assign, ExprKind::AssignOp(..) => ExprPrecedence::AssignOp, ExprKind::Field(..) => ExprPrecedence::Field, @@ -1186,6 +1187,9 @@ pub enum ExprKind { /// created during lowering cannot be made the parent of any other /// preexisting defs. Async(CaptureBy, NodeId, P), + /// An await expression (`my_future.await`). + Await(AwaitOrigin, P), + /// A try block (`try { ... }`). TryBlock(P), @@ -1287,6 +1291,15 @@ pub enum Movability { Movable, } +/// Whether an `await` comes from `await!` or `.await` syntax. +/// FIXME: this should be removed when support for legacy `await!` is removed. +/// https://github.com/rust-lang/rust/issues/60610 +#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, Copy)] +pub enum AwaitOrigin { + FieldLike, + MacroLike, +} + pub type Mac = Spanned; /// Represents a macro invocation. The `Path` indicates which macro diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 2a1f3c4801406..dda5ac1f4e16e 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -485,6 +485,10 @@ declare_features! ( // Allows async and await syntax. (active, async_await, "1.28.0", Some(50547), None), + // Allows await! macro-like syntax. + // This will likely be removed prior to stabilization of async/await. + (active, await_macro, "1.28.0", Some(50547), None), + // Allows reinterpretation of the bits of a value of one type as another type during const eval. (active, const_transmute, "1.29.0", Some(53605), None), @@ -2104,6 +2108,20 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { ast::ExprKind::Async(..) => { gate_feature_post!(&self, async_await, e.span, "async blocks are unstable"); } + ast::ExprKind::Await(origin, _) => { + match origin { + ast::AwaitOrigin::FieldLike => + gate_feature_post!(&self, async_await, e.span, "async/await is unstable"), + ast::AwaitOrigin::MacroLike => + gate_feature_post!( + &self, + await_macro, + e.span, + "`await!()` macro syntax is unstable, and will soon be removed \ + in favor of `.await` syntax." + ), + } + } _ => {} } visit::walk_expr(self, e); diff --git a/src/libsyntax/mut_visit.rs b/src/libsyntax/mut_visit.rs index 2e09235ca77b0..6eb8b1b5004c9 100644 --- a/src/libsyntax/mut_visit.rs +++ b/src/libsyntax/mut_visit.rs @@ -1185,6 +1185,7 @@ pub fn noop_visit_expr(Expr { node, id, span, attrs }: &mut Expr, vis.visit_id(node_id); vis.visit_block(body); } + ExprKind::Await(_origin, expr) => vis.visit_expr(expr), ExprKind::Assign(el, er) => { vis.visit_expr(el); vis.visit_expr(er); diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index d46feeab33599..c5d761885657d 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2751,6 +2751,14 @@ impl<'a> Parser<'a> { db.span_label(self.span, "expected expression"); db.note("variable declaration using `let` is a statement"); return Err(db); + } else if self.span.rust_2018() && self.eat_keyword(keywords::Await) { + // FIXME: remove this branch when `await!` is no longer supported + // https://github.com/rust-lang/rust/issues/60610 + self.expect(&token::Not)?; + self.expect(&token::OpenDelim(token::Paren))?; + let expr = self.parse_expr()?; + self.expect(&token::CloseDelim(token::Paren))?; + ex = ExprKind::Await(ast::AwaitOrigin::MacroLike, expr); } else if self.token.is_path_start() { let path = self.parse_path(PathStyle::Expr)?; @@ -3014,6 +3022,15 @@ impl<'a> Parser<'a> { // Assuming we have just parsed `.`, continue parsing into an expression. fn parse_dot_suffix(&mut self, self_arg: P, lo: Span) -> PResult<'a, P> { + if self.span.rust_2018() && self.eat_keyword(keywords::Await) { + let span = lo.to(self.prev_span); + let await_expr = self.mk_expr( + span, + ExprKind::Await(ast::AwaitOrigin::FieldLike, self_arg), + ThinVec::new(), + ); + return Ok(await_expr); + } let segment = self.parse_path_segment(PathStyle::Expr)?; self.check_trailing_angle_brackets(&segment, token::OpenDelim(token::Paren)); diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 2fa4f5263fbc5..fd7a39c576daa 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -99,6 +99,11 @@ pub(crate) fn ident_can_begin_expr(ident: ast::Ident, is_raw: bool) -> bool { ident_token.is_path_segment_keyword() || [ keywords::Async.name(), + + // FIXME: remove when `await!(..)` syntax is removed + // https://github.com/rust-lang/rust/issues/60610 + keywords::Await.name(), + keywords::Do.name(), keywords::Box.name(), keywords::Break.name(), diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 6c0fdfaa776f9..682621d40ab65 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -2250,6 +2250,18 @@ impl<'a> State<'a> { self.ibox(0)?; self.print_block_with_attrs(blk, attrs)?; } + ast::ExprKind::Await(origin, ref expr) => { + match origin { + ast::AwaitOrigin::MacroLike => { + self.s.word("await!")?; + self.print_expr_maybe_paren(expr, parser::PREC_FORCE_PAREN)?; + } + ast::AwaitOrigin::FieldLike => { + self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX)?; + self.s.word(".await")?; + } + } + } ast::ExprKind::Assign(ref lhs, ref rhs) => { let prec = AssocOp::Assign.precedence() as i8; self.print_expr_maybe_paren(lhs, prec + 1)?; diff --git a/src/libsyntax/util/parser.rs b/src/libsyntax/util/parser.rs index 5f15ede7b0b6a..80dabffaba9f4 100644 --- a/src/libsyntax/util/parser.rs +++ b/src/libsyntax/util/parser.rs @@ -267,6 +267,7 @@ pub enum ExprPrecedence { TryBlock, Struct, Async, + Await, Err, } @@ -301,6 +302,7 @@ impl ExprPrecedence { ExprPrecedence::Unary => PREC_PREFIX, // Unary, postfix + ExprPrecedence::Await | ExprPrecedence::Call | ExprPrecedence::MethodCall | ExprPrecedence::Field | @@ -346,6 +348,7 @@ pub fn contains_exterior_struct_lit(value: &ast::Expr) -> bool { // X { y: 1 } + X { y: 2 } contains_exterior_struct_lit(&lhs) || contains_exterior_struct_lit(&rhs) } + ast::ExprKind::Await(_, ref x) | ast::ExprKind::Unary(_, ref x) | ast::ExprKind::Cast(ref x, _) | ast::ExprKind::Type(ref x, _) | diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index fc99d10b0b6c6..0503e5644dbc5 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -768,6 +768,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { ExprKind::Async(_, _, ref body) => { visitor.visit_block(body); } + ExprKind::Await(_, ref expr) => visitor.visit_expr(expr), ExprKind::Assign(ref left_hand_expression, ref right_hand_expression) => { visitor.visit_expr(left_hand_expression); visitor.visit_expr(right_hand_expression); diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs index c806020039d26..a901afdff43e6 100644 --- a/src/libsyntax_pos/hygiene.rs +++ b/src/libsyntax_pos/hygiene.rs @@ -598,6 +598,7 @@ pub enum CompilerDesugaringKind { /// `impl Trait` with `Foo`. ExistentialReturnType, Async, + Await, ForLoop, } @@ -605,6 +606,7 @@ impl CompilerDesugaringKind { pub fn name(self) -> Symbol { Symbol::intern(match self { CompilerDesugaringKind::Async => "async", + CompilerDesugaringKind::Await => "await", CompilerDesugaringKind::QuestionMark => "?", CompilerDesugaringKind::TryBlock => "try block", CompilerDesugaringKind::ExistentialReturnType => "existential type", diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index cdbfabae7ce1f..20759217b54a0 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -84,6 +84,7 @@ symbols! { // Edition-specific keywords that are used in unstable Rust or reserved for future use. Async: "async", // >= 2018 Edition only + Await: "await", // >= 2018 Edition only Try: "try", // >= 2018 Edition only // Special lifetime names diff --git a/src/test/run-pass/async-await.rs b/src/test/run-pass/async-await.rs index e1b4328debd9a..49fd8b8b1ce27 100644 --- a/src/test/run-pass/async-await.rs +++ b/src/test/run-pass/async-await.rs @@ -1,7 +1,7 @@ // edition:2018 // aux-build:arc_wake.rs -#![feature(async_await, await_macro)] +#![feature(async_await)] extern crate arc_wake; @@ -46,14 +46,14 @@ impl Future for WakeOnceThenComplete { fn async_block(x: u8) -> impl Future { async move { - await!(wake_and_yield_once()); + wake_and_yield_once().await; x } } fn async_block_with_borrow_named_lifetime<'a>(x: &'a u8) -> impl Future + 'a { async move { - await!(wake_and_yield_once()); + wake_and_yield_once().await; *x } } @@ -61,43 +61,43 @@ fn async_block_with_borrow_named_lifetime<'a>(x: &'a u8) -> impl Future impl Future { async move { let future = async { - await!(wake_and_yield_once()); + wake_and_yield_once().await; x }; - await!(future) + future.await } } fn async_closure(x: u8) -> impl Future { (async move |x: u8| -> u8 { - await!(wake_and_yield_once()); + wake_and_yield_once().await; x })(x) } async fn async_fn(x: u8) -> u8 { - await!(wake_and_yield_once()); + wake_and_yield_once().await; x } async fn generic_async_fn(x: T) -> T { - await!(wake_and_yield_once()); + wake_and_yield_once().await; x } async fn async_fn_with_borrow(x: &u8) -> u8 { - await!(wake_and_yield_once()); + wake_and_yield_once().await; *x } async fn async_fn_with_borrow_named_lifetime<'a>(x: &'a u8) -> u8 { - await!(wake_and_yield_once()); + wake_and_yield_once().await; *x } fn async_fn_with_impl_future_named_lifetime<'a>(x: &'a u8) -> impl Future + 'a { async move { - await!(wake_and_yield_once()); + wake_and_yield_once().await; *x } } @@ -110,18 +110,18 @@ async fn async_fn_multiple_args(x: &u8, _y: &u8) -> u8 { */ async fn async_fn_multiple_args_named_lifetime<'a>(x: &'a u8, _y: &'a u8) -> u8 { - await!(wake_and_yield_once()); + wake_and_yield_once().await; *x } fn async_fn_with_internal_borrow(y: u8) -> impl Future { async move { - await!(async_fn_with_borrow_named_lifetime(&y)) + async_fn_with_borrow_named_lifetime(&y).await } } unsafe async fn unsafe_async_fn(x: u8) -> u8 { - await!(wake_and_yield_once()); + wake_and_yield_once().await; x } @@ -134,7 +134,7 @@ trait Bar { impl Foo { async fn async_method(x: u8) -> u8 { unsafe { - await!(unsafe_async_fn(x)) + unsafe_async_fn(x).await } } } @@ -165,7 +165,7 @@ fn main() { ($($fn_name:expr,)*) => { $( test_future_yields_once_then_returns(|x| { async move { - await!($fn_name(&x)) + $fn_name(&x).await } }); )* } @@ -181,7 +181,7 @@ fn main() { Foo::async_method, |x| { async move { - unsafe { await!(unsafe_async_fn(x)) } + unsafe { unsafe_async_fn(x).await } } }, } @@ -192,7 +192,7 @@ fn main() { async_fn_with_impl_future_named_lifetime, |x| { async move { - await!(async_fn_multiple_args_named_lifetime(x, x)) + async_fn_multiple_args_named_lifetime(x, x).await } }, } diff --git a/src/test/run-pass/await-macro.rs b/src/test/run-pass/await-macro.rs new file mode 100644 index 0000000000000..e1b4328debd9a --- /dev/null +++ b/src/test/run-pass/await-macro.rs @@ -0,0 +1,199 @@ +// edition:2018 +// aux-build:arc_wake.rs + +#![feature(async_await, await_macro)] + +extern crate arc_wake; + +use std::pin::Pin; +use std::future::Future; +use std::sync::{ + Arc, + atomic::{self, AtomicUsize}, +}; +use std::task::{Context, Poll}; +use arc_wake::ArcWake; + +struct Counter { + wakes: AtomicUsize, +} + +impl ArcWake for Counter { + fn wake(self: Arc) { + Self::wake_by_ref(&self) + } + fn wake_by_ref(arc_self: &Arc) { + arc_self.wakes.fetch_add(1, atomic::Ordering::SeqCst); + } +} + +struct WakeOnceThenComplete(bool); + +fn wake_and_yield_once() -> WakeOnceThenComplete { WakeOnceThenComplete(false) } + +impl Future for WakeOnceThenComplete { + type Output = (); + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { + if self.0 { + Poll::Ready(()) + } else { + cx.waker().wake_by_ref(); + self.0 = true; + Poll::Pending + } + } +} + +fn async_block(x: u8) -> impl Future { + async move { + await!(wake_and_yield_once()); + x + } +} + +fn async_block_with_borrow_named_lifetime<'a>(x: &'a u8) -> impl Future + 'a { + async move { + await!(wake_and_yield_once()); + *x + } +} + +fn async_nonmove_block(x: u8) -> impl Future { + async move { + let future = async { + await!(wake_and_yield_once()); + x + }; + await!(future) + } +} + +fn async_closure(x: u8) -> impl Future { + (async move |x: u8| -> u8 { + await!(wake_and_yield_once()); + x + })(x) +} + +async fn async_fn(x: u8) -> u8 { + await!(wake_and_yield_once()); + x +} + +async fn generic_async_fn(x: T) -> T { + await!(wake_and_yield_once()); + x +} + +async fn async_fn_with_borrow(x: &u8) -> u8 { + await!(wake_and_yield_once()); + *x +} + +async fn async_fn_with_borrow_named_lifetime<'a>(x: &'a u8) -> u8 { + await!(wake_and_yield_once()); + *x +} + +fn async_fn_with_impl_future_named_lifetime<'a>(x: &'a u8) -> impl Future + 'a { + async move { + await!(wake_and_yield_once()); + *x + } +} + +/* FIXME(cramertj) support when `existential type T<'a, 'b>:;` works +async fn async_fn_multiple_args(x: &u8, _y: &u8) -> u8 { + await!(wake_and_yield_once()); + *x +} +*/ + +async fn async_fn_multiple_args_named_lifetime<'a>(x: &'a u8, _y: &'a u8) -> u8 { + await!(wake_and_yield_once()); + *x +} + +fn async_fn_with_internal_borrow(y: u8) -> impl Future { + async move { + await!(async_fn_with_borrow_named_lifetime(&y)) + } +} + +unsafe async fn unsafe_async_fn(x: u8) -> u8 { + await!(wake_and_yield_once()); + x +} + +struct Foo; + +trait Bar { + fn foo() {} +} + +impl Foo { + async fn async_method(x: u8) -> u8 { + unsafe { + await!(unsafe_async_fn(x)) + } + } +} + +fn test_future_yields_once_then_returns(f: F) +where + F: FnOnce(u8) -> Fut, + Fut: Future, +{ + let mut fut = Box::pin(f(9)); + let counter = Arc::new(Counter { wakes: AtomicUsize::new(0) }); + let waker = ArcWake::into_waker(counter.clone()); + let mut cx = Context::from_waker(&waker); + assert_eq!(0, counter.wakes.load(atomic::Ordering::SeqCst)); + assert_eq!(Poll::Pending, fut.as_mut().poll(&mut cx)); + assert_eq!(1, counter.wakes.load(atomic::Ordering::SeqCst)); + assert_eq!(Poll::Ready(9), fut.as_mut().poll(&mut cx)); +} + +fn main() { + macro_rules! test { + ($($fn_name:expr,)*) => { $( + test_future_yields_once_then_returns($fn_name); + )* } + } + + macro_rules! test_with_borrow { + ($($fn_name:expr,)*) => { $( + test_future_yields_once_then_returns(|x| { + async move { + await!($fn_name(&x)) + } + }); + )* } + } + + test! { + async_block, + async_nonmove_block, + async_closure, + async_fn, + generic_async_fn, + async_fn_with_internal_borrow, + Foo::async_method, + |x| { + async move { + unsafe { await!(unsafe_async_fn(x)) } + } + }, + } + test_with_borrow! { + async_block_with_borrow_named_lifetime, + async_fn_with_borrow, + async_fn_with_borrow_named_lifetime, + async_fn_with_impl_future_named_lifetime, + |x| { + async move { + await!(async_fn_multiple_args_named_lifetime(x, x)) + } + }, + } +} diff --git a/src/test/run-pass/issue-55809.rs b/src/test/run-pass/issue-55809.rs index 12be6582a21e8..b7e60b773b416 100644 --- a/src/test/run-pass/issue-55809.rs +++ b/src/test/run-pass/issue-55809.rs @@ -1,7 +1,7 @@ // edition:2018 // run-pass -#![feature(async_await, await_macro)] +#![feature(async_await)] trait Foo { } @@ -14,15 +14,15 @@ async fn foo_async(_v: T) -> u8 where T: Foo { } async fn bad(v: T) -> u8 where T: Foo { - await!(foo_async(v)) + foo_async(v).await } async fn async_main() { let mut v = (); - let _ = await!(bad(&mut v)); - let _ = await!(foo_async(&mut v)); - let _ = await!(bad(v)); + let _ = bad(&mut v).await; + let _ = foo_async(&mut v).await; + let _ = bad(v).await; } fn main() { diff --git a/src/test/ui/await-keyword/2015-edition-error-in-non-macro-position.rs b/src/test/ui/await-keyword/2015-edition-error-in-non-macro-position.rs new file mode 100644 index 0000000000000..c4f3f3edc486e --- /dev/null +++ b/src/test/ui/await-keyword/2015-edition-error-in-non-macro-position.rs @@ -0,0 +1,36 @@ +#![feature(async_await, await_macro)] +#![allow(non_camel_case_types)] +#![deny(keyword_idents)] + +mod outer_mod { + pub mod await { //~ ERROR `await` is a keyword in the 2018 edition + //~^ WARN this was previously accepted by the compiler + pub struct await; //~ ERROR `await` is a keyword in the 2018 edition + //~^ WARN this was previously accepted by the compiler + } +} +use outer_mod::await::await; //~ ERROR `await` is a keyword in the 2018 edition +//~^ ERROR `await` is a keyword in the 2018 edition +//~^^ WARN this was previously accepted by the compiler +//~^^^ WARN this was previously accepted by the compiler + +struct Foo { await: () } +//~^ ERROR `await` is a keyword in the 2018 edition +//~^^ WARN this was previously accepted by the compiler + +impl Foo { fn await() {} } +//~^ ERROR `await` is a keyword in the 2018 edition +//~^^ WARN this was previously accepted by the compiler + +macro_rules! await { +//~^ ERROR `await` is a keyword in the 2018 edition +//~^^ WARN this was previously accepted by the compiler + () => {} +} + +fn main() { + match await { await => {} } //~ ERROR `await` is a keyword in the 2018 edition + //~^ ERROR `await` is a keyword in the 2018 edition + //~^^ WARN this was previously accepted by the compiler + //~^^^ WARN this was previously accepted by the compiler +} diff --git a/src/test/ui/await-keyword/2015-edition-error-in-non-macro-position.stderr b/src/test/ui/await-keyword/2015-edition-error-in-non-macro-position.stderr new file mode 100644 index 0000000000000..067ecd6a5138d --- /dev/null +++ b/src/test/ui/await-keyword/2015-edition-error-in-non-macro-position.stderr @@ -0,0 +1,88 @@ +error: `await` is a keyword in the 2018 edition + --> $DIR/2015-edition-error-in-non-macro-position.rs:6:13 + | +LL | pub mod await { + | ^^^^^ help: you can use a raw identifier to stay compatible: `r#await` + | +note: lint level defined here + --> $DIR/2015-edition-error-in-non-macro-position.rs:3:9 + | +LL | #![deny(keyword_idents)] + | ^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: `await` is a keyword in the 2018 edition + --> $DIR/2015-edition-error-in-non-macro-position.rs:8:20 + | +LL | pub struct await; + | ^^^^^ help: you can use a raw identifier to stay compatible: `r#await` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: `await` is a keyword in the 2018 edition + --> $DIR/2015-edition-error-in-non-macro-position.rs:12:16 + | +LL | use outer_mod::await::await; + | ^^^^^ help: you can use a raw identifier to stay compatible: `r#await` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: `await` is a keyword in the 2018 edition + --> $DIR/2015-edition-error-in-non-macro-position.rs:12:23 + | +LL | use outer_mod::await::await; + | ^^^^^ help: you can use a raw identifier to stay compatible: `r#await` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: `await` is a keyword in the 2018 edition + --> $DIR/2015-edition-error-in-non-macro-position.rs:17:14 + | +LL | struct Foo { await: () } + | ^^^^^ help: you can use a raw identifier to stay compatible: `r#await` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: `await` is a keyword in the 2018 edition + --> $DIR/2015-edition-error-in-non-macro-position.rs:21:15 + | +LL | impl Foo { fn await() {} } + | ^^^^^ help: you can use a raw identifier to stay compatible: `r#await` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: `await` is a keyword in the 2018 edition + --> $DIR/2015-edition-error-in-non-macro-position.rs:25:14 + | +LL | macro_rules! await { + | ^^^^^ help: you can use a raw identifier to stay compatible: `r#await` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: `await` is a keyword in the 2018 edition + --> $DIR/2015-edition-error-in-non-macro-position.rs:32:11 + | +LL | match await { await => {} } + | ^^^^^ help: you can use a raw identifier to stay compatible: `r#await` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: `await` is a keyword in the 2018 edition + --> $DIR/2015-edition-error-in-non-macro-position.rs:32:19 + | +LL | match await { await => {} } + | ^^^^^ help: you can use a raw identifier to stay compatible: `r#await` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: aborting due to 9 previous errors + diff --git a/src/test/ui/await-keyword/2015-edition-no-warnings-with-feature-gate.rs b/src/test/ui/await-keyword/2015-edition-no-warnings-with-feature-gate.rs deleted file mode 100644 index 92c60e7d6eed0..0000000000000 --- a/src/test/ui/await-keyword/2015-edition-no-warnings-with-feature-gate.rs +++ /dev/null @@ -1,16 +0,0 @@ -// compile-pass - -#![feature(async_await)] -#![allow(non_camel_case_types)] -#![deny(keyword_idents)] - -mod outer_mod { - pub mod await { - pub struct await; - } -} -use outer_mod::await::await; - -fn main() { - match await { await => {} } -} diff --git a/src/test/ui/await-keyword/2018-edition-error-in-non-macro-position.rs b/src/test/ui/await-keyword/2018-edition-error-in-non-macro-position.rs new file mode 100644 index 0000000000000..b2e8e4be17244 --- /dev/null +++ b/src/test/ui/await-keyword/2018-edition-error-in-non-macro-position.rs @@ -0,0 +1,27 @@ +// edition:2018 + +#![allow(non_camel_case_types)] +#![feature(async_await, await_macro)] + +mod outer_mod { + pub mod await { //~ ERROR expected identifier, found reserved keyword `await` + pub struct await; //~ ERROR expected identifier, found reserved keyword `await` + } +} +use self::outer_mod::await::await; //~ ERROR expected identifier, found reserved keyword `await` +//~^ ERROR expected identifier, found reserved keyword `await` + +struct Foo { await: () } +//~^ ERROR expected identifier, found reserved keyword `await` + +impl Foo { fn await() {} } +//~^ ERROR expected identifier, found reserved keyword `await` + +macro_rules! await { +//~^ ERROR expected identifier, found reserved keyword `await` + () => {} +} + +fn main() { + match await { await => () } //~ ERROR expected `!`, found `{` +} diff --git a/src/test/ui/await-keyword/2018-edition-error-in-non-macro-position.stderr b/src/test/ui/await-keyword/2018-edition-error-in-non-macro-position.stderr new file mode 100644 index 0000000000000..076a31bd9ced6 --- /dev/null +++ b/src/test/ui/await-keyword/2018-edition-error-in-non-macro-position.stderr @@ -0,0 +1,80 @@ +error: expected identifier, found reserved keyword `await` + --> $DIR/2018-edition-error-in-non-macro-position.rs:7:13 + | +LL | pub mod await { + | ^^^^^ expected identifier, found reserved keyword +help: you can escape reserved keywords to use them as identifiers + | +LL | pub mod r#await { + | ^^^^^^^ + +error: expected identifier, found reserved keyword `await` + --> $DIR/2018-edition-error-in-non-macro-position.rs:8:20 + | +LL | pub struct await; + | ^^^^^ expected identifier, found reserved keyword +help: you can escape reserved keywords to use them as identifiers + | +LL | pub struct r#await; + | ^^^^^^^ + +error: expected identifier, found reserved keyword `await` + --> $DIR/2018-edition-error-in-non-macro-position.rs:11:22 + | +LL | use self::outer_mod::await::await; + | ^^^^^ expected identifier, found reserved keyword +help: you can escape reserved keywords to use them as identifiers + | +LL | use self::outer_mod::r#await::await; + | ^^^^^^^ + +error: expected identifier, found reserved keyword `await` + --> $DIR/2018-edition-error-in-non-macro-position.rs:11:29 + | +LL | use self::outer_mod::await::await; + | ^^^^^ expected identifier, found reserved keyword +help: you can escape reserved keywords to use them as identifiers + | +LL | use self::outer_mod::await::r#await; + | ^^^^^^^ + +error: expected identifier, found reserved keyword `await` + --> $DIR/2018-edition-error-in-non-macro-position.rs:14:14 + | +LL | struct Foo { await: () } + | ^^^^^ expected identifier, found reserved keyword +help: you can escape reserved keywords to use them as identifiers + | +LL | struct Foo { r#await: () } + | ^^^^^^^ + +error: expected identifier, found reserved keyword `await` + --> $DIR/2018-edition-error-in-non-macro-position.rs:17:15 + | +LL | impl Foo { fn await() {} } + | ^^^^^ expected identifier, found reserved keyword +help: you can escape reserved keywords to use them as identifiers + | +LL | impl Foo { fn r#await() {} } + | ^^^^^^^ + +error: expected identifier, found reserved keyword `await` + --> $DIR/2018-edition-error-in-non-macro-position.rs:20:14 + | +LL | macro_rules! await { + | ^^^^^ expected identifier, found reserved keyword +help: you can escape reserved keywords to use them as identifiers + | +LL | macro_rules! r#await { + | ^^^^^^^ + +error: expected `!`, found `{` + --> $DIR/2018-edition-error-in-non-macro-position.rs:26:17 + | +LL | match await { await => () } + | ----- ^ expected `!` + | | + | while parsing this match expression + +error: aborting due to 8 previous errors + diff --git a/src/test/ui/await-keyword/2018-edition-error.rs b/src/test/ui/await-keyword/2018-edition-error.rs index 7ba3382ddf129..e0b2962ce9791 100644 --- a/src/test/ui/await-keyword/2018-edition-error.rs +++ b/src/test/ui/await-keyword/2018-edition-error.rs @@ -2,14 +2,13 @@ #![allow(non_camel_case_types)] mod outer_mod { - pub mod await { //~ ERROR `await` is a keyword - pub struct await; //~ ERROR `await` is a keyword + pub mod await { //~ ERROR expected identifier + pub struct await; //~ ERROR expected identifier } } -use self::outer_mod::await::await; //~ ERROR `await` is a keyword - //~^ ERROR `await` is a keyword +use self::outer_mod::await::await; //~ ERROR expected identifier + //~^ ERROR expected identifier, found reserved keyword `await` fn main() { - match await { await => () } //~ ERROR `await` is a keyword - //~^ ERROR `await` is a keyword + match await { await => () } //~ ERROR expected `!`, found `{` } diff --git a/src/test/ui/await-keyword/2018-edition-error.stderr b/src/test/ui/await-keyword/2018-edition-error.stderr index 67ff6c5675abf..c8bf9b42ca545 100644 --- a/src/test/ui/await-keyword/2018-edition-error.stderr +++ b/src/test/ui/await-keyword/2018-edition-error.stderr @@ -1,38 +1,50 @@ -error[E0721]: `await` is a keyword in the 2018 edition +error: expected identifier, found reserved keyword `await` --> $DIR/2018-edition-error.rs:5:13 | LL | pub mod await { - | ^^^^^ help: you can use a raw identifier to stay compatible: `r#await` + | ^^^^^ expected identifier, found reserved keyword +help: you can escape reserved keywords to use them as identifiers + | +LL | pub mod r#await { + | ^^^^^^^ -error[E0721]: `await` is a keyword in the 2018 edition +error: expected identifier, found reserved keyword `await` --> $DIR/2018-edition-error.rs:6:20 | LL | pub struct await; - | ^^^^^ help: you can use a raw identifier to stay compatible: `r#await` + | ^^^^^ expected identifier, found reserved keyword +help: you can escape reserved keywords to use them as identifiers + | +LL | pub struct r#await; + | ^^^^^^^ -error[E0721]: `await` is a keyword in the 2018 edition +error: expected identifier, found reserved keyword `await` --> $DIR/2018-edition-error.rs:9:22 | LL | use self::outer_mod::await::await; - | ^^^^^ help: you can use a raw identifier to stay compatible: `r#await` + | ^^^^^ expected identifier, found reserved keyword +help: you can escape reserved keywords to use them as identifiers + | +LL | use self::outer_mod::r#await::await; + | ^^^^^^^ -error[E0721]: `await` is a keyword in the 2018 edition +error: expected identifier, found reserved keyword `await` --> $DIR/2018-edition-error.rs:9:29 | LL | use self::outer_mod::await::await; - | ^^^^^ help: you can use a raw identifier to stay compatible: `r#await` - -error[E0721]: `await` is a keyword in the 2018 edition - --> $DIR/2018-edition-error.rs:13:11 + | ^^^^^ expected identifier, found reserved keyword +help: you can escape reserved keywords to use them as identifiers | -LL | match await { await => () } - | ^^^^^ help: you can use a raw identifier to stay compatible: `r#await` +LL | use self::outer_mod::await::r#await; + | ^^^^^^^ -error[E0721]: `await` is a keyword in the 2018 edition - --> $DIR/2018-edition-error.rs:13:19 +error: expected `!`, found `{` + --> $DIR/2018-edition-error.rs:13:17 | LL | match await { await => () } - | ^^^^^ help: you can use a raw identifier to stay compatible: `r#await` + | ----- ^ expected `!` + | | + | while parsing this match expression -error: aborting due to 6 previous errors +error: aborting due to 5 previous errors diff --git a/src/test/ui/await-keyword/2018-edition-no-error-with-feature-gate.rs b/src/test/ui/await-keyword/2018-edition-no-error-with-feature-gate.rs deleted file mode 100644 index 52d32c8351080..0000000000000 --- a/src/test/ui/await-keyword/2018-edition-no-error-with-feature-gate.rs +++ /dev/null @@ -1,16 +0,0 @@ -// compile-pass -// edition:2018 - -#![allow(non_camel_case_types)] -#![feature(async_await)] - -mod outer_mod { - pub mod await { - pub struct await; - } -} -use self::outer_mod::await::await; - -fn main() { - match await { await => () } -} diff --git a/src/test/ui/await-keyword/post_expansion_error.rs b/src/test/ui/await-keyword/post_expansion_error.rs index 96dd48052def8..b4c899b0d0295 100644 --- a/src/test/ui/await-keyword/post_expansion_error.rs +++ b/src/test/ui/await-keyword/post_expansion_error.rs @@ -6,5 +6,5 @@ macro_rules! r#await { fn main() { await!() - //~^ ERROR `await` is a keyword + //~^ ERROR expected expression, found `)` } diff --git a/src/test/ui/await-keyword/post_expansion_error.stderr b/src/test/ui/await-keyword/post_expansion_error.stderr index 9483f77422759..0996c38b3b6c6 100644 --- a/src/test/ui/await-keyword/post_expansion_error.stderr +++ b/src/test/ui/await-keyword/post_expansion_error.stderr @@ -1,8 +1,8 @@ -error[E0721]: `await` is a keyword in the 2018 edition - --> $DIR/post_expansion_error.rs:8:5 +error: expected expression, found `)` + --> $DIR/post_expansion_error.rs:8:12 | LL | await!() - | ^^^^^ help: you can use a raw identifier to stay compatible: `r#await` + | ^ expected expression error: aborting due to previous error diff --git a/src/test/ui/feature-gate/await-macro.rs b/src/test/ui/feature-gate/await-macro.rs new file mode 100644 index 0000000000000..291db9ba41370 --- /dev/null +++ b/src/test/ui/feature-gate/await-macro.rs @@ -0,0 +1,12 @@ +// gate-test-await_macro +// edition:2018 + +#![feature(async_await)] + +async fn bar() {} + +async fn foo() { + await!(bar()); //~ ERROR `await!()` macro syntax is unstable, and will soon be removed +} + +fn main() {} diff --git a/src/test/ui/feature-gate/await-macro.stderr b/src/test/ui/feature-gate/await-macro.stderr new file mode 100644 index 0000000000000..b6833655f6d8a --- /dev/null +++ b/src/test/ui/feature-gate/await-macro.stderr @@ -0,0 +1,12 @@ +error[E0658]: `await!()` macro syntax is unstable, and will soon be removed in favor of `.await` syntax. + --> $DIR/await-macro.rs:9:5 + | +LL | await!(bar()); + | ^^^^^ + | + = note: for more information, see https://github.com/rust-lang/rust/issues/50547 + = help: add #![feature(await_macro)] to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/generator/unresolved_type_param.rs b/src/test/ui/generator/unresolved_type_param.rs index f49369b125f6f..77174b0321782 100644 --- a/src/test/ui/generator/unresolved_type_param.rs +++ b/src/test/ui/generator/unresolved_type_param.rs @@ -2,13 +2,14 @@ // Error message should pinpoint the type parameter T as needing to be bound // (rather than give a general error message) // edition:2018 -#![feature(futures_api, async_await, await_macro)] +#![feature(async_await)] async fn bar() -> () {} async fn foo() { - await!(bar()); - //~^ ERROR type inside generator must be known in this context - //~| NOTE cannot infer type for `T` - //~| NOTE the type is part of the generator because of this `yield` + bar().await; + //~^ ERROR type inside generator must be known in this context + //~| NOTE cannot infer type for `T` + //~| NOTE the type is part of the generator because of this `yield` + //~| NOTE in this expansion of desugaring of `await` } fn main() {} diff --git a/src/test/ui/generator/unresolved_type_param.stderr b/src/test/ui/generator/unresolved_type_param.stderr index 57ccdda3f43a1..afb9adf4c77cc 100644 --- a/src/test/ui/generator/unresolved_type_param.stderr +++ b/src/test/ui/generator/unresolved_type_param.stderr @@ -1,15 +1,14 @@ error[E0698]: type inside generator must be known in this context - --> $DIR/unresolved_type_param.rs:9:16 + --> $DIR/unresolved_type_param.rs:9:5 | -LL | await!(bar()); - | ^^^ cannot infer type for `T` +LL | bar().await; + | ^^^ cannot infer type for `T` | note: the type is part of the generator because of this `yield` - --> $DIR/unresolved_type_param.rs:9:9 + --> $DIR/unresolved_type_param.rs:9:5 | -LL | await!(bar()); - | ^^^^^^^^^^^^^^ - = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) +LL | bar().await; + | ^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-51719.rs b/src/test/ui/issues/issue-51719.rs new file mode 100644 index 0000000000000..2c02ac01142bb --- /dev/null +++ b/src/test/ui/issues/issue-51719.rs @@ -0,0 +1,11 @@ +// edition:2018 +// +// Tests that the .await syntax can't be used to make a generator + +#![feature(async_await)] + +async fn foo() {} + +fn make_generator() { + let _gen = || foo.await; //~ ERROR `await` is only allowed inside `async` functions and blocks +} diff --git a/src/test/ui/issues/issue-51719.stderr b/src/test/ui/issues/issue-51719.stderr new file mode 100644 index 0000000000000..768909b66ec77 --- /dev/null +++ b/src/test/ui/issues/issue-51719.stderr @@ -0,0 +1,8 @@ +error[E0728]: `await` is only allowed inside `async` functions and blocks + --> $DIR/issue-51719.rs:10:19 + | +LL | let _gen = || foo.await; + | ^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/issues/issue-51751.rs b/src/test/ui/issues/issue-51751.rs new file mode 100644 index 0000000000000..7afd7ecc82649 --- /dev/null +++ b/src/test/ui/issues/issue-51751.rs @@ -0,0 +1,13 @@ +// edition:2018 + +#![feature(async_await)] + +async fn inc(limit: i64) -> i64 { + limit + 1 +} + +fn main() { + let result = inc(10000); + let finished = result.await; + //~^ ERROR `await` is only allowed inside `async` functions and blocks +} diff --git a/src/test/ui/issues/issue-51751.stderr b/src/test/ui/issues/issue-51751.stderr new file mode 100644 index 0000000000000..0c4cb034a9381 --- /dev/null +++ b/src/test/ui/issues/issue-51751.stderr @@ -0,0 +1,8 @@ +error[E0728]: `await` is only allowed inside `async` functions and blocks + --> $DIR/issue-51751.rs:11:20 + | +LL | let finished = result.await; + | ^^^^^^^^^^^^ + +error: aborting due to previous error +