Skip to content

Commit

Permalink
Add more tests for async/await
Browse files Browse the repository at this point in the history
  • Loading branch information
cramertj committed Jun 25, 2019
1 parent bbbb3e5 commit ba12e78
Show file tree
Hide file tree
Showing 8 changed files with 338 additions and 0 deletions.
59 changes: 59 additions & 0 deletions src/test/ui/async-await/async-fn-nonsend.rs
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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

0 comments on commit ba12e78

Please sign in to comment.