Skip to content

Commit

Permalink
Implement built-in await syntax
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
cramertj committed May 7, 2019
1 parent c3b8ab5 commit fe8760c
Show file tree
Hide file tree
Showing 37 changed files with 931 additions and 165 deletions.
2 changes: 2 additions & 0 deletions src/librustc/error_codes.rs
Expand Up @@ -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
}
306 changes: 275 additions & 31 deletions src/librustc/hir/lowering.rs

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions src/librustc/hir/mod.rs
Expand Up @@ -1606,6 +1606,8 @@ pub enum LocalSource {
/// }
/// ```
AsyncFn,
/// A desugared `<expr>.await`.
AwaitDesugar,
}

/// Hints at the original code for a `match _ { .. }`.
Expand All @@ -1624,6 +1626,8 @@ pub enum MatchSource {
ForLoopDesugar,
/// A desugared `?` operator.
TryDesugar,
/// A desugared `<expr>.await`.
AwaitDesugar,
}

/// The loop type that yielded an `ExprKind::Loop`.
Expand Down
1 change: 1 addition & 0 deletions src/librustc/ich/impls_syntax.rs
Expand Up @@ -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,
Expand Down
43 changes: 5 additions & 38 deletions src/librustc_lint/builtin.rs
Expand Up @@ -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};

Expand Down Expand Up @@ -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
Expand All @@ -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;
}

Expand Down
6 changes: 4 additions & 2 deletions src/librustc_mir/hair/pattern/check_match.rs
Expand Up @@ -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.
Expand Down Expand Up @@ -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 => {}
}
}
Expand Down
13 changes: 13 additions & 0 deletions src/libsyntax/ast.rs
Expand Up @@ -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,
Expand Down Expand Up @@ -1186,6 +1187,9 @@ pub enum ExprKind {
/// created during lowering cannot be made the parent of any other
/// preexisting defs.
Async(CaptureBy, NodeId, P<Block>),
/// An await expression (`my_future.await`).
Await(AwaitOrigin, P<Expr>),

/// A try block (`try { ... }`).
TryBlock(P<Block>),

Expand Down Expand Up @@ -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<Mac_>;

/// Represents a macro invocation. The `Path` indicates which macro
Expand Down
18 changes: 18 additions & 0 deletions src/libsyntax/feature_gate.rs
Expand Up @@ -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),

Expand Down Expand Up @@ -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!(<expr>)` macro syntax is unstable, and will soon be removed \
in favor of `<expr>.await` syntax."
),
}
}
_ => {}
}
visit::walk_expr(self, e);
Expand Down
1 change: 1 addition & 0 deletions src/libsyntax/mut_visit.rs
Expand Up @@ -1185,6 +1185,7 @@ pub fn noop_visit_expr<T: MutVisitor>(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);
Expand Down
17 changes: 17 additions & 0 deletions src/libsyntax/parse/parser.rs
Expand Up @@ -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)?;

Expand Down Expand Up @@ -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<Expr>, lo: Span) -> PResult<'a, P<Expr>> {
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));

Expand Down
5 changes: 5 additions & 0 deletions src/libsyntax/parse/token.rs
Expand Up @@ -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(),
Expand Down
12 changes: 12 additions & 0 deletions src/libsyntax/print/pprust.rs
Expand Up @@ -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)?;
Expand Down
3 changes: 3 additions & 0 deletions src/libsyntax/util/parser.rs
Expand Up @@ -267,6 +267,7 @@ pub enum ExprPrecedence {
TryBlock,
Struct,
Async,
Await,
Err,
}

Expand Down Expand Up @@ -301,6 +302,7 @@ impl ExprPrecedence {
ExprPrecedence::Unary => PREC_PREFIX,

// Unary, postfix
ExprPrecedence::Await |
ExprPrecedence::Call |
ExprPrecedence::MethodCall |
ExprPrecedence::Field |
Expand Down Expand Up @@ -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, _) |
Expand Down
1 change: 1 addition & 0 deletions src/libsyntax/visit.rs
Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions src/libsyntax_pos/hygiene.rs
Expand Up @@ -598,13 +598,15 @@ pub enum CompilerDesugaringKind {
/// `impl Trait` with `Foo`.
ExistentialReturnType,
Async,
Await,
ForLoop,
}

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",
Expand Down
1 change: 1 addition & 0 deletions src/libsyntax_pos/symbol.rs
Expand Up @@ -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
Expand Down

0 comments on commit fe8760c

Please sign in to comment.