summaryrefslogtreecommitdiffstats
path: root/tests/ui/coroutine
diff options
context:
space:
mode:
Diffstat (limited to 'tests/ui/coroutine')
-rw-r--r--tests/ui/coroutine/addassign-yield.rs35
-rw-r--r--tests/ui/coroutine/async-coroutine-issue-67158.rs6
-rw-r--r--tests/ui/coroutine/async-coroutine-issue-67158.stderr9
-rw-r--r--tests/ui/coroutine/auto-trait-regions.rs53
-rw-r--r--tests/ui/coroutine/auto-trait-regions.stderr55
-rw-r--r--tests/ui/coroutine/auxiliary/metadata-sufficient-for-layout.rs11
-rw-r--r--tests/ui/coroutine/auxiliary/unwind-aux.rs11
-rw-r--r--tests/ui/coroutine/auxiliary/xcrate-reachable.rs14
-rw-r--r--tests/ui/coroutine/auxiliary/xcrate.rs18
-rw-r--r--tests/ui/coroutine/borrow-in-tail-expr.rs11
-rw-r--r--tests/ui/coroutine/borrowing.rs20
-rw-r--r--tests/ui/coroutine/borrowing.stderr31
-rw-r--r--tests/ui/coroutine/clone-impl-async.rs70
-rw-r--r--tests/ui/coroutine/clone-impl-async.stderr171
-rw-r--r--tests/ui/coroutine/clone-impl-static.rs17
-rw-r--r--tests/ui/coroutine/clone-impl-static.stderr31
-rw-r--r--tests/ui/coroutine/clone-impl.rs73
-rw-r--r--tests/ui/coroutine/clone-impl.stderr138
-rw-r--r--tests/ui/coroutine/conditional-drop.rs61
-rw-r--r--tests/ui/coroutine/control-flow.rs53
-rw-r--r--tests/ui/coroutine/coroutine-region-requirements.migrate.stderr12
-rw-r--r--tests/ui/coroutine/coroutine-region-requirements.rs19
-rw-r--r--tests/ui/coroutine/coroutine-region-requirements.stderr11
-rw-r--r--tests/ui/coroutine/coroutine-resume-after-panic.rs25
-rw-r--r--tests/ui/coroutine/coroutine-with-nll.rs12
-rw-r--r--tests/ui/coroutine/coroutine-with-nll.stderr12
-rw-r--r--tests/ui/coroutine/coroutine-yielding-or-returning-itself.rs35
-rw-r--r--tests/ui/coroutine/coroutine-yielding-or-returning-itself.stderr53
-rw-r--r--tests/ui/coroutine/derived-drop-parent-expr.rs16
-rw-r--r--tests/ui/coroutine/discriminant.rs137
-rw-r--r--tests/ui/coroutine/drop-and-replace.rs45
-rw-r--r--tests/ui/coroutine/drop-control-flow.rs138
-rw-r--r--tests/ui/coroutine/drop-env.rs67
-rw-r--r--tests/ui/coroutine/drop-track-addassign-yield.rs40
-rw-r--r--tests/ui/coroutine/drop-tracking-parent-expression.rs68
-rw-r--r--tests/ui/coroutine/drop-tracking-parent-expression.stderr122
-rw-r--r--tests/ui/coroutine/drop-tracking-yielding-in-match-guards.rs11
-rw-r--r--tests/ui/coroutine/drop-yield-twice.rs15
-rw-r--r--tests/ui/coroutine/drop-yield-twice.stderr22
-rw-r--r--tests/ui/coroutine/dropck-resume.rs33
-rw-r--r--tests/ui/coroutine/dropck-resume.stderr15
-rw-r--r--tests/ui/coroutine/dropck.rs20
-rw-r--r--tests/ui/coroutine/dropck.stderr37
-rw-r--r--tests/ui/coroutine/gen_block.e2024.stderr19
-rw-r--r--tests/ui/coroutine/gen_block.none.stderr49
-rw-r--r--tests/ui/coroutine/gen_block.rs17
-rw-r--r--tests/ui/coroutine/gen_block_is_coro.rs18
-rw-r--r--tests/ui/coroutine/gen_block_is_coro.stderr21
-rw-r--r--tests/ui/coroutine/gen_block_is_iter.rs19
-rw-r--r--tests/ui/coroutine/gen_block_is_no_future.rs8
-rw-r--r--tests/ui/coroutine/gen_block_is_no_future.stderr12
-rw-r--r--tests/ui/coroutine/gen_block_iterate.rs35
-rw-r--r--tests/ui/coroutine/gen_block_move.fixed17
-rw-r--r--tests/ui/coroutine/gen_block_move.rs17
-rw-r--r--tests/ui/coroutine/gen_block_move.stderr30
-rw-r--r--tests/ui/coroutine/gen_block_panic.rs26
-rw-r--r--tests/ui/coroutine/gen_block_panic.stderr12
-rw-r--r--tests/ui/coroutine/gen_fn.e2024.stderr10
-rw-r--r--tests/ui/coroutine/gen_fn.none.stderr8
-rw-r--r--tests/ui/coroutine/gen_fn.rs8
-rw-r--r--tests/ui/coroutine/issue-102645.rs21
-rw-r--r--tests/ui/coroutine/issue-102645.stderr16
-rw-r--r--tests/ui/coroutine/issue-105084.rs42
-rw-r--r--tests/ui/coroutine/issue-105084.stderr51
-rw-r--r--tests/ui/coroutine/issue-110929-coroutine-conflict-error-ice.rs11
-rw-r--r--tests/ui/coroutine/issue-113279.rs27
-rw-r--r--tests/ui/coroutine/issue-113279.stderr16
-rw-r--r--tests/ui/coroutine/issue-44197.rs36
-rw-r--r--tests/ui/coroutine/issue-45729-unsafe-in-coroutine.mir.stderr11
-rw-r--r--tests/ui/coroutine/issue-45729-unsafe-in-coroutine.rs12
-rw-r--r--tests/ui/coroutine/issue-45729-unsafe-in-coroutine.thir.stderr11
-rw-r--r--tests/ui/coroutine/issue-48048.rs13
-rw-r--r--tests/ui/coroutine/issue-48048.stderr11
-rw-r--r--tests/ui/coroutine/issue-52304.rs11
-rw-r--r--tests/ui/coroutine/issue-52398.rs28
-rw-r--r--tests/ui/coroutine/issue-52398.stderr24
-rw-r--r--tests/ui/coroutine/issue-53548-1.rs20
-rw-r--r--tests/ui/coroutine/issue-53548.rs38
-rw-r--r--tests/ui/coroutine/issue-57017.rs55
-rw-r--r--tests/ui/coroutine/issue-57084.rs28
-rw-r--r--tests/ui/coroutine/issue-57084.stderr16
-rw-r--r--tests/ui/coroutine/issue-57478.rs16
-rw-r--r--tests/ui/coroutine/issue-58888.rs28
-rw-r--r--tests/ui/coroutine/issue-61442-stmt-expr-with-drop.rs32
-rw-r--r--tests/ui/coroutine/issue-62506-two_awaits.rs17
-rw-r--r--tests/ui/coroutine/issue-64620-yield-array-element.rs9
-rw-r--r--tests/ui/coroutine/issue-64620-yield-array-element.stderr9
-rw-r--r--tests/ui/coroutine/issue-68112.rs72
-rw-r--r--tests/ui/coroutine/issue-68112.stderr61
-rw-r--r--tests/ui/coroutine/issue-69017.rs18
-rw-r--r--tests/ui/coroutine/issue-69039.rs34
-rw-r--r--tests/ui/coroutine/issue-87142.rs32
-rw-r--r--tests/ui/coroutine/issue-88653.rs22
-rw-r--r--tests/ui/coroutine/issue-88653.stderr15
-rw-r--r--tests/ui/coroutine/issue-91477.rs7
-rw-r--r--tests/ui/coroutine/issue-91477.stderr9
-rw-r--r--tests/ui/coroutine/issue-93161.rs93
-rw-r--r--tests/ui/coroutine/iterator-count.rs44
-rw-r--r--tests/ui/coroutine/layout-error.rs28
-rw-r--r--tests/ui/coroutine/layout-error.stderr9
-rw-r--r--tests/ui/coroutine/live-upvar-across-yield.rs14
-rw-r--r--tests/ui/coroutine/match-bindings.rs23
-rw-r--r--tests/ui/coroutine/match-bindings.stderr17
-rw-r--r--tests/ui/coroutine/metadata-sufficient-for-layout.rs25
-rw-r--r--tests/ui/coroutine/metadata-sufficient-for-layout.stderr8
-rw-r--r--tests/ui/coroutine/nested_coroutine.rs21
-rw-r--r--tests/ui/coroutine/niche-in-coroutine.rs19
-rw-r--r--tests/ui/coroutine/non-static-is-unpin.rs21
-rw-r--r--tests/ui/coroutine/not-send-sync.rs27
-rw-r--r--tests/ui/coroutine/not-send-sync.stderr42
-rw-r--r--tests/ui/coroutine/overlap-locals.rs29
-rw-r--r--tests/ui/coroutine/panic-drops-resume.rs36
-rw-r--r--tests/ui/coroutine/panic-drops.rs57
-rw-r--r--tests/ui/coroutine/panic-safe.rs30
-rw-r--r--tests/ui/coroutine/parent-expression.rs68
-rw-r--r--tests/ui/coroutine/parent-expression.stderr122
-rw-r--r--tests/ui/coroutine/partial-drop.rs34
-rw-r--r--tests/ui/coroutine/partial-initialization-across-yield.rs43
-rw-r--r--tests/ui/coroutine/partial-initialization-across-yield.stderr33
-rw-r--r--tests/ui/coroutine/pattern-borrow.rs17
-rw-r--r--tests/ui/coroutine/pattern-borrow.stderr11
-rw-r--r--tests/ui/coroutine/pin-box-coroutine.rs13
-rw-r--r--tests/ui/coroutine/print/coroutine-print-verbose-1.rs60
-rw-r--r--tests/ui/coroutine/print/coroutine-print-verbose-1.stderr60
-rw-r--r--tests/ui/coroutine/print/coroutine-print-verbose-2.rs30
-rw-r--r--tests/ui/coroutine/print/coroutine-print-verbose-2.stderr42
-rw-r--r--tests/ui/coroutine/print/coroutine-print-verbose-3.rs12
-rw-r--r--tests/ui/coroutine/print/coroutine-print-verbose-3.stderr19
-rw-r--r--tests/ui/coroutine/reborrow-mut-upvar.rs16
-rw-r--r--tests/ui/coroutine/reborrow-mut-upvar.stderr17
-rw-r--r--tests/ui/coroutine/ref-escapes-but-not-over-yield.rs16
-rw-r--r--tests/ui/coroutine/ref-escapes-but-not-over-yield.stderr15
-rw-r--r--tests/ui/coroutine/ref-upvar-not-send.rs31
-rw-r--r--tests/ui/coroutine/ref-upvar-not-send.stderr50
-rw-r--r--tests/ui/coroutine/reinit-in-match-guard.rs25
-rw-r--r--tests/ui/coroutine/resume-after-return.rs28
-rw-r--r--tests/ui/coroutine/resume-arg-late-bound.rs17
-rw-r--r--tests/ui/coroutine/resume-arg-late-bound.stderr17
-rw-r--r--tests/ui/coroutine/resume-arg-size.rs29
-rw-r--r--tests/ui/coroutine/resume-live-across-yield.rs45
-rw-r--r--tests/ui/coroutine/retain-resume-ref.rs25
-rw-r--r--tests/ui/coroutine/retain-resume-ref.stderr13
-rw-r--r--tests/ui/coroutine/self_referential_gen_block.rs17
-rw-r--r--tests/ui/coroutine/self_referential_gen_block.stderr11
-rw-r--r--tests/ui/coroutine/size-moved-locals.rs78
-rw-r--r--tests/ui/coroutine/sized-yield.rs14
-rw-r--r--tests/ui/coroutine/sized-yield.stderr26
-rw-r--r--tests/ui/coroutine/smoke-resume-args.rs100
-rw-r--r--tests/ui/coroutine/smoke.rs177
-rw-r--r--tests/ui/coroutine/static-coroutine.rs20
-rw-r--r--tests/ui/coroutine/static-mut-reference-across-yield.rs32
-rw-r--r--tests/ui/coroutine/static-not-unpin.current.stderr19
-rw-r--r--tests/ui/coroutine/static-not-unpin.next.stderr19
-rw-r--r--tests/ui/coroutine/static-not-unpin.rs18
-rw-r--r--tests/ui/coroutine/static-reference-across-yield.rs16
-rw-r--r--tests/ui/coroutine/too-live-local-in-immovable-gen.rs21
-rw-r--r--tests/ui/coroutine/too-live-local-in-immovable-gen.stderr17
-rw-r--r--tests/ui/coroutine/too-many-parameters.rs8
-rw-r--r--tests/ui/coroutine/too-many-parameters.stderr9
-rw-r--r--tests/ui/coroutine/type-mismatch-error.rs22
-rw-r--r--tests/ui/coroutine/type-mismatch-error.stderr19
-rw-r--r--tests/ui/coroutine/type-mismatch-signature-deduction.rs18
-rw-r--r--tests/ui/coroutine/type-mismatch-signature-deduction.stderr33
-rw-r--r--tests/ui/coroutine/unresolved-ct-var.rs9
-rw-r--r--tests/ui/coroutine/unresolved-ct-var.stderr17
-rw-r--r--tests/ui/coroutine/unsized-capture-across-yield.rs22
-rw-r--r--tests/ui/coroutine/unsized-capture-across-yield.stderr23
-rw-r--r--tests/ui/coroutine/unsized-local-across-yield.rs21
-rw-r--r--tests/ui/coroutine/unsized-local-across-yield.stderr21
-rw-r--r--tests/ui/coroutine/unwind-abort-mix.rs13
-rw-r--r--tests/ui/coroutine/witness-ignore-fake-reads.rs34
-rw-r--r--tests/ui/coroutine/xcrate-reachable.rs14
-rw-r--r--tests/ui/coroutine/xcrate.rs30
-rw-r--r--tests/ui/coroutine/yield-in-args-rev.rs19
-rw-r--r--tests/ui/coroutine/yield-in-args-rev.stderr14
-rw-r--r--tests/ui/coroutine/yield-in-args.rs10
-rw-r--r--tests/ui/coroutine/yield-in-args.stderr9
-rw-r--r--tests/ui/coroutine/yield-in-const.rs6
-rw-r--r--tests/ui/coroutine/yield-in-const.stderr9
-rw-r--r--tests/ui/coroutine/yield-in-function.rs4
-rw-r--r--tests/ui/coroutine/yield-in-function.stderr9
-rw-r--r--tests/ui/coroutine/yield-in-initializer.rs17
-rw-r--r--tests/ui/coroutine/yield-in-initializer.stderr17
-rw-r--r--tests/ui/coroutine/yield-in-static.rs6
-rw-r--r--tests/ui/coroutine/yield-in-static.stderr9
-rw-r--r--tests/ui/coroutine/yield-outside-coroutine-issue-78653.rs7
-rw-r--r--tests/ui/coroutine/yield-outside-coroutine-issue-78653.stderr20
-rw-r--r--tests/ui/coroutine/yield-subtype.rs17
-rw-r--r--tests/ui/coroutine/yield-subtype.stderr14
-rw-r--r--tests/ui/coroutine/yield-while-iterating.rs75
-rw-r--r--tests/ui/coroutine/yield-while-iterating.stderr25
-rw-r--r--tests/ui/coroutine/yield-while-local-borrowed.rs49
-rw-r--r--tests/ui/coroutine/yield-while-local-borrowed.stderr21
-rw-r--r--tests/ui/coroutine/yield-while-ref-reborrowed.rs40
-rw-r--r--tests/ui/coroutine/yield-while-ref-reborrowed.stderr18
-rw-r--r--tests/ui/coroutine/yielding-in-match-guards.rs53
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);
+}