diff options
Diffstat (limited to 'tests/ui/coroutine')
196 files changed, 5875 insertions, 0 deletions
diff --git a/tests/ui/coroutine/addassign-yield.rs b/tests/ui/coroutine/addassign-yield.rs new file mode 100644 index 000000000..919a559f8 --- /dev/null +++ b/tests/ui/coroutine/addassign-yield.rs @@ -0,0 +1,35 @@ +// run-pass +// Regression test for broken MIR error (#61442) +// Due to the two possible evaluation orders for +// a '+=' expression (depending on whether or not the 'AddAssign' trait +// is being used), we were failing to account for all types that might +// possibly be live across a yield point. + +#![feature(coroutines)] + +fn foo() { + let _x = static || { + let mut s = String::new(); + s += { yield; "" }; + }; + + let _y = static || { + let x = &mut 0; + *{ yield; x } += match String::new() { _ => 0 }; + }; + + // Please don't ever actually write something like this + let _z = static || { + let x = &mut 0; + *{ + let inner = &mut 1; + *{ yield (); inner } += match String::new() { _ => 1}; + yield; + x + } += match String::new() { _ => 2 }; + }; +} + +fn main() { + foo() +} diff --git a/tests/ui/coroutine/async-coroutine-issue-67158.rs b/tests/ui/coroutine/async-coroutine-issue-67158.rs new file mode 100644 index 000000000..420454656 --- /dev/null +++ b/tests/ui/coroutine/async-coroutine-issue-67158.rs @@ -0,0 +1,6 @@ +#![feature(coroutines)] +// edition:2018 +// Regression test for #67158. +fn main() { + async { yield print!(":C") }; //~ ERROR `async` coroutines are not yet supported +} diff --git a/tests/ui/coroutine/async-coroutine-issue-67158.stderr b/tests/ui/coroutine/async-coroutine-issue-67158.stderr new file mode 100644 index 000000000..d583d3d5e --- /dev/null +++ b/tests/ui/coroutine/async-coroutine-issue-67158.stderr @@ -0,0 +1,9 @@ +error[E0727]: `async` coroutines are not yet supported + --> $DIR/async-coroutine-issue-67158.rs:5:13 + | +LL | async { yield print!(":C") }; + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0727`. diff --git a/tests/ui/coroutine/auto-trait-regions.rs b/tests/ui/coroutine/auto-trait-regions.rs new file mode 100644 index 000000000..5fce70e8e --- /dev/null +++ b/tests/ui/coroutine/auto-trait-regions.rs @@ -0,0 +1,53 @@ +#![feature(coroutines)] +#![feature(auto_traits)] +#![feature(negative_impls)] + +auto trait Foo {} + +struct No; + +impl !Foo for No {} + +struct A<'a, 'b>(&'a mut bool, &'b mut bool, No); + +impl<'a, 'b: 'a> Foo for A<'a, 'b> {} + +struct OnlyFooIfStaticRef(No); +impl Foo for &'static OnlyFooIfStaticRef {} + +struct OnlyFooIfRef(No); +impl<'a> Foo for &'a OnlyFooIfRef {} + +fn assert_foo<T: Foo>(f: T) {} + +fn main() { + // Make sure 'static is erased for coroutine interiors so we can't match it in trait selection + let x: &'static _ = &OnlyFooIfStaticRef(No); + let gen = move || { + let x = x; + yield; + assert_foo(x); + }; + assert_foo(gen); + //~^ ERROR implementation of `Foo` is not general enough + + // Allow impls which matches any lifetime + let x = &OnlyFooIfRef(No); + let gen = move || { + let x = x; + yield; + assert_foo(x); + }; + assert_foo(gen); // ok + + // Disallow impls which relates lifetimes in the coroutine interior + let gen = move || { + let a = A(&mut true, &mut true, No); + //~^ temporary value dropped while borrowed + //~| temporary value dropped while borrowed + yield; + assert_foo(a); + }; + assert_foo(gen); + //~^ ERROR not general enough +} diff --git a/tests/ui/coroutine/auto-trait-regions.stderr b/tests/ui/coroutine/auto-trait-regions.stderr new file mode 100644 index 000000000..a9a0bde2b --- /dev/null +++ b/tests/ui/coroutine/auto-trait-regions.stderr @@ -0,0 +1,55 @@ +error[E0716]: temporary value dropped while borrowed + --> $DIR/auto-trait-regions.rs:45:24 + | +LL | let a = A(&mut true, &mut true, No); + | ^^^^ - temporary value is freed at the end of this statement + | | + | creates a temporary value which is freed while still in use +... +LL | assert_foo(a); + | - borrow later used here + | +help: consider using a `let` binding to create a longer lived value + | +LL ~ let binding = true; +LL ~ let a = A(&mut binding, &mut true, No); + | + +error[E0716]: temporary value dropped while borrowed + --> $DIR/auto-trait-regions.rs:45:35 + | +LL | let a = A(&mut true, &mut true, No); + | ^^^^ - temporary value is freed at the end of this statement + | | + | creates a temporary value which is freed while still in use +... +LL | assert_foo(a); + | - borrow later used here + | +help: consider using a `let` binding to create a longer lived value + | +LL ~ let binding = true; +LL ~ let a = A(&mut true, &mut binding, No); + | + +error: implementation of `Foo` is not general enough + --> $DIR/auto-trait-regions.rs:31:5 + | +LL | assert_foo(gen); + | ^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough + | + = note: `&'0 OnlyFooIfStaticRef` must implement `Foo`, for any lifetime `'0`... + = note: ...but `Foo` is actually implemented for the type `&'static OnlyFooIfStaticRef` + +error: implementation of `Foo` is not general enough + --> $DIR/auto-trait-regions.rs:51:5 + | +LL | assert_foo(gen); + | ^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough + | + = note: `Foo` would have to be implemented for the type `A<'0, '1>`, for any two lifetimes `'0` and `'1`... + = note: ...but `Foo` is actually implemented for the type `A<'_, '2>`, for some specific lifetime `'2` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0716`. diff --git a/tests/ui/coroutine/auxiliary/metadata-sufficient-for-layout.rs b/tests/ui/coroutine/auxiliary/metadata-sufficient-for-layout.rs new file mode 100644 index 000000000..dc0521853 --- /dev/null +++ b/tests/ui/coroutine/auxiliary/metadata-sufficient-for-layout.rs @@ -0,0 +1,11 @@ +// compile-flags: --emit metadata +#![feature(coroutines, coroutine_trait)] + +use std::marker::Unpin; +use std::ops::Coroutine; + +pub fn g() -> impl Coroutine<(), Yield = (), Return = ()> { + || { + yield; + } +} diff --git a/tests/ui/coroutine/auxiliary/unwind-aux.rs b/tests/ui/coroutine/auxiliary/unwind-aux.rs new file mode 100644 index 000000000..215d67691 --- /dev/null +++ b/tests/ui/coroutine/auxiliary/unwind-aux.rs @@ -0,0 +1,11 @@ +// compile-flags: -Cpanic=unwind --crate-type=lib +// no-prefer-dynamic +// edition:2021 + +#![feature(coroutines)] +pub fn run<T>(a: T) { + let _ = move || { + drop(a); + yield; + }; +} diff --git a/tests/ui/coroutine/auxiliary/xcrate-reachable.rs b/tests/ui/coroutine/auxiliary/xcrate-reachable.rs new file mode 100644 index 000000000..673153f06 --- /dev/null +++ b/tests/ui/coroutine/auxiliary/xcrate-reachable.rs @@ -0,0 +1,14 @@ +#![feature(coroutines, coroutine_trait)] + +use std::ops::Coroutine; + +fn msg() -> u32 { + 0 +} + +pub fn foo() -> impl Coroutine<(), Yield = (), Return = u32> { + || { + yield; + return msg(); + } +} diff --git a/tests/ui/coroutine/auxiliary/xcrate.rs b/tests/ui/coroutine/auxiliary/xcrate.rs new file mode 100644 index 000000000..f749a95ad --- /dev/null +++ b/tests/ui/coroutine/auxiliary/xcrate.rs @@ -0,0 +1,18 @@ +#![feature(coroutines, coroutine_trait)] + +use std::marker::Unpin; +use std::ops::Coroutine; + +pub fn foo() -> impl Coroutine<(), Yield = (), Return = ()> { + || { + if false { + yield; + } + } +} + +pub fn bar<T: 'static>(t: T) -> Box<Coroutine<(), Yield = T, Return = ()> + Unpin> { + Box::new(|| { + yield t; + }) +} diff --git a/tests/ui/coroutine/borrow-in-tail-expr.rs b/tests/ui/coroutine/borrow-in-tail-expr.rs new file mode 100644 index 000000000..c1497ad29 --- /dev/null +++ b/tests/ui/coroutine/borrow-in-tail-expr.rs @@ -0,0 +1,11 @@ +// run-pass + +#![feature(coroutines)] + +fn main() { + let _a = || { + yield; + let a = String::new(); + a.len() + }; +} diff --git a/tests/ui/coroutine/borrowing.rs b/tests/ui/coroutine/borrowing.rs new file mode 100644 index 000000000..778eed8bd --- /dev/null +++ b/tests/ui/coroutine/borrowing.rs @@ -0,0 +1,20 @@ +#![feature(coroutines, coroutine_trait)] + +use std::ops::Coroutine; +use std::pin::Pin; + +fn main() { + let _b = { + let a = 3; + Pin::new(&mut || yield &a).resume(()) + //~^ ERROR: `a` does not live long enough + }; + + let _b = { + let a = 3; + || { + yield &a + //~^ ERROR: `a` does not live long enough + } + }; +} diff --git a/tests/ui/coroutine/borrowing.stderr b/tests/ui/coroutine/borrowing.stderr new file mode 100644 index 000000000..acd4cdafd --- /dev/null +++ b/tests/ui/coroutine/borrowing.stderr @@ -0,0 +1,31 @@ +error[E0597]: `a` does not live long enough + --> $DIR/borrowing.rs:9:33 + | +LL | let _b = { + | -- borrow later stored here +LL | let a = 3; +LL | Pin::new(&mut || yield &a).resume(()) + | -- ^ borrowed value does not live long enough + | | + | value captured here by coroutine +LL | +LL | }; + | - `a` dropped here while still borrowed + +error[E0597]: `a` does not live long enough + --> $DIR/borrowing.rs:16:20 + | +LL | let _b = { + | -- borrow later stored here +LL | let a = 3; +LL | || { + | -- value captured here by coroutine +LL | yield &a + | ^ borrowed value does not live long enough +... +LL | }; + | - `a` dropped here while still borrowed + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/coroutine/clone-impl-async.rs b/tests/ui/coroutine/clone-impl-async.rs new file mode 100644 index 000000000..e8e82f199 --- /dev/null +++ b/tests/ui/coroutine/clone-impl-async.rs @@ -0,0 +1,70 @@ +// edition:2021 +// gate-test-coroutine_clone +// Verifies that feature(coroutine_clone) doesn't allow async blocks to be cloned/copied. + +#![feature(coroutines, coroutine_clone)] + +use std::future::ready; + +struct NonClone; + +fn main() { + let inner_non_clone = async { + let non_clone = NonClone; + let () = ready(()).await; + drop(non_clone); + }; + check_copy(&inner_non_clone); + //~^ ERROR : Copy` is not satisfied + check_clone(&inner_non_clone); + //~^ ERROR : Clone` is not satisfied + + let non_clone = NonClone; + let outer_non_clone = async move { + drop(non_clone); + }; + check_copy(&outer_non_clone); + //~^ ERROR : Copy` is not satisfied + check_clone(&outer_non_clone); + //~^ ERROR : Clone` is not satisfied + + let maybe_copy_clone = async move {}; + check_copy(&maybe_copy_clone); + //~^ ERROR : Copy` is not satisfied + check_clone(&maybe_copy_clone); + //~^ ERROR : Clone` is not satisfied + + let inner_non_clone_fn = the_inner_non_clone_fn(); + check_copy(&inner_non_clone_fn); + //~^ ERROR : Copy` is not satisfied + check_clone(&inner_non_clone_fn); + //~^ ERROR : Clone` is not satisfied + + let outer_non_clone_fn = the_outer_non_clone_fn(NonClone); + check_copy(&outer_non_clone_fn); + //~^ ERROR : Copy` is not satisfied + check_clone(&outer_non_clone_fn); + //~^ ERROR : Clone` is not satisfied + + let maybe_copy_clone_fn = the_maybe_copy_clone_fn(); + check_copy(&maybe_copy_clone_fn); + //~^ ERROR : Copy` is not satisfied + check_clone(&maybe_copy_clone_fn); + //~^ ERROR : Clone` is not satisfied +} + +async fn the_inner_non_clone_fn() { + let non_clone = NonClone; + let () = ready(()).await; + drop(non_clone); +} + +async fn the_outer_non_clone_fn(non_clone: NonClone) { + let () = ready(()).await; + drop(non_clone); +} + +async fn the_maybe_copy_clone_fn() {} + +fn check_copy<T: Copy>(_x: &T) {} +fn check_clone<T: Clone>(_x: &T) {} diff --git a/tests/ui/coroutine/clone-impl-async.stderr b/tests/ui/coroutine/clone-impl-async.stderr new file mode 100644 index 000000000..d172dff3a --- /dev/null +++ b/tests/ui/coroutine/clone-impl-async.stderr @@ -0,0 +1,171 @@ +error[E0277]: the trait bound `{async block@$DIR/clone-impl-async.rs:12:27: 16:6}: Copy` is not satisfied + --> $DIR/clone-impl-async.rs:17:16 + | +LL | check_copy(&inner_non_clone); + | ---------- ^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `{async block@$DIR/clone-impl-async.rs:12:27: 16:6}` + | | + | required by a bound introduced by this call + | +note: required by a bound in `check_copy` + --> $DIR/clone-impl-async.rs:69:18 + | +LL | fn check_copy<T: Copy>(_x: &T) {} + | ^^^^ required by this bound in `check_copy` + +error[E0277]: the trait bound `{async block@$DIR/clone-impl-async.rs:12:27: 16:6}: Clone` is not satisfied + --> $DIR/clone-impl-async.rs:19:17 + | +LL | check_clone(&inner_non_clone); + | ----------- ^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `{async block@$DIR/clone-impl-async.rs:12:27: 16:6}` + | | + | required by a bound introduced by this call + | +note: required by a bound in `check_clone` + --> $DIR/clone-impl-async.rs:70:19 + | +LL | fn check_clone<T: Clone>(_x: &T) {} + | ^^^^^ required by this bound in `check_clone` + +error[E0277]: the trait bound `{async block@$DIR/clone-impl-async.rs:23:27: 25:6}: Copy` is not satisfied + --> $DIR/clone-impl-async.rs:26:16 + | +LL | check_copy(&outer_non_clone); + | ---------- ^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `{async block@$DIR/clone-impl-async.rs:23:27: 25:6}` + | | + | required by a bound introduced by this call + | +note: required by a bound in `check_copy` + --> $DIR/clone-impl-async.rs:69:18 + | +LL | fn check_copy<T: Copy>(_x: &T) {} + | ^^^^ required by this bound in `check_copy` + +error[E0277]: the trait bound `{async block@$DIR/clone-impl-async.rs:23:27: 25:6}: Clone` is not satisfied + --> $DIR/clone-impl-async.rs:28:17 + | +LL | check_clone(&outer_non_clone); + | ----------- ^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `{async block@$DIR/clone-impl-async.rs:23:27: 25:6}` + | | + | required by a bound introduced by this call + | +note: required by a bound in `check_clone` + --> $DIR/clone-impl-async.rs:70:19 + | +LL | fn check_clone<T: Clone>(_x: &T) {} + | ^^^^^ required by this bound in `check_clone` + +error[E0277]: the trait bound `{async block@$DIR/clone-impl-async.rs:31:28: 31:41}: Copy` is not satisfied + --> $DIR/clone-impl-async.rs:32:16 + | +LL | check_copy(&maybe_copy_clone); + | ---------- ^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `{async block@$DIR/clone-impl-async.rs:31:28: 31:41}` + | | + | required by a bound introduced by this call + | +note: required by a bound in `check_copy` + --> $DIR/clone-impl-async.rs:69:18 + | +LL | fn check_copy<T: Copy>(_x: &T) {} + | ^^^^ required by this bound in `check_copy` + +error[E0277]: the trait bound `{async block@$DIR/clone-impl-async.rs:31:28: 31:41}: Clone` is not satisfied + --> $DIR/clone-impl-async.rs:34:17 + | +LL | check_clone(&maybe_copy_clone); + | ----------- ^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `{async block@$DIR/clone-impl-async.rs:31:28: 31:41}` + | | + | required by a bound introduced by this call + | +note: required by a bound in `check_clone` + --> $DIR/clone-impl-async.rs:70:19 + | +LL | fn check_clone<T: Clone>(_x: &T) {} + | ^^^^^ required by this bound in `check_clone` + +error[E0277]: the trait bound `impl Future<Output = ()>: Copy` is not satisfied + --> $DIR/clone-impl-async.rs:38:16 + | +LL | check_copy(&inner_non_clone_fn); + | ---------- ^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `impl Future<Output = ()>` + | | + | required by a bound introduced by this call + | +note: required by a bound in `check_copy` + --> $DIR/clone-impl-async.rs:69:18 + | +LL | fn check_copy<T: Copy>(_x: &T) {} + | ^^^^ required by this bound in `check_copy` + +error[E0277]: the trait bound `impl Future<Output = ()>: Clone` is not satisfied + --> $DIR/clone-impl-async.rs:40:17 + | +LL | check_clone(&inner_non_clone_fn); + | ----------- ^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `impl Future<Output = ()>` + | | + | required by a bound introduced by this call + | +note: required by a bound in `check_clone` + --> $DIR/clone-impl-async.rs:70:19 + | +LL | fn check_clone<T: Clone>(_x: &T) {} + | ^^^^^ required by this bound in `check_clone` + +error[E0277]: the trait bound `impl Future<Output = ()>: Copy` is not satisfied + --> $DIR/clone-impl-async.rs:44:16 + | +LL | check_copy(&outer_non_clone_fn); + | ---------- ^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `impl Future<Output = ()>` + | | + | required by a bound introduced by this call + | +note: required by a bound in `check_copy` + --> $DIR/clone-impl-async.rs:69:18 + | +LL | fn check_copy<T: Copy>(_x: &T) {} + | ^^^^ required by this bound in `check_copy` + +error[E0277]: the trait bound `impl Future<Output = ()>: Clone` is not satisfied + --> $DIR/clone-impl-async.rs:46:17 + | +LL | check_clone(&outer_non_clone_fn); + | ----------- ^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `impl Future<Output = ()>` + | | + | required by a bound introduced by this call + | +note: required by a bound in `check_clone` + --> $DIR/clone-impl-async.rs:70:19 + | +LL | fn check_clone<T: Clone>(_x: &T) {} + | ^^^^^ required by this bound in `check_clone` + +error[E0277]: the trait bound `impl Future<Output = ()>: Copy` is not satisfied + --> $DIR/clone-impl-async.rs:50:16 + | +LL | check_copy(&maybe_copy_clone_fn); + | ---------- ^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `impl Future<Output = ()>` + | | + | required by a bound introduced by this call + | +note: required by a bound in `check_copy` + --> $DIR/clone-impl-async.rs:69:18 + | +LL | fn check_copy<T: Copy>(_x: &T) {} + | ^^^^ required by this bound in `check_copy` + +error[E0277]: the trait bound `impl Future<Output = ()>: Clone` is not satisfied + --> $DIR/clone-impl-async.rs:52:17 + | +LL | check_clone(&maybe_copy_clone_fn); + | ----------- ^^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `impl Future<Output = ()>` + | | + | required by a bound introduced by this call + | +note: required by a bound in `check_clone` + --> $DIR/clone-impl-async.rs:70:19 + | +LL | fn check_clone<T: Clone>(_x: &T) {} + | ^^^^^ required by this bound in `check_clone` + +error: aborting due to 12 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/coroutine/clone-impl-static.rs b/tests/ui/coroutine/clone-impl-static.rs new file mode 100644 index 000000000..9a165cf46 --- /dev/null +++ b/tests/ui/coroutine/clone-impl-static.rs @@ -0,0 +1,17 @@ +// gate-test-coroutine_clone +// Verifies that static coroutines cannot be cloned/copied. + +#![feature(coroutines, coroutine_clone)] + +fn main() { + let gen = static move || { + yield; + }; + check_copy(&gen); + //~^ ERROR Copy` is not satisfied + check_clone(&gen); + //~^ ERROR Clone` is not satisfied +} + +fn check_copy<T: Copy>(_x: &T) {} +fn check_clone<T: Clone>(_x: &T) {} diff --git a/tests/ui/coroutine/clone-impl-static.stderr b/tests/ui/coroutine/clone-impl-static.stderr new file mode 100644 index 000000000..8fa9fb12b --- /dev/null +++ b/tests/ui/coroutine/clone-impl-static.stderr @@ -0,0 +1,31 @@ +error[E0277]: the trait bound `{static coroutine@$DIR/clone-impl-static.rs:7:15: 7:29}: Copy` is not satisfied + --> $DIR/clone-impl-static.rs:10:16 + | +LL | check_copy(&gen); + | ---------- ^^^^ the trait `Copy` is not implemented for `{static coroutine@$DIR/clone-impl-static.rs:7:15: 7:29}` + | | + | required by a bound introduced by this call + | +note: required by a bound in `check_copy` + --> $DIR/clone-impl-static.rs:16:18 + | +LL | fn check_copy<T: Copy>(_x: &T) {} + | ^^^^ required by this bound in `check_copy` + +error[E0277]: the trait bound `{static coroutine@$DIR/clone-impl-static.rs:7:15: 7:29}: Clone` is not satisfied + --> $DIR/clone-impl-static.rs:12:17 + | +LL | check_clone(&gen); + | ----------- ^^^^ the trait `Clone` is not implemented for `{static coroutine@$DIR/clone-impl-static.rs:7:15: 7:29}` + | | + | required by a bound introduced by this call + | +note: required by a bound in `check_clone` + --> $DIR/clone-impl-static.rs:17:19 + | +LL | fn check_clone<T: Clone>(_x: &T) {} + | ^^^^^ required by this bound in `check_clone` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/coroutine/clone-impl.rs b/tests/ui/coroutine/clone-impl.rs new file mode 100644 index 000000000..eed6f851b --- /dev/null +++ b/tests/ui/coroutine/clone-impl.rs @@ -0,0 +1,73 @@ +// gate-test-coroutine_clone +// Verifies that non-static coroutines can be cloned/copied if all their upvars and locals held +// across awaits can be cloned/copied. + +#![feature(coroutines, coroutine_clone)] + +struct NonClone; + +fn main() { + let copyable: u32 = 123; + let clonable_0: Vec<u32> = Vec::new(); + let clonable_1: Vec<u32> = Vec::new(); + let non_clonable: NonClone = NonClone; + + let gen_copy_0 = move || { + yield; + drop(copyable); + }; + check_copy(&gen_copy_0); + check_clone(&gen_copy_0); + let gen_copy_1 = move || { + /* + let v = vec!['a']; + let n = NonClone; + drop(v); + drop(n); + */ + yield; + let v = vec!['a']; + let n = NonClone; + drop(n); + drop(copyable); + }; + check_copy(&gen_copy_1); + check_clone(&gen_copy_1); + let gen_clone_0 = move || { + let v = vec!['a']; + yield; + drop(v); + drop(clonable_0); + }; + check_copy(&gen_clone_0); + //~^ ERROR the trait bound `Vec<u32>: Copy` is not satisfied + //~| ERROR the trait bound `Vec<char>: Copy` is not satisfied + check_clone(&gen_clone_0); + let gen_clone_1 = move || { + let v = vec!['a']; + /* + let n = NonClone; + drop(n); + */ + yield; + let n = NonClone; + drop(n); + drop(v); + drop(clonable_1); + }; + check_copy(&gen_clone_1); + //~^ ERROR the trait bound `Vec<u32>: Copy` is not satisfied + //~| ERROR the trait bound `Vec<char>: Copy` is not satisfied + check_clone(&gen_clone_1); + let gen_non_clone = move || { + yield; + drop(non_clonable); + }; + check_copy(&gen_non_clone); + //~^ ERROR the trait bound `NonClone: Copy` is not satisfied + check_clone(&gen_non_clone); + //~^ ERROR the trait bound `NonClone: Clone` is not satisfied +} + +fn check_copy<T: Copy>(_x: &T) {} +fn check_clone<T: Clone>(_x: &T) {} diff --git a/tests/ui/coroutine/clone-impl.stderr b/tests/ui/coroutine/clone-impl.stderr new file mode 100644 index 000000000..82a6d0495 --- /dev/null +++ b/tests/ui/coroutine/clone-impl.stderr @@ -0,0 +1,138 @@ +error[E0277]: the trait bound `Vec<u32>: Copy` is not satisfied in `{coroutine@$DIR/clone-impl.rs:36:23: 36:30}` + --> $DIR/clone-impl.rs:42:5 + | +LL | let gen_clone_0 = move || { + | ------- within this `{coroutine@$DIR/clone-impl.rs:36:23: 36:30}` +... +LL | check_copy(&gen_clone_0); + | ^^^^^^^^^^ within `{coroutine@$DIR/clone-impl.rs:36:23: 36:30}`, the trait `Copy` is not implemented for `Vec<u32>` + | +note: captured value does not implement `Copy` + --> $DIR/clone-impl.rs:40:14 + | +LL | drop(clonable_0); + | ^^^^^^^^^^ has type `Vec<u32>` which does not implement `Copy` +note: required by a bound in `check_copy` + --> $DIR/clone-impl.rs:72:18 + | +LL | fn check_copy<T: Copy>(_x: &T) {} + | ^^^^ required by this bound in `check_copy` + +error[E0277]: the trait bound `Vec<char>: Copy` is not satisfied in `{coroutine@$DIR/clone-impl.rs:36:23: 36:30}` + --> $DIR/clone-impl.rs:42:5 + | +LL | let gen_clone_0 = move || { + | ------- within this `{coroutine@$DIR/clone-impl.rs:36:23: 36:30}` +... +LL | check_copy(&gen_clone_0); + | ^^^^^^^^^^ within `{coroutine@$DIR/clone-impl.rs:36:23: 36:30}`, the trait `Copy` is not implemented for `Vec<char>` + | +note: coroutine does not implement `Copy` as this value is used across a yield + --> $DIR/clone-impl.rs:38:9 + | +LL | let v = vec!['a']; + | - has type `Vec<char>` which does not implement `Copy` +LL | yield; + | ^^^^^ yield occurs here, with `v` maybe used later +note: required by a bound in `check_copy` + --> $DIR/clone-impl.rs:72:18 + | +LL | fn check_copy<T: Copy>(_x: &T) {} + | ^^^^ required by this bound in `check_copy` + +error[E0277]: the trait bound `Vec<u32>: Copy` is not satisfied in `{coroutine@$DIR/clone-impl.rs:46:23: 46:30}` + --> $DIR/clone-impl.rs:58:5 + | +LL | let gen_clone_1 = move || { + | ------- within this `{coroutine@$DIR/clone-impl.rs:46:23: 46:30}` +... +LL | check_copy(&gen_clone_1); + | ^^^^^^^^^^ within `{coroutine@$DIR/clone-impl.rs:46:23: 46:30}`, the trait `Copy` is not implemented for `Vec<u32>` + | +note: captured value does not implement `Copy` + --> $DIR/clone-impl.rs:56:14 + | +LL | drop(clonable_1); + | ^^^^^^^^^^ has type `Vec<u32>` which does not implement `Copy` +note: required by a bound in `check_copy` + --> $DIR/clone-impl.rs:72:18 + | +LL | fn check_copy<T: Copy>(_x: &T) {} + | ^^^^ required by this bound in `check_copy` + +error[E0277]: the trait bound `Vec<char>: Copy` is not satisfied in `{coroutine@$DIR/clone-impl.rs:46:23: 46:30}` + --> $DIR/clone-impl.rs:58:5 + | +LL | let gen_clone_1 = move || { + | ------- within this `{coroutine@$DIR/clone-impl.rs:46:23: 46:30}` +... +LL | check_copy(&gen_clone_1); + | ^^^^^^^^^^ within `{coroutine@$DIR/clone-impl.rs:46:23: 46:30}`, the trait `Copy` is not implemented for `Vec<char>` + | +note: coroutine does not implement `Copy` as this value is used across a yield + --> $DIR/clone-impl.rs:52:9 + | +LL | let v = vec!['a']; + | - has type `Vec<char>` which does not implement `Copy` +... +LL | yield; + | ^^^^^ yield occurs here, with `v` maybe used later +note: required by a bound in `check_copy` + --> $DIR/clone-impl.rs:72:18 + | +LL | fn check_copy<T: Copy>(_x: &T) {} + | ^^^^ required by this bound in `check_copy` + +error[E0277]: the trait bound `NonClone: Copy` is not satisfied in `{coroutine@$DIR/clone-impl.rs:62:25: 62:32}` + --> $DIR/clone-impl.rs:66:5 + | +LL | let gen_non_clone = move || { + | ------- within this `{coroutine@$DIR/clone-impl.rs:62:25: 62:32}` +... +LL | check_copy(&gen_non_clone); + | ^^^^^^^^^^ within `{coroutine@$DIR/clone-impl.rs:62:25: 62:32}`, the trait `Copy` is not implemented for `NonClone` + | +note: captured value does not implement `Copy` + --> $DIR/clone-impl.rs:64:14 + | +LL | drop(non_clonable); + | ^^^^^^^^^^^^ has type `NonClone` which does not implement `Copy` +note: required by a bound in `check_copy` + --> $DIR/clone-impl.rs:72:18 + | +LL | fn check_copy<T: Copy>(_x: &T) {} + | ^^^^ required by this bound in `check_copy` +help: consider annotating `NonClone` with `#[derive(Copy)]` + | +LL + #[derive(Copy)] +LL | struct NonClone; + | + +error[E0277]: the trait bound `NonClone: Clone` is not satisfied in `{coroutine@$DIR/clone-impl.rs:62:25: 62:32}` + --> $DIR/clone-impl.rs:68:5 + | +LL | let gen_non_clone = move || { + | ------- within this `{coroutine@$DIR/clone-impl.rs:62:25: 62:32}` +... +LL | check_clone(&gen_non_clone); + | ^^^^^^^^^^^ within `{coroutine@$DIR/clone-impl.rs:62:25: 62:32}`, the trait `Clone` is not implemented for `NonClone` + | +note: captured value does not implement `Clone` + --> $DIR/clone-impl.rs:64:14 + | +LL | drop(non_clonable); + | ^^^^^^^^^^^^ has type `NonClone` which does not implement `Clone` +note: required by a bound in `check_clone` + --> $DIR/clone-impl.rs:73:19 + | +LL | fn check_clone<T: Clone>(_x: &T) {} + | ^^^^^ required by this bound in `check_clone` +help: consider annotating `NonClone` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | struct NonClone; + | + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/coroutine/conditional-drop.rs b/tests/ui/coroutine/conditional-drop.rs new file mode 100644 index 000000000..634095c7a --- /dev/null +++ b/tests/ui/coroutine/conditional-drop.rs @@ -0,0 +1,61 @@ +// run-pass + +// revisions: default nomiropt +//[nomiropt]compile-flags: -Z mir-opt-level=0 + +#![feature(coroutines, coroutine_trait)] + +use std::ops::Coroutine; +use std::pin::Pin; +use std::sync::atomic::{AtomicUsize, Ordering}; + +static A: AtomicUsize = AtomicUsize::new(0); + +struct B; + +impl Drop for B { + fn drop(&mut self) { + A.fetch_add(1, Ordering::SeqCst); + } +} + + +fn test() -> bool { true } +fn test2() -> bool { false } + +fn main() { + t1(); + t2(); +} + +fn t1() { + let mut a = || { + let b = B; + if test() { + drop(b); + } + yield; + }; + + let n = A.load(Ordering::SeqCst); + Pin::new(&mut a).resume(()); + assert_eq!(A.load(Ordering::SeqCst), n + 1); + Pin::new(&mut a).resume(()); + assert_eq!(A.load(Ordering::SeqCst), n + 1); +} + +fn t2() { + let mut a = || { + let b = B; + if test2() { + drop(b); + } + yield; + }; + + let n = A.load(Ordering::SeqCst); + Pin::new(&mut a).resume(()); + assert_eq!(A.load(Ordering::SeqCst), n); + Pin::new(&mut a).resume(()); + assert_eq!(A.load(Ordering::SeqCst), n + 1); +} diff --git a/tests/ui/coroutine/control-flow.rs b/tests/ui/coroutine/control-flow.rs new file mode 100644 index 000000000..709b135b2 --- /dev/null +++ b/tests/ui/coroutine/control-flow.rs @@ -0,0 +1,53 @@ +// run-pass + +// revisions: default nomiropt +//[nomiropt]compile-flags: -Z mir-opt-level=0 + +#![feature(coroutines, coroutine_trait)] + +use std::marker::Unpin; +use std::ops::{CoroutineState, Coroutine}; +use std::pin::Pin; + +fn finish<T>(mut amt: usize, mut t: T) -> T::Return + where T: Coroutine<(), Yield = ()> + Unpin, +{ + loop { + match Pin::new(&mut t).resume(()) { + CoroutineState::Yielded(()) => amt = amt.checked_sub(1).unwrap(), + CoroutineState::Complete(ret) => { + assert_eq!(amt, 0); + return ret + } + } + } + +} + +fn main() { + finish(1, || yield); + finish(8, || { + for _ in 0..8 { + yield; + } + }); + finish(1, || { + if true { + yield; + } else { + } + }); + finish(1, || { + if false { + } else { + yield; + } + }); + finish(2, || { + if { yield; false } { + yield; + panic!() + } + yield + }); +} diff --git a/tests/ui/coroutine/coroutine-region-requirements.migrate.stderr b/tests/ui/coroutine/coroutine-region-requirements.migrate.stderr new file mode 100644 index 000000000..8a96d187f --- /dev/null +++ b/tests/ui/coroutine/coroutine-region-requirements.migrate.stderr @@ -0,0 +1,12 @@ +error[E0621]: explicit lifetime required in the type of `x` + --> $DIR/generator-region-requirements.rs:16:51 + | +LL | fn dangle(x: &mut i32) -> &'static mut i32 { + | -------- help: add explicit lifetime `'static` to the type of `x`: `&'static mut i32` +... +LL | GeneratorState::Complete(c) => return c, + | ^ lifetime `'static` required + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0621`. diff --git a/tests/ui/coroutine/coroutine-region-requirements.rs b/tests/ui/coroutine/coroutine-region-requirements.rs new file mode 100644 index 000000000..8bc34fdd2 --- /dev/null +++ b/tests/ui/coroutine/coroutine-region-requirements.rs @@ -0,0 +1,19 @@ +#![feature(coroutines, coroutine_trait)] +use std::ops::{Coroutine, CoroutineState}; +use std::pin::Pin; + +fn dangle(x: &mut i32) -> &'static mut i32 { + let mut g = || { + yield; + x + }; + loop { + match Pin::new(&mut g).resume(()) { + CoroutineState::Complete(c) => return c, + //~^ ERROR lifetime may not live long enough + CoroutineState::Yielded(_) => (), + } + } +} + +fn main() {} diff --git a/tests/ui/coroutine/coroutine-region-requirements.stderr b/tests/ui/coroutine/coroutine-region-requirements.stderr new file mode 100644 index 000000000..ad3183e76 --- /dev/null +++ b/tests/ui/coroutine/coroutine-region-requirements.stderr @@ -0,0 +1,11 @@ +error: lifetime may not live long enough + --> $DIR/coroutine-region-requirements.rs:12:51 + | +LL | fn dangle(x: &mut i32) -> &'static mut i32 { + | - let's call the lifetime of this reference `'1` +... +LL | CoroutineState::Complete(c) => return c, + | ^ returning this value requires that `'1` must outlive `'static` + +error: aborting due to previous error + diff --git a/tests/ui/coroutine/coroutine-resume-after-panic.rs b/tests/ui/coroutine/coroutine-resume-after-panic.rs new file mode 100644 index 000000000..5915f5ad9 --- /dev/null +++ b/tests/ui/coroutine/coroutine-resume-after-panic.rs @@ -0,0 +1,25 @@ +// run-fail +// needs-unwind +// error-pattern:coroutine resumed after panicking +// ignore-emscripten no processes + +// Test that we get the correct message for resuming a panicked coroutine. + +#![feature(coroutines, coroutine_trait)] + +use std::{ + ops::Coroutine, + pin::Pin, + panic, +}; + +fn main() { + let mut g = || { + panic!(); + yield; + }; + panic::catch_unwind(panic::AssertUnwindSafe(|| { + let x = Pin::new(&mut g).resume(()); + })); + Pin::new(&mut g).resume(()); +} diff --git a/tests/ui/coroutine/coroutine-with-nll.rs b/tests/ui/coroutine/coroutine-with-nll.rs new file mode 100644 index 000000000..28a3643fb --- /dev/null +++ b/tests/ui/coroutine/coroutine-with-nll.rs @@ -0,0 +1,12 @@ +#![feature(coroutines)] + +fn main() { + || { + // The reference in `_a` is a Legal with NLL since it ends before the yield + let _a = &mut true; + let b = &mut true; + //~^ borrow may still be in use when coroutine yields + yield (); + println!("{}", b); + }; +} diff --git a/tests/ui/coroutine/coroutine-with-nll.stderr b/tests/ui/coroutine/coroutine-with-nll.stderr new file mode 100644 index 000000000..ed58debe2 --- /dev/null +++ b/tests/ui/coroutine/coroutine-with-nll.stderr @@ -0,0 +1,12 @@ +error[E0626]: borrow may still be in use when coroutine yields + --> $DIR/coroutine-with-nll.rs:7:17 + | +LL | let b = &mut true; + | ^^^^^^^^^ +LL | +LL | yield (); + | -------- possible yield occurs here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0626`. diff --git a/tests/ui/coroutine/coroutine-yielding-or-returning-itself.rs b/tests/ui/coroutine/coroutine-yielding-or-returning-itself.rs new file mode 100644 index 000000000..3c91b3c93 --- /dev/null +++ b/tests/ui/coroutine/coroutine-yielding-or-returning-itself.rs @@ -0,0 +1,35 @@ +#![feature(coroutine_trait)] +#![feature(coroutines)] + +// Test that we cannot create a coroutine that returns a value of its +// own type. + +use std::ops::Coroutine; + +pub fn want_cyclic_coroutine_return<T>(_: T) + where T: Coroutine<Yield = (), Return = T> +{ +} + +fn supply_cyclic_coroutine_return() { + want_cyclic_coroutine_return(|| { + //~^ ERROR type mismatch + if false { yield None.unwrap(); } + None.unwrap() + }) +} + +pub fn want_cyclic_coroutine_yield<T>(_: T) + where T: Coroutine<Yield = T, Return = ()> +{ +} + +fn supply_cyclic_coroutine_yield() { + want_cyclic_coroutine_yield(|| { + //~^ ERROR type mismatch + if false { yield None.unwrap(); } + None.unwrap() + }) +} + +fn main() { } diff --git a/tests/ui/coroutine/coroutine-yielding-or-returning-itself.stderr b/tests/ui/coroutine/coroutine-yielding-or-returning-itself.stderr new file mode 100644 index 000000000..325030524 --- /dev/null +++ b/tests/ui/coroutine/coroutine-yielding-or-returning-itself.stderr @@ -0,0 +1,53 @@ +error[E0271]: type mismatch resolving `<{coroutine@$DIR/coroutine-yielding-or-returning-itself.rs:15:34: 15:36} as Coroutine>::Return == {coroutine@$DIR/coroutine-yielding-or-returning-itself.rs:15:34: 15:36}` + --> $DIR/coroutine-yielding-or-returning-itself.rs:15:34 + | +LL | want_cyclic_coroutine_return(|| { + | _____----------------------------_^ + | | | + | | required by a bound introduced by this call +LL | | +LL | | if false { yield None.unwrap(); } +LL | | None.unwrap() +LL | | }) + | |_____^ cyclic type of infinite size + | + = note: closures cannot capture themselves or take themselves as argument; + this error may be the result of a recent compiler bug-fix, + see issue #46062 <https://github.com/rust-lang/rust/issues/46062> + for more information +note: required by a bound in `want_cyclic_coroutine_return` + --> $DIR/coroutine-yielding-or-returning-itself.rs:10:36 + | +LL | pub fn want_cyclic_coroutine_return<T>(_: T) + | ---------------------------- required by a bound in this function +LL | where T: Coroutine<Yield = (), Return = T> + | ^^^^^^^^^^ required by this bound in `want_cyclic_coroutine_return` + +error[E0271]: type mismatch resolving `<{coroutine@$DIR/coroutine-yielding-or-returning-itself.rs:28:33: 28:35} as Coroutine>::Yield == {coroutine@$DIR/coroutine-yielding-or-returning-itself.rs:28:33: 28:35}` + --> $DIR/coroutine-yielding-or-returning-itself.rs:28:33 + | +LL | want_cyclic_coroutine_yield(|| { + | _____---------------------------_^ + | | | + | | required by a bound introduced by this call +LL | | +LL | | if false { yield None.unwrap(); } +LL | | None.unwrap() +LL | | }) + | |_____^ cyclic type of infinite size + | + = note: closures cannot capture themselves or take themselves as argument; + this error may be the result of a recent compiler bug-fix, + see issue #46062 <https://github.com/rust-lang/rust/issues/46062> + for more information +note: required by a bound in `want_cyclic_coroutine_yield` + --> $DIR/coroutine-yielding-or-returning-itself.rs:23:24 + | +LL | pub fn want_cyclic_coroutine_yield<T>(_: T) + | --------------------------- required by a bound in this function +LL | where T: Coroutine<Yield = T, Return = ()> + | ^^^^^^^^^ required by this bound in `want_cyclic_coroutine_yield` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/coroutine/derived-drop-parent-expr.rs b/tests/ui/coroutine/derived-drop-parent-expr.rs new file mode 100644 index 000000000..59a3e8478 --- /dev/null +++ b/tests/ui/coroutine/derived-drop-parent-expr.rs @@ -0,0 +1,16 @@ +// build-pass + +//! Like drop-tracking-parent-expression, but also tests that this doesn't ICE when building MIR +#![feature(coroutines)] + +fn assert_send<T: Send>(_thing: T) {} + +#[derive(Default)] +pub struct Client { pub nickname: String } + +fn main() { + let g = move || match drop(Client { ..Client::default() }) { + _status => yield, + }; + assert_send(g); +} diff --git a/tests/ui/coroutine/discriminant.rs b/tests/ui/coroutine/discriminant.rs new file mode 100644 index 000000000..73bdd9c86 --- /dev/null +++ b/tests/ui/coroutine/discriminant.rs @@ -0,0 +1,137 @@ +//! Tests that coroutine discriminant sizes and ranges are chosen optimally and that they are +//! reflected in the output of `mem::discriminant`. + +// run-pass + +#![feature(coroutines, coroutine_trait, core_intrinsics, discriminant_kind)] + +use std::intrinsics::discriminant_value; +use std::marker::{DiscriminantKind, Unpin}; +use std::mem::size_of_val; +use std::{cmp, ops::*}; + +macro_rules! yield25 { + ($e:expr) => { + yield $e; + yield $e; + yield $e; + yield $e; + yield $e; + + yield $e; + yield $e; + yield $e; + yield $e; + yield $e; + + yield $e; + yield $e; + yield $e; + yield $e; + yield $e; + + yield $e; + yield $e; + yield $e; + yield $e; + yield $e; + + yield $e; + yield $e; + yield $e; + yield $e; + yield $e; + }; +} + +/// Yields 250 times. +macro_rules! yield250 { + () => { + yield250!(()) + }; + + ($e:expr) => { + yield25!($e); + yield25!($e); + yield25!($e); + yield25!($e); + yield25!($e); + + yield25!($e); + yield25!($e); + yield25!($e); + yield25!($e); + yield25!($e); + }; +} + +fn cycle( + gen: impl Coroutine<()> + Unpin + DiscriminantKind<Discriminant = u32>, + expected_max_discr: u32, +) { + let mut gen = Box::pin(gen); + let mut max_discr = 0; + loop { + max_discr = cmp::max(max_discr, discriminant_value(gen.as_mut().get_mut())); + match gen.as_mut().resume(()) { + CoroutineState::Yielded(_) => {} + CoroutineState::Complete(_) => { + assert_eq!(max_discr, expected_max_discr); + return; + } + } + } +} + +fn main() { + // Has only one invalid discr. value. + let gen_u8_tiny_niche = || { + || { + // 3 reserved variants + + yield250!(); // 253 variants + + yield; // 254 + yield; // 255 + } + }; + + // Uses all values in the u8 discriminant. + let gen_u8_full = || { + || { + // 3 reserved variants + + yield250!(); // 253 variants + + yield; // 254 + yield; // 255 + yield; // 256 + } + }; + + // Barely needs a u16 discriminant. + let gen_u16 = || { + || { + // 3 reserved variants + + yield250!(); // 253 variants + + yield; // 254 + yield; // 255 + yield; // 256 + yield; // 257 + } + }; + + assert_eq!(size_of_val(&gen_u8_tiny_niche()), 1); + assert_eq!(size_of_val(&Some(gen_u8_tiny_niche())), 1); // uses niche + assert_eq!(size_of_val(&Some(Some(gen_u8_tiny_niche()))), 2); // cannot use niche anymore + assert_eq!(size_of_val(&gen_u8_full()), 1); + assert_eq!(size_of_val(&Some(gen_u8_full())), 2); // cannot use niche + assert_eq!(size_of_val(&gen_u16()), 2); + assert_eq!(size_of_val(&Some(gen_u16())), 2); // uses niche + + cycle(gen_u8_tiny_niche(), 254); + cycle(gen_u8_full(), 255); + cycle(gen_u16(), 256); +} diff --git a/tests/ui/coroutine/drop-and-replace.rs b/tests/ui/coroutine/drop-and-replace.rs new file mode 100644 index 000000000..38b757fac --- /dev/null +++ b/tests/ui/coroutine/drop-and-replace.rs @@ -0,0 +1,45 @@ +// run-pass +// Regression test for incorrect DropAndReplace behavior introduced in #60840 +// and fixed in #61373. When combined with the optimization implemented in +// #60187, this produced incorrect code for coroutines when a saved local was +// re-assigned. + +#![feature(coroutines, coroutine_trait)] + +use std::ops::{Coroutine, CoroutineState}; +use std::pin::Pin; + +#[derive(Debug, PartialEq)] +struct Foo(i32); + +impl Drop for Foo { + fn drop(&mut self) {} +} + +fn main() { + let mut a = || { + let mut x = Foo(4); + yield; + assert_eq!(x.0, 4); + + // At one point this tricked our dataflow analysis into thinking `x` was + // StorageDead after the assignment. + x = Foo(5); + assert_eq!(x.0, 5); + + { + let y = Foo(6); + yield; + assert_eq!(y.0, 6); + } + + assert_eq!(x.0, 5); + }; + + loop { + match Pin::new(&mut a).resume(()) { + CoroutineState::Complete(()) => break, + _ => (), + } + } +} diff --git a/tests/ui/coroutine/drop-control-flow.rs b/tests/ui/coroutine/drop-control-flow.rs new file mode 100644 index 000000000..55d08b8d5 --- /dev/null +++ b/tests/ui/coroutine/drop-control-flow.rs @@ -0,0 +1,138 @@ +// build-pass + +// A test to ensure coroutines capture values that were conditionally dropped, +// and also that values that are dropped along all paths to a yield do not get +// included in the coroutine type. + +#![feature(coroutines, negative_impls)] +#![allow(unused_assignments, dead_code)] + +struct Ptr; +impl<'a> Drop for Ptr { + fn drop(&mut self) {} +} + +struct NonSend; +impl !Send for NonSend {} + +fn assert_send<T: Send>(_: T) {} + +// This test case is reduced from tests/ui/drop/dynamic-drop-async.rs +fn one_armed_if(arg: bool) { + let _ = || { + let arr = [Ptr]; + if arg { + drop(arr); + } + yield; + }; +} + +fn two_armed_if(arg: bool) { + assert_send(|| { + let arr = [Ptr]; + if arg { + drop(arr); + } else { + drop(arr); + } + yield; + }) +} + +fn if_let(arg: Option<i32>) { + let _ = || { + let arr = [Ptr]; + if let Some(_) = arg { + drop(arr); + } + yield; + }; +} + +fn init_in_if(arg: bool) { + assert_send(|| { + let mut x = NonSend; + drop(x); + if arg { + x = NonSend; + } else { + yield; + } + }) +} + +fn init_in_match_arm(arg: Option<i32>) { + assert_send(|| { + let mut x = NonSend; + drop(x); + match arg { + Some(_) => x = NonSend, + None => yield, + } + }) +} + +fn reinit() { + let _ = || { + let mut arr = [Ptr]; + drop(arr); + arr = [Ptr]; + yield; + }; +} + +fn loop_uninit() { + let _ = || { + let mut arr = [Ptr]; + let mut count = 0; + drop(arr); + while count < 3 { + yield; + arr = [Ptr]; + count += 1; + } + }; +} + +fn nested_loop() { + let _ = || { + let mut arr = [Ptr]; + let mut count = 0; + drop(arr); + while count < 3 { + for _ in 0..3 { + yield; + } + arr = [Ptr]; + count += 1; + } + }; +} + +fn loop_continue(b: bool) { + let _ = || { + let mut arr = [Ptr]; + let mut count = 0; + drop(arr); + while count < 3 { + count += 1; + yield; + if b { + arr = [Ptr]; + continue; + } + } + }; +} + +fn main() { + one_armed_if(true); + if_let(Some(41)); + init_in_if(true); + init_in_match_arm(Some(41)); + reinit(); + loop_uninit(); + nested_loop(); + loop_continue(true); +} diff --git a/tests/ui/coroutine/drop-env.rs b/tests/ui/coroutine/drop-env.rs new file mode 100644 index 000000000..404c04343 --- /dev/null +++ b/tests/ui/coroutine/drop-env.rs @@ -0,0 +1,67 @@ +// run-pass + +// revisions: default nomiropt +//[nomiropt]compile-flags: -Z mir-opt-level=0 + +#![feature(coroutines, coroutine_trait)] +#![allow(dropping_copy_types)] + +use std::ops::Coroutine; +use std::pin::Pin; +use std::sync::atomic::{AtomicUsize, Ordering}; + +static A: AtomicUsize = AtomicUsize::new(0); + +struct B; + +impl Drop for B { + fn drop(&mut self) { + A.fetch_add(1, Ordering::SeqCst); + } +} + +fn main() { + t1(); + t2(); + t3(); +} + +fn t1() { + let b = B; + let mut foo = || { + yield; + drop(b); + }; + + let n = A.load(Ordering::SeqCst); + drop(Pin::new(&mut foo).resume(())); + assert_eq!(A.load(Ordering::SeqCst), n); + drop(foo); + assert_eq!(A.load(Ordering::SeqCst), n + 1); +} + +fn t2() { + let b = B; + let mut foo = || { + yield b; + }; + + let n = A.load(Ordering::SeqCst); + drop(Pin::new(&mut foo).resume(())); + assert_eq!(A.load(Ordering::SeqCst), n + 1); + drop(foo); + assert_eq!(A.load(Ordering::SeqCst), n + 1); +} + +fn t3() { + let b = B; + let foo = || { + yield; + drop(b); + }; + + let n = A.load(Ordering::SeqCst); + assert_eq!(A.load(Ordering::SeqCst), n); + drop(foo); + assert_eq!(A.load(Ordering::SeqCst), n + 1); +} diff --git a/tests/ui/coroutine/drop-track-addassign-yield.rs b/tests/ui/coroutine/drop-track-addassign-yield.rs new file mode 100644 index 000000000..6c5897458 --- /dev/null +++ b/tests/ui/coroutine/drop-track-addassign-yield.rs @@ -0,0 +1,40 @@ +// run-pass + +// Based on addassign-yield.rs, but with drop tracking enabled. Originally we did not implement +// the fake_read callback on ExprUseVisitor which caused this case to break. + +#![feature(coroutines)] + +fn foo() { + let _y = static || { + let x = &mut 0; + *{ + yield; + x + } += match String::new() { + _ => 0, + }; + }; + + // Please don't ever actually write something like this + let _z = static || { + let x = &mut 0; + *{ + let inner = &mut 1; + *{ + yield (); + inner + } += match String::new() { + _ => 1, + }; + yield; + x + } += match String::new() { + _ => 2, + }; + }; +} + +fn main() { + foo() +} diff --git a/tests/ui/coroutine/drop-tracking-parent-expression.rs b/tests/ui/coroutine/drop-tracking-parent-expression.rs new file mode 100644 index 000000000..4d40192c0 --- /dev/null +++ b/tests/ui/coroutine/drop-tracking-parent-expression.rs @@ -0,0 +1,68 @@ +#![feature(coroutines, negative_impls, rustc_attrs)] + +macro_rules! type_combinations { + ( + $( $name:ident => { $( $tt:tt )* } );* $(;)? + ) => { $( + mod $name { + $( $tt )* + + impl !Sync for Client {} + impl !Send for Client {} + } + + // Struct update syntax. This fails because the Client used in the update is considered + // dropped *after* the yield. + { + let g = move || match drop($name::Client { ..$name::Client::default() }) { + //~^ `significant_drop::Client` which is not `Send` + //~| `insignificant_dtor::Client` which is not `Send` + //~| `derived_drop::Client` which is not `Send` + _ => yield, + }; + assert_send(g); + //~^ ERROR cannot be sent between threads + //~| ERROR cannot be sent between threads + //~| ERROR cannot be sent between threads + } + + // Simple owned value. This works because the Client is considered moved into `drop`, + // even though the temporary expression doesn't end until after the yield. + { + let g = move || match drop($name::Client::default()) { + _ => yield, + }; + assert_send(g); + } + )* } +} + +fn assert_send<T: Send>(_thing: T) {} + +fn main() { + type_combinations!( + // OK + copy => { #[derive(Copy, Clone, Default)] pub struct Client; }; + // NOT OK: MIR borrowck thinks that this is used after the yield, even though + // this has no `Drop` impl and only the drops of the fields are observable. + // FIXME: this should compile. + derived_drop => { #[derive(Default)] pub struct Client { pub nickname: String } }; + // NOT OK + significant_drop => { + #[derive(Default)] + pub struct Client; + impl Drop for Client { + fn drop(&mut self) {} + } + }; + // NOT OK (we need to agree with MIR borrowck) + insignificant_dtor => { + #[derive(Default)] + #[rustc_insignificant_dtor] + pub struct Client; + impl Drop for Client { + fn drop(&mut self) {} + } + }; + ); +} diff --git a/tests/ui/coroutine/drop-tracking-parent-expression.stderr b/tests/ui/coroutine/drop-tracking-parent-expression.stderr new file mode 100644 index 000000000..6cd4ec833 --- /dev/null +++ b/tests/ui/coroutine/drop-tracking-parent-expression.stderr @@ -0,0 +1,122 @@ +error: coroutine cannot be sent between threads safely + --> $DIR/drop-tracking-parent-expression.rs:23:13 + | +LL | assert_send(g); + | ^^^^^^^^^^^ coroutine is not `Send` +... +LL | / type_combinations!( +LL | | // OK +LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; }; +LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though +... | +LL | | }; +LL | | ); + | |_____- in this macro invocation + | + = help: within `{coroutine@$DIR/drop-tracking-parent-expression.rs:17:21: 17:28}`, the trait `Send` is not implemented for `derived_drop::Client` +note: coroutine is not `Send` as this value is used across a yield + --> $DIR/drop-tracking-parent-expression.rs:21:22 + | +LL | let g = move || match drop($name::Client { ..$name::Client::default() }) { + | ------------------------ has type `derived_drop::Client` which is not `Send` +... +LL | _ => yield, + | ^^^^^ yield occurs here, with `$name::Client::default()` maybe used later +... +LL | / type_combinations!( +LL | | // OK +LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; }; +LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though +... | +LL | | }; +LL | | ); + | |_____- in this macro invocation +note: required by a bound in `assert_send` + --> $DIR/drop-tracking-parent-expression.rs:40:19 + | +LL | fn assert_send<T: Send>(_thing: T) {} + | ^^^^ required by this bound in `assert_send` + = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: coroutine cannot be sent between threads safely + --> $DIR/drop-tracking-parent-expression.rs:23:13 + | +LL | assert_send(g); + | ^^^^^^^^^^^ coroutine is not `Send` +... +LL | / type_combinations!( +LL | | // OK +LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; }; +LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though +... | +LL | | }; +LL | | ); + | |_____- in this macro invocation + | + = help: within `{coroutine@$DIR/drop-tracking-parent-expression.rs:17:21: 17:28}`, the trait `Send` is not implemented for `significant_drop::Client` +note: coroutine is not `Send` as this value is used across a yield + --> $DIR/drop-tracking-parent-expression.rs:21:22 + | +LL | let g = move || match drop($name::Client { ..$name::Client::default() }) { + | ------------------------ has type `significant_drop::Client` which is not `Send` +... +LL | _ => yield, + | ^^^^^ yield occurs here, with `$name::Client::default()` maybe used later +... +LL | / type_combinations!( +LL | | // OK +LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; }; +LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though +... | +LL | | }; +LL | | ); + | |_____- in this macro invocation +note: required by a bound in `assert_send` + --> $DIR/drop-tracking-parent-expression.rs:40:19 + | +LL | fn assert_send<T: Send>(_thing: T) {} + | ^^^^ required by this bound in `assert_send` + = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: coroutine cannot be sent between threads safely + --> $DIR/drop-tracking-parent-expression.rs:23:13 + | +LL | assert_send(g); + | ^^^^^^^^^^^ coroutine is not `Send` +... +LL | / type_combinations!( +LL | | // OK +LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; }; +LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though +... | +LL | | }; +LL | | ); + | |_____- in this macro invocation + | + = help: within `{coroutine@$DIR/drop-tracking-parent-expression.rs:17:21: 17:28}`, the trait `Send` is not implemented for `insignificant_dtor::Client` +note: coroutine is not `Send` as this value is used across a yield + --> $DIR/drop-tracking-parent-expression.rs:21:22 + | +LL | let g = move || match drop($name::Client { ..$name::Client::default() }) { + | ------------------------ has type `insignificant_dtor::Client` which is not `Send` +... +LL | _ => yield, + | ^^^^^ yield occurs here, with `$name::Client::default()` maybe used later +... +LL | / type_combinations!( +LL | | // OK +LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; }; +LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though +... | +LL | | }; +LL | | ); + | |_____- in this macro invocation +note: required by a bound in `assert_send` + --> $DIR/drop-tracking-parent-expression.rs:40:19 + | +LL | fn assert_send<T: Send>(_thing: T) {} + | ^^^^ required by this bound in `assert_send` + = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 3 previous errors + diff --git a/tests/ui/coroutine/drop-tracking-yielding-in-match-guards.rs b/tests/ui/coroutine/drop-tracking-yielding-in-match-guards.rs new file mode 100644 index 000000000..622765d82 --- /dev/null +++ b/tests/ui/coroutine/drop-tracking-yielding-in-match-guards.rs @@ -0,0 +1,11 @@ +// build-pass +// edition:2018 + +#![feature(coroutines)] + +fn main() { + let _ = static |x: u8| match x { + y if { yield } == y + 1 => (), + _ => (), + }; +} diff --git a/tests/ui/coroutine/drop-yield-twice.rs b/tests/ui/coroutine/drop-yield-twice.rs new file mode 100644 index 000000000..015343a27 --- /dev/null +++ b/tests/ui/coroutine/drop-yield-twice.rs @@ -0,0 +1,15 @@ +#![feature(negative_impls, coroutines)] + +struct Foo(i32); +impl !Send for Foo {} + +fn main() { + assert_send(|| { //~ ERROR coroutine cannot be sent between threads safely + let guard = Foo(42); + yield; + drop(guard); + yield; + }) +} + +fn assert_send<T: Send>(_: T) {} diff --git a/tests/ui/coroutine/drop-yield-twice.stderr b/tests/ui/coroutine/drop-yield-twice.stderr new file mode 100644 index 000000000..fbbedac57 --- /dev/null +++ b/tests/ui/coroutine/drop-yield-twice.stderr @@ -0,0 +1,22 @@ +error: coroutine cannot be sent between threads safely + --> $DIR/drop-yield-twice.rs:7:5 + | +LL | assert_send(|| { + | ^^^^^^^^^^^ coroutine is not `Send` + | + = help: within `{coroutine@$DIR/drop-yield-twice.rs:7:17: 7:19}`, the trait `Send` is not implemented for `Foo` +note: coroutine is not `Send` as this value is used across a yield + --> $DIR/drop-yield-twice.rs:9:9 + | +LL | let guard = Foo(42); + | ----- has type `Foo` which is not `Send` +LL | yield; + | ^^^^^ yield occurs here, with `guard` maybe used later +note: required by a bound in `assert_send` + --> $DIR/drop-yield-twice.rs:15:19 + | +LL | fn assert_send<T: Send>(_: T) {} + | ^^^^ required by this bound in `assert_send` + +error: aborting due to previous error + diff --git a/tests/ui/coroutine/dropck-resume.rs b/tests/ui/coroutine/dropck-resume.rs new file mode 100644 index 000000000..07ca4d37a --- /dev/null +++ b/tests/ui/coroutine/dropck-resume.rs @@ -0,0 +1,33 @@ +#![feature(coroutines, coroutine_trait)] + +use std::ops::{Coroutine, CoroutineState}; +use std::pin::Pin; + +struct SetToNone<'a: 'b, 'b>(&'b mut Option<&'a i32>); + +impl<'a, 'b> Drop for SetToNone<'a, 'b> { + fn drop(&mut self) { + *self.0 = None; + } +} + +fn drop_using_coroutine() -> i32 { + let mut y = Some(&0); + let z = &mut y; + let r; + { + let mut g = move |r| { + let _s = SetToNone(r); + yield; + }; + let mut g = Pin::new(&mut g); + g.as_mut().resume(z); + r = y.as_ref().unwrap(); + //~^ ERROR cannot borrow `y` as immutable because it is also borrowed as mutable + } + **r +} + +fn main() { + println!("{}", drop_using_coroutine()); +} diff --git a/tests/ui/coroutine/dropck-resume.stderr b/tests/ui/coroutine/dropck-resume.stderr new file mode 100644 index 000000000..028523978 --- /dev/null +++ b/tests/ui/coroutine/dropck-resume.stderr @@ -0,0 +1,15 @@ +error[E0502]: cannot borrow `y` as immutable because it is also borrowed as mutable + --> $DIR/dropck-resume.rs:25:13 + | +LL | let z = &mut y; + | ------ mutable borrow occurs here +... +LL | r = y.as_ref().unwrap(); + | ^ immutable borrow occurs here +LL | +LL | } + | - mutable borrow might be used here, when `g` is dropped and runs the destructor for coroutine + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0502`. diff --git a/tests/ui/coroutine/dropck.rs b/tests/ui/coroutine/dropck.rs new file mode 100644 index 000000000..450361c8d --- /dev/null +++ b/tests/ui/coroutine/dropck.rs @@ -0,0 +1,20 @@ +#![feature(coroutines, coroutine_trait)] + +use std::cell::RefCell; +use std::ops::Coroutine; +use std::pin::Pin; + +fn main() { + let (mut gen, cell); + cell = Box::new(RefCell::new(0)); + let ref_ = Box::leak(Box::new(Some(cell.borrow_mut()))); + //~^ ERROR `*cell` does not live long enough [E0597] + // the upvar is the non-dropck `&mut Option<Ref<'a, i32>>`. + gen = || { + // but the coroutine can use it to drop a `Ref<'a, i32>`. + let _d = ref_.take(); //~ ERROR `ref_` does not live long enough + yield; + }; + Pin::new(&mut gen).resume(()); + // drops the RefCell and then the Ref, leading to use-after-free +} diff --git a/tests/ui/coroutine/dropck.stderr b/tests/ui/coroutine/dropck.stderr new file mode 100644 index 000000000..241d6dfe0 --- /dev/null +++ b/tests/ui/coroutine/dropck.stderr @@ -0,0 +1,37 @@ +error[E0597]: `*cell` does not live long enough + --> $DIR/dropck.rs:10:40 + | +LL | let (mut gen, cell); + | ---- binding `cell` declared here +LL | cell = Box::new(RefCell::new(0)); +LL | let ref_ = Box::leak(Box::new(Some(cell.borrow_mut()))); + | ^^^^ borrowed value does not live long enough +... +LL | } + | - + | | + | `*cell` dropped here while still borrowed + | borrow might be used here, when `gen` is dropped and runs the destructor for coroutine + | + = note: values in a scope are dropped in the opposite order they are defined + +error[E0597]: `ref_` does not live long enough + --> $DIR/dropck.rs:15:18 + | +LL | gen = || { + | -- value captured here by coroutine +LL | // but the coroutine can use it to drop a `Ref<'a, i32>`. +LL | let _d = ref_.take(); + | ^^^^ borrowed value does not live long enough +... +LL | } + | - + | | + | `ref_` dropped here while still borrowed + | borrow might be used here, when `gen` is dropped and runs the destructor for coroutine + | + = note: values in a scope are dropped in the opposite order they are defined + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/coroutine/gen_block.e2024.stderr b/tests/ui/coroutine/gen_block.e2024.stderr new file mode 100644 index 000000000..f250e2f79 --- /dev/null +++ b/tests/ui/coroutine/gen_block.e2024.stderr @@ -0,0 +1,19 @@ +error[E0658]: yield syntax is experimental + --> $DIR/gen_block.rs:15:16 + | +LL | let _ = || yield true; + | ^^^^^^^^^^ + | + = note: see issue #43122 <https://github.com/rust-lang/rust/issues/43122> for more information + = help: add `#![feature(coroutines)]` to the crate attributes to enable + +error[E0282]: type annotations needed + --> $DIR/gen_block.rs:6:17 + | +LL | let x = gen {}; + | ^^ cannot infer type + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0282, E0658. +For more information about an error, try `rustc --explain E0282`. diff --git a/tests/ui/coroutine/gen_block.none.stderr b/tests/ui/coroutine/gen_block.none.stderr new file mode 100644 index 000000000..012a8308c --- /dev/null +++ b/tests/ui/coroutine/gen_block.none.stderr @@ -0,0 +1,49 @@ +error: expected identifier, found reserved keyword `yield` + --> $DIR/gen_block.rs:9:19 + | +LL | let y = gen { yield 42 }; + | --- ^^^^^ expected identifier, found reserved keyword + | | + | while parsing this struct + +error[E0422]: cannot find struct, variant or union type `gen` in this scope + --> $DIR/gen_block.rs:6:13 + | +LL | let x = gen {}; + | ^^^ not found in this scope + +error[E0422]: cannot find struct, variant or union type `gen` in this scope + --> $DIR/gen_block.rs:9:13 + | +LL | let y = gen { yield 42 }; + | ^^^ not found in this scope + +error[E0422]: cannot find struct, variant or union type `gen` in this scope + --> $DIR/gen_block.rs:12:5 + | +LL | gen {}; + | ^^^ not found in this scope + +error[E0658]: yield syntax is experimental + --> $DIR/gen_block.rs:15:16 + | +LL | let _ = || yield true; + | ^^^^^^^^^^ + | + = note: see issue #43122 <https://github.com/rust-lang/rust/issues/43122> for more information + = help: add `#![feature(coroutines)]` to the crate attributes to enable + +error[E0658]: yield syntax is experimental + --> $DIR/gen_block.rs:15:16 + | +LL | let _ = || yield true; + | ^^^^^^^^^^ + | + = note: see issue #43122 <https://github.com/rust-lang/rust/issues/43122> for more information + = help: add `#![feature(coroutines)]` to the crate attributes to enable + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0422, E0658. +For more information about an error, try `rustc --explain E0422`. diff --git a/tests/ui/coroutine/gen_block.rs b/tests/ui/coroutine/gen_block.rs new file mode 100644 index 000000000..852c7c455 --- /dev/null +++ b/tests/ui/coroutine/gen_block.rs @@ -0,0 +1,17 @@ +// revisions: e2024 none +//[e2024] compile-flags: --edition 2024 -Zunstable-options +#![cfg_attr(e2024, feature(gen_blocks))] + +fn main() { + let x = gen {}; + //[none]~^ ERROR: cannot find + //[e2024]~^^ ERROR: type annotations needed + let y = gen { yield 42 }; + //[none]~^ ERROR: found reserved keyword `yield` + //[none]~| ERROR: cannot find + gen {}; + //[none]~^ ERROR: cannot find + + let _ = || yield true; //[none]~ ERROR yield syntax is experimental + //~^ ERROR yield syntax is experimental +} diff --git a/tests/ui/coroutine/gen_block_is_coro.rs b/tests/ui/coroutine/gen_block_is_coro.rs new file mode 100644 index 000000000..c66ccefba --- /dev/null +++ b/tests/ui/coroutine/gen_block_is_coro.rs @@ -0,0 +1,18 @@ +//compile-flags: --edition 2024 -Zunstable-options +#![feature(coroutines, coroutine_trait, gen_blocks)] + +use std::ops::Coroutine; + +fn foo() -> impl Coroutine<Yield = u32, Return = ()> { //~ ERROR: Coroutine` is not satisfied + gen { yield 42 } +} + +fn bar() -> impl Coroutine<Yield = i64, Return = ()> { //~ ERROR: Coroutine` is not satisfied + gen { yield 42 } +} + +fn baz() -> impl Coroutine<Yield = i32, Return = ()> { //~ ERROR: Coroutine` is not satisfied + gen { yield 42 } +} + +fn main() {} diff --git a/tests/ui/coroutine/gen_block_is_coro.stderr b/tests/ui/coroutine/gen_block_is_coro.stderr new file mode 100644 index 000000000..83a674fa5 --- /dev/null +++ b/tests/ui/coroutine/gen_block_is_coro.stderr @@ -0,0 +1,21 @@ +error[E0277]: the trait bound `{gen block@$DIR/gen_block_is_coro.rs:7:5: 7:21}: Coroutine` is not satisfied + --> $DIR/gen_block_is_coro.rs:6:13 + | +LL | fn foo() -> impl Coroutine<Yield = u32, Return = ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Coroutine` is not implemented for `{gen block@$DIR/gen_block_is_coro.rs:7:5: 7:21}` + +error[E0277]: the trait bound `{gen block@$DIR/gen_block_is_coro.rs:11:5: 11:21}: Coroutine` is not satisfied + --> $DIR/gen_block_is_coro.rs:10:13 + | +LL | fn bar() -> impl Coroutine<Yield = i64, Return = ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Coroutine` is not implemented for `{gen block@$DIR/gen_block_is_coro.rs:11:5: 11:21}` + +error[E0277]: the trait bound `{gen block@$DIR/gen_block_is_coro.rs:15:5: 15:21}: Coroutine` is not satisfied + --> $DIR/gen_block_is_coro.rs:14:13 + | +LL | fn baz() -> impl Coroutine<Yield = i32, Return = ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Coroutine` is not implemented for `{gen block@$DIR/gen_block_is_coro.rs:15:5: 15:21}` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/coroutine/gen_block_is_iter.rs b/tests/ui/coroutine/gen_block_is_iter.rs new file mode 100644 index 000000000..92625cf7c --- /dev/null +++ b/tests/ui/coroutine/gen_block_is_iter.rs @@ -0,0 +1,19 @@ +// revisions: next old +//compile-flags: --edition 2024 -Zunstable-options +//[next] compile-flags: -Ztrait-solver=next +// check-pass +#![feature(gen_blocks)] + +fn foo() -> impl Iterator<Item = u32> { + gen { yield 42 } +} + +fn bar() -> impl Iterator<Item = i64> { + gen { yield 42 } +} + +fn baz() -> impl Iterator<Item = i32> { + gen { yield 42 } +} + +fn main() {} diff --git a/tests/ui/coroutine/gen_block_is_no_future.rs b/tests/ui/coroutine/gen_block_is_no_future.rs new file mode 100644 index 000000000..947665197 --- /dev/null +++ b/tests/ui/coroutine/gen_block_is_no_future.rs @@ -0,0 +1,8 @@ +//compile-flags: --edition 2024 -Zunstable-options +#![feature(gen_blocks)] + +fn foo() -> impl std::future::Future { //~ ERROR is not a future + gen { yield 42 } +} + +fn main() {} diff --git a/tests/ui/coroutine/gen_block_is_no_future.stderr b/tests/ui/coroutine/gen_block_is_no_future.stderr new file mode 100644 index 000000000..db0c3c19b --- /dev/null +++ b/tests/ui/coroutine/gen_block_is_no_future.stderr @@ -0,0 +1,12 @@ +error[E0277]: `{gen block@$DIR/gen_block_is_no_future.rs:5:5: 5:21}` is not a future + --> $DIR/gen_block_is_no_future.rs:4:13 + | +LL | fn foo() -> impl std::future::Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^ `{gen block@$DIR/gen_block_is_no_future.rs:5:5: 5:21}` is not a future + | + = help: the trait `Future` is not implemented for `{gen block@$DIR/gen_block_is_no_future.rs:5:5: 5:21}` + = note: {gen block@$DIR/gen_block_is_no_future.rs:5:5: 5:21} must be a future or must implement `IntoFuture` to be awaited + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/coroutine/gen_block_iterate.rs b/tests/ui/coroutine/gen_block_iterate.rs new file mode 100644 index 000000000..18e1bb887 --- /dev/null +++ b/tests/ui/coroutine/gen_block_iterate.rs @@ -0,0 +1,35 @@ +// revisions: next old +//compile-flags: --edition 2024 -Zunstable-options +//[next] compile-flags: -Ztrait-solver=next +// run-pass +#![feature(gen_blocks)] + +fn foo() -> impl Iterator<Item = u32> { + gen { yield 42; for x in 3..6 { yield x } } +} + +fn moved() -> impl Iterator<Item = u32> { + let mut x = "foo".to_string(); + gen move { + yield 42; + if x == "foo" { return } + x.clear(); + for x in 3..6 { yield x } + } +} + +fn main() { + let mut iter = foo(); + assert_eq!(iter.next(), Some(42)); + assert_eq!(iter.next(), Some(3)); + assert_eq!(iter.next(), Some(4)); + assert_eq!(iter.next(), Some(5)); + assert_eq!(iter.next(), None); + // `gen` blocks are fused + assert_eq!(iter.next(), None); + + let mut iter = moved(); + assert_eq!(iter.next(), Some(42)); + assert_eq!(iter.next(), None); + +} diff --git a/tests/ui/coroutine/gen_block_move.fixed b/tests/ui/coroutine/gen_block_move.fixed new file mode 100644 index 000000000..5c6c80623 --- /dev/null +++ b/tests/ui/coroutine/gen_block_move.fixed @@ -0,0 +1,17 @@ +// compile-flags: --edition 2024 -Zunstable-options +// run-rustfix +#![feature(gen_blocks)] + +fn moved() -> impl Iterator<Item = u32> { + let mut x = "foo".to_string(); + gen move { //~ ERROR: gen block may outlive the current function + yield 42; + if x == "foo" { return } + x.clear(); + for x in 3..6 { yield x } + } +} + +fn main() { + for _ in moved() {} +} diff --git a/tests/ui/coroutine/gen_block_move.rs b/tests/ui/coroutine/gen_block_move.rs new file mode 100644 index 000000000..abbf81324 --- /dev/null +++ b/tests/ui/coroutine/gen_block_move.rs @@ -0,0 +1,17 @@ +// compile-flags: --edition 2024 -Zunstable-options +// run-rustfix +#![feature(gen_blocks)] + +fn moved() -> impl Iterator<Item = u32> { + let mut x = "foo".to_string(); + gen { //~ ERROR: gen block may outlive the current function + yield 42; + if x == "foo" { return } + x.clear(); + for x in 3..6 { yield x } + } +} + +fn main() { + for _ in moved() {} +} diff --git a/tests/ui/coroutine/gen_block_move.stderr b/tests/ui/coroutine/gen_block_move.stderr new file mode 100644 index 000000000..b93ac65f5 --- /dev/null +++ b/tests/ui/coroutine/gen_block_move.stderr @@ -0,0 +1,30 @@ +error[E0373]: gen block may outlive the current function, but it borrows `x`, which is owned by the current function + --> $DIR/gen_block_move.rs:7:5 + | +LL | / gen { +LL | | yield 42; +LL | | if x == "foo" { return } +LL | | x.clear(); + | | - `x` is borrowed here +LL | | for x in 3..6 { yield x } +LL | | } + | |_____^ may outlive borrowed value `x` + | +note: gen block is returned here + --> $DIR/gen_block_move.rs:7:5 + | +LL | / gen { +LL | | yield 42; +LL | | if x == "foo" { return } +LL | | x.clear(); +LL | | for x in 3..6 { yield x } +LL | | } + | |_____^ +help: to force the gen block to take ownership of `x` (and any other referenced variables), use the `move` keyword + | +LL | gen move { + | ++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0373`. diff --git a/tests/ui/coroutine/gen_block_panic.rs b/tests/ui/coroutine/gen_block_panic.rs new file mode 100644 index 000000000..2da0eb512 --- /dev/null +++ b/tests/ui/coroutine/gen_block_panic.rs @@ -0,0 +1,26 @@ +//compile-flags: --edition 2024 -Zunstable-options +// run-pass +// needs-unwind +#![feature(gen_blocks)] + +fn main() { + let mut iter = gen { + yield 42; + panic!("foo"); + yield 69; //~ WARN: unreachable statement + }; + assert_eq!(iter.next(), Some(42)); + let mut tmp = std::panic::AssertUnwindSafe(&mut iter); + match std::panic::catch_unwind(move || tmp.next()) { + Ok(_) => unreachable!(), + Err(err) => assert_eq!(*err.downcast::<&'static str>().unwrap(), "foo"), + } + + match std::panic::catch_unwind(move || iter.next()) { + Ok(_) => unreachable!(), + Err(err) => assert_eq!( + *err.downcast::<&'static str>().unwrap(), + "`gen fn` should just keep returning `None` after panicking", + ), + } +} diff --git a/tests/ui/coroutine/gen_block_panic.stderr b/tests/ui/coroutine/gen_block_panic.stderr new file mode 100644 index 000000000..a0a6d1063 --- /dev/null +++ b/tests/ui/coroutine/gen_block_panic.stderr @@ -0,0 +1,12 @@ +warning: unreachable statement + --> $DIR/gen_block_panic.rs:10:9 + | +LL | panic!("foo"); + | ------------- any code following this expression is unreachable +LL | yield 69; + | ^^^^^^^^^ unreachable statement + | + = note: `#[warn(unreachable_code)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/coroutine/gen_fn.e2024.stderr b/tests/ui/coroutine/gen_fn.e2024.stderr new file mode 100644 index 000000000..388e10fd6 --- /dev/null +++ b/tests/ui/coroutine/gen_fn.e2024.stderr @@ -0,0 +1,10 @@ +error: `gen` functions are not yet implemented + --> $DIR/gen_fn.rs:4:1 + | +LL | gen fn foo() {} + | ^^^ + | + = help: for now you can use `gen {}` blocks and return `impl Iterator` instead + +error: aborting due to previous error + diff --git a/tests/ui/coroutine/gen_fn.none.stderr b/tests/ui/coroutine/gen_fn.none.stderr new file mode 100644 index 000000000..5e7bd9d8b --- /dev/null +++ b/tests/ui/coroutine/gen_fn.none.stderr @@ -0,0 +1,8 @@ +error: expected one of `#`, `async`, `const`, `default`, `extern`, `fn`, `pub`, `unsafe`, or `use`, found `gen` + --> $DIR/gen_fn.rs:4:1 + | +LL | gen fn foo() {} + | ^^^ expected one of 9 possible tokens + +error: aborting due to previous error + diff --git a/tests/ui/coroutine/gen_fn.rs b/tests/ui/coroutine/gen_fn.rs new file mode 100644 index 000000000..da515f263 --- /dev/null +++ b/tests/ui/coroutine/gen_fn.rs @@ -0,0 +1,8 @@ +// revisions: e2024 none +//[e2024] compile-flags: --edition 2024 -Zunstable-options + +gen fn foo() {} +//[none]~^ ERROR: expected one of `#`, `async`, `const`, `default`, `extern`, `fn`, `pub`, `unsafe`, or `use`, found `gen` +//[e2024]~^^ ERROR: `gen` functions are not yet implemented + +fn main() {} diff --git a/tests/ui/coroutine/issue-102645.rs b/tests/ui/coroutine/issue-102645.rs new file mode 100644 index 000000000..a0263510e --- /dev/null +++ b/tests/ui/coroutine/issue-102645.rs @@ -0,0 +1,21 @@ +#![feature(coroutines, coroutine_trait)] + +use std::ops::Coroutine; +use std::pin::Pin; + +fn main() { + let mut a = 5; + let mut b = || { + let d = 6; + yield; + _zzz(); // #break + a = d; + }; + Pin::new(&mut b).resume(); + //~^ ERROR this method takes 1 argument but 0 arguments were supplied + // This type error is required to reproduce the ICE... +} + +fn _zzz() { + () +} diff --git a/tests/ui/coroutine/issue-102645.stderr b/tests/ui/coroutine/issue-102645.stderr new file mode 100644 index 000000000..3db090346 --- /dev/null +++ b/tests/ui/coroutine/issue-102645.stderr @@ -0,0 +1,16 @@ +error[E0061]: this method takes 1 argument but 0 arguments were supplied + --> $DIR/issue-102645.rs:14:22 + | +LL | Pin::new(&mut b).resume(); + | ^^^^^^-- an argument of type `()` is missing + | +note: method defined here + --> $SRC_DIR/core/src/ops/coroutine.rs:LL:COL +help: provide the argument + | +LL | Pin::new(&mut b).resume(()); + | ~~~~ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0061`. diff --git a/tests/ui/coroutine/issue-105084.rs b/tests/ui/coroutine/issue-105084.rs new file mode 100644 index 000000000..7801f1bce --- /dev/null +++ b/tests/ui/coroutine/issue-105084.rs @@ -0,0 +1,42 @@ +#![feature(coroutines)] +#![feature(coroutine_clone)] +#![feature(coroutine_trait)] +#![feature(rustc_attrs, stmt_expr_attributes)] + +use std::ops::Coroutine; +use std::pin::Pin; + +fn copy<T: Copy>(x: T) -> T { + x +} + +fn main() { + let mut g = || { + // This is desuraged as 4 stages: + // - allocate a `*mut u8` with `exchange_malloc`; + // - create a Box that is ignored for trait computations; + // - compute fields (and yields); + // - assign to `t`. + let t = #[rustc_box] + Box::new((5, yield)); + drop(t); + }; + + // Allocate the temporary box. + Pin::new(&mut g).resume(()); + + // The temporary box is in coroutine locals. + // As it is not taken into account for trait computation, + // the coroutine is `Copy`. + let mut h = copy(g); + //~^ ERROR the trait bound `Box<(i32, ())>: Copy` is not satisfied in + + // We now have 2 boxes with the same backing allocation: + // one inside `g` and one inside `h`. + // Proceed and drop `t` in `g`. + Pin::new(&mut g).resume(()); + //~^ ERROR borrow of moved value: `g` + + // Proceed and drop `t` in `h` -> double free! + Pin::new(&mut h).resume(()); +} diff --git a/tests/ui/coroutine/issue-105084.stderr b/tests/ui/coroutine/issue-105084.stderr new file mode 100644 index 000000000..38f114ff7 --- /dev/null +++ b/tests/ui/coroutine/issue-105084.stderr @@ -0,0 +1,51 @@ +error[E0382]: borrow of moved value: `g` + --> $DIR/issue-105084.rs:37:14 + | +LL | let mut g = || { + | ----- move occurs because `g` has type `{coroutine@$DIR/issue-105084.rs:14:17: 14:19}`, which does not implement the `Copy` trait +... +LL | let mut h = copy(g); + | - value moved here +... +LL | Pin::new(&mut g).resume(()); + | ^^^^^^ value borrowed here after move + | +note: consider changing this parameter type in function `copy` to borrow instead if owning the value isn't necessary + --> $DIR/issue-105084.rs:9:21 + | +LL | fn copy<T: Copy>(x: T) -> T { + | ---- ^ this parameter takes ownership of the value + | | + | in this function +help: consider cloning the value if the performance cost is acceptable + | +LL | let mut h = copy(g.clone()); + | ++++++++ + +error[E0277]: the trait bound `Box<(i32, ())>: Copy` is not satisfied in `{coroutine@$DIR/issue-105084.rs:14:17: 14:19}` + --> $DIR/issue-105084.rs:31:17 + | +LL | let mut g = || { + | -- within this `{coroutine@$DIR/issue-105084.rs:14:17: 14:19}` +... +LL | let mut h = copy(g); + | ^^^^ within `{coroutine@$DIR/issue-105084.rs:14:17: 14:19}`, the trait `Copy` is not implemented for `Box<(i32, ())>` + | +note: coroutine does not implement `Copy` as this value is used across a yield + --> $DIR/issue-105084.rs:21:22 + | +LL | Box::new((5, yield)); + | -------------^^^^^-- + | | | + | | yield occurs here, with `Box::new((5, yield))` maybe used later + | has type `Box<(i32, ())>` which does not implement `Copy` +note: required by a bound in `copy` + --> $DIR/issue-105084.rs:9:12 + | +LL | fn copy<T: Copy>(x: T) -> T { + | ^^^^ required by this bound in `copy` + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0277, E0382. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/coroutine/issue-110929-coroutine-conflict-error-ice.rs b/tests/ui/coroutine/issue-110929-coroutine-conflict-error-ice.rs new file mode 100644 index 000000000..ad39b71b0 --- /dev/null +++ b/tests/ui/coroutine/issue-110929-coroutine-conflict-error-ice.rs @@ -0,0 +1,11 @@ +// edition:2021 +// check-pass +#![feature(coroutines)] + +fn main() { + let x = &mut (); + || { + let _c = || yield *&mut *x; + || _ = &mut *x; + }; +} diff --git a/tests/ui/coroutine/issue-113279.rs b/tests/ui/coroutine/issue-113279.rs new file mode 100644 index 000000000..f251c924c --- /dev/null +++ b/tests/ui/coroutine/issue-113279.rs @@ -0,0 +1,27 @@ +#![feature(coroutines)] + +// `foo` attempts to dereference `""`, which results in an error being reported. Later, the +// coroutine transform for `foo` then produces a union which contains a `str` type - unions should +// not contain unsized types, but this is okay because an error has been reported already. +// When const propagation happens later in compilation, it attempts to compute the layout of the +// coroutine (as part of checking whether something can be const propagated) and in turn attempts +// to compute the layout of `str` in the context of a union - where this caused an ICE. This test +// makes sure that doesn't happen again. + +fn foo() { + let _y = static || { + let x = &mut 0; + *{ + yield; + x + } += match { *"" }.len() { + //~^ ERROR cannot move a value of type `str` [E0161] + //~^^ ERROR cannot move out of a shared reference [E0507] + _ => 0, + }; + }; +} + +fn main() { + foo() +} diff --git a/tests/ui/coroutine/issue-113279.stderr b/tests/ui/coroutine/issue-113279.stderr new file mode 100644 index 000000000..cc9b64ef9 --- /dev/null +++ b/tests/ui/coroutine/issue-113279.stderr @@ -0,0 +1,16 @@ +error[E0161]: cannot move a value of type `str` + --> $DIR/issue-113279.rs:17:20 + | +LL | } += match { *"" }.len() { + | ^^^^^^^ the size of `str` cannot be statically determined + +error[E0507]: cannot move out of a shared reference + --> $DIR/issue-113279.rs:17:22 + | +LL | } += match { *"" }.len() { + | ^^^ move occurs because value has type `str`, which does not implement the `Copy` trait + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0161, E0507. +For more information about an error, try `rustc --explain E0161`. diff --git a/tests/ui/coroutine/issue-44197.rs b/tests/ui/coroutine/issue-44197.rs new file mode 100644 index 000000000..c0326bdae --- /dev/null +++ b/tests/ui/coroutine/issue-44197.rs @@ -0,0 +1,36 @@ +// run-pass + +#![feature(coroutines, coroutine_trait)] + +use std::ops::{Coroutine, CoroutineState}; +use std::pin::Pin; + +fn foo(_: &str) -> String { + String::new() +} + +fn bar(baz: String) -> impl Coroutine<(), Yield = String, Return = ()> { + move || { + yield foo(&baz); + } +} + +fn foo2(_: &str) -> Result<String, ()> { + Err(()) +} + +fn bar2(baz: String) -> impl Coroutine<(), Yield = String, Return = ()> { + move || { + if let Ok(quux) = foo2(&baz) { + yield quux; + } + } +} + +fn main() { + assert_eq!( + Pin::new(&mut bar(String::new())).resume(()), + CoroutineState::Yielded(String::new()) + ); + assert_eq!(Pin::new(&mut bar2(String::new())).resume(()), CoroutineState::Complete(())); +} diff --git a/tests/ui/coroutine/issue-45729-unsafe-in-coroutine.mir.stderr b/tests/ui/coroutine/issue-45729-unsafe-in-coroutine.mir.stderr new file mode 100644 index 000000000..a9a0d6296 --- /dev/null +++ b/tests/ui/coroutine/issue-45729-unsafe-in-coroutine.mir.stderr @@ -0,0 +1,11 @@ +error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block + --> $DIR/issue-45729-unsafe-in-coroutine.rs:8:9 + | +LL | *(1 as *mut u32) = 42; + | ^^^^^^^^^^^^^^^^^^^^^ dereference of raw pointer + | + = note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/coroutine/issue-45729-unsafe-in-coroutine.rs b/tests/ui/coroutine/issue-45729-unsafe-in-coroutine.rs new file mode 100644 index 000000000..7961b5859 --- /dev/null +++ b/tests/ui/coroutine/issue-45729-unsafe-in-coroutine.rs @@ -0,0 +1,12 @@ +// revisions: mir thir +// [thir]compile-flags: -Z thir-unsafeck + +#![feature(coroutines)] + +fn main() { + let _ = || { + *(1 as *mut u32) = 42; + //~^ ERROR dereference of raw pointer is unsafe + yield; + }; +} diff --git a/tests/ui/coroutine/issue-45729-unsafe-in-coroutine.thir.stderr b/tests/ui/coroutine/issue-45729-unsafe-in-coroutine.thir.stderr new file mode 100644 index 000000000..22c83e9a3 --- /dev/null +++ b/tests/ui/coroutine/issue-45729-unsafe-in-coroutine.thir.stderr @@ -0,0 +1,11 @@ +error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block + --> $DIR/issue-45729-unsafe-in-coroutine.rs:8:9 + | +LL | *(1 as *mut u32) = 42; + | ^^^^^^^^^^^^^^^^ dereference of raw pointer + | + = note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/coroutine/issue-48048.rs b/tests/ui/coroutine/issue-48048.rs new file mode 100644 index 000000000..b61b7c770 --- /dev/null +++ b/tests/ui/coroutine/issue-48048.rs @@ -0,0 +1,13 @@ +#![feature(coroutines)] + +fn main() { + let x = (|_| {},); + + || { + let x = x; + + x.0({ //~ ERROR borrow may still be in use when coroutine yields + yield; + }); + }; +} diff --git a/tests/ui/coroutine/issue-48048.stderr b/tests/ui/coroutine/issue-48048.stderr new file mode 100644 index 000000000..bb9f189fa --- /dev/null +++ b/tests/ui/coroutine/issue-48048.stderr @@ -0,0 +1,11 @@ +error[E0626]: borrow may still be in use when coroutine yields + --> $DIR/issue-48048.rs:9:9 + | +LL | x.0({ + | ^^^ +LL | yield; + | ----- possible yield occurs here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0626`. diff --git a/tests/ui/coroutine/issue-52304.rs b/tests/ui/coroutine/issue-52304.rs new file mode 100644 index 000000000..fed3a5f19 --- /dev/null +++ b/tests/ui/coroutine/issue-52304.rs @@ -0,0 +1,11 @@ +// check-pass + +#![feature(coroutines, coroutine_trait)] + +use std::ops::Coroutine; + +pub fn example() -> impl Coroutine { + || yield &1 +} + +fn main() {} diff --git a/tests/ui/coroutine/issue-52398.rs b/tests/ui/coroutine/issue-52398.rs new file mode 100644 index 000000000..8d651d0e2 --- /dev/null +++ b/tests/ui/coroutine/issue-52398.rs @@ -0,0 +1,28 @@ +// run-pass +#![allow(unused_variables)] + +#![feature(coroutines)] + +use std::cell::RefCell; + +struct A; + +impl A { + fn test(&self, a: ()) {} +} + +fn main() { + // Test that the MIR local with type &A created for the auto-borrow adjustment + // is caught by typeck + move || { //~ WARN unused coroutine that must be used + A.test(yield); + }; + + // Test that the std::cell::Ref temporary returned from the `borrow` call + // is caught by typeck + let y = RefCell::new(true); + static move || { //~ WARN unused coroutine that must be used + yield *y.borrow(); + return "Done"; + }; +} diff --git a/tests/ui/coroutine/issue-52398.stderr b/tests/ui/coroutine/issue-52398.stderr new file mode 100644 index 000000000..18d816da4 --- /dev/null +++ b/tests/ui/coroutine/issue-52398.stderr @@ -0,0 +1,24 @@ +warning: unused coroutine that must be used + --> $DIR/issue-52398.rs:17:5 + | +LL | / move || { +LL | | A.test(yield); +LL | | }; + | |_____^ + | + = note: coroutines are lazy and do nothing unless resumed + = note: `#[warn(unused_must_use)]` on by default + +warning: unused coroutine that must be used + --> $DIR/issue-52398.rs:24:5 + | +LL | / static move || { +LL | | yield *y.borrow(); +LL | | return "Done"; +LL | | }; + | |_____^ + | + = note: coroutines are lazy and do nothing unless resumed + +warning: 2 warnings emitted + diff --git a/tests/ui/coroutine/issue-53548-1.rs b/tests/ui/coroutine/issue-53548-1.rs new file mode 100644 index 000000000..4be8e95f3 --- /dev/null +++ b/tests/ui/coroutine/issue-53548-1.rs @@ -0,0 +1,20 @@ +// A variant of #53548 that does not actually require coroutines, +// but which encountered the same ICE/error. See `issue-53548.rs` +// for details. +// +// check-pass + +use std::cell::RefCell; +use std::rc::Rc; + +trait Trait: 'static {} + +struct Store<C> { + inner: Rc<RefCell<Option<C>>>, +} + +fn main() { + let store = Store::<Box<for<'a> fn(&(dyn Trait + 'a))>> { + inner: Default::default(), + }; +} diff --git a/tests/ui/coroutine/issue-53548.rs b/tests/ui/coroutine/issue-53548.rs new file mode 100644 index 000000000..bb267f74a --- /dev/null +++ b/tests/ui/coroutine/issue-53548.rs @@ -0,0 +1,38 @@ +// Regression test for #53548. The `Box<dyn Trait>` type below is +// expanded to `Box<dyn Trait + 'static>`, but the coroutine "witness" +// that results is `for<'r> { Box<dyn Trait + 'r> }`. The WF code was +// encountering an ICE (when debug-assertions were enabled) and an +// unexpected compilation error (without debug-asserions) when trying +// to process this `'r` region bound. In particular, to be WF, the +// region bound must meet the requirements of the trait, and hence we +// got `for<'r> { 'r: 'static }`. This would ICE because the `Binder` +// constructor we were using was assering that no higher-ranked +// regions were involved (because the WF code is supposed to skip +// those). The error (if debug-asserions were disabled) came because +// we obviously cannot prove that `'r: 'static` for any region `'r`. +// Pursuant with our "lazy WF" strategy for higher-ranked regions, the +// fix is not to require that `for<'r> { 'r: 'static }` holds (this is +// also analogous to what we would do for higher-ranked regions +// appearing within the trait in other positions). +// +// check-pass + +#![feature(coroutines)] + +use std::cell::RefCell; +use std::rc::Rc; + +trait Trait: 'static {} + +struct Store<C> { + inner: Rc<RefCell<Option<C>>>, +} + +fn main() { + Box::new(static move || { + let store = Store::<Box<dyn Trait>> { + inner: Default::default(), + }; + yield (); + }); +} diff --git a/tests/ui/coroutine/issue-57017.rs b/tests/ui/coroutine/issue-57017.rs new file mode 100644 index 000000000..4f63abbdb --- /dev/null +++ b/tests/ui/coroutine/issue-57017.rs @@ -0,0 +1,55 @@ +// build-pass +#![feature(coroutines, negative_impls)] +#![allow(dropping_references, dropping_copy_types)] + +macro_rules! type_combinations { + ( + $( $name:ident => { $( $tt:tt )* } );* + ) => { $( + mod $name { + pub mod unsync { + $( $tt )* + + impl !Sync for Client {} + } + pub mod unsend { + $( $tt )* + + impl !Send for Client {} + } + } + + // This is the same bug as issue 57017, but using yield instead of await + { + let g = move || match drop(&$name::unsync::Client::default()) { + _status => yield, + }; + assert_send(g); + } + + // This tests that `Client` is properly considered to be dropped after moving it into the + // function. + { + let g = move || match drop($name::unsend::Client::default()) { + _status => yield, + }; + assert_send(g); + } + )* } +} + +fn assert_send<T: Send>(_thing: T) {} + +fn main() { + type_combinations!( + copy => { #[derive(Copy, Clone, Default)] pub struct Client; }; + derived_drop => { #[derive(Default)] pub struct Client { pub nickname: String } }; + significant_drop => { + #[derive(Default)] + pub struct Client; + impl Drop for Client { + fn drop(&mut self) {} + } + } + ); +} diff --git a/tests/ui/coroutine/issue-57084.rs b/tests/ui/coroutine/issue-57084.rs new file mode 100644 index 000000000..95bed5b15 --- /dev/null +++ b/tests/ui/coroutine/issue-57084.rs @@ -0,0 +1,28 @@ +// This issue reproduces an ICE on compile (E.g. fails on 2018-12-19 nightly). +// "cannot relate bound region: ReLateBound(DebruijnIndex(1), BrAnon(1)) <= '?1" +// run-pass +// edition:2018 +#![feature(coroutines,coroutine_trait)] +use std::ops::Coroutine; + +fn with<F>(f: F) -> impl Coroutine<Yield=(), Return=()> +where F: Fn() -> () +{ + move || { + loop { + match f() { + _ => yield, + } + } + } +} + +fn main() { + let data = &vec![1]; + || { //~ WARN unused coroutine that must be used + let _to_pin = with(move || println!("{:p}", data)); + loop { + yield + } + }; +} diff --git a/tests/ui/coroutine/issue-57084.stderr b/tests/ui/coroutine/issue-57084.stderr new file mode 100644 index 000000000..9f5b79a6a --- /dev/null +++ b/tests/ui/coroutine/issue-57084.stderr @@ -0,0 +1,16 @@ +warning: unused coroutine that must be used + --> $DIR/issue-57084.rs:22:5 + | +LL | / || { +LL | | let _to_pin = with(move || println!("{:p}", data)); +LL | | loop { +LL | | yield +LL | | } +LL | | }; + | |_____^ + | + = note: coroutines are lazy and do nothing unless resumed + = note: `#[warn(unused_must_use)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/coroutine/issue-57478.rs b/tests/ui/coroutine/issue-57478.rs new file mode 100644 index 000000000..716e4c67b --- /dev/null +++ b/tests/ui/coroutine/issue-57478.rs @@ -0,0 +1,16 @@ +// check-pass + +#![feature(negative_impls, coroutines)] + +struct Foo; +impl !Send for Foo {} + +fn main() { + assert_send(|| { + let guard = Foo; + drop(guard); + yield; + }) +} + +fn assert_send<T: Send>(_: T) {} diff --git a/tests/ui/coroutine/issue-58888.rs b/tests/ui/coroutine/issue-58888.rs new file mode 100644 index 000000000..af8e60ce4 --- /dev/null +++ b/tests/ui/coroutine/issue-58888.rs @@ -0,0 +1,28 @@ +// run-pass +// compile-flags: -g +// ignore-asmjs wasm2js does not support source maps yet + +#![feature(coroutines, coroutine_trait)] + +use std::ops::Coroutine; + +struct Database; + +impl Database { + fn get_connection(&self) -> impl Iterator<Item = ()> { + Some(()).into_iter() + } + + fn check_connection(&self) -> impl Coroutine<Yield = (), Return = ()> + '_ { + move || { + let iter = self.get_connection(); + for i in iter { + yield i + } + } + } +} + +fn main() { + Database.check_connection(); +} diff --git a/tests/ui/coroutine/issue-61442-stmt-expr-with-drop.rs b/tests/ui/coroutine/issue-61442-stmt-expr-with-drop.rs new file mode 100644 index 000000000..cff6c24a8 --- /dev/null +++ b/tests/ui/coroutine/issue-61442-stmt-expr-with-drop.rs @@ -0,0 +1,32 @@ +// Test that we don't consider temporaries for statement expressions as live +// across yields + +// check-pass +// edition:2018 + +#![feature(coroutines, coroutine_trait)] + +use std::ops::Coroutine; + +async fn drop_and_await() { + async {}; + async {}.await; +} + +fn drop_and_yield() { + let x = || { + String::new(); + yield; + }; + Box::pin(x).as_mut().resume(()); + let y = static || { + String::new(); + yield; + }; + Box::pin(y).as_mut().resume(()); +} + +fn main() { + drop_and_await(); + drop_and_yield(); +} diff --git a/tests/ui/coroutine/issue-62506-two_awaits.rs b/tests/ui/coroutine/issue-62506-two_awaits.rs new file mode 100644 index 000000000..b50e2a45c --- /dev/null +++ b/tests/ui/coroutine/issue-62506-two_awaits.rs @@ -0,0 +1,17 @@ +// Output = String caused an ICE whereas Output = &'static str compiled successfully. +// Broken MIR: coroutine contains type std::string::String in MIR, +// but typeck only knows about {<S as T>::Future, ()} +// check-pass +// edition:2018 + +use std::future::Future; + +pub trait T { + type Future: Future<Output = String>; + fn bar() -> Self::Future; +} +pub async fn foo<S>() where S: T { + S::bar().await; + S::bar().await; +} +pub fn main() {} diff --git a/tests/ui/coroutine/issue-64620-yield-array-element.rs b/tests/ui/coroutine/issue-64620-yield-array-element.rs new file mode 100644 index 000000000..a9307d306 --- /dev/null +++ b/tests/ui/coroutine/issue-64620-yield-array-element.rs @@ -0,0 +1,9 @@ +// Regression test for #64620 + +#![feature(coroutines)] + +pub fn crash(arr: [usize; 1]) { + yield arr[0]; //~ ERROR: yield expression outside of coroutine literal +} + +fn main() {} diff --git a/tests/ui/coroutine/issue-64620-yield-array-element.stderr b/tests/ui/coroutine/issue-64620-yield-array-element.stderr new file mode 100644 index 000000000..47632d083 --- /dev/null +++ b/tests/ui/coroutine/issue-64620-yield-array-element.stderr @@ -0,0 +1,9 @@ +error[E0627]: yield expression outside of coroutine literal + --> $DIR/issue-64620-yield-array-element.rs:6:5 + | +LL | yield arr[0]; + | ^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0627`. diff --git a/tests/ui/coroutine/issue-68112.rs b/tests/ui/coroutine/issue-68112.rs new file mode 100644 index 000000000..e2be704da --- /dev/null +++ b/tests/ui/coroutine/issue-68112.rs @@ -0,0 +1,72 @@ +#![feature(coroutines, coroutine_trait)] + +use std::{ + cell::RefCell, + sync::Arc, + pin::Pin, + ops::{Coroutine, CoroutineState}, +}; + +pub struct Ready<T>(Option<T>); +impl<T: Unpin> Coroutine<()> for Ready<T> { + type Return = T; + type Yield = (); + fn resume(mut self: Pin<&mut Self>, _args: ()) -> CoroutineState<(), T> { + CoroutineState::Complete(self.0.take().unwrap()) + } +} +pub fn make_gen1<T>(t: T) -> Ready<T> { + Ready(Some(t)) +} + +fn require_send(_: impl Send) {} +//~^ NOTE required by a bound +//~| NOTE required by a bound +//~| NOTE required by this bound +//~| NOTE required by this bound + +fn make_non_send_coroutine() -> impl Coroutine<Return = Arc<RefCell<i32>>> { + make_gen1(Arc::new(RefCell::new(0))) +} + +fn test1() { + let send_gen = || { + let _non_send_gen = make_non_send_coroutine(); + //~^ NOTE not `Send` + yield; + //~^ NOTE yield occurs here + //~| NOTE value is used across a yield + }; + require_send(send_gen); + //~^ ERROR coroutine cannot be sent between threads + //~| NOTE not `Send` + //~| NOTE use `std::sync::RwLock` instead +} + +pub fn make_gen2<T>(t: T) -> impl Coroutine<Return = T> { +//~^ NOTE appears within the type +//~| NOTE expansion of desugaring + || { //~ NOTE used within this coroutine + yield; + t + } +} +fn make_non_send_coroutine2() -> impl Coroutine<Return = Arc<RefCell<i32>>> { //~ NOTE appears within the type +//~^ NOTE expansion of desugaring + make_gen2(Arc::new(RefCell::new(0))) +} + +fn test2() { + let send_gen = || { //~ NOTE used within this coroutine + let _non_send_gen = make_non_send_coroutine2(); + yield; + }; + require_send(send_gen); + //~^ ERROR `RefCell<i32>` cannot be shared between threads safely + //~| NOTE `RefCell<i32>` cannot be shared between threads safely + //~| NOTE required for + //~| NOTE captures the following types + //~| NOTE use `std::sync::RwLock` instead +} + +fn main() {} diff --git a/tests/ui/coroutine/issue-68112.stderr b/tests/ui/coroutine/issue-68112.stderr new file mode 100644 index 000000000..5efa72ad5 --- /dev/null +++ b/tests/ui/coroutine/issue-68112.stderr @@ -0,0 +1,61 @@ +error: coroutine cannot be sent between threads safely + --> $DIR/issue-68112.rs:40:5 + | +LL | require_send(send_gen); + | ^^^^^^^^^^^^ coroutine is not `Send` + | + = help: the trait `Sync` is not implemented for `RefCell<i32>` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead +note: coroutine is not `Send` as this value is used across a yield + --> $DIR/issue-68112.rs:36:9 + | +LL | let _non_send_gen = make_non_send_coroutine(); + | ------------- has type `impl Coroutine<Return = Arc<RefCell<i32>>>` which is not `Send` +LL | +LL | yield; + | ^^^^^ yield occurs here, with `_non_send_gen` maybe used later +note: required by a bound in `require_send` + --> $DIR/issue-68112.rs:22:25 + | +LL | fn require_send(_: impl Send) {} + | ^^^^ required by this bound in `require_send` + +error[E0277]: `RefCell<i32>` cannot be shared between threads safely + --> $DIR/issue-68112.rs:64:5 + | +LL | require_send(send_gen); + | ^^^^^^^^^^^^ `RefCell<i32>` cannot be shared between threads safely + | + = help: the trait `Sync` is not implemented for `RefCell<i32>` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead + = note: required for `Arc<RefCell<i32>>` to implement `Send` +note: required because it's used within this coroutine + --> $DIR/issue-68112.rs:49:5 + | +LL | || { + | ^^ +note: required because it appears within the type `impl Coroutine<Return = Arc<RefCell<i32>>>` + --> $DIR/issue-68112.rs:46:30 + | +LL | pub fn make_gen2<T>(t: T) -> impl Coroutine<Return = T> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: required because it appears within the type `impl Coroutine<Return = Arc<RefCell<i32>>>` + --> $DIR/issue-68112.rs:54:34 + | +LL | fn make_non_send_coroutine2() -> impl Coroutine<Return = Arc<RefCell<i32>>> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: required because it captures the following types: `impl Coroutine<Return = Arc<RefCell<i32>>>` +note: required because it's used within this coroutine + --> $DIR/issue-68112.rs:60:20 + | +LL | let send_gen = || { + | ^^ +note: required by a bound in `require_send` + --> $DIR/issue-68112.rs:22:25 + | +LL | fn require_send(_: impl Send) {} + | ^^^^ required by this bound in `require_send` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/coroutine/issue-69017.rs b/tests/ui/coroutine/issue-69017.rs new file mode 100644 index 000000000..7aaa1ee03 --- /dev/null +++ b/tests/ui/coroutine/issue-69017.rs @@ -0,0 +1,18 @@ +// This issue reproduces an ICE on compile +// Fails on 2020-02-08 nightly +// regressed commit: https://github.com/rust-lang/rust/commit/f8fd4624474a68bd26694eff3536b9f3a127b2d3 +// +// check-pass + +#![feature(coroutine_trait)] +#![feature(coroutines)] + +use std::ops::Coroutine; + +fn gen() -> impl Coroutine<usize> { + |_: usize| { + println!("-> {}", yield); + } +} + +fn main() {} diff --git a/tests/ui/coroutine/issue-69039.rs b/tests/ui/coroutine/issue-69039.rs new file mode 100644 index 000000000..041985e15 --- /dev/null +++ b/tests/ui/coroutine/issue-69039.rs @@ -0,0 +1,34 @@ +// run-pass + +#![feature(coroutines, coroutine_trait)] + +use std::ops::{Coroutine, CoroutineState}; + +fn mkstr(my_name: String, my_mood: String) -> String { + format!("{} is {}", my_name.trim(), my_mood.trim()) +} + +fn my_scenario() -> impl Coroutine<String, Yield = &'static str, Return = String> { + |_arg: String| { + let my_name = yield "What is your name?"; + let my_mood = yield "How are you feeling?"; + mkstr(my_name, my_mood) + } +} + +fn main() { + let mut my_session = Box::pin(my_scenario()); + + assert_eq!( + my_session.as_mut().resume("_arg".to_string()), + CoroutineState::Yielded("What is your name?") + ); + assert_eq!( + my_session.as_mut().resume("Your Name".to_string()), + CoroutineState::Yielded("How are you feeling?") + ); + assert_eq!( + my_session.as_mut().resume("Sensory Organs".to_string()), + CoroutineState::Complete("Your Name is Sensory Organs".to_string()) + ); +} diff --git a/tests/ui/coroutine/issue-87142.rs b/tests/ui/coroutine/issue-87142.rs new file mode 100644 index 000000000..b5708c4b3 --- /dev/null +++ b/tests/ui/coroutine/issue-87142.rs @@ -0,0 +1,32 @@ +// compile-flags: -Cdebuginfo=2 +// build-pass + +// Regression test for #87142 +// This test needs the above flags and the "lib" crate type. + +#![feature(impl_trait_in_assoc_type, coroutine_trait, coroutines)] +#![crate_type = "lib"] + +use std::ops::Coroutine; + +pub trait CoroutineProviderAlt: Sized { + type Coro: Coroutine<(), Return = (), Yield = ()>; + + fn start(ctx: Context<Self>) -> Self::Coro; +} + +pub struct Context<G: 'static + CoroutineProviderAlt> { + pub link: Box<G::Coro>, +} + +impl CoroutineProviderAlt for () { + type Coro = impl Coroutine<(), Return = (), Yield = ()>; + fn start(ctx: Context<Self>) -> Self::Coro { + move || { + match ctx { + _ => (), + } + yield (); + } + } +} diff --git a/tests/ui/coroutine/issue-88653.rs b/tests/ui/coroutine/issue-88653.rs new file mode 100644 index 000000000..ec4c20547 --- /dev/null +++ b/tests/ui/coroutine/issue-88653.rs @@ -0,0 +1,22 @@ +// Regression test for #88653, where a confusing warning about a +// type mismatch in coroutine arguments was issued. + +#![feature(coroutines, coroutine_trait)] + +use std::ops::Coroutine; + +fn foo(bar: bool) -> impl Coroutine<(bool,)> { + //~^ ERROR: type mismatch in coroutine arguments [E0631] + //~| NOTE: expected due to this + //~| NOTE: expected coroutine signature `fn((bool,)) -> _` + //~| NOTE: in this expansion of desugaring of `impl Trait` + //~| NOTE: in this expansion of desugaring of `impl Trait` + |bar| { + //~^ NOTE: found signature defined here + if bar { + yield bar; + } + } +} + +fn main() {} diff --git a/tests/ui/coroutine/issue-88653.stderr b/tests/ui/coroutine/issue-88653.stderr new file mode 100644 index 000000000..3ae50b5af --- /dev/null +++ b/tests/ui/coroutine/issue-88653.stderr @@ -0,0 +1,15 @@ +error[E0631]: type mismatch in coroutine arguments + --> $DIR/issue-88653.rs:8:22 + | +LL | fn foo(bar: bool) -> impl Coroutine<(bool,)> { + | ^^^^^^^^^^^^^^^^^^^^^^^ expected due to this +... +LL | |bar| { + | ----- found signature defined here + | + = note: expected coroutine signature `fn((bool,)) -> _` + found coroutine signature `fn(bool) -> _` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0631`. diff --git a/tests/ui/coroutine/issue-91477.rs b/tests/ui/coroutine/issue-91477.rs new file mode 100644 index 000000000..c98546f79 --- /dev/null +++ b/tests/ui/coroutine/issue-91477.rs @@ -0,0 +1,7 @@ +#![feature(coroutines)] + +fn foo() -> impl Sized { + yield 1; //~ ERROR E0627 +} + +fn main() {} diff --git a/tests/ui/coroutine/issue-91477.stderr b/tests/ui/coroutine/issue-91477.stderr new file mode 100644 index 000000000..0ab3c1fba --- /dev/null +++ b/tests/ui/coroutine/issue-91477.stderr @@ -0,0 +1,9 @@ +error[E0627]: yield expression outside of coroutine literal + --> $DIR/issue-91477.rs:4:5 + | +LL | yield 1; + | ^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0627`. diff --git a/tests/ui/coroutine/issue-93161.rs b/tests/ui/coroutine/issue-93161.rs new file mode 100644 index 000000000..ae8603b7c --- /dev/null +++ b/tests/ui/coroutine/issue-93161.rs @@ -0,0 +1,93 @@ +// edition:2021 +// run-pass + +#![feature(never_type)] + +use std::future::Future; + +// See if we can run a basic `async fn` +pub async fn foo(x: &u32, y: u32) -> u32 { + let y = &y; + let z = 9; + let z = &z; + let y = async { *y + *z }.await; + let a = 10; + let a = &a; + *x + y + *a +} + +async fn add(x: u32, y: u32) -> u32 { + let a = async { x + y }; + a.await +} + +async fn build_aggregate(a: u32, b: u32, c: u32, d: u32) -> u32 { + let x = (add(a, b).await, add(c, d).await); + x.0 + x.1 +} + +enum Never {} +fn never() -> Never { + panic!() +} + +async fn includes_never(crash: bool, x: u32) -> u32 { + let result = async { x * x }.await; + if !crash { + return result; + } + #[allow(unused)] + let bad = never(); + result *= async { x + x }.await; + drop(bad); + result +} + +async fn partial_init(x: u32) -> u32 { + #[allow(unreachable_code)] + let _x: (String, !) = (String::new(), return async { x + x }.await); +} + +async fn read_exact(_from: &mut &[u8], _to: &mut [u8]) -> Option<()> { + Some(()) +} + +async fn hello_world() { + let data = [0u8; 1]; + let mut reader = &data[..]; + + let mut marker = [0u8; 1]; + read_exact(&mut reader, &mut marker).await.unwrap(); +} + +fn run_fut<T>(fut: impl Future<Output = T>) -> T { + use std::sync::Arc; + use std::task::{Context, Poll, Wake, Waker}; + + struct MyWaker; + impl Wake for MyWaker { + fn wake(self: Arc<Self>) { + unimplemented!() + } + } + + let waker = Waker::from(Arc::new(MyWaker)); + let mut context = Context::from_waker(&waker); + + let mut pinned = Box::pin(fut); + loop { + match pinned.as_mut().poll(&mut context) { + Poll::Pending => continue, + Poll::Ready(v) => return v, + } + } +} + +fn main() { + let x = 5; + assert_eq!(run_fut(foo(&x, 7)), 31); + assert_eq!(run_fut(build_aggregate(1, 2, 3, 4)), 10); + assert_eq!(run_fut(includes_never(false, 4)), 16); + assert_eq!(run_fut(partial_init(4)), 8); + run_fut(hello_world()); +} diff --git a/tests/ui/coroutine/iterator-count.rs b/tests/ui/coroutine/iterator-count.rs new file mode 100644 index 000000000..322e56f8a --- /dev/null +++ b/tests/ui/coroutine/iterator-count.rs @@ -0,0 +1,44 @@ +// run-pass + +#![feature(coroutines, coroutine_trait)] + +use std::marker::Unpin; +use std::ops::{Coroutine, CoroutineState}; +use std::pin::Pin; + +struct W<T>(T); + +// This impl isn't safe in general, but the coroutine used in this test is movable +// so it won't cause problems. +impl<T: Coroutine<(), Return = ()> + Unpin> Iterator for W<T> { + type Item = T::Yield; + + fn next(&mut self) -> Option<Self::Item> { + match Pin::new(&mut self.0).resume(()) { + CoroutineState::Complete(..) => None, + CoroutineState::Yielded(v) => Some(v), + } + } +} + +fn test() -> impl Coroutine<(), Return = (), Yield = u8> + Unpin { + || { + for i in 1..6 { + yield i + } + } +} + +fn main() { + let end = 11; + + let closure_test = |start| { + move || { + for i in start..end { + yield i + } + } + }; + + assert!(W(test()).chain(W(closure_test(6))).eq(1..11)); +} diff --git a/tests/ui/coroutine/layout-error.rs b/tests/ui/coroutine/layout-error.rs new file mode 100644 index 000000000..87da60700 --- /dev/null +++ b/tests/ui/coroutine/layout-error.rs @@ -0,0 +1,28 @@ +// Verifies that computing a layout of a coroutine tainted by type errors +// doesn't ICE. Regression test for #80998. +// +// edition:2018 + +#![feature(type_alias_impl_trait)] +use std::future::Future; + +pub struct Task<F: Future>(F); +impl<F: Future> Task<F> { + const fn new() -> Self { + todo!() + } + fn spawn(&self, _: impl FnOnce() -> F) { + todo!() + } +} + +fn main() { + async fn cb() { + let a = Foo; //~ ERROR cannot find value `Foo` in this scope + } + + type F = impl Future; + // Check that statics are inhabited computes they layout. + static POOL: Task<F> = Task::new(); + Task::spawn(&POOL, || cb()); +} diff --git a/tests/ui/coroutine/layout-error.stderr b/tests/ui/coroutine/layout-error.stderr new file mode 100644 index 000000000..b1a258f4f --- /dev/null +++ b/tests/ui/coroutine/layout-error.stderr @@ -0,0 +1,9 @@ +error[E0425]: cannot find value `Foo` in this scope + --> $DIR/layout-error.rs:21:17 + | +LL | let a = Foo; + | ^^^ not found in this scope + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/coroutine/live-upvar-across-yield.rs b/tests/ui/coroutine/live-upvar-across-yield.rs new file mode 100644 index 000000000..740a446e7 --- /dev/null +++ b/tests/ui/coroutine/live-upvar-across-yield.rs @@ -0,0 +1,14 @@ +// run-pass + +#![feature(coroutines, coroutine_trait)] + +use std::ops::Coroutine; +use std::pin::Pin; + +fn main() { + let b = |_| 3; + let mut a = || { + b(yield); + }; + Pin::new(&mut a).resume(()); +} diff --git a/tests/ui/coroutine/match-bindings.rs b/tests/ui/coroutine/match-bindings.rs new file mode 100644 index 000000000..1a5b3cdb0 --- /dev/null +++ b/tests/ui/coroutine/match-bindings.rs @@ -0,0 +1,23 @@ +// run-pass +#![allow(dead_code)] + +#![feature(coroutines)] + +enum Enum { + A(String), + B +} + +fn main() { + || { //~ WARN unused coroutine that must be used + loop { + if let true = true { + match Enum::A(String::new()) { + Enum::A(_var) => {} + Enum::B => {} + } + } + yield; + } + }; +} diff --git a/tests/ui/coroutine/match-bindings.stderr b/tests/ui/coroutine/match-bindings.stderr new file mode 100644 index 000000000..a7aa6eadb --- /dev/null +++ b/tests/ui/coroutine/match-bindings.stderr @@ -0,0 +1,17 @@ +warning: unused coroutine that must be used + --> $DIR/match-bindings.rs:12:5 + | +LL | / || { +LL | | loop { +LL | | if let true = true { +LL | | match Enum::A(String::new()) { +... | +LL | | } +LL | | }; + | |_____^ + | + = note: coroutines are lazy and do nothing unless resumed + = note: `#[warn(unused_must_use)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/coroutine/metadata-sufficient-for-layout.rs b/tests/ui/coroutine/metadata-sufficient-for-layout.rs new file mode 100644 index 000000000..434a28015 --- /dev/null +++ b/tests/ui/coroutine/metadata-sufficient-for-layout.rs @@ -0,0 +1,25 @@ +// Check that the layout of a coroutine is available when auxiliary crate +// is compiled with --emit metadata. +// +// Regression test for #80998. +// +// aux-build:metadata-sufficient-for-layout.rs + +#![feature(type_alias_impl_trait, rustc_attrs)] +#![feature(coroutine_trait)] + +extern crate metadata_sufficient_for_layout; + +use std::ops::Coroutine; + +type F = impl Coroutine<(), Yield = (), Return = ()>; + +// Static queries the layout of the coroutine. +static A: Option<F> = None; + +fn f() -> F { + metadata_sufficient_for_layout::g() +} + +#[rustc_error] +fn main() {} //~ ERROR diff --git a/tests/ui/coroutine/metadata-sufficient-for-layout.stderr b/tests/ui/coroutine/metadata-sufficient-for-layout.stderr new file mode 100644 index 000000000..3488b04f2 --- /dev/null +++ b/tests/ui/coroutine/metadata-sufficient-for-layout.stderr @@ -0,0 +1,8 @@ +error: fatal error triggered by #[rustc_error] + --> $DIR/metadata-sufficient-for-layout.rs:25:1 + | +LL | fn main() {} + | ^^^^^^^^^ + +error: aborting due to previous error + diff --git a/tests/ui/coroutine/nested_coroutine.rs b/tests/ui/coroutine/nested_coroutine.rs new file mode 100644 index 000000000..04f4aa771 --- /dev/null +++ b/tests/ui/coroutine/nested_coroutine.rs @@ -0,0 +1,21 @@ +// run-pass + +#![feature(coroutines, coroutine_trait)] + +use std::ops::{Coroutine, CoroutineState}; +use std::pin::Pin; + +fn main() { + let _coroutine = || { + let mut sub_coroutine = || { + yield 2; + }; + + match Pin::new(&mut sub_coroutine).resume(()) { + CoroutineState::Yielded(x) => { + yield x; + } + _ => panic!(), + }; + }; +} diff --git a/tests/ui/coroutine/niche-in-coroutine.rs b/tests/ui/coroutine/niche-in-coroutine.rs new file mode 100644 index 000000000..7ad4c6bc9 --- /dev/null +++ b/tests/ui/coroutine/niche-in-coroutine.rs @@ -0,0 +1,19 @@ +// Test that niche finding works with captured coroutine upvars. + +// run-pass + +#![feature(coroutines)] + +use std::mem::size_of_val; + +fn take<T>(_: T) {} + +fn main() { + let x = false; + let gen1 = || { + yield; + take(x); + }; + + assert_eq!(size_of_val(&gen1), size_of_val(&Some(gen1))); +} diff --git a/tests/ui/coroutine/non-static-is-unpin.rs b/tests/ui/coroutine/non-static-is-unpin.rs new file mode 100644 index 000000000..d6ded53ae --- /dev/null +++ b/tests/ui/coroutine/non-static-is-unpin.rs @@ -0,0 +1,21 @@ +// revisions: current next +//[next] compile-flags: -Ztrait-solver=next +// run-pass + +#![feature(coroutines, coroutine_trait)] +#![allow(dropping_copy_types)] + +use std::marker::{PhantomPinned, Unpin}; + +fn assert_unpin<G: Unpin>(_: G) { +} + +fn main() { + // Even though this coroutine holds a `PhantomPinned` in its environment, it + // remains `Unpin`. + assert_unpin(|| { + let pinned = PhantomPinned; + yield; + drop(pinned); + }); +} diff --git a/tests/ui/coroutine/not-send-sync.rs b/tests/ui/coroutine/not-send-sync.rs new file mode 100644 index 000000000..dd6182c10 --- /dev/null +++ b/tests/ui/coroutine/not-send-sync.rs @@ -0,0 +1,27 @@ +#![feature(coroutines)] +#![feature(negative_impls)] + +struct NotSend; +struct NotSync; + +impl !Send for NotSend {} +impl !Sync for NotSync {} + +fn main() { + fn assert_sync<T: Sync>(_: T) {} + fn assert_send<T: Send>(_: T) {} + + assert_sync(|| { + //~^ ERROR: coroutine cannot be shared between threads safely + let a = NotSync; + yield; + drop(a); + }); + + assert_send(|| { + //~^ ERROR: coroutine cannot be sent between threads safely + let a = NotSend; + yield; + drop(a); + }); +} diff --git a/tests/ui/coroutine/not-send-sync.stderr b/tests/ui/coroutine/not-send-sync.stderr new file mode 100644 index 000000000..b33a1e63a --- /dev/null +++ b/tests/ui/coroutine/not-send-sync.stderr @@ -0,0 +1,42 @@ +error: coroutine cannot be shared between threads safely + --> $DIR/not-send-sync.rs:14:5 + | +LL | assert_sync(|| { + | ^^^^^^^^^^^ coroutine is not `Sync` + | + = help: within `{coroutine@$DIR/not-send-sync.rs:14:17: 14:19}`, the trait `Sync` is not implemented for `NotSync` +note: coroutine is not `Sync` as this value is used across a yield + --> $DIR/not-send-sync.rs:17:9 + | +LL | let a = NotSync; + | - has type `NotSync` which is not `Sync` +LL | yield; + | ^^^^^ yield occurs here, with `a` maybe used later +note: required by a bound in `assert_sync` + --> $DIR/not-send-sync.rs:11:23 + | +LL | fn assert_sync<T: Sync>(_: T) {} + | ^^^^ required by this bound in `assert_sync` + +error: coroutine cannot be sent between threads safely + --> $DIR/not-send-sync.rs:21:5 + | +LL | assert_send(|| { + | ^^^^^^^^^^^ coroutine is not `Send` + | + = help: within `{coroutine@$DIR/not-send-sync.rs:21:17: 21:19}`, the trait `Send` is not implemented for `NotSend` +note: coroutine is not `Send` as this value is used across a yield + --> $DIR/not-send-sync.rs:24:9 + | +LL | let a = NotSend; + | - has type `NotSend` which is not `Send` +LL | yield; + | ^^^^^ yield occurs here, with `a` maybe used later +note: required by a bound in `assert_send` + --> $DIR/not-send-sync.rs:12:23 + | +LL | fn assert_send<T: Send>(_: T) {} + | ^^^^ required by this bound in `assert_send` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/coroutine/overlap-locals.rs b/tests/ui/coroutine/overlap-locals.rs new file mode 100644 index 000000000..7c151270b --- /dev/null +++ b/tests/ui/coroutine/overlap-locals.rs @@ -0,0 +1,29 @@ +// run-pass + +#![feature(coroutines)] + +fn main() { + let a = || { + { + let w: i32 = 4; + yield; + println!("{:?}", w); + } + { + let x: i32 = 5; + yield; + println!("{:?}", x); + } + { + let y: i32 = 6; + yield; + println!("{:?}", y); + } + { + let z: i32 = 7; + yield; + println!("{:?}", z); + } + }; + assert_eq!(8, std::mem::size_of_val(&a)); +} diff --git a/tests/ui/coroutine/panic-drops-resume.rs b/tests/ui/coroutine/panic-drops-resume.rs new file mode 100644 index 000000000..e866f216a --- /dev/null +++ b/tests/ui/coroutine/panic-drops-resume.rs @@ -0,0 +1,36 @@ +//! Tests that panics inside a coroutine will correctly drop the initial resume argument. + +// run-pass +// needs-unwind + +#![feature(coroutines, coroutine_trait)] + +use std::ops::Coroutine; +use std::panic::{catch_unwind, AssertUnwindSafe}; +use std::pin::Pin; +use std::sync::atomic::{AtomicUsize, Ordering}; + +static DROP: AtomicUsize = AtomicUsize::new(0); + +struct Dropper {} + +impl Drop for Dropper { + fn drop(&mut self) { + DROP.fetch_add(1, Ordering::SeqCst); + } +} + +fn main() { + let mut gen = |_arg| { + if true { + panic!(); + } + yield (); + }; + let mut gen = Pin::new(&mut gen); + + assert_eq!(DROP.load(Ordering::Acquire), 0); + let res = catch_unwind(AssertUnwindSafe(|| gen.as_mut().resume(Dropper {}))); + assert!(res.is_err()); + assert_eq!(DROP.load(Ordering::Acquire), 1); +} diff --git a/tests/ui/coroutine/panic-drops.rs b/tests/ui/coroutine/panic-drops.rs new file mode 100644 index 000000000..7e37279b9 --- /dev/null +++ b/tests/ui/coroutine/panic-drops.rs @@ -0,0 +1,57 @@ +// run-pass +// needs-unwind + + +#![feature(coroutines, coroutine_trait)] + +use std::ops::Coroutine; +use std::panic; +use std::pin::Pin; +use std::sync::atomic::{AtomicUsize, Ordering}; + +static A: AtomicUsize = AtomicUsize::new(0); + +struct B; + +impl Drop for B { + fn drop(&mut self) { + A.fetch_add(1, Ordering::SeqCst); + } +} + +fn bool_true() -> bool { + true +} + +fn main() { + let b = B; + let mut foo = || { + if bool_true() { + panic!(); + } + drop(b); + yield; + }; + + assert_eq!(A.load(Ordering::SeqCst), 0); + let res = panic::catch_unwind(panic::AssertUnwindSafe(|| { + Pin::new(&mut foo).resume(()) + })); + assert!(res.is_err()); + assert_eq!(A.load(Ordering::SeqCst), 1); + + let mut foo = || { + if bool_true() { + panic!(); + } + drop(B); + yield; + }; + + assert_eq!(A.load(Ordering::SeqCst), 1); + let res = panic::catch_unwind(panic::AssertUnwindSafe(|| { + Pin::new(&mut foo).resume(()) + })); + assert!(res.is_err()); + assert_eq!(A.load(Ordering::SeqCst), 1); +} diff --git a/tests/ui/coroutine/panic-safe.rs b/tests/ui/coroutine/panic-safe.rs new file mode 100644 index 000000000..9aa427565 --- /dev/null +++ b/tests/ui/coroutine/panic-safe.rs @@ -0,0 +1,30 @@ +// run-pass +// needs-unwind + + +#![feature(coroutines, coroutine_trait)] + +use std::ops::Coroutine; +use std::pin::Pin; +use std::panic; + +fn main() { + let mut foo = || { + if true { + panic!(); + } + yield; + }; + + let res = panic::catch_unwind(panic::AssertUnwindSafe(|| { + Pin::new(&mut foo).resume(()) + })); + assert!(res.is_err()); + + for _ in 0..10 { + let res = panic::catch_unwind(panic::AssertUnwindSafe(|| { + Pin::new(&mut foo).resume(()) + })); + assert!(res.is_err()); + } +} diff --git a/tests/ui/coroutine/parent-expression.rs b/tests/ui/coroutine/parent-expression.rs new file mode 100644 index 000000000..4d40192c0 --- /dev/null +++ b/tests/ui/coroutine/parent-expression.rs @@ -0,0 +1,68 @@ +#![feature(coroutines, negative_impls, rustc_attrs)] + +macro_rules! type_combinations { + ( + $( $name:ident => { $( $tt:tt )* } );* $(;)? + ) => { $( + mod $name { + $( $tt )* + + impl !Sync for Client {} + impl !Send for Client {} + } + + // Struct update syntax. This fails because the Client used in the update is considered + // dropped *after* the yield. + { + let g = move || match drop($name::Client { ..$name::Client::default() }) { + //~^ `significant_drop::Client` which is not `Send` + //~| `insignificant_dtor::Client` which is not `Send` + //~| `derived_drop::Client` which is not `Send` + _ => yield, + }; + assert_send(g); + //~^ ERROR cannot be sent between threads + //~| ERROR cannot be sent between threads + //~| ERROR cannot be sent between threads + } + + // Simple owned value. This works because the Client is considered moved into `drop`, + // even though the temporary expression doesn't end until after the yield. + { + let g = move || match drop($name::Client::default()) { + _ => yield, + }; + assert_send(g); + } + )* } +} + +fn assert_send<T: Send>(_thing: T) {} + +fn main() { + type_combinations!( + // OK + copy => { #[derive(Copy, Clone, Default)] pub struct Client; }; + // NOT OK: MIR borrowck thinks that this is used after the yield, even though + // this has no `Drop` impl and only the drops of the fields are observable. + // FIXME: this should compile. + derived_drop => { #[derive(Default)] pub struct Client { pub nickname: String } }; + // NOT OK + significant_drop => { + #[derive(Default)] + pub struct Client; + impl Drop for Client { + fn drop(&mut self) {} + } + }; + // NOT OK (we need to agree with MIR borrowck) + insignificant_dtor => { + #[derive(Default)] + #[rustc_insignificant_dtor] + pub struct Client; + impl Drop for Client { + fn drop(&mut self) {} + } + }; + ); +} diff --git a/tests/ui/coroutine/parent-expression.stderr b/tests/ui/coroutine/parent-expression.stderr new file mode 100644 index 000000000..6b611bc3f --- /dev/null +++ b/tests/ui/coroutine/parent-expression.stderr @@ -0,0 +1,122 @@ +error: coroutine cannot be sent between threads safely + --> $DIR/parent-expression.rs:23:13 + | +LL | assert_send(g); + | ^^^^^^^^^^^ coroutine is not `Send` +... +LL | / type_combinations!( +LL | | // OK +LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; }; +LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though +... | +LL | | }; +LL | | ); + | |_____- in this macro invocation + | + = help: within `{coroutine@$DIR/parent-expression.rs:17:21: 17:28}`, the trait `Send` is not implemented for `derived_drop::Client` +note: coroutine is not `Send` as this value is used across a yield + --> $DIR/parent-expression.rs:21:22 + | +LL | let g = move || match drop($name::Client { ..$name::Client::default() }) { + | ------------------------ has type `derived_drop::Client` which is not `Send` +... +LL | _ => yield, + | ^^^^^ yield occurs here, with `$name::Client::default()` maybe used later +... +LL | / type_combinations!( +LL | | // OK +LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; }; +LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though +... | +LL | | }; +LL | | ); + | |_____- in this macro invocation +note: required by a bound in `assert_send` + --> $DIR/parent-expression.rs:40:19 + | +LL | fn assert_send<T: Send>(_thing: T) {} + | ^^^^ required by this bound in `assert_send` + = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: coroutine cannot be sent between threads safely + --> $DIR/parent-expression.rs:23:13 + | +LL | assert_send(g); + | ^^^^^^^^^^^ coroutine is not `Send` +... +LL | / type_combinations!( +LL | | // OK +LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; }; +LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though +... | +LL | | }; +LL | | ); + | |_____- in this macro invocation + | + = help: within `{coroutine@$DIR/parent-expression.rs:17:21: 17:28}`, the trait `Send` is not implemented for `significant_drop::Client` +note: coroutine is not `Send` as this value is used across a yield + --> $DIR/parent-expression.rs:21:22 + | +LL | let g = move || match drop($name::Client { ..$name::Client::default() }) { + | ------------------------ has type `significant_drop::Client` which is not `Send` +... +LL | _ => yield, + | ^^^^^ yield occurs here, with `$name::Client::default()` maybe used later +... +LL | / type_combinations!( +LL | | // OK +LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; }; +LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though +... | +LL | | }; +LL | | ); + | |_____- in this macro invocation +note: required by a bound in `assert_send` + --> $DIR/parent-expression.rs:40:19 + | +LL | fn assert_send<T: Send>(_thing: T) {} + | ^^^^ required by this bound in `assert_send` + = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: coroutine cannot be sent between threads safely + --> $DIR/parent-expression.rs:23:13 + | +LL | assert_send(g); + | ^^^^^^^^^^^ coroutine is not `Send` +... +LL | / type_combinations!( +LL | | // OK +LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; }; +LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though +... | +LL | | }; +LL | | ); + | |_____- in this macro invocation + | + = help: within `{coroutine@$DIR/parent-expression.rs:17:21: 17:28}`, the trait `Send` is not implemented for `insignificant_dtor::Client` +note: coroutine is not `Send` as this value is used across a yield + --> $DIR/parent-expression.rs:21:22 + | +LL | let g = move || match drop($name::Client { ..$name::Client::default() }) { + | ------------------------ has type `insignificant_dtor::Client` which is not `Send` +... +LL | _ => yield, + | ^^^^^ yield occurs here, with `$name::Client::default()` maybe used later +... +LL | / type_combinations!( +LL | | // OK +LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; }; +LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though +... | +LL | | }; +LL | | ); + | |_____- in this macro invocation +note: required by a bound in `assert_send` + --> $DIR/parent-expression.rs:40:19 + | +LL | fn assert_send<T: Send>(_thing: T) {} + | ^^^^ required by this bound in `assert_send` + = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 3 previous errors + diff --git a/tests/ui/coroutine/partial-drop.rs b/tests/ui/coroutine/partial-drop.rs new file mode 100644 index 000000000..a4347f52a --- /dev/null +++ b/tests/ui/coroutine/partial-drop.rs @@ -0,0 +1,34 @@ +// check-pass +#![feature(negative_impls, coroutines)] + +struct Foo; +impl !Send for Foo {} + +struct Bar { + foo: Foo, + x: i32, +} + +fn main() { + assert_send(|| { + let guard = Bar { foo: Foo, x: 42 }; + drop(guard.foo); + yield; + }); + + assert_send(|| { + let mut guard = Bar { foo: Foo, x: 42 }; + drop(guard); + guard = Bar { foo: Foo, x: 23 }; + yield; + }); + + assert_send(|| { + let guard = Bar { foo: Foo, x: 42 }; + let Bar { foo, x } = guard; + drop(foo); + yield; + }); +} + +fn assert_send<T: Send>(_: T) {} diff --git a/tests/ui/coroutine/partial-initialization-across-yield.rs b/tests/ui/coroutine/partial-initialization-across-yield.rs new file mode 100644 index 000000000..75ad5a228 --- /dev/null +++ b/tests/ui/coroutine/partial-initialization-across-yield.rs @@ -0,0 +1,43 @@ +// Test that we don't allow yielding from a coroutine while a local is partially +// initialized. + +#![feature(coroutines)] + +struct S { x: i32, y: i32 } +struct T(i32, i32); + +fn test_tuple() { + let _ = || { + let mut t: (i32, i32); + t.0 = 42; //~ ERROR E0381 + yield; + t.1 = 88; + let _ = t; + }; +} + +fn test_tuple_struct() { + let _ = || { + let mut t: T; + t.0 = 42; //~ ERROR E0381 + yield; + t.1 = 88; + let _ = t; + }; +} + +fn test_struct() { + let _ = || { + let mut t: S; + t.x = 42; //~ ERROR E0381 + yield; + t.y = 88; + let _ = t; + }; +} + +fn main() { + test_tuple(); + test_tuple_struct(); + test_struct(); +} diff --git a/tests/ui/coroutine/partial-initialization-across-yield.stderr b/tests/ui/coroutine/partial-initialization-across-yield.stderr new file mode 100644 index 000000000..3f9f1c046 --- /dev/null +++ b/tests/ui/coroutine/partial-initialization-across-yield.stderr @@ -0,0 +1,33 @@ +error[E0381]: partially assigned binding `t` isn't fully initialized + --> $DIR/partial-initialization-across-yield.rs:12:9 + | +LL | let mut t: (i32, i32); + | ----- binding declared here but left uninitialized +LL | t.0 = 42; + | ^^^^^^^^ `t` partially assigned here but it isn't fully initialized + | + = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` + +error[E0381]: partially assigned binding `t` isn't fully initialized + --> $DIR/partial-initialization-across-yield.rs:22:9 + | +LL | let mut t: T; + | ----- binding declared here but left uninitialized +LL | t.0 = 42; + | ^^^^^^^^ `t` partially assigned here but it isn't fully initialized + | + = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` + +error[E0381]: partially assigned binding `t` isn't fully initialized + --> $DIR/partial-initialization-across-yield.rs:32:9 + | +LL | let mut t: S; + | ----- binding declared here but left uninitialized +LL | t.x = 42; + | ^^^^^^^^ `t` partially assigned here but it isn't fully initialized + | + = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0381`. diff --git a/tests/ui/coroutine/pattern-borrow.rs b/tests/ui/coroutine/pattern-borrow.rs new file mode 100644 index 000000000..76084433d --- /dev/null +++ b/tests/ui/coroutine/pattern-borrow.rs @@ -0,0 +1,17 @@ +#![feature(coroutines)] + +enum Test { A(i32), B, } + +fn main() { } + +fn fun(test: Test) { + move || { + if let Test::A(ref _a) = test { //~ ERROR borrow may still be in use when coroutine yields + yield (); + _a.use_ref(); + } + }; +} + +trait Fake { fn use_mut(&mut self) { } fn use_ref(&self) { } } +impl<T> Fake for T { } diff --git a/tests/ui/coroutine/pattern-borrow.stderr b/tests/ui/coroutine/pattern-borrow.stderr new file mode 100644 index 000000000..ddb3bf662 --- /dev/null +++ b/tests/ui/coroutine/pattern-borrow.stderr @@ -0,0 +1,11 @@ +error[E0626]: borrow may still be in use when coroutine yields + --> $DIR/pattern-borrow.rs:9:24 + | +LL | if let Test::A(ref _a) = test { + | ^^^^^^ +LL | yield (); + | -------- possible yield occurs here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0626`. diff --git a/tests/ui/coroutine/pin-box-coroutine.rs b/tests/ui/coroutine/pin-box-coroutine.rs new file mode 100644 index 000000000..e348551a6 --- /dev/null +++ b/tests/ui/coroutine/pin-box-coroutine.rs @@ -0,0 +1,13 @@ +// run-pass + +#![feature(coroutines, coroutine_trait)] + +use std::ops::Coroutine; + +fn assert_coroutine<G: Coroutine>(_: G) { +} + +fn main() { + assert_coroutine(static || yield); + assert_coroutine(Box::pin(static || yield)); +} diff --git a/tests/ui/coroutine/print/coroutine-print-verbose-1.rs b/tests/ui/coroutine/print/coroutine-print-verbose-1.rs new file mode 100644 index 000000000..c47d7572c --- /dev/null +++ b/tests/ui/coroutine/print/coroutine-print-verbose-1.rs @@ -0,0 +1,60 @@ +// compile-flags: -Zverbose + +// Same as: tests/ui/coroutine/issue-68112.stderr + +#![feature(coroutines, coroutine_trait)] + +use std::{ + cell::RefCell, + sync::Arc, + pin::Pin, + ops::{Coroutine, CoroutineState}, +}; + +pub struct Ready<T>(Option<T>); +impl<T: Unpin> Coroutine<()> for Ready<T> { + type Return = T; + type Yield = (); + fn resume(mut self: Pin<&mut Self>, _args: ()) -> CoroutineState<(), T> { + CoroutineState::Complete(self.0.take().unwrap()) + } +} +pub fn make_gen1<T>(t: T) -> Ready<T> { + Ready(Some(t)) +} + +fn require_send(_: impl Send) {} + +fn make_non_send_coroutine() -> impl Coroutine<Return = Arc<RefCell<i32>>> { + make_gen1(Arc::new(RefCell::new(0))) +} + +fn test1() { + let send_gen = || { + let _non_send_gen = make_non_send_coroutine(); + yield; + }; + require_send(send_gen); + //~^ ERROR coroutine cannot be sent between threads +} + +pub fn make_gen2<T>(t: T) -> impl Coroutine<Return = T> { + || { + yield; + t + } +} +fn make_non_send_coroutine2() -> impl Coroutine<Return = Arc<RefCell<i32>>> { + make_gen2(Arc::new(RefCell::new(0))) +} + +fn test2() { + let send_gen = || { + let _non_send_gen = make_non_send_coroutine2(); + yield; + }; + require_send(send_gen); + //~^ ERROR `RefCell<i32>` cannot be shared between threads safely +} + +fn main() {} diff --git a/tests/ui/coroutine/print/coroutine-print-verbose-1.stderr b/tests/ui/coroutine/print/coroutine-print-verbose-1.stderr new file mode 100644 index 000000000..bcdcbc154 --- /dev/null +++ b/tests/ui/coroutine/print/coroutine-print-verbose-1.stderr @@ -0,0 +1,60 @@ +error: coroutine cannot be sent between threads safely + --> $DIR/coroutine-print-verbose-1.rs:37:5 + | +LL | require_send(send_gen); + | ^^^^^^^^^^^^ coroutine is not `Send` + | + = help: the trait `Sync` is not implemented for `RefCell<i32>` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead +note: coroutine is not `Send` as this value is used across a yield + --> $DIR/coroutine-print-verbose-1.rs:35:9 + | +LL | let _non_send_gen = make_non_send_coroutine(); + | ------------- has type `Opaque(DefId(0:34 ~ coroutine_print_verbose_1[75fb]::make_non_send_coroutine::{opaque#0}), [])` which is not `Send` +LL | yield; + | ^^^^^ yield occurs here, with `_non_send_gen` maybe used later +note: required by a bound in `require_send` + --> $DIR/coroutine-print-verbose-1.rs:26:25 + | +LL | fn require_send(_: impl Send) {} + | ^^^^ required by this bound in `require_send` + +error[E0277]: `RefCell<i32>` cannot be shared between threads safely + --> $DIR/coroutine-print-verbose-1.rs:56:5 + | +LL | require_send(send_gen); + | ^^^^^^^^^^^^ `RefCell<i32>` cannot be shared between threads safely + | + = help: the trait `Sync` is not implemented for `RefCell<i32>` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead + = note: required for `Arc<RefCell<i32>>` to implement `Send` +note: required because it's used within this coroutine + --> $DIR/coroutine-print-verbose-1.rs:42:5 + | +LL | || { + | ^^ +note: required because it appears within the type `Opaque(DefId(0:35 ~ coroutine_print_verbose_1[75fb]::make_gen2::{opaque#0}), [Arc<RefCell<i32>>])` + --> $DIR/coroutine-print-verbose-1.rs:41:30 + | +LL | pub fn make_gen2<T>(t: T) -> impl Coroutine<Return = T> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: required because it appears within the type `Opaque(DefId(0:36 ~ coroutine_print_verbose_1[75fb]::make_non_send_coroutine2::{opaque#0}), [])` + --> $DIR/coroutine-print-verbose-1.rs:47:34 + | +LL | fn make_non_send_coroutine2() -> impl Coroutine<Return = Arc<RefCell<i32>>> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: required because it captures the following types: `Opaque(DefId(0:36 ~ coroutine_print_verbose_1[75fb]::make_non_send_coroutine2::{opaque#0}), [])` +note: required because it's used within this coroutine + --> $DIR/coroutine-print-verbose-1.rs:52:20 + | +LL | let send_gen = || { + | ^^ +note: required by a bound in `require_send` + --> $DIR/coroutine-print-verbose-1.rs:26:25 + | +LL | fn require_send(_: impl Send) {} + | ^^^^ required by this bound in `require_send` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/coroutine/print/coroutine-print-verbose-2.rs b/tests/ui/coroutine/print/coroutine-print-verbose-2.rs new file mode 100644 index 000000000..c65c33cb4 --- /dev/null +++ b/tests/ui/coroutine/print/coroutine-print-verbose-2.rs @@ -0,0 +1,30 @@ +// compile-flags: -Zverbose + +// Same as test/ui/coroutine/not-send-sync.rs +#![feature(coroutines)] +#![feature(negative_impls)] + +struct NotSend; +struct NotSync; + +impl !Send for NotSend {} +impl !Sync for NotSync {} + +fn main() { + fn assert_sync<T: Sync>(_: T) {} + fn assert_send<T: Send>(_: T) {} + + assert_sync(|| { + //~^ ERROR: coroutine cannot be shared between threads safely + let a = NotSync; + yield; + drop(a); + }); + + assert_send(|| { + //~^ ERROR: coroutine cannot be sent between threads safely + let a = NotSend; + yield; + drop(a); + }); +} diff --git a/tests/ui/coroutine/print/coroutine-print-verbose-2.stderr b/tests/ui/coroutine/print/coroutine-print-verbose-2.stderr new file mode 100644 index 000000000..e9c7a8ffc --- /dev/null +++ b/tests/ui/coroutine/print/coroutine-print-verbose-2.stderr @@ -0,0 +1,42 @@ +error: coroutine cannot be shared between threads safely + --> $DIR/coroutine-print-verbose-2.rs:17:5 + | +LL | assert_sync(|| { + | ^^^^^^^^^^^ coroutine is not `Sync` + | + = help: within `{main::{closure#0} upvar_tys=() {main::{closure#0}}}`, the trait `Sync` is not implemented for `NotSync` +note: coroutine is not `Sync` as this value is used across a yield + --> $DIR/coroutine-print-verbose-2.rs:20:9 + | +LL | let a = NotSync; + | - has type `NotSync` which is not `Sync` +LL | yield; + | ^^^^^ yield occurs here, with `a` maybe used later +note: required by a bound in `assert_sync` + --> $DIR/coroutine-print-verbose-2.rs:14:23 + | +LL | fn assert_sync<T: Sync>(_: T) {} + | ^^^^ required by this bound in `assert_sync` + +error: coroutine cannot be sent between threads safely + --> $DIR/coroutine-print-verbose-2.rs:24:5 + | +LL | assert_send(|| { + | ^^^^^^^^^^^ coroutine is not `Send` + | + = help: within `{main::{closure#1} upvar_tys=() {main::{closure#1}}}`, the trait `Send` is not implemented for `NotSend` +note: coroutine is not `Send` as this value is used across a yield + --> $DIR/coroutine-print-verbose-2.rs:27:9 + | +LL | let a = NotSend; + | - has type `NotSend` which is not `Send` +LL | yield; + | ^^^^^ yield occurs here, with `a` maybe used later +note: required by a bound in `assert_send` + --> $DIR/coroutine-print-verbose-2.rs:15:23 + | +LL | fn assert_send<T: Send>(_: T) {} + | ^^^^ required by this bound in `assert_send` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/coroutine/print/coroutine-print-verbose-3.rs b/tests/ui/coroutine/print/coroutine-print-verbose-3.rs new file mode 100644 index 000000000..3e4bb6281 --- /dev/null +++ b/tests/ui/coroutine/print/coroutine-print-verbose-3.rs @@ -0,0 +1,12 @@ +// compile-flags: -Zverbose + +#![feature(coroutines, coroutine_trait)] + +fn main() { + let x = "Type mismatch test"; + let coroutine :() = || { + //~^ ERROR mismatched types + yield 1i32; + return x + }; +} diff --git a/tests/ui/coroutine/print/coroutine-print-verbose-3.stderr b/tests/ui/coroutine/print/coroutine-print-verbose-3.stderr new file mode 100644 index 000000000..fb80f29d1 --- /dev/null +++ b/tests/ui/coroutine/print/coroutine-print-verbose-3.stderr @@ -0,0 +1,19 @@ +error[E0308]: mismatched types + --> $DIR/coroutine-print-verbose-3.rs:7:25 + | +LL | let coroutine :() = || { + | ____________________--___^ + | | | + | | expected due to this +LL | | +LL | | yield 1i32; +LL | | return x +LL | | }; + | |_____^ expected `()`, found coroutine + | + = note: expected unit type `()` + found coroutine `{main::{closure#0} upvar_tys=(unavailable)}` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/coroutine/reborrow-mut-upvar.rs b/tests/ui/coroutine/reborrow-mut-upvar.rs new file mode 100644 index 000000000..e4f717be8 --- /dev/null +++ b/tests/ui/coroutine/reborrow-mut-upvar.rs @@ -0,0 +1,16 @@ +// run-pass + +#![feature(coroutines)] + +fn _run(bar: &mut i32) { + || { //~ WARN unused coroutine that must be used + { + let _baz = &*bar; + yield; + } + + *bar = 2; + }; +} + +fn main() {} diff --git a/tests/ui/coroutine/reborrow-mut-upvar.stderr b/tests/ui/coroutine/reborrow-mut-upvar.stderr new file mode 100644 index 000000000..5b614ac4b --- /dev/null +++ b/tests/ui/coroutine/reborrow-mut-upvar.stderr @@ -0,0 +1,17 @@ +warning: unused coroutine that must be used + --> $DIR/reborrow-mut-upvar.rs:6:5 + | +LL | / || { +LL | | { +LL | | let _baz = &*bar; +LL | | yield; +... | +LL | | *bar = 2; +LL | | }; + | |_____^ + | + = note: coroutines are lazy and do nothing unless resumed + = note: `#[warn(unused_must_use)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/coroutine/ref-escapes-but-not-over-yield.rs b/tests/ui/coroutine/ref-escapes-but-not-over-yield.rs new file mode 100644 index 000000000..a9c13188f --- /dev/null +++ b/tests/ui/coroutine/ref-escapes-but-not-over-yield.rs @@ -0,0 +1,16 @@ +#![feature(coroutines)] + +fn foo(x: &i32) { + // In this case, a reference to `b` escapes the coroutine, but not + // because of a yield. We see that there is no yield in the scope of + // `b` and give the more generic error message. + let mut a = &3; + let mut b = move || { + yield(); + let b = 5; + a = &b; + //~^ ERROR borrowed data escapes outside of coroutine + }; +} + +fn main() { } diff --git a/tests/ui/coroutine/ref-escapes-but-not-over-yield.stderr b/tests/ui/coroutine/ref-escapes-but-not-over-yield.stderr new file mode 100644 index 000000000..4c8694e67 --- /dev/null +++ b/tests/ui/coroutine/ref-escapes-but-not-over-yield.stderr @@ -0,0 +1,15 @@ +error[E0521]: borrowed data escapes outside of coroutine + --> $DIR/ref-escapes-but-not-over-yield.rs:11:9 + | +LL | let mut a = &3; + | ----- `a` declared here, outside of the coroutine body +... +LL | a = &b; + | ^^^^-- + | | | + | | borrow is only valid in the coroutine body + | reference to `b` escapes the coroutine body here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0521`. diff --git a/tests/ui/coroutine/ref-upvar-not-send.rs b/tests/ui/coroutine/ref-upvar-not-send.rs new file mode 100644 index 000000000..487fdeea2 --- /dev/null +++ b/tests/ui/coroutine/ref-upvar-not-send.rs @@ -0,0 +1,31 @@ +// For `Send` coroutines, suggest a `T: Sync` requirement for `&T` upvars, +// and suggest a `T: Send` requirement for `&mut T` upvars. + +#![feature(coroutines)] + +fn assert_send<T: Send>(_: T) {} +//~^ NOTE required by a bound in `assert_send` +//~| NOTE required by this bound in `assert_send` +//~| NOTE required by a bound in `assert_send` +//~| NOTE required by this bound in `assert_send` + +fn main() { + let x: &*mut () = &std::ptr::null_mut(); + let y: &mut *mut () = &mut std::ptr::null_mut(); + assert_send(move || { + //~^ ERROR coroutine cannot be sent between threads safely + //~| NOTE coroutine is not `Send` + yield; + let _x = x; + }); + //~^^ NOTE captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync` + //~| NOTE has type `&*mut ()` which is not `Send`, because `*mut ()` is not `Sync` + assert_send(move || { + //~^ ERROR coroutine cannot be sent between threads safely + //~| NOTE coroutine is not `Send` + yield; + let _y = y; + }); + //~^^ NOTE captured value is not `Send` because `&mut` references cannot be sent unless their referent is `Send` + //~| NOTE has type `&mut *mut ()` which is not `Send`, because `*mut ()` is not `Send` +} diff --git a/tests/ui/coroutine/ref-upvar-not-send.stderr b/tests/ui/coroutine/ref-upvar-not-send.stderr new file mode 100644 index 000000000..7f18c6fba --- /dev/null +++ b/tests/ui/coroutine/ref-upvar-not-send.stderr @@ -0,0 +1,50 @@ +error: coroutine cannot be sent between threads safely + --> $DIR/ref-upvar-not-send.rs:15:17 + | +LL | assert_send(move || { + | _________________^ +LL | | +LL | | +LL | | yield; +LL | | let _x = x; +LL | | }); + | |_____^ coroutine is not `Send` + | + = help: the trait `Sync` is not implemented for `*mut ()` +note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync` + --> $DIR/ref-upvar-not-send.rs:19:18 + | +LL | let _x = x; + | ^ has type `&*mut ()` which is not `Send`, because `*mut ()` is not `Sync` +note: required by a bound in `assert_send` + --> $DIR/ref-upvar-not-send.rs:6:19 + | +LL | fn assert_send<T: Send>(_: T) {} + | ^^^^ required by this bound in `assert_send` + +error: coroutine cannot be sent between threads safely + --> $DIR/ref-upvar-not-send.rs:23:17 + | +LL | assert_send(move || { + | _________________^ +LL | | +LL | | +LL | | yield; +LL | | let _y = y; +LL | | }); + | |_____^ coroutine is not `Send` + | + = help: within `{coroutine@$DIR/ref-upvar-not-send.rs:23:17: 23:24}`, the trait `Send` is not implemented for `*mut ()` +note: captured value is not `Send` because `&mut` references cannot be sent unless their referent is `Send` + --> $DIR/ref-upvar-not-send.rs:27:18 + | +LL | let _y = y; + | ^ has type `&mut *mut ()` which is not `Send`, because `*mut ()` is not `Send` +note: required by a bound in `assert_send` + --> $DIR/ref-upvar-not-send.rs:6:19 + | +LL | fn assert_send<T: Send>(_: T) {} + | ^^^^ required by this bound in `assert_send` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/coroutine/reinit-in-match-guard.rs b/tests/ui/coroutine/reinit-in-match-guard.rs new file mode 100644 index 000000000..1895de1f1 --- /dev/null +++ b/tests/ui/coroutine/reinit-in-match-guard.rs @@ -0,0 +1,25 @@ +// build-pass + +#![feature(coroutines)] + +#![allow(unused_assignments, dead_code)] + +fn main() { + let _ = || { + let mut x = vec![22_usize]; + std::mem::drop(x); + match y() { + true if { + x = vec![]; + false + } => {} + _ => { + yield; + } + } + }; +} + +fn y() -> bool { + true +} diff --git a/tests/ui/coroutine/resume-after-return.rs b/tests/ui/coroutine/resume-after-return.rs new file mode 100644 index 000000000..acbd8740a --- /dev/null +++ b/tests/ui/coroutine/resume-after-return.rs @@ -0,0 +1,28 @@ +// run-pass +// needs-unwind + + +#![feature(coroutines, coroutine_trait)] + +use std::ops::{CoroutineState, Coroutine}; +use std::pin::Pin; +use std::panic; + +fn main() { + let mut foo = || { + if true { + return + } + yield; + }; + + match Pin::new(&mut foo).resume(()) { + CoroutineState::Complete(()) => {} + s => panic!("bad state: {:?}", s), + } + + match panic::catch_unwind(move || Pin::new(&mut foo).resume(())) { + Ok(_) => panic!("coroutine successfully resumed"), + Err(_) => {} + } +} diff --git a/tests/ui/coroutine/resume-arg-late-bound.rs b/tests/ui/coroutine/resume-arg-late-bound.rs new file mode 100644 index 000000000..dd6d318af --- /dev/null +++ b/tests/ui/coroutine/resume-arg-late-bound.rs @@ -0,0 +1,17 @@ +//! Tests that we cannot produce a coroutine that accepts a resume argument +//! with any lifetime and then stores it across a `yield`. + +#![feature(coroutines, coroutine_trait)] + +use std::ops::Coroutine; + +fn test(a: impl for<'a> Coroutine<&'a mut bool>) {} + +fn main() { + let gen = |arg: &mut bool| { + yield (); + *arg = true; + }; + test(gen); + //~^ ERROR mismatched types +} diff --git a/tests/ui/coroutine/resume-arg-late-bound.stderr b/tests/ui/coroutine/resume-arg-late-bound.stderr new file mode 100644 index 000000000..f1a8a8ed7 --- /dev/null +++ b/tests/ui/coroutine/resume-arg-late-bound.stderr @@ -0,0 +1,17 @@ +error[E0308]: mismatched types + --> $DIR/resume-arg-late-bound.rs:15:5 + | +LL | test(gen); + | ^^^^^^^^^ one type is more general than the other + | + = note: expected trait `for<'a> Coroutine<&'a mut bool>` + found trait `Coroutine<&mut bool>` +note: the lifetime requirement is introduced here + --> $DIR/resume-arg-late-bound.rs:8:17 + | +LL | fn test(a: impl for<'a> Coroutine<&'a mut bool>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/coroutine/resume-arg-size.rs b/tests/ui/coroutine/resume-arg-size.rs new file mode 100644 index 000000000..22bb469f9 --- /dev/null +++ b/tests/ui/coroutine/resume-arg-size.rs @@ -0,0 +1,29 @@ +#![feature(coroutines)] +#![allow(dropping_copy_types)] + +// run-pass + +use std::mem::size_of_val; + +fn main() { + // Coroutine taking a `Copy`able resume arg. + let gen_copy = |mut x: usize| { + loop { + drop(x); + x = yield; + } + }; + + // Coroutine taking a non-`Copy` resume arg. + let gen_move = |mut x: Box<usize>| { + loop { + drop(x); + x = yield; + } + }; + + // Neither of these coroutines have the resume arg live across the `yield`, so they should be + // 1 Byte in size (only storing the discriminant) + assert_eq!(size_of_val(&gen_copy), 1); + assert_eq!(size_of_val(&gen_move), 1); +} diff --git a/tests/ui/coroutine/resume-live-across-yield.rs b/tests/ui/coroutine/resume-live-across-yield.rs new file mode 100644 index 000000000..935e7d326 --- /dev/null +++ b/tests/ui/coroutine/resume-live-across-yield.rs @@ -0,0 +1,45 @@ +// run-pass + +#![feature(coroutines, coroutine_trait)] + +use std::ops::{Coroutine, CoroutineState}; +use std::pin::Pin; +use std::sync::atomic::{AtomicUsize, Ordering}; + +static DROP: AtomicUsize = AtomicUsize::new(0); + +#[derive(PartialEq, Eq, Debug)] +struct Dropper(String); + +impl Drop for Dropper { + fn drop(&mut self) { + DROP.fetch_add(1, Ordering::SeqCst); + } +} + +fn main() { + let mut g = |mut _d| { + _d = yield; + _d + }; + + let mut g = Pin::new(&mut g); + + assert_eq!( + g.as_mut().resume(Dropper(String::from("Hello world!"))), + CoroutineState::Yielded(()) + ); + assert_eq!(DROP.load(Ordering::Acquire), 0); + match g.as_mut().resume(Dropper(String::from("Number Two"))) { + CoroutineState::Complete(dropper) => { + assert_eq!(DROP.load(Ordering::Acquire), 1); + assert_eq!(dropper.0, "Number Two"); + drop(dropper); + assert_eq!(DROP.load(Ordering::Acquire), 2); + } + _ => unreachable!(), + } + + drop(g); + assert_eq!(DROP.load(Ordering::Acquire), 2); +} diff --git a/tests/ui/coroutine/retain-resume-ref.rs b/tests/ui/coroutine/retain-resume-ref.rs new file mode 100644 index 000000000..c9f995ab0 --- /dev/null +++ b/tests/ui/coroutine/retain-resume-ref.rs @@ -0,0 +1,25 @@ +//! This test ensures that a mutable reference cannot be passed as a resume argument twice. + +#![feature(coroutines, coroutine_trait)] + +use std::marker::Unpin; +use std::ops::{ + Coroutine, + CoroutineState::{self, *}, +}; +use std::pin::Pin; + +fn main() { + let mut thing = String::from("hello"); + + let mut gen = |r| { + if false { + yield r; + } + }; + + let mut gen = Pin::new(&mut gen); + gen.as_mut().resume(&mut thing); + gen.as_mut().resume(&mut thing); + //~^ cannot borrow `thing` as mutable more than once at a time +} diff --git a/tests/ui/coroutine/retain-resume-ref.stderr b/tests/ui/coroutine/retain-resume-ref.stderr new file mode 100644 index 000000000..e33310d12 --- /dev/null +++ b/tests/ui/coroutine/retain-resume-ref.stderr @@ -0,0 +1,13 @@ +error[E0499]: cannot borrow `thing` as mutable more than once at a time + --> $DIR/retain-resume-ref.rs:23:25 + | +LL | gen.as_mut().resume(&mut thing); + | ---------- first mutable borrow occurs here +LL | gen.as_mut().resume(&mut thing); + | ------ ^^^^^^^^^^ second mutable borrow occurs here + | | + | first borrow later used by call + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0499`. diff --git a/tests/ui/coroutine/self_referential_gen_block.rs b/tests/ui/coroutine/self_referential_gen_block.rs new file mode 100644 index 000000000..14daa2e9c --- /dev/null +++ b/tests/ui/coroutine/self_referential_gen_block.rs @@ -0,0 +1,17 @@ +// compile-flags: --edition 2024 -Zunstable-options +#![feature(gen_blocks)] +//! This test checks that we don't allow self-referential generators + +fn main() { + let mut x = { + let mut x = gen { + let y = 42; + let z = &y; //~ ERROR: borrow may still be in use when `gen` block yields + yield 43; + panic!("{z}"); + }; + x.next(); + Box::new(x) + }; + x.next(); +} diff --git a/tests/ui/coroutine/self_referential_gen_block.stderr b/tests/ui/coroutine/self_referential_gen_block.stderr new file mode 100644 index 000000000..586f53df8 --- /dev/null +++ b/tests/ui/coroutine/self_referential_gen_block.stderr @@ -0,0 +1,11 @@ +error[E0626]: borrow may still be in use when `gen` block yields + --> $DIR/self_referential_gen_block.rs:9:21 + | +LL | let z = &y; + | ^^ +LL | yield 43; + | -------- possible yield occurs here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0626`. diff --git a/tests/ui/coroutine/size-moved-locals.rs b/tests/ui/coroutine/size-moved-locals.rs new file mode 100644 index 000000000..cfbbb9c1b --- /dev/null +++ b/tests/ui/coroutine/size-moved-locals.rs @@ -0,0 +1,78 @@ +// run-pass +// Test that we don't duplicate storage for a variable that is moved to another +// binding. This used to happen in the presence of unwind and drop edges (see +// `complex` below.) +// +// The exact sizes here can change (we'd like to know when they do). What we +// don't want to see is the `complex` coroutine size being upwards of 2048 bytes +// (which would indicate it is reserving space for two copies of Foo.) +// +// See issue #59123 for a full explanation. + +// edition:2018 +// ignore-wasm32 issue #62807 +// ignore-asmjs issue #62807 +// needs-unwind Size of Closures change on panic=abort + +#![feature(coroutines, coroutine_trait)] + +use std::ops::Coroutine; + +const FOO_SIZE: usize = 1024; +struct Foo(#[allow(unused_tuple_struct_fields)] [u8; FOO_SIZE]); + +impl Drop for Foo { + fn drop(&mut self) {} +} + +fn move_before_yield() -> impl Coroutine<Yield = (), Return = ()> { + static || { + let first = Foo([0; FOO_SIZE]); + let _second = first; + yield; + // _second dropped here + } +} + +fn noop() {} + +fn move_before_yield_with_noop() -> impl Coroutine<Yield = (), Return = ()> { + static || { + let first = Foo([0; FOO_SIZE]); + noop(); + let _second = first; + yield; + // _second dropped here + } +} + +// Today we don't have NRVO (we allocate space for both `first` and `second`,) +// but we can overlap `first` with `_third`. +fn overlap_move_points() -> impl Coroutine<Yield = (), Return = ()> { + static || { + let first = Foo([0; FOO_SIZE]); + yield; + let second = first; + yield; + let _third = second; + yield; + } +} + +fn overlap_x_and_y() -> impl Coroutine<Yield = (), Return = ()> { + static || { + let x = Foo([0; FOO_SIZE]); + yield; + drop(x); + let y = Foo([0; FOO_SIZE]); + yield; + drop(y); + } +} + +fn main() { + assert_eq!(1025, std::mem::size_of_val(&move_before_yield())); + assert_eq!(1026, std::mem::size_of_val(&move_before_yield_with_noop())); + assert_eq!(2051, std::mem::size_of_val(&overlap_move_points())); + assert_eq!(1026, std::mem::size_of_val(&overlap_x_and_y())); +} diff --git a/tests/ui/coroutine/sized-yield.rs b/tests/ui/coroutine/sized-yield.rs new file mode 100644 index 000000000..1368c88b5 --- /dev/null +++ b/tests/ui/coroutine/sized-yield.rs @@ -0,0 +1,14 @@ +#![feature(coroutines, coroutine_trait)] + +use std::ops::Coroutine; +use std::pin::Pin; + +fn main() { + let s = String::from("foo"); + let mut gen = move || { + //~^ ERROR the size for values of type + yield s[..]; + }; + Pin::new(&mut gen).resume(()); + //~^ ERROR the size for values of type +} diff --git a/tests/ui/coroutine/sized-yield.stderr b/tests/ui/coroutine/sized-yield.stderr new file mode 100644 index 000000000..40663ac12 --- /dev/null +++ b/tests/ui/coroutine/sized-yield.stderr @@ -0,0 +1,26 @@ +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/sized-yield.rs:8:27 + | +LL | let mut gen = move || { + | ___________________________^ +LL | | +LL | | yield s[..]; +LL | | }; + | |_____^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `str` + = note: the yield type of a coroutine must have a statically known size + +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/sized-yield.rs:12:24 + | +LL | Pin::new(&mut gen).resume(()); + | ^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `str` +note: required by a bound in `CoroutineState` + --> $SRC_DIR/core/src/ops/coroutine.rs:LL:COL + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/coroutine/smoke-resume-args.rs b/tests/ui/coroutine/smoke-resume-args.rs new file mode 100644 index 000000000..a80198985 --- /dev/null +++ b/tests/ui/coroutine/smoke-resume-args.rs @@ -0,0 +1,100 @@ +// run-pass + +// revisions: default nomiropt +//[nomiropt]compile-flags: -Z mir-opt-level=0 + +#![feature(coroutines, coroutine_trait)] + +use std::fmt::Debug; +use std::marker::Unpin; +use std::ops::{ + Coroutine, + CoroutineState::{self, *}, +}; +use std::pin::Pin; +use std::sync::atomic::{AtomicUsize, Ordering}; + +fn drain<G: Coroutine<R, Yield = Y> + Unpin, R, Y>( + gen: &mut G, + inout: Vec<(R, CoroutineState<Y, G::Return>)>, +) where + Y: Debug + PartialEq, + G::Return: Debug + PartialEq, +{ + let mut gen = Pin::new(gen); + + for (input, out) in inout { + assert_eq!(gen.as_mut().resume(input), out); + } +} + +static DROPS: AtomicUsize = AtomicUsize::new(0); + +#[derive(Debug, PartialEq)] +struct DropMe; + +impl Drop for DropMe { + fn drop(&mut self) { + DROPS.fetch_add(1, Ordering::SeqCst); + } +} + +fn expect_drops<T>(expected_drops: usize, f: impl FnOnce() -> T) -> T { + DROPS.store(0, Ordering::SeqCst); + + let res = f(); + + let actual_drops = DROPS.load(Ordering::SeqCst); + assert_eq!(actual_drops, expected_drops); + res +} + +fn main() { + drain( + &mut |mut b| { + while b != 0 { + b = yield (b + 1); + } + -1 + }, + vec![(1, Yielded(2)), (-45, Yielded(-44)), (500, Yielded(501)), (0, Complete(-1))], + ); + + expect_drops(2, || drain(&mut |a| yield a, vec![(DropMe, Yielded(DropMe))])); + + expect_drops(6, || { + drain( + &mut |a| yield yield a, + vec![(DropMe, Yielded(DropMe)), (DropMe, Yielded(DropMe)), (DropMe, Complete(DropMe))], + ) + }); + + #[allow(unreachable_code)] + expect_drops(2, || drain(&mut |a| yield return a, vec![(DropMe, Complete(DropMe))])); + + expect_drops(2, || { + drain( + &mut |a: DropMe| { + if false { yield () } else { a } + }, + vec![(DropMe, Complete(DropMe))], + ) + }); + + expect_drops(4, || { + drain( + #[allow(unused_assignments, unused_variables)] + &mut |mut a: DropMe| { + a = yield; + a = yield; + a = yield; + }, + vec![ + (DropMe, Yielded(())), + (DropMe, Yielded(())), + (DropMe, Yielded(())), + (DropMe, Complete(())), + ], + ) + }); +} diff --git a/tests/ui/coroutine/smoke.rs b/tests/ui/coroutine/smoke.rs new file mode 100644 index 000000000..b74ed2686 --- /dev/null +++ b/tests/ui/coroutine/smoke.rs @@ -0,0 +1,177 @@ +// run-pass + +// revisions: default nomiropt +//[nomiropt]compile-flags: -Z mir-opt-level=0 + +// ignore-emscripten no threads support +// compile-flags: --test + +#![feature(coroutines, coroutine_trait)] + +use std::ops::{CoroutineState, Coroutine}; +use std::pin::Pin; +use std::thread; + +#[test] +fn simple() { + let mut foo = || { + if false { + yield; + } + }; + + match Pin::new(&mut foo).resume(()) { + CoroutineState::Complete(()) => {} + s => panic!("bad state: {:?}", s), + } +} + +#[test] +fn return_capture() { + let a = String::from("foo"); + let mut foo = || { + if false { + yield; + } + a + }; + + match Pin::new(&mut foo).resume(()) { + CoroutineState::Complete(ref s) if *s == "foo" => {} + s => panic!("bad state: {:?}", s), + } +} + +#[test] +fn simple_yield() { + let mut foo = || { + yield; + }; + + match Pin::new(&mut foo).resume(()) { + CoroutineState::Yielded(()) => {} + s => panic!("bad state: {:?}", s), + } + match Pin::new(&mut foo).resume(()) { + CoroutineState::Complete(()) => {} + s => panic!("bad state: {:?}", s), + } +} + +#[test] +fn yield_capture() { + let b = String::from("foo"); + let mut foo = || { + yield b; + }; + + match Pin::new(&mut foo).resume(()) { + CoroutineState::Yielded(ref s) if *s == "foo" => {} + s => panic!("bad state: {:?}", s), + } + match Pin::new(&mut foo).resume(()) { + CoroutineState::Complete(()) => {} + s => panic!("bad state: {:?}", s), + } +} + +#[test] +fn simple_yield_value() { + let mut foo = || { + yield String::from("bar"); + return String::from("foo") + }; + + match Pin::new(&mut foo).resume(()) { + CoroutineState::Yielded(ref s) if *s == "bar" => {} + s => panic!("bad state: {:?}", s), + } + match Pin::new(&mut foo).resume(()) { + CoroutineState::Complete(ref s) if *s == "foo" => {} + s => panic!("bad state: {:?}", s), + } +} + +#[test] +fn return_after_yield() { + let a = String::from("foo"); + let mut foo = || { + yield; + return a + }; + + match Pin::new(&mut foo).resume(()) { + CoroutineState::Yielded(()) => {} + s => panic!("bad state: {:?}", s), + } + match Pin::new(&mut foo).resume(()) { + CoroutineState::Complete(ref s) if *s == "foo" => {} + s => panic!("bad state: {:?}", s), + } +} + +#[test] +fn send_and_sync() { + assert_send_sync(|| { + yield + }); + assert_send_sync(|| { + yield String::from("foo"); + }); + assert_send_sync(|| { + yield; + return String::from("foo"); + }); + let a = 3; + assert_send_sync(|| { + yield a; + return + }); + let a = 3; + assert_send_sync(move || { + yield a; + return + }); + let a = String::from("a"); + assert_send_sync(|| { + yield ; + drop(a); + return + }); + let a = String::from("a"); + assert_send_sync(move || { + yield ; + drop(a); + return + }); + + fn assert_send_sync<T: Send + Sync>(_: T) {} +} + +#[test] +fn send_over_threads() { + let mut foo = || { yield }; + thread::spawn(move || { + match Pin::new(&mut foo).resume(()) { + CoroutineState::Yielded(()) => {} + s => panic!("bad state: {:?}", s), + } + match Pin::new(&mut foo).resume(()) { + CoroutineState::Complete(()) => {} + s => panic!("bad state: {:?}", s), + } + }).join().unwrap(); + + let a = String::from("a"); + let mut foo = || { yield a }; + thread::spawn(move || { + match Pin::new(&mut foo).resume(()) { + CoroutineState::Yielded(ref s) if *s == "a" => {} + s => panic!("bad state: {:?}", s), + } + match Pin::new(&mut foo).resume(()) { + CoroutineState::Complete(()) => {} + s => panic!("bad state: {:?}", s), + } + }).join().unwrap(); +} diff --git a/tests/ui/coroutine/static-coroutine.rs b/tests/ui/coroutine/static-coroutine.rs new file mode 100644 index 000000000..f9fd65b97 --- /dev/null +++ b/tests/ui/coroutine/static-coroutine.rs @@ -0,0 +1,20 @@ +// run-pass + +#![feature(coroutines, coroutine_trait)] + +use std::pin::Pin; +use std::ops::{Coroutine, CoroutineState}; + +fn main() { + let mut coroutine = static || { + let a = true; + let b = &a; + yield; + assert_eq!(b as *const _, &a as *const _); + }; + // SAFETY: We shadow the original coroutine variable so have no safe API to + // move it after this point. + let mut coroutine = unsafe { Pin::new_unchecked(&mut coroutine) }; + assert_eq!(coroutine.as_mut().resume(()), CoroutineState::Yielded(())); + assert_eq!(coroutine.as_mut().resume(()), CoroutineState::Complete(())); +} diff --git a/tests/ui/coroutine/static-mut-reference-across-yield.rs b/tests/ui/coroutine/static-mut-reference-across-yield.rs new file mode 100644 index 000000000..07f810856 --- /dev/null +++ b/tests/ui/coroutine/static-mut-reference-across-yield.rs @@ -0,0 +1,32 @@ +// build-pass +// revisions: mir thir +// [thir]compile-flags: -Zthir-unsafeck + +#![feature(coroutines)] + +static mut A: [i32; 5] = [1, 2, 3, 4, 5]; + +fn is_send_sync<T: Send + Sync>(_: T) {} + +fn main() { + unsafe { + let gen_index = static || { + let u = A[{ + yield; + 1 + }]; + }; + let gen_match = static || match A { + i if { + yield; + true + } => + { + () + } + _ => (), + }; + is_send_sync(gen_index); + is_send_sync(gen_match); + } +} diff --git a/tests/ui/coroutine/static-not-unpin.current.stderr b/tests/ui/coroutine/static-not-unpin.current.stderr new file mode 100644 index 000000000..cd607904f --- /dev/null +++ b/tests/ui/coroutine/static-not-unpin.current.stderr @@ -0,0 +1,19 @@ +error[E0277]: `{static coroutine@$DIR/static-not-unpin.rs:14:25: 14:34}` cannot be unpinned + --> $DIR/static-not-unpin.rs:17:18 + | +LL | assert_unpin(coroutine); + | ------------ ^^^^^^^^^ the trait `Unpin` is not implemented for `{static coroutine@$DIR/static-not-unpin.rs:14:25: 14:34}` + | | + | required by a bound introduced by this call + | + = note: consider using the `pin!` macro + consider using `Box::pin` if you need to access the pinned value outside of the current scope +note: required by a bound in `assert_unpin` + --> $DIR/static-not-unpin.rs:10:20 + | +LL | fn assert_unpin<T: Unpin>(_: T) { + | ^^^^^ required by this bound in `assert_unpin` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/coroutine/static-not-unpin.next.stderr b/tests/ui/coroutine/static-not-unpin.next.stderr new file mode 100644 index 000000000..cd607904f --- /dev/null +++ b/tests/ui/coroutine/static-not-unpin.next.stderr @@ -0,0 +1,19 @@ +error[E0277]: `{static coroutine@$DIR/static-not-unpin.rs:14:25: 14:34}` cannot be unpinned + --> $DIR/static-not-unpin.rs:17:18 + | +LL | assert_unpin(coroutine); + | ------------ ^^^^^^^^^ the trait `Unpin` is not implemented for `{static coroutine@$DIR/static-not-unpin.rs:14:25: 14:34}` + | | + | required by a bound introduced by this call + | + = note: consider using the `pin!` macro + consider using `Box::pin` if you need to access the pinned value outside of the current scope +note: required by a bound in `assert_unpin` + --> $DIR/static-not-unpin.rs:10:20 + | +LL | fn assert_unpin<T: Unpin>(_: T) { + | ^^^^^ required by this bound in `assert_unpin` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/coroutine/static-not-unpin.rs b/tests/ui/coroutine/static-not-unpin.rs new file mode 100644 index 000000000..6ce78046d --- /dev/null +++ b/tests/ui/coroutine/static-not-unpin.rs @@ -0,0 +1,18 @@ +// revisions: current next +//[next] compile-flags: -Ztrait-solver=next + +#![feature(coroutines)] + +// normalize-stderr-test "std::pin::Unpin" -> "std::marker::Unpin" + +use std::marker::Unpin; + +fn assert_unpin<T: Unpin>(_: T) { +} + +fn main() { + let mut coroutine = static || { + yield; + }; + assert_unpin(coroutine); //~ ERROR E0277 +} diff --git a/tests/ui/coroutine/static-reference-across-yield.rs b/tests/ui/coroutine/static-reference-across-yield.rs new file mode 100644 index 000000000..6496d8b86 --- /dev/null +++ b/tests/ui/coroutine/static-reference-across-yield.rs @@ -0,0 +1,16 @@ +// build-pass +#![feature(coroutines)] + +static A: [i32; 5] = [1, 2, 3, 4, 5]; + +fn main() { + static || { + let u = A[{yield; 1}]; + }; + static || { + match A { + i if { yield; true } => (), + _ => (), + } + }; +} diff --git a/tests/ui/coroutine/too-live-local-in-immovable-gen.rs b/tests/ui/coroutine/too-live-local-in-immovable-gen.rs new file mode 100644 index 000000000..7eaa15522 --- /dev/null +++ b/tests/ui/coroutine/too-live-local-in-immovable-gen.rs @@ -0,0 +1,21 @@ +// run-pass +#![allow(unused_unsafe)] + +#![feature(coroutines)] + +fn main() { + unsafe { + static move || { //~ WARN unused coroutine that must be used + // Tests that the coroutine transformation finds out that `a` is not live + // during the yield expression. Type checking will also compute liveness + // and it should also find out that `a` is not live. + // The compiler will panic if the coroutine transformation finds that + // `a` is live and type checking finds it dead. + let a = { + yield (); + 4i32 + }; + let _ = &a; + }; + } +} diff --git a/tests/ui/coroutine/too-live-local-in-immovable-gen.stderr b/tests/ui/coroutine/too-live-local-in-immovable-gen.stderr new file mode 100644 index 000000000..4a67dbe71 --- /dev/null +++ b/tests/ui/coroutine/too-live-local-in-immovable-gen.stderr @@ -0,0 +1,17 @@ +warning: unused coroutine that must be used + --> $DIR/too-live-local-in-immovable-gen.rs:8:9 + | +LL | / static move || { +LL | | // Tests that the coroutine transformation finds out that `a` is not live +LL | | // during the yield expression. Type checking will also compute liveness +LL | | // and it should also find out that `a` is not live. +... | +LL | | let _ = &a; +LL | | }; + | |_________^ + | + = note: coroutines are lazy and do nothing unless resumed + = note: `#[warn(unused_must_use)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/coroutine/too-many-parameters.rs b/tests/ui/coroutine/too-many-parameters.rs new file mode 100644 index 000000000..377d80c7b --- /dev/null +++ b/tests/ui/coroutine/too-many-parameters.rs @@ -0,0 +1,8 @@ +#![feature(coroutines)] + +fn main() { + |(), ()| { + //~^ error: too many parameters for a coroutine + yield; + }; +} diff --git a/tests/ui/coroutine/too-many-parameters.stderr b/tests/ui/coroutine/too-many-parameters.stderr new file mode 100644 index 000000000..54cf42e78 --- /dev/null +++ b/tests/ui/coroutine/too-many-parameters.stderr @@ -0,0 +1,9 @@ +error[E0628]: too many parameters for a coroutine (expected 0 or 1 parameters) + --> $DIR/too-many-parameters.rs:4:5 + | +LL | |(), ()| { + | ^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0628`. diff --git a/tests/ui/coroutine/type-mismatch-error.rs b/tests/ui/coroutine/type-mismatch-error.rs new file mode 100644 index 000000000..0d04c2148 --- /dev/null +++ b/tests/ui/coroutine/type-mismatch-error.rs @@ -0,0 +1,22 @@ +//! Test that we get the expected type mismatch error instead of "closure is expected to take 0 +//! arguments" (which got introduced after implementing resume arguments). + +#![feature(coroutines, coroutine_trait)] + +use std::ops::Coroutine; + +fn f<G: Coroutine>(_: G, _: G::Return) {} + +fn main() { + f( + |a: u8| { + if false { + yield (); + } else { + a + //~^ error: `if` and `else` have incompatible types + } + }, + 0u8, + ); +} diff --git a/tests/ui/coroutine/type-mismatch-error.stderr b/tests/ui/coroutine/type-mismatch-error.stderr new file mode 100644 index 000000000..8f5949533 --- /dev/null +++ b/tests/ui/coroutine/type-mismatch-error.stderr @@ -0,0 +1,19 @@ +error[E0308]: `if` and `else` have incompatible types + --> $DIR/type-mismatch-error.rs:16:17 + | +LL | / if false { +LL | | yield (); + | | --------- + | | | | + | | | help: consider removing this semicolon + | | expected because of this +LL | | } else { +LL | | a + | | ^ expected `()`, found `u8` +LL | | +LL | | } + | |_____________- `if` and `else` have incompatible types + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/coroutine/type-mismatch-signature-deduction.rs b/tests/ui/coroutine/type-mismatch-signature-deduction.rs new file mode 100644 index 000000000..d4ca622e8 --- /dev/null +++ b/tests/ui/coroutine/type-mismatch-signature-deduction.rs @@ -0,0 +1,18 @@ +#![feature(coroutines, coroutine_trait)] + +use std::ops::Coroutine; + +fn foo() -> impl Coroutine<Return = i32> { + //~^ ERROR type mismatch + || { + if false { + return Ok(6); + } + + yield (); + + 5 //~ ERROR mismatched types [E0308] + } +} + +fn main() {} diff --git a/tests/ui/coroutine/type-mismatch-signature-deduction.stderr b/tests/ui/coroutine/type-mismatch-signature-deduction.stderr new file mode 100644 index 000000000..f26e30a8e --- /dev/null +++ b/tests/ui/coroutine/type-mismatch-signature-deduction.stderr @@ -0,0 +1,33 @@ +error[E0308]: mismatched types + --> $DIR/type-mismatch-signature-deduction.rs:14:9 + | +LL | 5 + | ^ expected `Result<{integer}, _>`, found integer + | + = note: expected enum `Result<{integer}, _>` + found type `{integer}` +note: return type inferred to be `Result<{integer}, _>` here + --> $DIR/type-mismatch-signature-deduction.rs:9:20 + | +LL | return Ok(6); + | ^^^^^ +help: try wrapping the expression in a variant of `Result` + | +LL | Ok(5) + | +++ + +LL | Err(5) + | ++++ + + +error[E0271]: type mismatch resolving `<{coroutine@$DIR/type-mismatch-signature-deduction.rs:7:5: 7:7} as Coroutine>::Return == i32` + --> $DIR/type-mismatch-signature-deduction.rs:5:13 + | +LL | fn foo() -> impl Coroutine<Return = i32> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Result<{integer}, _>`, found `i32` + | + = note: expected enum `Result<{integer}, _>` + found type `i32` + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0271, E0308. +For more information about an error, try `rustc --explain E0271`. diff --git a/tests/ui/coroutine/unresolved-ct-var.rs b/tests/ui/coroutine/unresolved-ct-var.rs new file mode 100644 index 000000000..0316385fb --- /dev/null +++ b/tests/ui/coroutine/unresolved-ct-var.rs @@ -0,0 +1,9 @@ +// incremental +// edition:2021 + +fn main() { + let _ = async { + let s = std::array::from_fn(|_| ()).await; + //~^ ERROR `[(); _]` is not a future + }; +} diff --git a/tests/ui/coroutine/unresolved-ct-var.stderr b/tests/ui/coroutine/unresolved-ct-var.stderr new file mode 100644 index 000000000..9badc1dc2 --- /dev/null +++ b/tests/ui/coroutine/unresolved-ct-var.stderr @@ -0,0 +1,17 @@ +error[E0277]: `[(); _]` is not a future + --> $DIR/unresolved-ct-var.rs:6:45 + | +LL | let s = std::array::from_fn(|_| ()).await; + | ----------------------------^^^^^ + | | || + | | |`[(); _]` is not a future + | | help: remove the `.await` + | this call returns `[(); _]` + | + = help: the trait `Future` is not implemented for `[(); _]` + = note: [(); _] must be a future or must implement `IntoFuture` to be awaited + = note: required for `[(); _]` to implement `IntoFuture` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/coroutine/unsized-capture-across-yield.rs b/tests/ui/coroutine/unsized-capture-across-yield.rs new file mode 100644 index 000000000..ef9cbc1d6 --- /dev/null +++ b/tests/ui/coroutine/unsized-capture-across-yield.rs @@ -0,0 +1,22 @@ +#![feature(coroutine_trait)] +#![feature(coroutines)] +#![feature(unsized_locals)] +//~^ WARN the feature `unsized_locals` is incomplete and may not be safe to use and/or cause compiler crashes + +use std::ops::Coroutine; + +fn capture() -> impl Coroutine { + let b: [u8] = *(Box::new([]) as Box<[u8]>); + move || { + println!("{:?}", &b); + //~^ ERROR the size for values of type `[u8]` cannot be known at compilation time + + yield; + + for elem in b.iter() {} + } +} + +fn main() { + capture(); +} diff --git a/tests/ui/coroutine/unsized-capture-across-yield.stderr b/tests/ui/coroutine/unsized-capture-across-yield.stderr new file mode 100644 index 000000000..8a5b968a5 --- /dev/null +++ b/tests/ui/coroutine/unsized-capture-across-yield.stderr @@ -0,0 +1,23 @@ +warning: the feature `unsized_locals` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/unsized-capture-across-yield.rs:3:12 + | +LL | #![feature(unsized_locals)] + | ^^^^^^^^^^^^^^ + | + = note: see issue #48055 <https://github.com/rust-lang/rust/issues/48055> for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/unsized-capture-across-yield.rs:11:27 + | +LL | move || { + | -- this closure captures all values by move +LL | println!("{:?}", &b); + | ^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: all values captured by value by a closure must have a statically known size + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/coroutine/unsized-local-across-yield.rs b/tests/ui/coroutine/unsized-local-across-yield.rs new file mode 100644 index 000000000..7a8ed60e4 --- /dev/null +++ b/tests/ui/coroutine/unsized-local-across-yield.rs @@ -0,0 +1,21 @@ +#![feature(coroutine_trait)] +#![feature(coroutines)] +#![feature(unsized_locals)] +//~^ WARN the feature `unsized_locals` is incomplete and may not be safe to use and/or cause compiler crashes + +use std::ops::Coroutine; + +fn across() -> impl Coroutine { + move || { + let b: [u8] = *(Box::new([]) as Box<[u8]>); + //~^ ERROR the size for values of type `[u8]` cannot be known at compilation time + + yield; + + for elem in b.iter() {} + } +} + +fn main() { + across(); +} diff --git a/tests/ui/coroutine/unsized-local-across-yield.stderr b/tests/ui/coroutine/unsized-local-across-yield.stderr new file mode 100644 index 000000000..1942f266e --- /dev/null +++ b/tests/ui/coroutine/unsized-local-across-yield.stderr @@ -0,0 +1,21 @@ +warning: the feature `unsized_locals` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/unsized-local-across-yield.rs:3:12 + | +LL | #![feature(unsized_locals)] + | ^^^^^^^^^^^^^^ + | + = note: see issue #48055 <https://github.com/rust-lang/rust/issues/48055> for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/unsized-local-across-yield.rs:10:13 + | +LL | let b: [u8] = *(Box::new([]) as Box<[u8]>); + | ^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: all values live across `yield` must have a statically known size + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/coroutine/unwind-abort-mix.rs b/tests/ui/coroutine/unwind-abort-mix.rs new file mode 100644 index 000000000..869b3e4f4 --- /dev/null +++ b/tests/ui/coroutine/unwind-abort-mix.rs @@ -0,0 +1,13 @@ +// Ensure that coroutine drop glue is valid when mixing different panic +// strategies. Regression test for #116953. +// +// no-prefer-dynamic +// build-pass +// aux-build:unwind-aux.rs +// compile-flags: -Cpanic=abort +// needs-unwind +extern crate unwind_aux; + +pub fn main() { + unwind_aux::run(String::new()); +} diff --git a/tests/ui/coroutine/witness-ignore-fake-reads.rs b/tests/ui/coroutine/witness-ignore-fake-reads.rs new file mode 100644 index 000000000..ccf9ce8b4 --- /dev/null +++ b/tests/ui/coroutine/witness-ignore-fake-reads.rs @@ -0,0 +1,34 @@ +// check-pass +// edition: 2021 + +// regression test for #117059 +struct SendNotSync(*const ()); +unsafe impl Send for SendNotSync {} +// impl !Sync for SendNotSync {} // automatically disabled + +struct Inner { + stream: SendNotSync, + state: bool, +} + +struct SendSync; +impl std::ops::Deref for SendSync { + type Target = Inner; + fn deref(&self) -> &Self::Target { + todo!(); + } +} + +async fn next() { + let inner = SendSync; + match inner.state { + true if false => {} + false => async {}.await, + _ => {} + } +} + +fn is_send<T: Send>(_: T) {} +fn main() { + is_send(next()) +} diff --git a/tests/ui/coroutine/xcrate-reachable.rs b/tests/ui/coroutine/xcrate-reachable.rs new file mode 100644 index 000000000..c63284488 --- /dev/null +++ b/tests/ui/coroutine/xcrate-reachable.rs @@ -0,0 +1,14 @@ +// run-pass + +// aux-build:xcrate-reachable.rs + +#![feature(coroutine_trait)] + +extern crate xcrate_reachable as foo; + +use std::ops::Coroutine; +use std::pin::Pin; + +fn main() { + Pin::new(&mut foo::foo()).resume(()); +} diff --git a/tests/ui/coroutine/xcrate.rs b/tests/ui/coroutine/xcrate.rs new file mode 100644 index 000000000..4572d1cfd --- /dev/null +++ b/tests/ui/coroutine/xcrate.rs @@ -0,0 +1,30 @@ +// run-pass + +// aux-build:xcrate.rs + +#![feature(coroutines, coroutine_trait)] + +extern crate xcrate; + +use std::ops::{Coroutine, CoroutineState}; +use std::pin::Pin; + +fn main() { + let mut foo = xcrate::foo(); + + match Pin::new(&mut foo).resume(()) { + CoroutineState::Complete(()) => {} + s => panic!("bad state: {:?}", s), + } + + let mut foo = xcrate::bar(3); + + match Pin::new(&mut foo).resume(()) { + CoroutineState::Yielded(3) => {} + s => panic!("bad state: {:?}", s), + } + match Pin::new(&mut foo).resume(()) { + CoroutineState::Complete(()) => {} + s => panic!("bad state: {:?}", s), + } +} diff --git a/tests/ui/coroutine/yield-in-args-rev.rs b/tests/ui/coroutine/yield-in-args-rev.rs new file mode 100644 index 000000000..b22c32ccd --- /dev/null +++ b/tests/ui/coroutine/yield-in-args-rev.rs @@ -0,0 +1,19 @@ +// run-pass +#![allow(dead_code)] + +// Test that a borrow that occurs after a yield in the same +// argument list is not treated as live across the yield by +// type-checking. + +#![feature(coroutines)] + +fn foo(_a: (), _b: &bool) {} + +fn bar() { + || { //~ WARN unused coroutine that must be used + let b = true; + foo(yield, &b); + }; +} + +fn main() { } diff --git a/tests/ui/coroutine/yield-in-args-rev.stderr b/tests/ui/coroutine/yield-in-args-rev.stderr new file mode 100644 index 000000000..dbf46739e --- /dev/null +++ b/tests/ui/coroutine/yield-in-args-rev.stderr @@ -0,0 +1,14 @@ +warning: unused coroutine that must be used + --> $DIR/yield-in-args-rev.rs:13:5 + | +LL | / || { +LL | | let b = true; +LL | | foo(yield, &b); +LL | | }; + | |_____^ + | + = note: coroutines are lazy and do nothing unless resumed + = note: `#[warn(unused_must_use)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/coroutine/yield-in-args.rs b/tests/ui/coroutine/yield-in-args.rs new file mode 100644 index 000000000..b2827148d --- /dev/null +++ b/tests/ui/coroutine/yield-in-args.rs @@ -0,0 +1,10 @@ +#![feature(coroutines)] + +fn foo(_b: &bool, _a: ()) {} + +fn main() { + || { + let b = true; + foo(&b, yield); //~ ERROR + }; +} diff --git a/tests/ui/coroutine/yield-in-args.stderr b/tests/ui/coroutine/yield-in-args.stderr new file mode 100644 index 000000000..4ff97281d --- /dev/null +++ b/tests/ui/coroutine/yield-in-args.stderr @@ -0,0 +1,9 @@ +error[E0626]: borrow may still be in use when coroutine yields + --> $DIR/yield-in-args.rs:8:13 + | +LL | foo(&b, yield); + | ^^ ----- possible yield occurs here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0626`. diff --git a/tests/ui/coroutine/yield-in-const.rs b/tests/ui/coroutine/yield-in-const.rs new file mode 100644 index 000000000..22651f32c --- /dev/null +++ b/tests/ui/coroutine/yield-in-const.rs @@ -0,0 +1,6 @@ +#![feature(coroutines)] + +const A: u8 = { yield 3u8; 3u8}; +//~^ ERROR yield expression outside + +fn main() {} diff --git a/tests/ui/coroutine/yield-in-const.stderr b/tests/ui/coroutine/yield-in-const.stderr new file mode 100644 index 000000000..7afcd8340 --- /dev/null +++ b/tests/ui/coroutine/yield-in-const.stderr @@ -0,0 +1,9 @@ +error[E0627]: yield expression outside of coroutine literal + --> $DIR/yield-in-const.rs:3:17 + | +LL | const A: u8 = { yield 3u8; 3u8}; + | ^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0627`. diff --git a/tests/ui/coroutine/yield-in-function.rs b/tests/ui/coroutine/yield-in-function.rs new file mode 100644 index 000000000..a99312043 --- /dev/null +++ b/tests/ui/coroutine/yield-in-function.rs @@ -0,0 +1,4 @@ +#![feature(coroutines)] + +fn main() { yield; } +//~^ ERROR yield expression outside diff --git a/tests/ui/coroutine/yield-in-function.stderr b/tests/ui/coroutine/yield-in-function.stderr new file mode 100644 index 000000000..b2f839a65 --- /dev/null +++ b/tests/ui/coroutine/yield-in-function.stderr @@ -0,0 +1,9 @@ +error[E0627]: yield expression outside of coroutine literal + --> $DIR/yield-in-function.rs:3:13 + | +LL | fn main() { yield; } + | ^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0627`. diff --git a/tests/ui/coroutine/yield-in-initializer.rs b/tests/ui/coroutine/yield-in-initializer.rs new file mode 100644 index 000000000..5a7b3a4fe --- /dev/null +++ b/tests/ui/coroutine/yield-in-initializer.rs @@ -0,0 +1,17 @@ +// run-pass + +#![feature(coroutines)] + +fn main() { + static || { //~ WARN unused coroutine that must be used + loop { + // Test that `opt` is not live across the yield, even when borrowed in a loop + // See https://github.com/rust-lang/rust/issues/52792 + let opt = { + yield; + true + }; + let _ = &opt; + } + }; +} diff --git a/tests/ui/coroutine/yield-in-initializer.stderr b/tests/ui/coroutine/yield-in-initializer.stderr new file mode 100644 index 000000000..614df43f2 --- /dev/null +++ b/tests/ui/coroutine/yield-in-initializer.stderr @@ -0,0 +1,17 @@ +warning: unused coroutine that must be used + --> $DIR/yield-in-initializer.rs:6:5 + | +LL | / static || { +LL | | loop { +LL | | // Test that `opt` is not live across the yield, even when borrowed in a loop +LL | | // See https://github.com/rust-lang/rust/issues/52792 +... | +LL | | } +LL | | }; + | |_____^ + | + = note: coroutines are lazy and do nothing unless resumed + = note: `#[warn(unused_must_use)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/coroutine/yield-in-static.rs b/tests/ui/coroutine/yield-in-static.rs new file mode 100644 index 000000000..45e0380d4 --- /dev/null +++ b/tests/ui/coroutine/yield-in-static.rs @@ -0,0 +1,6 @@ +#![feature(coroutines)] + +static B: u8 = { yield 3u8; 3u8}; +//~^ ERROR yield expression outside + +fn main() {} diff --git a/tests/ui/coroutine/yield-in-static.stderr b/tests/ui/coroutine/yield-in-static.stderr new file mode 100644 index 000000000..17d58325e --- /dev/null +++ b/tests/ui/coroutine/yield-in-static.stderr @@ -0,0 +1,9 @@ +error[E0627]: yield expression outside of coroutine literal + --> $DIR/yield-in-static.rs:3:18 + | +LL | static B: u8 = { yield 3u8; 3u8}; + | ^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0627`. diff --git a/tests/ui/coroutine/yield-outside-coroutine-issue-78653.rs b/tests/ui/coroutine/yield-outside-coroutine-issue-78653.rs new file mode 100644 index 000000000..31025c33b --- /dev/null +++ b/tests/ui/coroutine/yield-outside-coroutine-issue-78653.rs @@ -0,0 +1,7 @@ +#![feature(coroutines)] + +fn main() { + yield || for i in 0 { } + //~^ ERROR yield expression outside of coroutine literal + //~| ERROR `{integer}` is not an iterator +} diff --git a/tests/ui/coroutine/yield-outside-coroutine-issue-78653.stderr b/tests/ui/coroutine/yield-outside-coroutine-issue-78653.stderr new file mode 100644 index 000000000..f28f89135 --- /dev/null +++ b/tests/ui/coroutine/yield-outside-coroutine-issue-78653.stderr @@ -0,0 +1,20 @@ +error[E0627]: yield expression outside of coroutine literal + --> $DIR/yield-outside-coroutine-issue-78653.rs:4:5 + | +LL | yield || for i in 0 { } + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: `{integer}` is not an iterator + --> $DIR/yield-outside-coroutine-issue-78653.rs:4:23 + | +LL | yield || for i in 0 { } + | ^ `{integer}` is not an iterator + | + = help: the trait `Iterator` is not implemented for `{integer}` + = note: if you want to iterate between `start` until a value `end`, use the exclusive range syntax `start..end` or the inclusive range syntax `start..=end` + = note: required for `{integer}` to implement `IntoIterator` + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0277, E0627. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/coroutine/yield-subtype.rs b/tests/ui/coroutine/yield-subtype.rs new file mode 100644 index 000000000..3595d4498 --- /dev/null +++ b/tests/ui/coroutine/yield-subtype.rs @@ -0,0 +1,17 @@ +// run-pass +#![allow(dead_code)] +#![allow(dead_code)] + +#![feature(coroutines)] + +fn bar<'a>() { + let a: &'static str = "hi"; + let b: &'a str = a; + + || { //~ WARN unused coroutine that must be used + yield a; + yield b; + }; +} + +fn main() {} diff --git a/tests/ui/coroutine/yield-subtype.stderr b/tests/ui/coroutine/yield-subtype.stderr new file mode 100644 index 000000000..5e7ae9f58 --- /dev/null +++ b/tests/ui/coroutine/yield-subtype.stderr @@ -0,0 +1,14 @@ +warning: unused coroutine that must be used + --> $DIR/yield-subtype.rs:11:5 + | +LL | / || { +LL | | yield a; +LL | | yield b; +LL | | }; + | |_____^ + | + = note: coroutines are lazy and do nothing unless resumed + = note: `#[warn(unused_must_use)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/coroutine/yield-while-iterating.rs b/tests/ui/coroutine/yield-while-iterating.rs new file mode 100644 index 000000000..66ac6d392 --- /dev/null +++ b/tests/ui/coroutine/yield-while-iterating.rs @@ -0,0 +1,75 @@ +#![feature(coroutines, coroutine_trait)] + +use std::ops::{CoroutineState, Coroutine}; +use std::cell::Cell; +use std::pin::Pin; + +fn yield_during_iter_owned_data(x: Vec<i32>) { + // The coroutine owns `x`, so we error out when yielding with a + // reference to it. This winds up becoming a rather confusing + // regionck error -- in particular, we would freeze with the + // reference in scope, and it doesn't live long enough. + let _b = move || { + for p in &x { //~ ERROR + yield(); + } + }; +} + +fn yield_during_iter_borrowed_slice(x: &[i32]) { + let _b = move || { + for p in x { + yield(); + } + }; +} + +fn yield_during_iter_borrowed_slice_2() { + let mut x = vec![22_i32]; + let _b = || { + for p in &x { + yield(); + } + }; + println!("{:?}", x); +} + +fn yield_during_iter_borrowed_slice_3() { + // OK to take a mutable ref to `x` and yield + // up pointers from it: + let mut x = vec![22_i32]; + let mut b = || { + for p in &mut x { + yield p; + } + }; + Pin::new(&mut b).resume(()); +} + +fn yield_during_iter_borrowed_slice_4() { + // ...but not OK to do that while reading + // from `x` too + let mut x = vec![22_i32]; + let mut b = || { + for p in &mut x { + yield p; + } + }; + println!("{}", x[0]); //~ ERROR + Pin::new(&mut b).resume(()); +} + +fn yield_during_range_iter() { + // Should be OK. + let mut b = || { + let v = vec![1,2,3]; + let len = v.len(); + for i in 0..len { + let x = v[i]; + yield x; + } + }; + Pin::new(&mut b).resume(()); +} + +fn main() { } diff --git a/tests/ui/coroutine/yield-while-iterating.stderr b/tests/ui/coroutine/yield-while-iterating.stderr new file mode 100644 index 000000000..5330121f3 --- /dev/null +++ b/tests/ui/coroutine/yield-while-iterating.stderr @@ -0,0 +1,25 @@ +error[E0626]: borrow may still be in use when coroutine yields + --> $DIR/yield-while-iterating.rs:13:18 + | +LL | for p in &x { + | ^^ +LL | yield(); + | ------- possible yield occurs here + +error[E0502]: cannot borrow `x` as immutable because it is also borrowed as mutable + --> $DIR/yield-while-iterating.rs:58:20 + | +LL | let mut b = || { + | -- mutable borrow occurs here +LL | for p in &mut x { + | - first borrow occurs due to use of `x` in coroutine +... +LL | println!("{}", x[0]); + | ^ immutable borrow occurs here +LL | Pin::new(&mut b).resume(()); + | ------ mutable borrow later used here + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0502, E0626. +For more information about an error, try `rustc --explain E0502`. diff --git a/tests/ui/coroutine/yield-while-local-borrowed.rs b/tests/ui/coroutine/yield-while-local-borrowed.rs new file mode 100644 index 000000000..7f8d1d454 --- /dev/null +++ b/tests/ui/coroutine/yield-while-local-borrowed.rs @@ -0,0 +1,49 @@ +#![feature(coroutines, coroutine_trait)] + +use std::cell::Cell; +use std::ops::{Coroutine, CoroutineState}; +use std::pin::Pin; + +fn borrow_local_inline() { + // Not OK to yield with a borrow of a temporary. + // + // (This error occurs because the region shows up in the type of + // `b` and gets extended by region inference.) + let mut b = move || { + let a = &mut 3; + //~^ ERROR borrow may still be in use when coroutine yields + yield (); + println!("{}", a); + }; + Pin::new(&mut b).resume(()); +} + +fn borrow_local_inline_done() { + // No error here -- `a` is not in scope at the point of `yield`. + let mut b = move || { + { + let a = &mut 3; + } + yield (); + }; + Pin::new(&mut b).resume(()); +} + +fn borrow_local() { + // Not OK to yield with a borrow of a temporary. + // + // (This error occurs because the region shows up in the type of + // `b` and gets extended by region inference.) + let mut b = move || { + let a = 3; + { + let b = &a; + //~^ ERROR borrow may still be in use when coroutine yields + yield (); + println!("{}", b); + } + }; + Pin::new(&mut b).resume(()); +} + +fn main() {} diff --git a/tests/ui/coroutine/yield-while-local-borrowed.stderr b/tests/ui/coroutine/yield-while-local-borrowed.stderr new file mode 100644 index 000000000..8fe981de9 --- /dev/null +++ b/tests/ui/coroutine/yield-while-local-borrowed.stderr @@ -0,0 +1,21 @@ +error[E0626]: borrow may still be in use when coroutine yields + --> $DIR/yield-while-local-borrowed.rs:13:17 + | +LL | let a = &mut 3; + | ^^^^^^ +LL | +LL | yield (); + | -------- possible yield occurs here + +error[E0626]: borrow may still be in use when coroutine yields + --> $DIR/yield-while-local-borrowed.rs:40:21 + | +LL | let b = &a; + | ^^ +LL | +LL | yield (); + | -------- possible yield occurs here + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0626`. diff --git a/tests/ui/coroutine/yield-while-ref-reborrowed.rs b/tests/ui/coroutine/yield-while-ref-reborrowed.rs new file mode 100644 index 000000000..07c591758 --- /dev/null +++ b/tests/ui/coroutine/yield-while-ref-reborrowed.rs @@ -0,0 +1,40 @@ +#![feature(coroutines, coroutine_trait)] + +use std::ops::{CoroutineState, Coroutine}; +use std::cell::Cell; +use std::pin::Pin; + +fn reborrow_shared_ref(x: &i32) { + // This is OK -- we have a borrow live over the yield, but it's of + // data that outlives the coroutine. + let mut b = move || { + let a = &*x; + yield(); + println!("{}", a); + }; + Pin::new(&mut b).resume(()); +} + +fn reborrow_mutable_ref(x: &mut i32) { + // This is OK -- we have a borrow live over the yield, but it's of + // data that outlives the coroutine. + let mut b = move || { + let a = &mut *x; + yield(); + println!("{}", a); + }; + Pin::new(&mut b).resume(()); +} + +fn reborrow_mutable_ref_2(x: &mut i32) { + // ...but not OK to go on using `x`. + let mut b = || { + let a = &mut *x; + yield(); + println!("{}", a); + }; + println!("{}", x); //~ ERROR + Pin::new(&mut b).resume(()); +} + +fn main() { } diff --git a/tests/ui/coroutine/yield-while-ref-reborrowed.stderr b/tests/ui/coroutine/yield-while-ref-reborrowed.stderr new file mode 100644 index 000000000..e60a95316 --- /dev/null +++ b/tests/ui/coroutine/yield-while-ref-reborrowed.stderr @@ -0,0 +1,18 @@ +error[E0501]: cannot borrow `x` as immutable because previous closure requires unique access + --> $DIR/yield-while-ref-reborrowed.rs:36:20 + | +LL | let mut b = || { + | -- coroutine construction occurs here +LL | let a = &mut *x; + | -- first borrow occurs due to use of `x` in coroutine +... +LL | println!("{}", x); + | ^ second borrow occurs here +LL | Pin::new(&mut b).resume(()); + | ------ first borrow later used here + | + = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0501`. diff --git a/tests/ui/coroutine/yielding-in-match-guards.rs b/tests/ui/coroutine/yielding-in-match-guards.rs new file mode 100644 index 000000000..a9575a9e7 --- /dev/null +++ b/tests/ui/coroutine/yielding-in-match-guards.rs @@ -0,0 +1,53 @@ +// build-pass +// edition:2018 + +// This test is derived from +// https://github.com/rust-lang/rust/issues/72651#issuecomment-668720468 + +// This test demonstrates that, in `async fn g()`, +// indeed a temporary borrow `y` from `x` is live +// while `f().await` is being evaluated. +// Thus, `&'_ u8` should be included in type signature +// of the underlying coroutine. + +#![feature(if_let_guard)] + +async fn f() -> u8 { 1 } +async fn foo() -> [bool; 10] { [false; 10] } + +pub async fn g(x: u8) { + match x { + y if f().await == y => (), + _ => (), + } +} + +// #78366: check the reference to the binding is recorded even if the binding is not autorefed + +async fn h(x: usize) { + match x { + y if foo().await[y] => (), + _ => (), + } +} + +async fn i(x: u8) { + match x { + y if f().await == y + 1 => (), + _ => (), + } +} + +async fn j(x: u8) { + match x { + y if let (1, 42) = (f().await, y) => (), + _ => (), + } +} + +fn main() { + let _ = g(10); + let _ = h(9); + let _ = i(8); + let _ = j(7); +} |