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

Add more tests for async/await #62106

Merged
merged 1 commit into from
Jun 28, 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
59 changes: 59 additions & 0 deletions src/test/ui/async-await/async-fn-nonsend.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// compile-fail
// edition:2018
// compile-flags: --crate-type lib

#![feature(async_await)]

use std::{
cell::RefCell,
fmt::Debug,
rc::Rc,
};

fn non_sync() -> impl Debug { RefCell::new(()) }

fn non_send() -> impl Debug { Rc::new(()) }

fn take_ref<T>(_: &T) {}

async fn fut() {}

async fn fut_arg<T>(_: T) {}

async fn local_dropped_before_await() {
// FIXME: it'd be nice for this to be allowed in a `Send` `async fn`
let x = non_send();
drop(x);
fut().await;
}

async fn non_send_temporary_in_match() {
// We could theoretically make this work as well (produce a `Send` future)
// for scrutinees / temporaries that can or will
// be dropped prior to the match body
// (e.g. `Copy` types).
match Some(non_send()) {
Some(_) => fut().await,
None => {}
}
}

async fn non_sync_with_method_call() {
// FIXME: it'd be nice for this to work.
let f: &mut std::fmt::Formatter = panic!();
if non_sync().fmt(f).unwrap() == () {
fut().await;
}
}

fn assert_send(_: impl Send) {}

pub fn pass_assert() {
assert_send(local_dropped_before_await());
//~^ ERROR `std::rc::Rc<()>` cannot be sent between threads safely
assert_send(non_send_temporary_in_match());
//~^ ERROR `std::rc::Rc<()>` cannot be sent between threads safely
assert_send(non_sync_with_method_call());
//~^ ERROR `dyn std::fmt::Write` cannot be sent between threads safely
//~^^ ERROR `*mut (dyn std::ops::Fn() + 'static)` cannot be shared between threads safely
}
87 changes: 87 additions & 0 deletions src/test/ui/async-await/async-fn-nonsend.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
error[E0277]: `std::rc::Rc<()>` cannot be sent between threads safely
--> $DIR/async-fn-nonsend.rs:52:5
|
LL | assert_send(local_dropped_before_await());
| ^^^^^^^^^^^ `std::rc::Rc<()>` cannot be sent between threads safely
|
= help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<()>`
= note: required because it appears within the type `impl std::fmt::Debug`
= note: required because it appears within the type `{impl std::fmt::Debug, impl std::future::Future, ()}`
= note: required because it appears within the type `[static generator@$DIR/async-fn-nonsend.rs:23:39: 28:2 {impl std::fmt::Debug, impl std::future::Future, ()}]`
= note: required because it appears within the type `std::future::GenFuture<[static generator@$DIR/async-fn-nonsend.rs:23:39: 28:2 {impl std::fmt::Debug, impl std::future::Future, ()}]>`
= note: required because it appears within the type `impl std::future::Future`
= note: required because it appears within the type `impl std::future::Future`
note: required by `assert_send`
--> $DIR/async-fn-nonsend.rs:49:1
|
LL | fn assert_send(_: impl Send) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0277]: `std::rc::Rc<()>` cannot be sent between threads safely
--> $DIR/async-fn-nonsend.rs:54:5
|
LL | assert_send(non_send_temporary_in_match());
| ^^^^^^^^^^^ `std::rc::Rc<()>` cannot be sent between threads safely
|
= help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<()>`
= note: required because it appears within the type `impl std::fmt::Debug`
= note: required because it appears within the type `{fn(impl std::fmt::Debug) -> std::option::Option<impl std::fmt::Debug> {std::option::Option::<impl std::fmt::Debug>::Some}, fn() -> impl std::fmt::Debug {non_send}, impl std::fmt::Debug, std::option::Option<impl std::fmt::Debug>, impl std::future::Future, ()}`
= note: required because it appears within the type `[static generator@$DIR/async-fn-nonsend.rs:30:40: 39:2 {fn(impl std::fmt::Debug) -> std::option::Option<impl std::fmt::Debug> {std::option::Option::<impl std::fmt::Debug>::Some}, fn() -> impl std::fmt::Debug {non_send}, impl std::fmt::Debug, std::option::Option<impl std::fmt::Debug>, impl std::future::Future, ()}]`
= note: required because it appears within the type `std::future::GenFuture<[static generator@$DIR/async-fn-nonsend.rs:30:40: 39:2 {fn(impl std::fmt::Debug) -> std::option::Option<impl std::fmt::Debug> {std::option::Option::<impl std::fmt::Debug>::Some}, fn() -> impl std::fmt::Debug {non_send}, impl std::fmt::Debug, std::option::Option<impl std::fmt::Debug>, impl std::future::Future, ()}]>`
= note: required because it appears within the type `impl std::future::Future`
= note: required because it appears within the type `impl std::future::Future`
note: required by `assert_send`
--> $DIR/async-fn-nonsend.rs:49:1
|
LL | fn assert_send(_: impl Send) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0277]: `dyn std::fmt::Write` cannot be sent between threads safely
--> $DIR/async-fn-nonsend.rs:56:5
|
LL | assert_send(non_sync_with_method_call());
| ^^^^^^^^^^^ `dyn std::fmt::Write` cannot be sent between threads safely
|
= help: the trait `std::marker::Send` is not implemented for `dyn std::fmt::Write`
= note: required because of the requirements on the impl of `std::marker::Send` for `&mut dyn std::fmt::Write`
= note: required because it appears within the type `std::fmt::Formatter<'_>`
= note: required because of the requirements on the impl of `std::marker::Send` for `&mut std::fmt::Formatter<'_>`
= note: required because it appears within the type `for<'r, 's> {&'r mut std::fmt::Formatter<'s>, bool, impl std::future::Future, ()}`
= note: required because it appears within the type `[static generator@$DIR/async-fn-nonsend.rs:41:38: 47:2 for<'r, 's> {&'r mut std::fmt::Formatter<'s>, bool, impl std::future::Future, ()}]`
= note: required because it appears within the type `std::future::GenFuture<[static generator@$DIR/async-fn-nonsend.rs:41:38: 47:2 for<'r, 's> {&'r mut std::fmt::Formatter<'s>, bool, impl std::future::Future, ()}]>`
= note: required because it appears within the type `impl std::future::Future`
= note: required because it appears within the type `impl std::future::Future`
note: required by `assert_send`
--> $DIR/async-fn-nonsend.rs:49:1
|
LL | fn assert_send(_: impl Send) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0277]: `*mut (dyn std::ops::Fn() + 'static)` cannot be shared between threads safely
--> $DIR/async-fn-nonsend.rs:56:5
|
LL | assert_send(non_sync_with_method_call());
| ^^^^^^^^^^^ `*mut (dyn std::ops::Fn() + 'static)` cannot be shared between threads safely
|
= help: within `std::fmt::ArgumentV1<'_>`, the trait `std::marker::Sync` is not implemented for `*mut (dyn std::ops::Fn() + 'static)`
= note: required because it appears within the type `std::marker::PhantomData<*mut (dyn std::ops::Fn() + 'static)>`
= note: required because it appears within the type `core::fmt::Void`
= note: required because it appears within the type `&core::fmt::Void`
= note: required because it appears within the type `std::fmt::ArgumentV1<'_>`
= note: required because of the requirements on the impl of `std::marker::Send` for `std::slice::Iter<'_, std::fmt::ArgumentV1<'_>>`
= note: required because it appears within the type `std::fmt::Formatter<'_>`
= note: required because of the requirements on the impl of `std::marker::Send` for `&mut std::fmt::Formatter<'_>`
= note: required because it appears within the type `for<'r, 's> {&'r mut std::fmt::Formatter<'s>, bool, impl std::future::Future, ()}`
= note: required because it appears within the type `[static generator@$DIR/async-fn-nonsend.rs:41:38: 47:2 for<'r, 's> {&'r mut std::fmt::Formatter<'s>, bool, impl std::future::Future, ()}]`
= note: required because it appears within the type `std::future::GenFuture<[static generator@$DIR/async-fn-nonsend.rs:41:38: 47:2 for<'r, 's> {&'r mut std::fmt::Formatter<'s>, bool, impl std::future::Future, ()}]>`
= note: required because it appears within the type `impl std::future::Future`
= note: required because it appears within the type `impl std::future::Future`
note: required by `assert_send`
--> $DIR/async-fn-nonsend.rs:49:1
|
LL | fn assert_send(_: impl Send) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 4 previous errors

For more information about this error, try `rustc --explain E0277`.
59 changes: 59 additions & 0 deletions src/test/ui/async-await/async-fn-send-uses-nonsend.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// compile-pass
// edition:2018
// compile-flags: --crate-type lib

#![feature(async_await)]

use std::{
cell::RefCell,
fmt::Debug,
rc::Rc,
};

fn non_sync() -> impl Debug { RefCell::new(()) }

fn non_send() -> impl Debug { Rc::new(()) }

fn take_ref<T>(_: &T) {}

async fn fut() {}

async fn fut_arg<T>(_: T) {}

async fn still_send() {
fut().await;
println!("{:?} {:?}", non_send(), non_sync());
fut().await;
drop(non_send());
drop(non_sync());
fut().await;
fut_arg(non_sync()).await;

// Note: all temporaries in `if let` and `match` scrutinee
// are dropped at the *end* of the blocks, so using `non_send()`
// in either of those positions with an await in the middle will
// cause a `!Send` future. It might be nice in the future to allow
// this for `Copy` types, since they can be "dropped" early without
// affecting the end user.
if let Some(_) = Some(non_sync()) {
fut().await;
}
match Some(non_sync()) {
Some(_) => fut().await,
None => fut().await,
}

let _ = non_send();
fut().await;

{
let _x = non_send();
}
fut().await;
}

fn assert_send(_: impl Send) {}

pub fn pass_assert() {
assert_send(still_send());
}
90 changes: 90 additions & 0 deletions src/test/ui/async-await/generics-and-bounds.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// compile-pass
// edition:2018
// compile-flags: --crate-type lib

#![feature(async_await)]

use std::future::Future;

pub async fn simple_generic<T>() {}

pub trait Foo {
fn foo(&self) {}
}

struct FooType;
impl Foo for FooType {}

pub async fn call_generic_bound<F: Foo>(f: F) {
f.foo()
}

pub async fn call_where_clause<F>(f: F)
where
F: Foo,
{
f.foo()
}

pub async fn call_impl_trait(f: impl Foo) {
f.foo()
}

pub async fn call_with_ref(f: &impl Foo) {
f.foo()
}

pub fn async_fn_with_same_generic_params_unifies() {
let mut a = call_generic_bound(FooType);
a = call_generic_bound(FooType);

let mut b = call_where_clause(FooType);
b = call_where_clause(FooType);

let mut c = call_impl_trait(FooType);
c = call_impl_trait(FooType);

let f_one = FooType;
let f_two = FooType;
let mut d = call_with_ref(&f_one);
d = call_with_ref(&f_two);
}

pub fn simple_generic_block<T>() -> impl Future<Output = ()> {
async move {}
}

pub fn call_generic_bound_block<F: Foo>(f: F) -> impl Future<Output = ()> {
async move { f.foo() }
}

pub fn call_where_clause_block<F>(f: F) -> impl Future<Output = ()>
where
F: Foo,
{
async move { f.foo() }
}

pub fn call_impl_trait_block(f: impl Foo) -> impl Future<Output = ()> {
async move { f.foo() }
}

pub fn call_with_ref_block<'a>(f: &'a (impl Foo + 'a)) -> impl Future<Output = ()> + 'a {
async move { f.foo() }
}

pub fn async_block_with_same_generic_params_unifies() {
let mut a = call_generic_bound_block(FooType);
a = call_generic_bound_block(FooType);

let mut b = call_where_clause_block(FooType);
b = call_where_clause_block(FooType);

let mut c = call_impl_trait_block(FooType);
c = call_impl_trait_block(FooType);

let f_one = FooType;
let f_two = FooType;
let mut d = call_with_ref_block(&f_one);
d = call_with_ref_block(&f_two);
}
8 changes: 8 additions & 0 deletions src/test/ui/async-await/no-async-const.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// compile-fail
// edition:2018
// compile-flags: --crate-type lib

#![feature(async_await)]

pub async const fn x() {}
//~^ ERROR expected one of `fn` or `unsafe`, found `const`
8 changes: 8 additions & 0 deletions src/test/ui/async-await/no-async-const.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
error: expected one of `fn` or `unsafe`, found `const`
--> $DIR/no-async-const.rs:7:11
|
LL | pub async const fn x() {}
| ^^^^^ expected one of `fn` or `unsafe` here

error: aborting due to previous error

9 changes: 9 additions & 0 deletions src/test/ui/async-await/no-const-async.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// compile-fail
// edition:2018
// compile-flags: --crate-type lib

#![feature(async_await)]

pub const async fn x() {}
//~^ ERROR expected identifier, found reserved keyword `async`
//~^^ expected `:`, found keyword `fn`
18 changes: 18 additions & 0 deletions src/test/ui/async-await/no-const-async.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
error: expected identifier, found reserved keyword `async`
--> $DIR/no-const-async.rs:7:11
|
LL | pub const async fn x() {}
| ^^^^^ expected identifier, found reserved keyword
help: you can escape reserved keywords to use them as identifiers
|
LL | pub const r#async fn x() {}
| ^^^^^^^

error: expected `:`, found keyword `fn`
--> $DIR/no-const-async.rs:7:17
|
LL | pub const async fn x() {}
| ^^ expected `:`

error: aborting due to 2 previous errors