Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reduce reliance on await!(...) macro #62324

Merged
merged 3 commits into from
Jul 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 28 additions & 24 deletions src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ use crate::tokenstream::TokenTree;

use errors::{Applicability, DiagnosticBuilder, Handler};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync::Lock;
use rustc_target::spec::abi::Abi;
use syntax_pos::{Span, DUMMY_SP, MultiSpan};
use log::debug;
Expand Down Expand Up @@ -573,6 +574,9 @@ declare_features! (
// Allows `impl Trait` with multiple unrelated lifetimes.
(active, member_constraints, "1.37.0", Some(61977), None),

// Allows `async || body` closures.
(active, async_closure, "1.37.0", Some(62290), None),

// -------------------------------------------------------------------------
// feature-group-end: actual feature gates
// -------------------------------------------------------------------------
Expand Down Expand Up @@ -2191,9 +2195,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
"labels on blocks are unstable");
}
}
ast::ExprKind::Closure(_, ast::IsAsync::Async { .. }, ..) => {
gate_feature_post!(&self, async_await, e.span, "async closures are unstable");
}
ast::ExprKind::Async(..) => {
gate_feature_post!(&self, async_await, e.span, "async blocks are unstable");
}
Expand Down Expand Up @@ -2527,6 +2528,10 @@ pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute],
features
}

fn for_each_in_lock<T>(vec: &Lock<Vec<T>>, f: impl Fn(&T)) {
vec.borrow().iter().for_each(f);
}

pub fn check_crate(krate: &ast::Crate,
sess: &ParseSess,
features: &Features,
Expand All @@ -2539,27 +2544,26 @@ pub fn check_crate(krate: &ast::Crate,
plugin_attributes,
};

sess
.param_attr_spans
.borrow()
.iter()
.for_each(|span| gate_feature!(
&ctx,
param_attrs,
*span,
"attributes on function parameters are unstable"
));

sess
.let_chains_spans
.borrow()
.iter()
.for_each(|span| gate_feature!(
&ctx,
let_chains,
*span,
"`let` expressions in this position are experimental"
));
for_each_in_lock(&sess.param_attr_spans, |span| gate_feature!(
&ctx,
param_attrs,
*span,
"attributes on function parameters are unstable"
));

for_each_in_lock(&sess.let_chains_spans, |span| gate_feature!(
&ctx,
let_chains,
*span,
"`let` expressions in this position are experimental"
));

for_each_in_lock(&sess.async_closure_spans, |span| gate_feature!(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How are these not for loops?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's terser and has less repetition this way and I don't really fancy for loops since they invite using break and continue which seems preferable to avoid if possible in favor of a more functional-ish approach.

&ctx,
async_closure,
*span,
"async closures are unstable"
));

let visitor = &mut PostExpansionVisitor {
context: &ctx,
Expand Down
1 change: 1 addition & 0 deletions src/libsyntax/parse/lexer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1474,6 +1474,7 @@ mod tests {
ambiguous_block_expr_parse: Lock::new(FxHashMap::default()),
param_attr_spans: Lock::new(Vec::new()),
let_chains_spans: Lock::new(Vec::new()),
async_closure_spans: Lock::new(Vec::new()),
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/libsyntax/parse/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ pub struct ParseSess {
pub param_attr_spans: Lock<Vec<Span>>,
// Places where `let` exprs were used and should be feature gated according to `let_chains`.
pub let_chains_spans: Lock<Vec<Span>>,
// Places where `async || ..` exprs were used and should be feature gated.
pub async_closure_spans: Lock<Vec<Span>>,
}

impl ParseSess {
Expand Down Expand Up @@ -84,6 +86,7 @@ impl ParseSess {
ambiguous_block_expr_parse: Lock::new(FxHashMap::default()),
param_attr_spans: Lock::new(Vec::new()),
let_chains_spans: Lock::new(Vec::new()),
async_closure_spans: Lock::new(Vec::new()),
}
}

Expand Down
36 changes: 21 additions & 15 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3221,21 +3221,24 @@ impl<'a> Parser<'a> {
-> PResult<'a, P<Expr>>
{
let lo = self.token.span;

let movability = if self.eat_keyword(kw::Static) {
Movability::Static
} else {
Movability::Movable
};

let asyncness = if self.token.span.rust_2018() {
self.parse_asyncness()
} else {
IsAsync::NotAsync
};
let capture_clause = if self.eat_keyword(kw::Move) {
CaptureBy::Value
} else {
CaptureBy::Ref
};
if asyncness.is_async() {
// Feature gate `async ||` closures.
self.sess.async_closure_spans.borrow_mut().push(self.prev_span);
}

let capture_clause = self.parse_capture_clause();
let decl = self.parse_fn_block_decl()?;
let decl_hi = self.prev_span;
let body = match decl.output {
Expand All @@ -3257,7 +3260,7 @@ impl<'a> Parser<'a> {
attrs))
}

// `else` token already eaten
/// `else` token already eaten
fn parse_else_expr(&mut self) -> PResult<'a, P<Expr>> {
if self.eat_keyword(kw::If) {
return self.parse_if_expr(ThinVec::new());
Expand Down Expand Up @@ -3306,7 +3309,7 @@ impl<'a> Parser<'a> {
Ok(self.mk_expr(span, ExprKind::While(cond, body, opt_label), attrs))
}

// parse `loop {...}`, `loop` token already eaten
/// Parse `loop {...}`, `loop` token already eaten.
fn parse_loop_expr(&mut self, opt_label: Option<Label>,
span_lo: Span,
mut attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> {
Expand All @@ -3316,17 +3319,20 @@ impl<'a> Parser<'a> {
Ok(self.mk_expr(span, ExprKind::Loop(body, opt_label), attrs))
}

/// Parses an `async move {...}` expression.
pub fn parse_async_block(&mut self, mut attrs: ThinVec<Attribute>)
-> PResult<'a, P<Expr>>
{
let span_lo = self.token.span;
self.expect_keyword(kw::Async)?;
let capture_clause = if self.eat_keyword(kw::Move) {
/// Parse an optional `move` prefix to a closure lke construct.
fn parse_capture_clause(&mut self) -> CaptureBy {
if self.eat_keyword(kw::Move) {
CaptureBy::Value
} else {
CaptureBy::Ref
};
}
}

/// Parses an `async move? {...}` expression.
pub fn parse_async_block(&mut self, mut attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> {
let span_lo = self.token.span;
self.expect_keyword(kw::Async)?;
let capture_clause = self.parse_capture_clause();
let (iattrs, body) = self.parse_inner_attrs_and_block()?;
attrs.extend(iattrs);
Ok(self.mk_expr(
Expand Down
1 change: 1 addition & 0 deletions src/libsyntax_pos/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ symbols! {
associated_type_defaults,
associated_types,
async_await,
async_closure,
attr,
attributes,
attr_literals,
Expand Down
16 changes: 8 additions & 8 deletions src/test/run-pass/async-await/async-fn-size.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// edition:2018

#![feature(async_await, await_macro)]
#![feature(async_await)]

#[path = "../auxiliary/arc_wake.rs"]
mod arc_wake;
Expand Down Expand Up @@ -58,31 +58,31 @@ fn wait(fut: impl Future<Output = u8>) -> u8 {
fn base() -> WakeOnceThenComplete { WakeOnceThenComplete(false, 1) }

async fn await1_level1() -> u8 {
await!(base())
base().await
}

async fn await2_level1() -> u8 {
await!(base()) + await!(base())
base().await + base().await
}

async fn await3_level1() -> u8 {
await!(base()) + await!(base()) + await!(base())
base().await + base().await + base().await
}

async fn await3_level2() -> u8 {
await!(await3_level1()) + await!(await3_level1()) + await!(await3_level1())
await3_level1().await + await3_level1().await + await3_level1().await
}

async fn await3_level3() -> u8 {
await!(await3_level2()) + await!(await3_level2()) + await!(await3_level2())
await3_level2().await + await3_level2().await + await3_level2().await
}

async fn await3_level4() -> u8 {
await!(await3_level3()) + await!(await3_level3()) + await!(await3_level3())
await3_level3().await + await3_level3().await + await3_level3().await
}

async fn await3_level5() -> u8 {
await!(await3_level4()) + await!(await3_level4()) + await!(await3_level4())
await3_level4().await + await3_level4().await + await3_level4().await
}

fn main() {
Expand Down
4 changes: 2 additions & 2 deletions src/test/run-pass/async-await/issue-60709.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// handled incorrectly in generators.
// compile-flags: -Copt-level=z -Cdebuginfo=2 --edition=2018

#![feature(async_await, await_macro)]
#![feature(async_await)]
#![allow(unused)]

use std::future::Future;
Expand All @@ -22,7 +22,7 @@ impl Future for Never {
fn main() {
let fut = async {
let _rc = Rc::new(()); // Also crashes with Arc
await!(Never());
Never().await;
};
let _bla = fut; // Moving the future is required.
}
8 changes: 0 additions & 8 deletions src/test/ui/async-await/async-await.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,6 @@ fn async_nonmove_block(x: u8) -> impl Future<Output = u8> {
}
}

fn async_closure(x: u8) -> impl Future<Output = u8> {
(async move |x: u8| -> u8 {
wake_and_yield_once().await;
x
})(x)
}

async fn async_fn(x: u8) -> u8 {
wake_and_yield_once().await;
x
Expand Down Expand Up @@ -180,7 +173,6 @@ fn main() {
test! {
async_block,
async_nonmove_block,
async_closure,
async_fn,
generic_async_fn,
async_fn_with_internal_borrow,
Expand Down
12 changes: 12 additions & 0 deletions src/test/ui/async-await/async-closure-matches-expr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// compile-pass
// edition:2018

#![feature(async_await, async_closure)]

macro_rules! match_expr {
($x:expr) => {}
}

fn main() {
match_expr!(async || {});
}
81 changes: 81 additions & 0 deletions src/test/ui/async-await/async-closure.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// run-pass

// edition:2018
// aux-build:arc_wake.rs

#![feature(async_await, async_closure)]

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>) {
Self::wake_by_ref(&self)
}
fn wake_by_ref(arc_self: &Arc<Self>) {
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_closure(x: u8) -> impl Future<Output = u8> {
(async move |x: u8| -> u8 {
wake_and_yield_once().await;
x
})(x)
}

fn test_future_yields_once_then_returns<F, Fut>(f: F)
where
F: FnOnce(u8) -> Fut,
Fut: Future<Output = u8>,
{
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);
)* }
}

test! {
async_closure,
}
}
2 changes: 1 addition & 1 deletion src/test/ui/async-await/async-fn-path-elision.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// edition:2018

#![feature(async_await, await_macro)]
#![feature(async_await)]
#![allow(dead_code)]

struct HasLifetime<'a>(&'a bool);
Expand Down
Loading