diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index fd1cb3d803b09..c68280f2e1100 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -580,6 +580,9 @@ declare_features! ( // Allows the use of `#[cfg(doctest)]`, set when rustdoc is collecting doctests (active, cfg_doctest, "1.37.0", Some(62210), None), + // Allows `async unsafe` functions. + (active, async_unsafe, "1.38.0", Some(62501), None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- @@ -2585,6 +2588,13 @@ pub fn check_crate(krate: &ast::Crate, "async closures are unstable" )); + for_each_in_lock(&sess.async_unsafe_spans, |span| gate_feature!( + &ctx, + async_unsafe, + *span, + "async unsafe functions are unstable" + )); + let visitor = &mut PostExpansionVisitor { context: &ctx, builtin_attributes: &*BUILTIN_ATTRIBUTE_MAP, diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs index d0c4e8d6a5634..4ecd198f28af5 100644 --- a/src/libsyntax/parse/lexer/mod.rs +++ b/src/libsyntax/parse/lexer/mod.rs @@ -1388,6 +1388,7 @@ mod tests { param_attr_spans: Lock::new(Vec::new()), let_chains_spans: Lock::new(Vec::new()), async_closure_spans: Lock::new(Vec::new()), + async_unsafe_spans: Lock::new(Vec::new()), } } diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index 4c4551b1757ac..cbfa0620391e5 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -59,6 +59,8 @@ pub struct ParseSess { pub let_chains_spans: Lock>, // Places where `async || ..` exprs were used and should be feature gated. pub async_closure_spans: Lock>, + // Places where `async unsafe fn` was used and should be feature gated. + pub async_unsafe_spans: Lock>, } impl ParseSess { @@ -87,6 +89,7 @@ impl ParseSess { param_attr_spans: Lock::new(Vec::new()), let_chains_spans: Lock::new(Vec::new()), async_closure_spans: Lock::new(Vec::new()), + async_unsafe_spans: Lock::new(Vec::new()), } } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index a95b6891fb9d5..55e1b2494d548 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -5690,11 +5690,16 @@ impl<'a> Parser<'a> { let is_const_fn = self.eat_keyword(kw::Const); let const_span = self.prev_span; let asyncness = self.parse_asyncness(); - if let IsAsync::Async { .. } = asyncness { + let async_span = self.prev_span; + if asyncness.is_async() { self.ban_async_in_2015(self.prev_span); } - let asyncness = respan(self.prev_span, asyncness); let unsafety = self.parse_unsafety(); + if asyncness.is_async() && unsafety == Unsafety::Unsafe { + // Feature gate `async unsafe` functions. + self.sess.async_unsafe_spans.borrow_mut().push(self.prev_span); + } + let asyncness = respan(async_span, asyncness); let (constness, unsafety, abi) = if is_const_fn { (respan(const_span, Constness::Const), unsafety, Abi::Rust) } else { @@ -7196,6 +7201,10 @@ impl<'a> Parser<'a> { // ASYNC FUNCTION ITEM self.bump(); // `async` let unsafety = self.parse_unsafety(); // `unsafe`? + if unsafety == Unsafety::Unsafe { + // Feature gate `async unsafe` functions. + self.sess.async_unsafe_spans.borrow_mut().push(self.prev_span); + } self.expect_keyword(kw::Fn)?; // `fn` let fn_span = self.prev_span; let (ident, item_, extra_attrs) = diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index 89fcf3b1f8f19..f8b1352b63070 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -148,6 +148,7 @@ symbols! { associated_types, async_await, async_closure, + async_unsafe, attr, attributes, attr_literals, diff --git a/src/test/ui/async-await/async-await.rs b/src/test/ui/async-await/async-await.rs index 29622c9d030a3..000a932bb5d57 100644 --- a/src/test/ui/async-await/async-await.rs +++ b/src/test/ui/async-await/async-await.rs @@ -2,12 +2,13 @@ // edition:2018 // aux-build:arc_wake.rs +// aux-build:wake_once.rs #![feature(async_await)] extern crate arc_wake; +extern crate wake_once; -use std::pin::Pin; use std::future::Future; use std::sync::{ Arc, @@ -15,6 +16,7 @@ use std::sync::{ }; use std::task::{Context, Poll}; use arc_wake::ArcWake; +use wake_once::wake_and_yield_once; struct Counter { wakes: AtomicUsize, @@ -29,23 +31,6 @@ impl ArcWake for Counter { } } -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 { wake_and_yield_once().await; @@ -115,29 +100,6 @@ fn async_fn_with_internal_borrow(y: u8) -> impl Future { } } -async unsafe fn unsafe_async_fn(x: u8) -> u8 { - wake_and_yield_once().await; - x -} - -struct Foo; - -trait Bar { - fn foo() {} -} - -impl Foo { - async fn async_assoc_item(x: u8) -> u8 { - unsafe { - unsafe_async_fn(x).await - } - } - - async unsafe fn async_unsafe_assoc_item(x: u8) -> u8 { - unsafe_async_fn(x).await - } -} - fn test_future_yields_once_then_returns(f: F) where F: FnOnce(u8) -> Fut, @@ -176,17 +138,6 @@ fn main() { async_fn, generic_async_fn, async_fn_with_internal_borrow, - Foo::async_assoc_item, - |x| { - async move { - unsafe { unsafe_async_fn(x).await } - } - }, - |x| { - async move { - unsafe { Foo::async_unsafe_assoc_item(x).await } - } - }, } test_with_borrow! { async_block_with_borrow_named_lifetime, diff --git a/src/test/ui/async-await/async-unsafe-await.rs b/src/test/ui/async-await/async-unsafe-await.rs new file mode 100644 index 0000000000000..43617d7a82c5d --- /dev/null +++ b/src/test/ui/async-await/async-unsafe-await.rs @@ -0,0 +1,92 @@ +// run-pass + +// edition:2018 +// aux-build:arc_wake.rs +// aux-build:wake_once.rs + +#![feature(async_await, async_unsafe)] + +extern crate arc_wake; +extern crate wake_once; + +use std::future::Future; +use std::sync::{ + Arc, + atomic::{self, AtomicUsize}, +}; +use std::task::{Context, Poll}; +use arc_wake::ArcWake; +use wake_once::wake_and_yield_once; + +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); + } +} + +async unsafe fn unsafe_async_fn(x: u8) -> u8 { + wake_and_yield_once().await; + x +} + +struct Foo; + +trait Bar { + fn foo() {} +} + +impl Foo { + async fn async_assoc_item(x: u8) -> u8 { + unsafe { + unsafe_async_fn(x).await + } + } + + async unsafe fn async_unsafe_assoc_item(x: u8) -> u8 { + unsafe_async_fn(x).await + } +} + +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); + )* } + } + + test! { + Foo::async_assoc_item, + |x| { + async move { + unsafe { unsafe_async_fn(x).await } + } + }, + |x| { + async move { + unsafe { Foo::async_unsafe_assoc_item(x).await } + } + }, + } +} diff --git a/src/test/ui/async-await/async-unsafe-macro.rs b/src/test/ui/async-await/async-unsafe-macro.rs new file mode 100644 index 0000000000000..fb992f52b5b56 --- /dev/null +++ b/src/test/ui/async-await/async-unsafe-macro.rs @@ -0,0 +1,92 @@ +// run-pass + +// edition:2018 +// aux-build:arc_wake.rs +// aux-build:wake_once.rs + +#![feature(async_await, async_unsafe, await_macro)] + +extern crate arc_wake; +extern crate wake_once; + +use std::future::Future; +use std::sync::{ + Arc, + atomic::{self, AtomicUsize}, +}; +use std::task::{Context, Poll}; +use arc_wake::ArcWake; +use wake_once::wake_and_yield_once; + +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); + } +} + +async unsafe fn unsafe_async_fn(x: u8) -> u8 { + await!(wake_and_yield_once()); + x +} + +struct Foo; + +trait Bar { + fn foo() {} +} + +impl Foo { + async fn async_assoc_item(x: u8) -> u8 { + unsafe { + await!(unsafe_async_fn(x)) + } + } + + async unsafe fn async_unsafe_assoc_item(x: u8) -> u8 { + 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); + )* } + } + + test! { + Foo::async_assoc_item, + |x| { + async move { + unsafe { await!(unsafe_async_fn(x)) } + } + }, + |x| { + async move { + unsafe { await!(Foo::async_unsafe_assoc_item(x)) } + } + }, + } +} diff --git a/src/test/ui/async-await/auxiliary/wake_once.rs b/src/test/ui/async-await/auxiliary/wake_once.rs new file mode 100644 index 0000000000000..3f9d02fb5863f --- /dev/null +++ b/src/test/ui/async-await/auxiliary/wake_once.rs @@ -0,0 +1,22 @@ +// edition:2018 + +use std::pin::Pin; +use std::future::Future; +use std::task::{Context, Poll}; + +pub struct WakeOnceThenComplete(bool); + +pub 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 + } + } +} diff --git a/src/test/ui/async-await/await-macro.rs b/src/test/ui/async-await/await-macro.rs index c37835d73e92b..90413cb85cfbb 100644 --- a/src/test/ui/async-await/await-macro.rs +++ b/src/test/ui/async-await/await-macro.rs @@ -2,12 +2,13 @@ // edition:2018 // aux-build:arc_wake.rs +// aux-build:wake_once.rs #![feature(async_await, async_closure, await_macro)] extern crate arc_wake; +extern crate wake_once; -use std::pin::Pin; use std::future::Future; use std::sync::{ Arc, @@ -15,6 +16,7 @@ use std::sync::{ }; use std::task::{Context, Poll}; use arc_wake::ArcWake; +use wake_once::wake_and_yield_once; struct Counter { wakes: AtomicUsize, @@ -29,23 +31,6 @@ impl ArcWake for Counter { } } -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()); @@ -122,25 +107,6 @@ fn async_fn_with_internal_borrow(y: u8) -> impl Future { } } -async unsafe 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, @@ -180,12 +146,6 @@ fn main() { 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, diff --git a/src/test/ui/async-await/feature-async-unsafe.rs b/src/test/ui/async-await/feature-async-unsafe.rs new file mode 100644 index 0000000000000..ed797460434c9 --- /dev/null +++ b/src/test/ui/async-await/feature-async-unsafe.rs @@ -0,0 +1,12 @@ +// edition:2018 +// gate-test-async_unsafe + +struct S {} + +impl S { + #[cfg(FALSE)] async unsafe fn f() {} //~ ERROR async unsafe functions are unstable +} + +#[cfg(FALSE)] async unsafe fn g() {} //~ ERROR async unsafe functions are unstable + +fn main() {} diff --git a/src/test/ui/async-await/feature-async-unsafe.stderr b/src/test/ui/async-await/feature-async-unsafe.stderr new file mode 100644 index 0000000000000..a9c243d45dc89 --- /dev/null +++ b/src/test/ui/async-await/feature-async-unsafe.stderr @@ -0,0 +1,21 @@ +error[E0658]: async unsafe functions are unstable + --> $DIR/feature-async-unsafe.rs:7:25 + | +LL | #[cfg(FALSE)] async unsafe fn f() {} + | ^^^^^^ + | + = note: for more information, see https://github.com/rust-lang/rust/issues/62501 + = help: add #![feature(async_unsafe)] to the crate attributes to enable + +error[E0658]: async unsafe functions are unstable + --> $DIR/feature-async-unsafe.rs:10:21 + | +LL | #[cfg(FALSE)] async unsafe fn g() {} + | ^^^^^^ + | + = note: for more information, see https://github.com/rust-lang/rust/issues/62501 + = help: add #![feature(async_unsafe)] to the crate attributes to enable + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`.