diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
commit | 698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch) | |
tree | 173a775858bd501c378080a10dca74132f05bc50 /src/test/ui/generator | |
parent | Initial commit. (diff) | |
download | rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip |
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/test/ui/generator')
153 files changed, 4470 insertions, 0 deletions
diff --git a/src/test/ui/generator/addassign-yield.rs b/src/test/ui/generator/addassign-yield.rs new file mode 100644 index 000000000..66f22bf31 --- /dev/null +++ b/src/test/ui/generator/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(generators)] + +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/src/test/ui/generator/async-generator-issue-67158.rs b/src/test/ui/generator/async-generator-issue-67158.rs new file mode 100644 index 000000000..8125a7a9b --- /dev/null +++ b/src/test/ui/generator/async-generator-issue-67158.rs @@ -0,0 +1,6 @@ +#![feature(generators)] +// edition:2018 +// Regression test for #67158. +fn main() { + async { yield print!(":C") }; //~ ERROR `async` generators are not yet supported +} diff --git a/src/test/ui/generator/async-generator-issue-67158.stderr b/src/test/ui/generator/async-generator-issue-67158.stderr new file mode 100644 index 000000000..7270d188e --- /dev/null +++ b/src/test/ui/generator/async-generator-issue-67158.stderr @@ -0,0 +1,9 @@ +error[E0727]: `async` generators are not yet supported + --> $DIR/async-generator-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/src/test/ui/generator/auto-trait-regions.rs b/src/test/ui/generator/auto-trait-regions.rs new file mode 100644 index 000000000..ea4b0d554 --- /dev/null +++ b/src/test/ui/generator/auto-trait-regions.rs @@ -0,0 +1,53 @@ +#![feature(generators)] +#![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 generator interiors so we can't match it in trait selection + let x: &'static _ = &OnlyFooIfStaticRef(No); + let gen = || { + 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 = || { + let x = x; + yield; + assert_foo(x); + }; + assert_foo(gen); // ok + + // Disallow impls which relates lifetimes in the generator interior + let gen = || { + 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/src/test/ui/generator/auto-trait-regions.stderr b/src/test/ui/generator/auto-trait-regions.stderr new file mode 100644 index 000000000..23324af61 --- /dev/null +++ b/src/test/ui/generator/auto-trait-regions.stderr @@ -0,0 +1,47 @@ +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 which is freed while still in use +... +LL | assert_foo(a); + | - borrow later used here + | + = note: consider using a `let` binding to create a longer lived value + +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 which is freed while still in use +... +LL | assert_foo(a); + | - borrow later used here + | + = note: consider using a `let` binding to create a longer lived value + +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/src/test/ui/generator/auxiliary/metadata-sufficient-for-layout.rs b/src/test/ui/generator/auxiliary/metadata-sufficient-for-layout.rs new file mode 100644 index 000000000..207c2735f --- /dev/null +++ b/src/test/ui/generator/auxiliary/metadata-sufficient-for-layout.rs @@ -0,0 +1,11 @@ +// compile-flags: --emit metadata +#![feature(generators, generator_trait)] + +use std::marker::Unpin; +use std::ops::Generator; + +pub fn g() -> impl Generator<(), Yield = (), Return = ()> { + || { + yield; + } +} diff --git a/src/test/ui/generator/auxiliary/xcrate-reachable.rs b/src/test/ui/generator/auxiliary/xcrate-reachable.rs new file mode 100644 index 000000000..2dd5ea675 --- /dev/null +++ b/src/test/ui/generator/auxiliary/xcrate-reachable.rs @@ -0,0 +1,14 @@ +#![feature(generators, generator_trait)] + +use std::ops::Generator; + +fn msg() -> u32 { + 0 +} + +pub fn foo() -> impl Generator<(), Yield=(), Return=u32> { + || { + yield; + return msg(); + } +} diff --git a/src/test/ui/generator/auxiliary/xcrate.rs b/src/test/ui/generator/auxiliary/xcrate.rs new file mode 100644 index 000000000..d07abd091 --- /dev/null +++ b/src/test/ui/generator/auxiliary/xcrate.rs @@ -0,0 +1,18 @@ +#![feature(generators, generator_trait)] + +use std::marker::Unpin; +use std::ops::Generator; + +pub fn foo() -> impl Generator<(), Yield = (), Return = ()> { + || { + if false { + yield; + } + } +} + +pub fn bar<T: 'static>(t: T) -> Box<Generator<(), Yield = T, Return = ()> + Unpin> { + Box::new(|| { + yield t; + }) +} diff --git a/src/test/ui/generator/borrow-in-tail-expr.rs b/src/test/ui/generator/borrow-in-tail-expr.rs new file mode 100644 index 000000000..540f5e3e1 --- /dev/null +++ b/src/test/ui/generator/borrow-in-tail-expr.rs @@ -0,0 +1,11 @@ +// run-pass + +#![feature(generators)] + +fn main() { + let _a = || { + yield; + let a = String::new(); + a.len() + }; +} diff --git a/src/test/ui/generator/borrowing.rs b/src/test/ui/generator/borrowing.rs new file mode 100644 index 000000000..d36592583 --- /dev/null +++ b/src/test/ui/generator/borrowing.rs @@ -0,0 +1,20 @@ +#![feature(generators, generator_trait)] + +use std::ops::Generator; +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/src/test/ui/generator/borrowing.stderr b/src/test/ui/generator/borrowing.stderr new file mode 100644 index 000000000..38e1ace8c --- /dev/null +++ b/src/test/ui/generator/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 generator +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 generator +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/src/test/ui/generator/conditional-drop.rs b/src/test/ui/generator/conditional-drop.rs new file mode 100644 index 000000000..0927df869 --- /dev/null +++ b/src/test/ui/generator/conditional-drop.rs @@ -0,0 +1,61 @@ +// run-pass + +// revisions: default nomiropt +//[nomiropt]compile-flags: -Z mir-opt-level=0 + +#![feature(generators, generator_trait)] + +use std::ops::Generator; +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/src/test/ui/generator/control-flow.rs b/src/test/ui/generator/control-flow.rs new file mode 100644 index 000000000..4f69c7855 --- /dev/null +++ b/src/test/ui/generator/control-flow.rs @@ -0,0 +1,53 @@ +// run-pass + +// revisions: default nomiropt +//[nomiropt]compile-flags: -Z mir-opt-level=0 + +#![feature(generators, generator_trait)] + +use std::marker::Unpin; +use std::ops::{GeneratorState, Generator}; +use std::pin::Pin; + +fn finish<T>(mut amt: usize, mut t: T) -> T::Return + where T: Generator<(), Yield = ()> + Unpin, +{ + loop { + match Pin::new(&mut t).resume(()) { + GeneratorState::Yielded(()) => amt = amt.checked_sub(1).unwrap(), + GeneratorState::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/src/test/ui/generator/derived-drop-parent-expr.rs b/src/test/ui/generator/derived-drop-parent-expr.rs new file mode 100644 index 000000000..4bd34346a --- /dev/null +++ b/src/test/ui/generator/derived-drop-parent-expr.rs @@ -0,0 +1,17 @@ +// build-pass +// compile-flags:-Zdrop-tracking + +//! Like drop-tracking-parent-expression, but also tests that this doesn't ICE when building MIR +#![feature(generators)] + +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/src/test/ui/generator/discriminant.rs b/src/test/ui/generator/discriminant.rs new file mode 100644 index 000000000..195e77022 --- /dev/null +++ b/src/test/ui/generator/discriminant.rs @@ -0,0 +1,137 @@ +//! Tests that generator discriminant sizes and ranges are chosen optimally and that they are +//! reflected in the output of `mem::discriminant`. + +// run-pass + +#![feature(generators, generator_trait, core_intrinsics, discriminant_kind)] + +use std::intrinsics::discriminant_value; +use std::marker::{Unpin, DiscriminantKind}; +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 Generator<()> + 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(()) { + GeneratorState::Yielded(_) => {} + GeneratorState::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/src/test/ui/generator/drop-and-replace.rs b/src/test/ui/generator/drop-and-replace.rs new file mode 100644 index 000000000..a9a50a122 --- /dev/null +++ b/src/test/ui/generator/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 generators when a saved local was +// re-assigned. + +#![feature(generators, generator_trait)] + +use std::ops::{Generator, GeneratorState}; +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(()) { + GeneratorState::Complete(()) => break, + _ => (), + } + } +} diff --git a/src/test/ui/generator/drop-control-flow.rs b/src/test/ui/generator/drop-control-flow.rs new file mode 100644 index 000000000..d38368000 --- /dev/null +++ b/src/test/ui/generator/drop-control-flow.rs @@ -0,0 +1,139 @@ +// build-pass +// compile-flags: -Zdrop-tracking + +// A test to ensure generators 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 generator type. + +#![feature(generators, 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 src/test/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/src/test/ui/generator/drop-env.rs b/src/test/ui/generator/drop-env.rs new file mode 100644 index 000000000..66dfb8c2c --- /dev/null +++ b/src/test/ui/generator/drop-env.rs @@ -0,0 +1,66 @@ +// run-pass + +// revisions: default nomiropt +//[nomiropt]compile-flags: -Z mir-opt-level=0 + +#![feature(generators, generator_trait)] + +use std::ops::Generator; +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/src/test/ui/generator/drop-track-addassign-yield.rs b/src/test/ui/generator/drop-track-addassign-yield.rs new file mode 100644 index 000000000..71cfb170b --- /dev/null +++ b/src/test/ui/generator/drop-track-addassign-yield.rs @@ -0,0 +1,41 @@ +// run-pass +// compile-flags: -Zdrop-tracking + +// 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(generators)] + +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/src/test/ui/generator/drop-tracking-parent-expression.rs b/src/test/ui/generator/drop-tracking-parent-expression.rs new file mode 100644 index 000000000..d40f1b8f6 --- /dev/null +++ b/src/test/ui/generator/drop-tracking-parent-expression.rs @@ -0,0 +1,69 @@ +// compile-flags: -Zdrop-tracking +#![feature(generators, 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/src/test/ui/generator/drop-tracking-parent-expression.stderr b/src/test/ui/generator/drop-tracking-parent-expression.stderr new file mode 100644 index 000000000..522a300b3 --- /dev/null +++ b/src/test/ui/generator/drop-tracking-parent-expression.stderr @@ -0,0 +1,128 @@ +error: generator cannot be sent between threads safely + --> $DIR/drop-tracking-parent-expression.rs:24:13 + | +LL | assert_send(g); + | ^^^^^^^^^^^ generator 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 `[generator@$DIR/drop-tracking-parent-expression.rs:18:21: 18:28]`, the trait `Send` is not implemented for `derived_drop::Client` +note: generator is not `Send` as this value is used across a yield + --> $DIR/drop-tracking-parent-expression.rs:22: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 | }; + | - `$name::Client::default()` is later dropped here +... +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:41: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: generator cannot be sent between threads safely + --> $DIR/drop-tracking-parent-expression.rs:24:13 + | +LL | assert_send(g); + | ^^^^^^^^^^^ generator 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 `[generator@$DIR/drop-tracking-parent-expression.rs:18:21: 18:28]`, the trait `Send` is not implemented for `significant_drop::Client` +note: generator is not `Send` as this value is used across a yield + --> $DIR/drop-tracking-parent-expression.rs:22: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 | }; + | - `$name::Client::default()` is later dropped here +... +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:41: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: generator cannot be sent between threads safely + --> $DIR/drop-tracking-parent-expression.rs:24:13 + | +LL | assert_send(g); + | ^^^^^^^^^^^ generator 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 `[generator@$DIR/drop-tracking-parent-expression.rs:18:21: 18:28]`, the trait `Send` is not implemented for `insignificant_dtor::Client` +note: generator is not `Send` as this value is used across a yield + --> $DIR/drop-tracking-parent-expression.rs:22: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 | }; + | - `$name::Client::default()` is later dropped here +... +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:41: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/src/test/ui/generator/drop-tracking-yielding-in-match-guards.rs b/src/test/ui/generator/drop-tracking-yielding-in-match-guards.rs new file mode 100644 index 000000000..646365e43 --- /dev/null +++ b/src/test/ui/generator/drop-tracking-yielding-in-match-guards.rs @@ -0,0 +1,12 @@ +// build-pass +// edition:2018 +// compile-flags: -Zdrop-tracking + +#![feature(generators)] + +fn main() { + let _ = static |x: u8| match x { + y if { yield } == y + 1 => (), + _ => (), + }; +} diff --git a/src/test/ui/generator/drop-yield-twice.rs b/src/test/ui/generator/drop-yield-twice.rs new file mode 100644 index 000000000..f484cbb8d --- /dev/null +++ b/src/test/ui/generator/drop-yield-twice.rs @@ -0,0 +1,15 @@ +#![feature(negative_impls, generators)] + +struct Foo(i32); +impl !Send for Foo {} + +fn main() { + assert_send(|| { //~ ERROR generator cannot be sent between threads safely + let guard = Foo(42); + yield; + drop(guard); + yield; + }) +} + +fn assert_send<T: Send>(_: T) {} diff --git a/src/test/ui/generator/drop-yield-twice.stderr b/src/test/ui/generator/drop-yield-twice.stderr new file mode 100644 index 000000000..5bc6ea560 --- /dev/null +++ b/src/test/ui/generator/drop-yield-twice.stderr @@ -0,0 +1,25 @@ +error: generator cannot be sent between threads safely + --> $DIR/drop-yield-twice.rs:7:5 + | +LL | assert_send(|| { + | ^^^^^^^^^^^ generator is not `Send` + | + = help: within `[generator@$DIR/drop-yield-twice.rs:7:17: 7:19]`, the trait `Send` is not implemented for `Foo` +note: generator 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 +... +LL | }) + | - `guard` is later dropped here +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/src/test/ui/generator/dropck-resume.rs b/src/test/ui/generator/dropck-resume.rs new file mode 100644 index 000000000..4c18077f3 --- /dev/null +++ b/src/test/ui/generator/dropck-resume.rs @@ -0,0 +1,33 @@ +#![feature(generators, generator_trait)] + +use std::ops::{Generator, GeneratorState}; +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_generator() -> 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_generator()); +} diff --git a/src/test/ui/generator/dropck-resume.stderr b/src/test/ui/generator/dropck-resume.stderr new file mode 100644 index 000000000..b0756eb55 --- /dev/null +++ b/src/test/ui/generator/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 generator + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0502`. diff --git a/src/test/ui/generator/dropck.rs b/src/test/ui/generator/dropck.rs new file mode 100644 index 000000000..f82111a76 --- /dev/null +++ b/src/test/ui/generator/dropck.rs @@ -0,0 +1,20 @@ +#![feature(generators, generator_trait)] + +use std::cell::RefCell; +use std::ops::Generator; +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 generator 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/src/test/ui/generator/dropck.stderr b/src/test/ui/generator/dropck.stderr new file mode 100644 index 000000000..7bb188352 --- /dev/null +++ b/src/test/ui/generator/dropck.stderr @@ -0,0 +1,34 @@ +error[E0597]: `*cell` does not live long enough + --> $DIR/dropck.rs:10:40 + | +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 generator + | + = 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 generator +LL | // but the generator 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 generator + | + = 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/src/test/ui/generator/generator-region-requirements.migrate.stderr b/src/test/ui/generator/generator-region-requirements.migrate.stderr new file mode 100644 index 000000000..8a96d187f --- /dev/null +++ b/src/test/ui/generator/generator-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/src/test/ui/generator/generator-region-requirements.rs b/src/test/ui/generator/generator-region-requirements.rs new file mode 100644 index 000000000..7269a79ca --- /dev/null +++ b/src/test/ui/generator/generator-region-requirements.rs @@ -0,0 +1,19 @@ +#![feature(generators, generator_trait)] +use std::ops::{Generator, GeneratorState}; +use std::pin::Pin; + +fn dangle(x: &mut i32) -> &'static mut i32 { + let mut g = || { + yield; + x + }; + loop { + match Pin::new(&mut g).resume(()) { + GeneratorState::Complete(c) => return c, + //~^ ERROR lifetime may not live long enough + GeneratorState::Yielded(_) => (), + } + } +} + +fn main() {} diff --git a/src/test/ui/generator/generator-region-requirements.stderr b/src/test/ui/generator/generator-region-requirements.stderr new file mode 100644 index 000000000..87f604672 --- /dev/null +++ b/src/test/ui/generator/generator-region-requirements.stderr @@ -0,0 +1,11 @@ +error: lifetime may not live long enough + --> $DIR/generator-region-requirements.rs:12:51 + | +LL | fn dangle(x: &mut i32) -> &'static mut i32 { + | - let's call the lifetime of this reference `'1` +... +LL | GeneratorState::Complete(c) => return c, + | ^ returning this value requires that `'1` must outlive `'static` + +error: aborting due to previous error + diff --git a/src/test/ui/generator/generator-resume-after-panic.rs b/src/test/ui/generator/generator-resume-after-panic.rs new file mode 100644 index 000000000..f2e67f1f7 --- /dev/null +++ b/src/test/ui/generator/generator-resume-after-panic.rs @@ -0,0 +1,25 @@ +// run-fail +// needs-unwind +// error-pattern:generator resumed after panicking +// ignore-emscripten no processes + +// Test that we get the correct message for resuming a panicked generator. + +#![feature(generators, generator_trait)] + +use std::{ + ops::Generator, + 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/src/test/ui/generator/generator-with-nll.rs b/src/test/ui/generator/generator-with-nll.rs new file mode 100644 index 000000000..cee3e6d22 --- /dev/null +++ b/src/test/ui/generator/generator-with-nll.rs @@ -0,0 +1,12 @@ +#![feature(generators)] + +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 generator yields + yield (); + println!("{}", b); + }; +} diff --git a/src/test/ui/generator/generator-with-nll.stderr b/src/test/ui/generator/generator-with-nll.stderr new file mode 100644 index 000000000..14199aeb9 --- /dev/null +++ b/src/test/ui/generator/generator-with-nll.stderr @@ -0,0 +1,12 @@ +error[E0626]: borrow may still be in use when generator yields + --> $DIR/generator-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/src/test/ui/generator/generator-yielding-or-returning-itself.rs b/src/test/ui/generator/generator-yielding-or-returning-itself.rs new file mode 100644 index 000000000..30788e3c1 --- /dev/null +++ b/src/test/ui/generator/generator-yielding-or-returning-itself.rs @@ -0,0 +1,35 @@ +#![feature(generator_trait)] +#![feature(generators)] + +// Test that we cannot create a generator that returns a value of its +// own type. + +use std::ops::Generator; + +pub fn want_cyclic_generator_return<T>(_: T) + where T: Generator<Yield = (), Return = T> +{ +} + +fn supply_cyclic_generator_return() { + want_cyclic_generator_return(|| { + //~^ ERROR type mismatch + if false { yield None.unwrap(); } + None.unwrap() + }) +} + +pub fn want_cyclic_generator_yield<T>(_: T) + where T: Generator<Yield = T, Return = ()> +{ +} + +fn supply_cyclic_generator_yield() { + want_cyclic_generator_yield(|| { + //~^ ERROR type mismatch + if false { yield None.unwrap(); } + None.unwrap() + }) +} + +fn main() { } diff --git a/src/test/ui/generator/generator-yielding-or-returning-itself.stderr b/src/test/ui/generator/generator-yielding-or-returning-itself.stderr new file mode 100644 index 000000000..2a39a08ee --- /dev/null +++ b/src/test/ui/generator/generator-yielding-or-returning-itself.stderr @@ -0,0 +1,39 @@ +error[E0271]: type mismatch resolving `<[generator@$DIR/generator-yielding-or-returning-itself.rs:15:34: 15:36] as Generator>::Return == [generator@$DIR/generator-yielding-or-returning-itself.rs:15:34: 15:36]` + --> $DIR/generator-yielding-or-returning-itself.rs:15:5 + | +LL | want_cyclic_generator_return(|| { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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_generator_return` + --> $DIR/generator-yielding-or-returning-itself.rs:10:36 + | +LL | pub fn want_cyclic_generator_return<T>(_: T) + | ---------------------------- required by a bound in this +LL | where T: Generator<Yield = (), Return = T> + | ^^^^^^^^^^ required by this bound in `want_cyclic_generator_return` + +error[E0271]: type mismatch resolving `<[generator@$DIR/generator-yielding-or-returning-itself.rs:28:33: 28:35] as Generator>::Yield == [generator@$DIR/generator-yielding-or-returning-itself.rs:28:33: 28:35]` + --> $DIR/generator-yielding-or-returning-itself.rs:28:5 + | +LL | want_cyclic_generator_yield(|| { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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_generator_yield` + --> $DIR/generator-yielding-or-returning-itself.rs:23:24 + | +LL | pub fn want_cyclic_generator_yield<T>(_: T) + | --------------------------- required by a bound in this +LL | where T: Generator<Yield = T, Return = ()> + | ^^^^^^^^^ required by this bound in `want_cyclic_generator_yield` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0271`. diff --git a/src/test/ui/generator/issue-44197.rs b/src/test/ui/generator/issue-44197.rs new file mode 100644 index 000000000..389b9d139 --- /dev/null +++ b/src/test/ui/generator/issue-44197.rs @@ -0,0 +1,36 @@ +// run-pass + +#![feature(generators, generator_trait)] + +use std::ops::{Generator, GeneratorState}; +use std::pin::Pin; + +fn foo(_: &str) -> String { + String::new() +} + +fn bar(baz: String) -> impl Generator<(), Yield = String, Return = ()> { + move || { + yield foo(&baz); + } +} + +fn foo2(_: &str) -> Result<String, ()> { + Err(()) +} + +fn bar2(baz: String) -> impl Generator<(), Yield = String, Return = ()> { + move || { + if let Ok(quux) = foo2(&baz) { + yield quux; + } + } +} + +fn main() { + assert_eq!( + Pin::new(&mut bar(String::new())).resume(()), + GeneratorState::Yielded(String::new()) + ); + assert_eq!(Pin::new(&mut bar2(String::new())).resume(()), GeneratorState::Complete(())); +} diff --git a/src/test/ui/generator/issue-45729-unsafe-in-generator.mir.stderr b/src/test/ui/generator/issue-45729-unsafe-in-generator.mir.stderr new file mode 100644 index 000000000..3afbea079 --- /dev/null +++ b/src/test/ui/generator/issue-45729-unsafe-in-generator.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-generator.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/src/test/ui/generator/issue-45729-unsafe-in-generator.rs b/src/test/ui/generator/issue-45729-unsafe-in-generator.rs new file mode 100644 index 000000000..379c36d2c --- /dev/null +++ b/src/test/ui/generator/issue-45729-unsafe-in-generator.rs @@ -0,0 +1,12 @@ +// revisions: mir thir +// [thir]compile-flags: -Z thir-unsafeck + +#![feature(generators)] + +fn main() { + let _ = || { + *(1 as *mut u32) = 42; + //~^ ERROR dereference of raw pointer is unsafe + yield; + }; +} diff --git a/src/test/ui/generator/issue-45729-unsafe-in-generator.thir.stderr b/src/test/ui/generator/issue-45729-unsafe-in-generator.thir.stderr new file mode 100644 index 000000000..10d768f19 --- /dev/null +++ b/src/test/ui/generator/issue-45729-unsafe-in-generator.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-generator.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/src/test/ui/generator/issue-48048.rs b/src/test/ui/generator/issue-48048.rs new file mode 100644 index 000000000..992bbc97a --- /dev/null +++ b/src/test/ui/generator/issue-48048.rs @@ -0,0 +1,13 @@ +#![feature(generators)] + +fn main() { + let x = (|_| {},); + + || { + let x = x; + + x.0({ //~ ERROR borrow may still be in use when generator yields + yield; + }); + }; +} diff --git a/src/test/ui/generator/issue-48048.stderr b/src/test/ui/generator/issue-48048.stderr new file mode 100644 index 000000000..234235839 --- /dev/null +++ b/src/test/ui/generator/issue-48048.stderr @@ -0,0 +1,11 @@ +error[E0626]: borrow may still be in use when generator 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/src/test/ui/generator/issue-52304.rs b/src/test/ui/generator/issue-52304.rs new file mode 100644 index 000000000..3e9de765b --- /dev/null +++ b/src/test/ui/generator/issue-52304.rs @@ -0,0 +1,11 @@ +// check-pass + +#![feature(generators, generator_trait)] + +use std::ops::Generator; + +pub fn example() -> impl Generator { + || yield &1 +} + +fn main() {} diff --git a/src/test/ui/generator/issue-52398.rs b/src/test/ui/generator/issue-52398.rs new file mode 100644 index 000000000..ada380d11 --- /dev/null +++ b/src/test/ui/generator/issue-52398.rs @@ -0,0 +1,28 @@ +// run-pass +#![allow(unused_variables)] + +#![feature(generators)] + +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 generator 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 generator that must be used + yield *y.borrow(); + return "Done"; + }; +} diff --git a/src/test/ui/generator/issue-52398.stderr b/src/test/ui/generator/issue-52398.stderr new file mode 100644 index 000000000..3f8ebb5a7 --- /dev/null +++ b/src/test/ui/generator/issue-52398.stderr @@ -0,0 +1,24 @@ +warning: unused generator that must be used + --> $DIR/issue-52398.rs:17:5 + | +LL | / move || { +LL | | A.test(yield); +LL | | }; + | |______^ + | + = note: `#[warn(unused_must_use)]` on by default + = note: generators are lazy and do nothing unless resumed + +warning: unused generator that must be used + --> $DIR/issue-52398.rs:24:5 + | +LL | / static move || { +LL | | yield *y.borrow(); +LL | | return "Done"; +LL | | }; + | |______^ + | + = note: generators are lazy and do nothing unless resumed + +warning: 2 warnings emitted + diff --git a/src/test/ui/generator/issue-53548-1.rs b/src/test/ui/generator/issue-53548-1.rs new file mode 100644 index 000000000..173ae3c6f --- /dev/null +++ b/src/test/ui/generator/issue-53548-1.rs @@ -0,0 +1,20 @@ +// A variant of #53548 that does not actually require generators, +// 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/src/test/ui/generator/issue-53548.rs b/src/test/ui/generator/issue-53548.rs new file mode 100644 index 000000000..3ebabb914 --- /dev/null +++ b/src/test/ui/generator/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 generator "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(generators)] + +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/src/test/ui/generator/issue-57017.rs b/src/test/ui/generator/issue-57017.rs new file mode 100644 index 000000000..c0bde3b44 --- /dev/null +++ b/src/test/ui/generator/issue-57017.rs @@ -0,0 +1,55 @@ +// build-pass +// compile-flags: -Zdrop-tracking +#![feature(generators, negative_impls)] + +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/src/test/ui/generator/issue-57084.rs b/src/test/ui/generator/issue-57084.rs new file mode 100644 index 000000000..2a5c3dd05 --- /dev/null +++ b/src/test/ui/generator/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)) <= '_#1r" +// run-pass +// edition:2018 +#![feature(generators,generator_trait)] +use std::ops::Generator; + +fn with<F>(f: F) -> impl Generator<Yield=(), Return=()> +where F: Fn() -> () +{ + move || { + loop { + match f() { + _ => yield, + } + } + } +} + +fn main() { + let data = &vec![1]; + || { //~ WARN unused generator that must be used + let _to_pin = with(move || println!("{:p}", data)); + loop { + yield + } + }; +} diff --git a/src/test/ui/generator/issue-57084.stderr b/src/test/ui/generator/issue-57084.stderr new file mode 100644 index 000000000..32a04f94d --- /dev/null +++ b/src/test/ui/generator/issue-57084.stderr @@ -0,0 +1,16 @@ +warning: unused generator 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: `#[warn(unused_must_use)]` on by default + = note: generators are lazy and do nothing unless resumed + +warning: 1 warning emitted + diff --git a/src/test/ui/generator/issue-57478.rs b/src/test/ui/generator/issue-57478.rs new file mode 100644 index 000000000..91407ea18 --- /dev/null +++ b/src/test/ui/generator/issue-57478.rs @@ -0,0 +1,17 @@ +// check-pass +// compile-flags: -Zdrop-tracking + +#![feature(negative_impls, generators)] + +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/src/test/ui/generator/issue-58888.rs b/src/test/ui/generator/issue-58888.rs new file mode 100644 index 000000000..d42d09d40 --- /dev/null +++ b/src/test/ui/generator/issue-58888.rs @@ -0,0 +1,28 @@ +// run-pass +// compile-flags: -g +// ignore-asmjs wasm2js does not support source maps yet + +#![feature(generators, generator_trait)] + +use std::ops::Generator; + +struct Database; + +impl Database { + fn get_connection(&self) -> impl Iterator<Item = ()> { + Some(()).into_iter() + } + + fn check_connection(&self) -> impl Generator<Yield = (), Return = ()> + '_ { + move || { + let iter = self.get_connection(); + for i in iter { + yield i + } + } + } +} + +fn main() { + Database.check_connection(); +} diff --git a/src/test/ui/generator/issue-61442-stmt-expr-with-drop.rs b/src/test/ui/generator/issue-61442-stmt-expr-with-drop.rs new file mode 100644 index 000000000..187c37402 --- /dev/null +++ b/src/test/ui/generator/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(generators, generator_trait)] + +use std::ops::Generator; + +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/src/test/ui/generator/issue-62506-two_awaits.rs b/src/test/ui/generator/issue-62506-two_awaits.rs new file mode 100644 index 000000000..672e16b78 --- /dev/null +++ b/src/test/ui/generator/issue-62506-two_awaits.rs @@ -0,0 +1,17 @@ +// Output = String caused an ICE whereas Output = &'static str compiled successfully. +// Broken MIR: generator 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/src/test/ui/generator/issue-64620-yield-array-element.rs b/src/test/ui/generator/issue-64620-yield-array-element.rs new file mode 100644 index 000000000..2cbe8f516 --- /dev/null +++ b/src/test/ui/generator/issue-64620-yield-array-element.rs @@ -0,0 +1,9 @@ +// Regression test for #64620 + +#![feature(generators)] + +pub fn crash(arr: [usize; 1]) { + yield arr[0]; //~ ERROR: yield expression outside of generator literal +} + +fn main() {} diff --git a/src/test/ui/generator/issue-64620-yield-array-element.stderr b/src/test/ui/generator/issue-64620-yield-array-element.stderr new file mode 100644 index 000000000..48383c2ed --- /dev/null +++ b/src/test/ui/generator/issue-64620-yield-array-element.stderr @@ -0,0 +1,9 @@ +error[E0627]: yield expression outside of generator 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/src/test/ui/generator/issue-68112.rs b/src/test/ui/generator/issue-68112.rs new file mode 100644 index 000000000..3fcef773b --- /dev/null +++ b/src/test/ui/generator/issue-68112.rs @@ -0,0 +1,70 @@ +#![feature(generators, generator_trait)] + +use std::{ + cell::RefCell, + sync::Arc, + pin::Pin, + ops::{Generator, GeneratorState}, +}; + +pub struct Ready<T>(Option<T>); +impl<T> Generator<()> for Ready<T> { + type Return = T; + type Yield = (); + fn resume(mut self: Pin<&mut Self>, _args: ()) -> GeneratorState<(), T> { + GeneratorState::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_generator() -> impl Generator<Return = Arc<RefCell<i32>>> { + make_gen1(Arc::new(RefCell::new(0))) +} + +fn test1() { + let send_gen = || { + let _non_send_gen = make_non_send_generator(); + //~^ NOTE not `Send` + yield; + //~^ NOTE yield occurs here + //~| NOTE value is used across a yield + }; //~ NOTE later dropped here + require_send(send_gen); + //~^ ERROR generator cannot be sent between threads + //~| NOTE not `Send` +} + +pub fn make_gen2<T>(t: T) -> impl Generator<Return = T> { +//~^ NOTE appears within the type +//~| NOTE expansion of desugaring + || { //~ NOTE used within this generator + yield; + t + } +} +fn make_non_send_generator2() -> impl Generator<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 generator + let _non_send_gen = make_non_send_generator2(); + yield; + }; + require_send(send_gen); + //~^ ERROR `RefCell<i32>` cannot be shared between threads safely + //~| NOTE `RefCell<i32>` cannot be shared between threads safely + //~| NOTE requirements on the impl + //~| NOTE captures the following types +} + +fn main() {} diff --git a/src/test/ui/generator/issue-68112.stderr b/src/test/ui/generator/issue-68112.stderr new file mode 100644 index 000000000..1d5b97e98 --- /dev/null +++ b/src/test/ui/generator/issue-68112.stderr @@ -0,0 +1,62 @@ +error: generator cannot be sent between threads safely + --> $DIR/issue-68112.rs:40:5 + | +LL | require_send(send_gen); + | ^^^^^^^^^^^^ generator is not `Send` + | + = help: the trait `Sync` is not implemented for `RefCell<i32>` +note: generator 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_generator(); + | ------------- has type `impl Generator<Return = Arc<RefCell<i32>>>` which is not `Send` +LL | +LL | yield; + | ^^^^^ yield occurs here, with `_non_send_gen` maybe used later +... +LL | }; + | - `_non_send_gen` is later dropped here +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:63: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: required because of the requirements on the impl of `Send` for `Arc<RefCell<i32>>` +note: required because it's used within this generator + --> $DIR/issue-68112.rs:48:5 + | +LL | || { + | ^^ +note: required because it appears within the type `impl Generator<Return = Arc<RefCell<i32>>>` + --> $DIR/issue-68112.rs:45:30 + | +LL | pub fn make_gen2<T>(t: T) -> impl Generator<Return = T> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: required because it appears within the type `impl Generator<Return = Arc<RefCell<i32>>>` + --> $DIR/issue-68112.rs:53:34 + | +LL | fn make_non_send_generator2() -> impl Generator<Return = Arc<RefCell<i32>>> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: required because it captures the following types: `impl Generator<Return = Arc<RefCell<i32>>>`, `()` +note: required because it's used within this generator + --> $DIR/issue-68112.rs:59: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/src/test/ui/generator/issue-69017.rs b/src/test/ui/generator/issue-69017.rs new file mode 100644 index 000000000..511deb60e --- /dev/null +++ b/src/test/ui/generator/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(generator_trait)] +#![feature(generators)] + +use std::ops::Generator; + +fn gen() -> impl Generator<usize> { + |_: usize| { + println!("-> {}", yield); + } +} + +fn main() {} diff --git a/src/test/ui/generator/issue-69039.rs b/src/test/ui/generator/issue-69039.rs new file mode 100644 index 000000000..ccc141860 --- /dev/null +++ b/src/test/ui/generator/issue-69039.rs @@ -0,0 +1,34 @@ +// run-pass + +#![feature(generators, generator_trait)] + +use std::ops::{Generator, GeneratorState}; + +fn mkstr(my_name: String, my_mood: String) -> String { + format!("{} is {}", my_name.trim(), my_mood.trim()) +} + +fn my_scenario() -> impl Generator<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()), + GeneratorState::Yielded("What is your name?") + ); + assert_eq!( + my_session.as_mut().resume("Your Name".to_string()), + GeneratorState::Yielded("How are you feeling?") + ); + assert_eq!( + my_session.as_mut().resume("Sensory Organs".to_string()), + GeneratorState::Complete("Your Name is Sensory Organs".to_string()) + ); +} diff --git a/src/test/ui/generator/issue-87142.rs b/src/test/ui/generator/issue-87142.rs new file mode 100644 index 000000000..fc10d04d4 --- /dev/null +++ b/src/test/ui/generator/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(type_alias_impl_trait, generator_trait, generators)] +#![crate_type = "lib"] + +use std::ops::Generator; + +pub trait GeneratorProviderAlt: Sized { + type Gen: Generator<(), Return = (), Yield = ()>; + + fn start(ctx: Context<Self>) -> Self::Gen; +} + +pub struct Context<G: 'static + GeneratorProviderAlt> { + pub link: Box<G::Gen>, +} + +impl GeneratorProviderAlt for () { + type Gen = impl Generator<(), Return = (), Yield = ()>; + fn start(ctx: Context<Self>) -> Self::Gen { + move || { + match ctx { + _ => (), + } + yield (); + } + } +} diff --git a/src/test/ui/generator/issue-88653.rs b/src/test/ui/generator/issue-88653.rs new file mode 100644 index 000000000..1d9377bce --- /dev/null +++ b/src/test/ui/generator/issue-88653.rs @@ -0,0 +1,22 @@ +// Regression test for #88653, where a confusing warning about a +// type mismatch in generator arguments was issued. + +#![feature(generators, generator_trait)] + +use std::ops::Generator; + +fn foo(bar: bool) -> impl Generator<(bool,)> { + //~^ ERROR: type mismatch in generator arguments [E0631] + //~| NOTE: expected due to this + //~| NOTE: expected generator 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/src/test/ui/generator/issue-88653.stderr b/src/test/ui/generator/issue-88653.stderr new file mode 100644 index 000000000..b742c6e2f --- /dev/null +++ b/src/test/ui/generator/issue-88653.stderr @@ -0,0 +1,15 @@ +error[E0631]: type mismatch in generator arguments + --> $DIR/issue-88653.rs:8:22 + | +LL | fn foo(bar: bool) -> impl Generator<(bool,)> { + | ^^^^^^^^^^^^^^^^^^^^^^^ expected due to this +... +LL | |bar| { + | ----- found signature defined here + | + = note: expected generator signature `fn((bool,)) -> _` + found generator signature `fn(bool) -> _` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0631`. diff --git a/src/test/ui/generator/issue-91477.rs b/src/test/ui/generator/issue-91477.rs new file mode 100644 index 000000000..6c027feb4 --- /dev/null +++ b/src/test/ui/generator/issue-91477.rs @@ -0,0 +1,7 @@ +#![feature(generators)] + +fn foo() -> impl Sized { + yield 1; //~ ERROR E0627 +} + +fn main() {} diff --git a/src/test/ui/generator/issue-91477.stderr b/src/test/ui/generator/issue-91477.stderr new file mode 100644 index 000000000..4597dc1bc --- /dev/null +++ b/src/test/ui/generator/issue-91477.stderr @@ -0,0 +1,9 @@ +error[E0627]: yield expression outside of generator 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/src/test/ui/generator/issue-93161.rs b/src/test/ui/generator/issue-93161.rs new file mode 100644 index 000000000..92305609c --- /dev/null +++ b/src/test/ui/generator/issue-93161.rs @@ -0,0 +1,94 @@ +// edition:2021 +// run-pass +// compile-flags: -Zdrop-tracking + +#![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/src/test/ui/generator/iterator-count.rs b/src/test/ui/generator/iterator-count.rs new file mode 100644 index 000000000..90eefe02f --- /dev/null +++ b/src/test/ui/generator/iterator-count.rs @@ -0,0 +1,44 @@ +// run-pass + +#![feature(generators, generator_trait)] + +use std::marker::Unpin; +use std::ops::{GeneratorState, Generator}; +use std::pin::Pin; + +struct W<T>(T); + +// This impl isn't safe in general, but the generator used in this test is movable +// so it won't cause problems. +impl<T: Generator<(), Return = ()> + Unpin> Iterator for W<T> { + type Item = T::Yield; + + fn next(&mut self) -> Option<Self::Item> { + match Pin::new(&mut self.0).resume(()) { + GeneratorState::Complete(..) => None, + GeneratorState::Yielded(v) => Some(v), + } + } +} + +fn test() -> impl Generator<(), 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/src/test/ui/generator/layout-error.rs b/src/test/ui/generator/layout-error.rs new file mode 100644 index 000000000..7c3d18740 --- /dev/null +++ b/src/test/ui/generator/layout-error.rs @@ -0,0 +1,28 @@ +// Verifies that computing a layout of a generator 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/src/test/ui/generator/layout-error.stderr b/src/test/ui/generator/layout-error.stderr new file mode 100644 index 000000000..b1a258f4f --- /dev/null +++ b/src/test/ui/generator/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/src/test/ui/generator/live-upvar-across-yield.rs b/src/test/ui/generator/live-upvar-across-yield.rs new file mode 100644 index 000000000..6a2e42a55 --- /dev/null +++ b/src/test/ui/generator/live-upvar-across-yield.rs @@ -0,0 +1,14 @@ +// run-pass + +#![feature(generators, generator_trait)] + +use std::ops::Generator; +use std::pin::Pin; + +fn main() { + let b = |_| 3; + let mut a = || { + b(yield); + }; + Pin::new(&mut a).resume(()); +} diff --git a/src/test/ui/generator/match-bindings.rs b/src/test/ui/generator/match-bindings.rs new file mode 100644 index 000000000..865904a57 --- /dev/null +++ b/src/test/ui/generator/match-bindings.rs @@ -0,0 +1,23 @@ +// run-pass +#![allow(dead_code)] + +#![feature(generators)] + +enum Enum { + A(String), + B +} + +fn main() { + || { //~ WARN unused generator that must be used + loop { + if let true = true { + match Enum::A(String::new()) { + Enum::A(_var) => {} + Enum::B => {} + } + } + yield; + } + }; +} diff --git a/src/test/ui/generator/match-bindings.stderr b/src/test/ui/generator/match-bindings.stderr new file mode 100644 index 000000000..4fd1e26f0 --- /dev/null +++ b/src/test/ui/generator/match-bindings.stderr @@ -0,0 +1,17 @@ +warning: unused generator 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: `#[warn(unused_must_use)]` on by default + = note: generators are lazy and do nothing unless resumed + +warning: 1 warning emitted + diff --git a/src/test/ui/generator/metadata-sufficient-for-layout.rs b/src/test/ui/generator/metadata-sufficient-for-layout.rs new file mode 100644 index 000000000..d0e648ee7 --- /dev/null +++ b/src/test/ui/generator/metadata-sufficient-for-layout.rs @@ -0,0 +1,25 @@ +// Check that the layout of a generator 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(generator_trait)] + +extern crate metadata_sufficient_for_layout; + +use std::ops::Generator; + +type F = impl Generator<(), Yield = (), Return = ()>; + +// Static queries the layout of the generator. +static A: Option<F> = None; + +fn f() -> F { + metadata_sufficient_for_layout::g() +} + +#[rustc_error] +fn main() {} //~ ERROR diff --git a/src/test/ui/generator/metadata-sufficient-for-layout.stderr b/src/test/ui/generator/metadata-sufficient-for-layout.stderr new file mode 100644 index 000000000..3488b04f2 --- /dev/null +++ b/src/test/ui/generator/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/src/test/ui/generator/nested_generators.rs b/src/test/ui/generator/nested_generators.rs new file mode 100644 index 000000000..45519150e --- /dev/null +++ b/src/test/ui/generator/nested_generators.rs @@ -0,0 +1,21 @@ +// run-pass + +#![feature(generators, generator_trait)] + +use std::ops::{Generator, GeneratorState}; +use std::pin::Pin; + +fn main() { + let _generator = || { + let mut sub_generator = || { + yield 2; + }; + + match Pin::new(&mut sub_generator).resume(()) { + GeneratorState::Yielded(x) => { + yield x; + } + _ => panic!(), + }; + }; +} diff --git a/src/test/ui/generator/niche-in-generator.rs b/src/test/ui/generator/niche-in-generator.rs new file mode 100644 index 000000000..42bee81f5 --- /dev/null +++ b/src/test/ui/generator/niche-in-generator.rs @@ -0,0 +1,19 @@ +// Test that niche finding works with captured generator upvars. + +// run-pass + +#![feature(generators)] + +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/src/test/ui/generator/non-static-is-unpin.rs b/src/test/ui/generator/non-static-is-unpin.rs new file mode 100644 index 000000000..96d0a8e28 --- /dev/null +++ b/src/test/ui/generator/non-static-is-unpin.rs @@ -0,0 +1,18 @@ +// run-pass + +#![feature(generators, generator_trait)] + +use std::marker::{PhantomPinned, Unpin}; + +fn assert_unpin<G: Unpin>(_: G) { +} + +fn main() { + // Even though this generator holds a `PhantomPinned` in its environment, it + // remains `Unpin`. + assert_unpin(|| { + let pinned = PhantomPinned; + yield; + drop(pinned); + }); +} diff --git a/src/test/ui/generator/not-send-sync.rs b/src/test/ui/generator/not-send-sync.rs new file mode 100644 index 000000000..8ca5565fb --- /dev/null +++ b/src/test/ui/generator/not-send-sync.rs @@ -0,0 +1,21 @@ +#![feature(generators)] + +use std::cell::Cell; + +fn main() { + fn assert_sync<T: Sync>(_: T) {} + fn assert_send<T: Send>(_: T) {} + + assert_sync(|| { + //~^ ERROR: generator cannot be shared between threads safely + let a = Cell::new(2); + yield; + }); + + let a = Cell::new(2); + assert_send(|| { + //~^ ERROR: E0277 + drop(&a); + yield; + }); +} diff --git a/src/test/ui/generator/not-send-sync.stderr b/src/test/ui/generator/not-send-sync.stderr new file mode 100644 index 000000000..0b31bb4fd --- /dev/null +++ b/src/test/ui/generator/not-send-sync.stderr @@ -0,0 +1,44 @@ +error[E0277]: `Cell<i32>` cannot be shared between threads safely + --> $DIR/not-send-sync.rs:16:5 + | +LL | assert_send(|| { + | ^^^^^^^^^^^ `Cell<i32>` cannot be shared between threads safely + | + = help: the trait `Sync` is not implemented for `Cell<i32>` + = note: required because of the requirements on the impl of `Send` for `&Cell<i32>` +note: required because it's used within this generator + --> $DIR/not-send-sync.rs:16:17 + | +LL | assert_send(|| { + | ^^ +note: required by a bound in `assert_send` + --> $DIR/not-send-sync.rs:7:23 + | +LL | fn assert_send<T: Send>(_: T) {} + | ^^^^ required by this bound in `assert_send` + +error: generator cannot be shared between threads safely + --> $DIR/not-send-sync.rs:9:5 + | +LL | assert_sync(|| { + | ^^^^^^^^^^^ generator is not `Sync` + | + = help: within `[generator@$DIR/not-send-sync.rs:9:17: 9:19]`, the trait `Sync` is not implemented for `Cell<i32>` +note: generator is not `Sync` as this value is used across a yield + --> $DIR/not-send-sync.rs:12:9 + | +LL | let a = Cell::new(2); + | - has type `Cell<i32>` which is not `Sync` +LL | yield; + | ^^^^^ yield occurs here, with `a` maybe used later +LL | }); + | - `a` is later dropped here +note: required by a bound in `assert_sync` + --> $DIR/not-send-sync.rs:6:23 + | +LL | fn assert_sync<T: Sync>(_: T) {} + | ^^^^ required by this bound in `assert_sync` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/generator/overlap-locals.rs b/src/test/ui/generator/overlap-locals.rs new file mode 100644 index 000000000..101c8714f --- /dev/null +++ b/src/test/ui/generator/overlap-locals.rs @@ -0,0 +1,29 @@ +// run-pass + +#![feature(generators)] + +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/src/test/ui/generator/panic-drops-resume.rs b/src/test/ui/generator/panic-drops-resume.rs new file mode 100644 index 000000000..8d8eb6a97 --- /dev/null +++ b/src/test/ui/generator/panic-drops-resume.rs @@ -0,0 +1,38 @@ +//! Tests that panics inside a generator will correctly drop the initial resume argument. + +// run-pass +// needs-unwind +// ignore-wasm no unwind support +// ignore-emscripten no unwind support + +#![feature(generators, generator_trait)] + +use std::ops::Generator; +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/src/test/ui/generator/panic-drops.rs b/src/test/ui/generator/panic-drops.rs new file mode 100644 index 000000000..a9de4e7fc --- /dev/null +++ b/src/test/ui/generator/panic-drops.rs @@ -0,0 +1,58 @@ +// run-pass +// needs-unwind + +// ignore-wasm32-bare compiled with panic=abort by default + +#![feature(generators, generator_trait)] + +use std::ops::Generator; +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/src/test/ui/generator/panic-safe.rs b/src/test/ui/generator/panic-safe.rs new file mode 100644 index 000000000..14a0c8dba --- /dev/null +++ b/src/test/ui/generator/panic-safe.rs @@ -0,0 +1,31 @@ +// run-pass +// needs-unwind + +// ignore-wasm32-bare compiled with panic=abort by default + +#![feature(generators, generator_trait)] + +use std::ops::Generator; +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/src/test/ui/generator/partial-drop.rs b/src/test/ui/generator/partial-drop.rs new file mode 100644 index 000000000..c872fb7f3 --- /dev/null +++ b/src/test/ui/generator/partial-drop.rs @@ -0,0 +1,42 @@ +// compile-flags: -Zdrop-tracking + +#![feature(negative_impls, generators)] + +struct Foo; +impl !Send for Foo {} + +struct Bar { + foo: Foo, + x: i32, +} + +fn main() { + assert_send(|| { + //~^ ERROR generator cannot be sent between threads safely + // FIXME: it would be nice to make this work. + let guard = Bar { foo: Foo, x: 42 }; + drop(guard.foo); + yield; + }); + + assert_send(|| { + //~^ ERROR generator cannot be sent between threads safely + // FIXME: it would be nice to make this work. + let guard = Bar { foo: Foo, x: 42 }; + drop(guard); + guard.foo = Foo; + guard.x = 23; + yield; + }); + + assert_send(|| { + //~^ ERROR generator cannot be sent between threads safely + // FIXME: it would be nice to make this work. + let guard = Bar { foo: Foo, x: 42 }; + let Bar { foo, x } = guard; + drop(foo); + yield; + }); +} + +fn assert_send<T: Send>(_: T) {} diff --git a/src/test/ui/generator/partial-drop.stderr b/src/test/ui/generator/partial-drop.stderr new file mode 100644 index 000000000..1004fc64d --- /dev/null +++ b/src/test/ui/generator/partial-drop.stderr @@ -0,0 +1,71 @@ +error: generator cannot be sent between threads safely + --> $DIR/partial-drop.rs:14:5 + | +LL | assert_send(|| { + | ^^^^^^^^^^^ generator is not `Send` + | + = help: within `[generator@$DIR/partial-drop.rs:14:17: 14:19]`, the trait `Send` is not implemented for `Foo` +note: generator is not `Send` as this value is used across a yield + --> $DIR/partial-drop.rs:19:9 + | +LL | let guard = Bar { foo: Foo, x: 42 }; + | ----- has type `Bar` which is not `Send` +LL | drop(guard.foo); +LL | yield; + | ^^^^^ yield occurs here, with `guard` maybe used later +LL | }); + | - `guard` is later dropped here +note: required by a bound in `assert_send` + --> $DIR/partial-drop.rs:42:19 + | +LL | fn assert_send<T: Send>(_: T) {} + | ^^^^ required by this bound in `assert_send` + +error: generator cannot be sent between threads safely + --> $DIR/partial-drop.rs:22:5 + | +LL | assert_send(|| { + | ^^^^^^^^^^^ generator is not `Send` + | + = help: within `[generator@$DIR/partial-drop.rs:22:17: 22:19]`, the trait `Send` is not implemented for `Foo` +note: generator is not `Send` as this value is used across a yield + --> $DIR/partial-drop.rs:29:9 + | +LL | let guard = Bar { foo: Foo, x: 42 }; + | ----- has type `Bar` which is not `Send` +... +LL | yield; + | ^^^^^ yield occurs here, with `guard` maybe used later +LL | }); + | - `guard` is later dropped here +note: required by a bound in `assert_send` + --> $DIR/partial-drop.rs:42:19 + | +LL | fn assert_send<T: Send>(_: T) {} + | ^^^^ required by this bound in `assert_send` + +error: generator cannot be sent between threads safely + --> $DIR/partial-drop.rs:32:5 + | +LL | assert_send(|| { + | ^^^^^^^^^^^ generator is not `Send` + | + = help: within `[generator@$DIR/partial-drop.rs:32:17: 32:19]`, the trait `Send` is not implemented for `Foo` +note: generator is not `Send` as this value is used across a yield + --> $DIR/partial-drop.rs:38:9 + | +LL | let guard = Bar { foo: Foo, x: 42 }; + | ----- has type `Bar` which is not `Send` +... +LL | yield; + | ^^^^^ yield occurs here, with `guard` maybe used later +LL | }); + | - `guard` is later dropped here +note: required by a bound in `assert_send` + --> $DIR/partial-drop.rs:42:19 + | +LL | fn assert_send<T: Send>(_: T) {} + | ^^^^ required by this bound in `assert_send` + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/generator/partial-initialization-across-yield.rs b/src/test/ui/generator/partial-initialization-across-yield.rs new file mode 100644 index 000000000..65d9e6d39 --- /dev/null +++ b/src/test/ui/generator/partial-initialization-across-yield.rs @@ -0,0 +1,43 @@ +// Test that we don't allow yielding from a generator while a local is partially +// initialized. + +#![feature(generators)] + +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/src/test/ui/generator/partial-initialization-across-yield.stderr b/src/test/ui/generator/partial-initialization-across-yield.stderr new file mode 100644 index 000000000..3f9f1c046 --- /dev/null +++ b/src/test/ui/generator/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/src/test/ui/generator/pattern-borrow.rs b/src/test/ui/generator/pattern-borrow.rs new file mode 100644 index 000000000..d19363708 --- /dev/null +++ b/src/test/ui/generator/pattern-borrow.rs @@ -0,0 +1,17 @@ +#![feature(generators)] + +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 generator yields + yield (); + _a.use_ref(); + } + }; +} + +trait Fake { fn use_mut(&mut self) { } fn use_ref(&self) { } } +impl<T> Fake for T { } diff --git a/src/test/ui/generator/pattern-borrow.stderr b/src/test/ui/generator/pattern-borrow.stderr new file mode 100644 index 000000000..d78da5104 --- /dev/null +++ b/src/test/ui/generator/pattern-borrow.stderr @@ -0,0 +1,11 @@ +error[E0626]: borrow may still be in use when generator 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/src/test/ui/generator/pin-box-generator.rs b/src/test/ui/generator/pin-box-generator.rs new file mode 100644 index 000000000..c3136f5c0 --- /dev/null +++ b/src/test/ui/generator/pin-box-generator.rs @@ -0,0 +1,13 @@ +// run-pass + +#![feature(generators, generator_trait)] + +use std::ops::Generator; + +fn assert_generator<G: Generator>(_: G) { +} + +fn main() { + assert_generator(static || yield); + assert_generator(Box::pin(static || yield)); +} diff --git a/src/test/ui/generator/print/generator-print-verbose-1.rs b/src/test/ui/generator/print/generator-print-verbose-1.rs new file mode 100644 index 000000000..fe0687722 --- /dev/null +++ b/src/test/ui/generator/print/generator-print-verbose-1.rs @@ -0,0 +1,60 @@ +// compile-flags: -Zverbose + +// Same as: src/test/ui/generator/issue-68112.stderr + +#![feature(generators, generator_trait)] + +use std::{ + cell::RefCell, + sync::Arc, + pin::Pin, + ops::{Generator, GeneratorState}, +}; + +pub struct Ready<T>(Option<T>); +impl<T> Generator<()> for Ready<T> { + type Return = T; + type Yield = (); + fn resume(mut self: Pin<&mut Self>, _args: ()) -> GeneratorState<(), T> { + GeneratorState::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_generator() -> impl Generator<Return = Arc<RefCell<i32>>> { + make_gen1(Arc::new(RefCell::new(0))) +} + +fn test1() { + let send_gen = || { + let _non_send_gen = make_non_send_generator(); + yield; + }; + require_send(send_gen); + //~^ ERROR generator cannot be sent between threads +} + +pub fn make_gen2<T>(t: T) -> impl Generator<Return = T> { + || { + yield; + t + } +} +fn make_non_send_generator2() -> impl Generator<Return = Arc<RefCell<i32>>> { + make_gen2(Arc::new(RefCell::new(0))) +} + +fn test2() { + let send_gen = || { + let _non_send_gen = make_non_send_generator2(); + yield; + }; + require_send(send_gen); + //~^ ERROR `RefCell<i32>` cannot be shared between threads safely +} + +fn main() {} diff --git a/src/test/ui/generator/print/generator-print-verbose-1.stderr b/src/test/ui/generator/print/generator-print-verbose-1.stderr new file mode 100644 index 000000000..5b61f1e8f --- /dev/null +++ b/src/test/ui/generator/print/generator-print-verbose-1.stderr @@ -0,0 +1,60 @@ +error: generator cannot be sent between threads safely + --> $DIR/generator-print-verbose-1.rs:37:5 + | +LL | require_send(send_gen); + | ^^^^^^^^^^^^ generator is not `Send` + | + = help: the trait `Sync` is not implemented for `RefCell<i32>` +note: generator is not `Send` as this value is used across a yield + --> $DIR/generator-print-verbose-1.rs:35:9 + | +LL | let _non_send_gen = make_non_send_generator(); + | ------------- has type `Opaque(DefId(0:34 ~ generator_print_verbose_1[749a]::make_non_send_generator::{opaque#0}), [])` which is not `Send` +LL | yield; + | ^^^^^ yield occurs here, with `_non_send_gen` maybe used later +LL | }; + | - `_non_send_gen` is later dropped here +note: required by a bound in `require_send` + --> $DIR/generator-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/generator-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: required because of the requirements on the impl of `Send` for `Arc<RefCell<i32>>` +note: required because it's used within this generator + --> $DIR/generator-print-verbose-1.rs:42:5 + | +LL | || { + | ^^ +note: required because it appears within the type `Opaque(DefId(0:39 ~ generator_print_verbose_1[749a]::make_gen2::{opaque#0}), [std::sync::Arc<std::cell::RefCell<i32>>])` + --> $DIR/generator-print-verbose-1.rs:41:30 + | +LL | pub fn make_gen2<T>(t: T) -> impl Generator<Return = T> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: required because it appears within the type `Opaque(DefId(0:42 ~ generator_print_verbose_1[749a]::make_non_send_generator2::{opaque#0}), [])` + --> $DIR/generator-print-verbose-1.rs:47:34 + | +LL | fn make_non_send_generator2() -> impl Generator<Return = Arc<RefCell<i32>>> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: required because it captures the following types: `Opaque(DefId(0:42 ~ generator_print_verbose_1[749a]::make_non_send_generator2::{opaque#0}), [])`, `()` +note: required because it's used within this generator + --> $DIR/generator-print-verbose-1.rs:52:20 + | +LL | let send_gen = || { + | ^^ +note: required by a bound in `require_send` + --> $DIR/generator-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/src/test/ui/generator/print/generator-print-verbose-2.rs b/src/test/ui/generator/print/generator-print-verbose-2.rs new file mode 100644 index 000000000..d914719cb --- /dev/null +++ b/src/test/ui/generator/print/generator-print-verbose-2.rs @@ -0,0 +1,24 @@ +// compile-flags: -Zverbose + +// Same as test/ui/generator/not-send-sync.rs +#![feature(generators)] + +use std::cell::Cell; + +fn main() { + fn assert_sync<T: Sync>(_: T) {} + fn assert_send<T: Send>(_: T) {} + + assert_sync(|| { + //~^ ERROR: generator cannot be shared between threads safely + let a = Cell::new(2); + yield; + }); + + let a = Cell::new(2); + assert_send(|| { + //~^ ERROR: E0277 + drop(&a); + yield; + }); +} diff --git a/src/test/ui/generator/print/generator-print-verbose-2.stderr b/src/test/ui/generator/print/generator-print-verbose-2.stderr new file mode 100644 index 000000000..eb79d2e6e --- /dev/null +++ b/src/test/ui/generator/print/generator-print-verbose-2.stderr @@ -0,0 +1,44 @@ +error[E0277]: `Cell<i32>` cannot be shared between threads safely + --> $DIR/generator-print-verbose-2.rs:19:5 + | +LL | assert_send(|| { + | ^^^^^^^^^^^ `Cell<i32>` cannot be shared between threads safely + | + = help: the trait `Sync` is not implemented for `Cell<i32>` + = note: required because of the requirements on the impl of `Send` for `&'_#4r Cell<i32>` +note: required because it's used within this generator + --> $DIR/generator-print-verbose-2.rs:19:17 + | +LL | assert_send(|| { + | ^^ +note: required by a bound in `assert_send` + --> $DIR/generator-print-verbose-2.rs:10:23 + | +LL | fn assert_send<T: Send>(_: T) {} + | ^^^^ required by this bound in `assert_send` + +error: generator cannot be shared between threads safely + --> $DIR/generator-print-verbose-2.rs:12:5 + | +LL | assert_sync(|| { + | ^^^^^^^^^^^ generator is not `Sync` + | + = help: within `[main::{closure#0} upvar_tys=() {Cell<i32>, ()}]`, the trait `Sync` is not implemented for `Cell<i32>` +note: generator is not `Sync` as this value is used across a yield + --> $DIR/generator-print-verbose-2.rs:15:9 + | +LL | let a = Cell::new(2); + | - has type `Cell<i32>` which is not `Sync` +LL | yield; + | ^^^^^ yield occurs here, with `a` maybe used later +LL | }); + | - `a` is later dropped here +note: required by a bound in `assert_sync` + --> $DIR/generator-print-verbose-2.rs:9:23 + | +LL | fn assert_sync<T: Sync>(_: T) {} + | ^^^^ required by this bound in `assert_sync` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/generator/print/generator-print-verbose-3.rs b/src/test/ui/generator/print/generator-print-verbose-3.rs new file mode 100644 index 000000000..8689539ec --- /dev/null +++ b/src/test/ui/generator/print/generator-print-verbose-3.rs @@ -0,0 +1,12 @@ +// compile-flags: -Zverbose + +#![feature(generators, generator_trait)] + +fn main() { + let x = "Type mismatch test"; + let generator :() = || { + //~^ ERROR mismatched types + yield 1i32; + return x + }; +} diff --git a/src/test/ui/generator/print/generator-print-verbose-3.stderr b/src/test/ui/generator/print/generator-print-verbose-3.stderr new file mode 100644 index 000000000..d15646259 --- /dev/null +++ b/src/test/ui/generator/print/generator-print-verbose-3.stderr @@ -0,0 +1,19 @@ +error[E0308]: mismatched types + --> $DIR/generator-print-verbose-3.rs:7:25 + | +LL | let generator :() = || { + | ____________________--___^ + | | | + | | expected due to this +LL | | +LL | | yield 1i32; +LL | | return x +LL | | }; + | |_____^ expected `()`, found generator + | + = note: expected unit type `()` + found generator `[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/src/test/ui/generator/reborrow-mut-upvar.rs b/src/test/ui/generator/reborrow-mut-upvar.rs new file mode 100644 index 000000000..dbd9e24e2 --- /dev/null +++ b/src/test/ui/generator/reborrow-mut-upvar.rs @@ -0,0 +1,16 @@ +// run-pass + +#![feature(generators)] + +fn _run(bar: &mut i32) { + || { //~ WARN unused generator that must be used + { + let _baz = &*bar; + yield; + } + + *bar = 2; + }; +} + +fn main() {} diff --git a/src/test/ui/generator/reborrow-mut-upvar.stderr b/src/test/ui/generator/reborrow-mut-upvar.stderr new file mode 100644 index 000000000..ff511b766 --- /dev/null +++ b/src/test/ui/generator/reborrow-mut-upvar.stderr @@ -0,0 +1,17 @@ +warning: unused generator that must be used + --> $DIR/reborrow-mut-upvar.rs:6:5 + | +LL | / || { +LL | | { +LL | | let _baz = &*bar; +LL | | yield; +... | +LL | | *bar = 2; +LL | | }; + | |______^ + | + = note: `#[warn(unused_must_use)]` on by default + = note: generators are lazy and do nothing unless resumed + +warning: 1 warning emitted + diff --git a/src/test/ui/generator/ref-escapes-but-not-over-yield.rs b/src/test/ui/generator/ref-escapes-but-not-over-yield.rs new file mode 100644 index 000000000..3856d8233 --- /dev/null +++ b/src/test/ui/generator/ref-escapes-but-not-over-yield.rs @@ -0,0 +1,16 @@ +#![feature(generators)] + +fn foo(x: &i32) { + // In this case, a reference to `b` escapes the generator, 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 generator + }; +} + +fn main() { } diff --git a/src/test/ui/generator/ref-escapes-but-not-over-yield.stderr b/src/test/ui/generator/ref-escapes-but-not-over-yield.stderr new file mode 100644 index 000000000..5fc810040 --- /dev/null +++ b/src/test/ui/generator/ref-escapes-but-not-over-yield.stderr @@ -0,0 +1,15 @@ +error[E0521]: borrowed data escapes outside of generator + --> $DIR/ref-escapes-but-not-over-yield.rs:11:9 + | +LL | let mut a = &3; + | ----- `a` declared here, outside of the generator body +... +LL | a = &b; + | ^^^^-- + | | | + | | borrow is only valid in the generator body + | reference to `b` escapes the generator body here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0521`. diff --git a/src/test/ui/generator/reinit-in-match-guard.rs b/src/test/ui/generator/reinit-in-match-guard.rs new file mode 100644 index 000000000..260b341a5 --- /dev/null +++ b/src/test/ui/generator/reinit-in-match-guard.rs @@ -0,0 +1,25 @@ +// build-pass + +#![feature(generators)] + +#![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/src/test/ui/generator/resume-after-return.rs b/src/test/ui/generator/resume-after-return.rs new file mode 100644 index 000000000..538609b98 --- /dev/null +++ b/src/test/ui/generator/resume-after-return.rs @@ -0,0 +1,29 @@ +// run-pass +// needs-unwind + +// ignore-wasm32-bare compiled with panic=abort by default + +#![feature(generators, generator_trait)] + +use std::ops::{GeneratorState, Generator}; +use std::pin::Pin; +use std::panic; + +fn main() { + let mut foo = || { + if true { + return + } + yield; + }; + + match Pin::new(&mut foo).resume(()) { + GeneratorState::Complete(()) => {} + s => panic!("bad state: {:?}", s), + } + + match panic::catch_unwind(move || Pin::new(&mut foo).resume(())) { + Ok(_) => panic!("generator successfully resumed"), + Err(_) => {} + } +} diff --git a/src/test/ui/generator/resume-arg-late-bound.rs b/src/test/ui/generator/resume-arg-late-bound.rs new file mode 100644 index 000000000..1c35ba80d --- /dev/null +++ b/src/test/ui/generator/resume-arg-late-bound.rs @@ -0,0 +1,17 @@ +//! Tests that we cannot produce a generator that accepts a resume argument +//! with any lifetime and then stores it across a `yield`. + +#![feature(generators, generator_trait)] + +use std::ops::Generator; + +fn test(a: impl for<'a> Generator<&'a mut bool>) {} + +fn main() { + let gen = |arg: &mut bool| { + yield (); + *arg = true; + }; + test(gen); + //~^ ERROR mismatched types +} diff --git a/src/test/ui/generator/resume-arg-late-bound.stderr b/src/test/ui/generator/resume-arg-late-bound.stderr new file mode 100644 index 000000000..34ee4036c --- /dev/null +++ b/src/test/ui/generator/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> Generator<&'a mut bool>` + found trait `Generator<&mut bool>` +note: the lifetime requirement is introduced here + --> $DIR/resume-arg-late-bound.rs:8:17 + | +LL | fn test(a: impl for<'a> Generator<&'a mut bool>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/generator/resume-arg-size.rs b/src/test/ui/generator/resume-arg-size.rs new file mode 100644 index 000000000..b93dc54f7 --- /dev/null +++ b/src/test/ui/generator/resume-arg-size.rs @@ -0,0 +1,28 @@ +#![feature(generators)] + +// run-pass + +use std::mem::size_of_val; + +fn main() { + // Generator taking a `Copy`able resume arg. + let gen_copy = |mut x: usize| { + loop { + drop(x); + x = yield; + } + }; + + // Generator taking a non-`Copy` resume arg. + let gen_move = |mut x: Box<usize>| { + loop { + drop(x); + x = yield; + } + }; + + // Neither of these generators 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/src/test/ui/generator/resume-live-across-yield.rs b/src/test/ui/generator/resume-live-across-yield.rs new file mode 100644 index 000000000..4c4cf117a --- /dev/null +++ b/src/test/ui/generator/resume-live-across-yield.rs @@ -0,0 +1,45 @@ +// run-pass + +#![feature(generators, generator_trait)] + +use std::ops::{Generator, GeneratorState}; +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!"))), + GeneratorState::Yielded(()) + ); + assert_eq!(DROP.load(Ordering::Acquire), 0); + match g.as_mut().resume(Dropper(String::from("Number Two"))) { + GeneratorState::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/src/test/ui/generator/retain-resume-ref.rs b/src/test/ui/generator/retain-resume-ref.rs new file mode 100644 index 000000000..0606ea71c --- /dev/null +++ b/src/test/ui/generator/retain-resume-ref.rs @@ -0,0 +1,25 @@ +//! This test ensures that a mutable reference cannot be passed as a resume argument twice. + +#![feature(generators, generator_trait)] + +use std::marker::Unpin; +use std::ops::{ + Generator, + GeneratorState::{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/src/test/ui/generator/retain-resume-ref.stderr b/src/test/ui/generator/retain-resume-ref.stderr new file mode 100644 index 000000000..e33310d12 --- /dev/null +++ b/src/test/ui/generator/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/src/test/ui/generator/size-moved-locals.rs b/src/test/ui/generator/size-moved-locals.rs new file mode 100644 index 000000000..3c756a86f --- /dev/null +++ b/src/test/ui/generator/size-moved-locals.rs @@ -0,0 +1,77 @@ +// 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` generator 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 + +#![feature(generators, generator_trait)] + +use std::ops::Generator; + +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 Generator<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 Generator<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 Generator<Yield = (), Return = ()> { + static || { + let first = Foo([0; FOO_SIZE]); + yield; + let second = first; + yield; + let _third = second; + yield; + } +} + +fn overlap_x_and_y() -> impl Generator<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/src/test/ui/generator/sized-yield.rs b/src/test/ui/generator/sized-yield.rs new file mode 100644 index 000000000..c6dd738d6 --- /dev/null +++ b/src/test/ui/generator/sized-yield.rs @@ -0,0 +1,14 @@ +#![feature(generators, generator_trait)] + +use std::ops::Generator; +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/src/test/ui/generator/sized-yield.stderr b/src/test/ui/generator/sized-yield.stderr new file mode 100644 index 000000000..ea2a48d13 --- /dev/null +++ b/src/test/ui/generator/sized-yield.stderr @@ -0,0 +1,29 @@ +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/sized-yield.rs:8:26 + | +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 generator 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:23 + | +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 `GeneratorState` + --> $SRC_DIR/core/src/ops/generator.rs:LL:COL + | +LL | pub enum GeneratorState<Y, R> { + | ^ required by this bound in `GeneratorState` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/generator/smoke-resume-args.rs b/src/test/ui/generator/smoke-resume-args.rs new file mode 100644 index 000000000..fa9271c53 --- /dev/null +++ b/src/test/ui/generator/smoke-resume-args.rs @@ -0,0 +1,100 @@ +// run-pass + +// revisions: default nomiropt +//[nomiropt]compile-flags: -Z mir-opt-level=0 + +#![feature(generators, generator_trait)] + +use std::fmt::Debug; +use std::marker::Unpin; +use std::ops::{ + Generator, + GeneratorState::{self, *}, +}; +use std::pin::Pin; +use std::sync::atomic::{AtomicUsize, Ordering}; + +fn drain<G: Generator<R, Yield = Y> + Unpin, R, Y>( + gen: &mut G, + inout: Vec<(R, GeneratorState<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/src/test/ui/generator/smoke.rs b/src/test/ui/generator/smoke.rs new file mode 100644 index 000000000..7a917a05d --- /dev/null +++ b/src/test/ui/generator/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(generators, generator_trait)] + +use std::ops::{GeneratorState, Generator}; +use std::pin::Pin; +use std::thread; + +#[test] +fn simple() { + let mut foo = || { + if false { + yield; + } + }; + + match Pin::new(&mut foo).resume(()) { + GeneratorState::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(()) { + GeneratorState::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(()) { + GeneratorState::Yielded(()) => {} + s => panic!("bad state: {:?}", s), + } + match Pin::new(&mut foo).resume(()) { + GeneratorState::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(()) { + GeneratorState::Yielded(ref s) if *s == "foo" => {} + s => panic!("bad state: {:?}", s), + } + match Pin::new(&mut foo).resume(()) { + GeneratorState::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(()) { + GeneratorState::Yielded(ref s) if *s == "bar" => {} + s => panic!("bad state: {:?}", s), + } + match Pin::new(&mut foo).resume(()) { + GeneratorState::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(()) { + GeneratorState::Yielded(()) => {} + s => panic!("bad state: {:?}", s), + } + match Pin::new(&mut foo).resume(()) { + GeneratorState::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(()) { + GeneratorState::Yielded(()) => {} + s => panic!("bad state: {:?}", s), + } + match Pin::new(&mut foo).resume(()) { + GeneratorState::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(()) { + GeneratorState::Yielded(ref s) if *s == "a" => {} + s => panic!("bad state: {:?}", s), + } + match Pin::new(&mut foo).resume(()) { + GeneratorState::Complete(()) => {} + s => panic!("bad state: {:?}", s), + } + }).join().unwrap(); +} diff --git a/src/test/ui/generator/static-generators.rs b/src/test/ui/generator/static-generators.rs new file mode 100644 index 000000000..d098bf1e6 --- /dev/null +++ b/src/test/ui/generator/static-generators.rs @@ -0,0 +1,20 @@ +// run-pass + +#![feature(generators, generator_trait)] + +use std::pin::Pin; +use std::ops::{Generator, GeneratorState}; + +fn main() { + let mut generator = static || { + let a = true; + let b = &a; + yield; + assert_eq!(b as *const _, &a as *const _); + }; + // SAFETY: We shadow the original generator variable so have no safe API to + // move it after this point. + let mut generator = unsafe { Pin::new_unchecked(&mut generator) }; + assert_eq!(generator.as_mut().resume(()), GeneratorState::Yielded(())); + assert_eq!(generator.as_mut().resume(()), GeneratorState::Complete(())); +} diff --git a/src/test/ui/generator/static-mut-reference-across-yield.rs b/src/test/ui/generator/static-mut-reference-across-yield.rs new file mode 100644 index 000000000..0fa6d9cdc --- /dev/null +++ b/src/test/ui/generator/static-mut-reference-across-yield.rs @@ -0,0 +1,32 @@ +// build-pass +// revisions: mir thir +// [thir]compile-flags: -Zthir-unsafeck + +#![feature(generators)] + +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/src/test/ui/generator/static-not-unpin.rs b/src/test/ui/generator/static-not-unpin.rs new file mode 100644 index 000000000..cfcb94737 --- /dev/null +++ b/src/test/ui/generator/static-not-unpin.rs @@ -0,0 +1,15 @@ +#![feature(generators)] + +// normalize-stderr-test "std::pin::Unpin" -> "std::marker::Unpin" + +use std::marker::Unpin; + +fn assert_unpin<T: Unpin>(_: T) { +} + +fn main() { + let mut generator = static || { + yield; + }; + assert_unpin(generator); //~ ERROR E0277 +} diff --git a/src/test/ui/generator/static-not-unpin.stderr b/src/test/ui/generator/static-not-unpin.stderr new file mode 100644 index 000000000..e3859595f --- /dev/null +++ b/src/test/ui/generator/static-not-unpin.stderr @@ -0,0 +1,18 @@ +error[E0277]: `[static generator@$DIR/static-not-unpin.rs:11:25: 11:34]` cannot be unpinned + --> $DIR/static-not-unpin.rs:14:18 + | +LL | assert_unpin(generator); + | ------------ ^^^^^^^^^ the trait `Unpin` is not implemented for `[static generator@$DIR/static-not-unpin.rs:11:25: 11:34]` + | | + | required by a bound introduced by this call + | + = note: consider using `Box::pin` +note: required by a bound in `assert_unpin` + --> $DIR/static-not-unpin.rs:7: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/src/test/ui/generator/static-reference-across-yield.rs b/src/test/ui/generator/static-reference-across-yield.rs new file mode 100644 index 000000000..23b11593b --- /dev/null +++ b/src/test/ui/generator/static-reference-across-yield.rs @@ -0,0 +1,16 @@ +// build-pass +#![feature(generators)] + +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/src/test/ui/generator/too-live-local-in-immovable-gen.rs b/src/test/ui/generator/too-live-local-in-immovable-gen.rs new file mode 100644 index 000000000..e0b856db7 --- /dev/null +++ b/src/test/ui/generator/too-live-local-in-immovable-gen.rs @@ -0,0 +1,21 @@ +// run-pass +#![allow(unused_unsafe)] + +#![feature(generators)] + +fn main() { + unsafe { + static move || { //~ WARN unused generator that must be used + // Tests that the generator 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 generator transformation finds that + // `a` is live and type checking finds it dead. + let a = { + yield (); + 4i32 + }; + let _ = &a; + }; + } +} diff --git a/src/test/ui/generator/too-live-local-in-immovable-gen.stderr b/src/test/ui/generator/too-live-local-in-immovable-gen.stderr new file mode 100644 index 000000000..72a2bd4eb --- /dev/null +++ b/src/test/ui/generator/too-live-local-in-immovable-gen.stderr @@ -0,0 +1,17 @@ +warning: unused generator that must be used + --> $DIR/too-live-local-in-immovable-gen.rs:8:9 + | +LL | / static move || { +LL | | // Tests that the generator 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: `#[warn(unused_must_use)]` on by default + = note: generators are lazy and do nothing unless resumed + +warning: 1 warning emitted + diff --git a/src/test/ui/generator/too-many-parameters.rs b/src/test/ui/generator/too-many-parameters.rs new file mode 100644 index 000000000..7a353ea29 --- /dev/null +++ b/src/test/ui/generator/too-many-parameters.rs @@ -0,0 +1,8 @@ +#![feature(generators)] + +fn main() { + |(), ()| { + //~^ error: too many parameters for a generator + yield; + }; +} diff --git a/src/test/ui/generator/too-many-parameters.stderr b/src/test/ui/generator/too-many-parameters.stderr new file mode 100644 index 000000000..22d40db3f --- /dev/null +++ b/src/test/ui/generator/too-many-parameters.stderr @@ -0,0 +1,9 @@ +error[E0628]: too many parameters for a generator (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/src/test/ui/generator/type-mismatch-error.rs b/src/test/ui/generator/type-mismatch-error.rs new file mode 100644 index 000000000..d39c788a8 --- /dev/null +++ b/src/test/ui/generator/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(generators, generator_trait)] + +use std::ops::Generator; + +fn f<G: Generator>(_: G, _: G::Return) {} + +fn main() { + f( + |a: u8| { + if false { + yield (); + } else { + a + //~^ error: `if` and `else` have incompatible types + } + }, + 0u8, + ); +} diff --git a/src/test/ui/generator/type-mismatch-error.stderr b/src/test/ui/generator/type-mismatch-error.stderr new file mode 100644 index 000000000..8f5949533 --- /dev/null +++ b/src/test/ui/generator/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/src/test/ui/generator/type-mismatch-signature-deduction.rs b/src/test/ui/generator/type-mismatch-signature-deduction.rs new file mode 100644 index 000000000..8d1ce6c7a --- /dev/null +++ b/src/test/ui/generator/type-mismatch-signature-deduction.rs @@ -0,0 +1,18 @@ +#![feature(generators, generator_trait)] + +use std::ops::Generator; + +fn foo() -> impl Generator<Return = i32> { + //~^ ERROR type mismatch + || { + if false { + return Ok(6); + } + + yield (); + + 5 //~ ERROR mismatched types [E0308] + } +} + +fn main() {} diff --git a/src/test/ui/generator/type-mismatch-signature-deduction.stderr b/src/test/ui/generator/type-mismatch-signature-deduction.stderr new file mode 100644 index 000000000..7938fc809 --- /dev/null +++ b/src/test/ui/generator/type-mismatch-signature-deduction.stderr @@ -0,0 +1,27 @@ +error[E0308]: mismatched types + --> $DIR/type-mismatch-signature-deduction.rs:14:9 + | +LL | 5 + | ^ expected enum `Result`, 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); + | ^^^^^ + +error[E0271]: type mismatch resolving `<[generator@$DIR/type-mismatch-signature-deduction.rs:7:5: 7:7] as Generator>::Return == i32` + --> $DIR/type-mismatch-signature-deduction.rs:5:13 + | +LL | fn foo() -> impl Generator<Return = i32> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `Result`, 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/src/test/ui/generator/xcrate-reachable.rs b/src/test/ui/generator/xcrate-reachable.rs new file mode 100644 index 000000000..1b1cff338 --- /dev/null +++ b/src/test/ui/generator/xcrate-reachable.rs @@ -0,0 +1,14 @@ +// run-pass + +// aux-build:xcrate-reachable.rs + +#![feature(generator_trait)] + +extern crate xcrate_reachable as foo; + +use std::ops::Generator; +use std::pin::Pin; + +fn main() { + Pin::new(&mut foo::foo()).resume(()); +} diff --git a/src/test/ui/generator/xcrate.rs b/src/test/ui/generator/xcrate.rs new file mode 100644 index 000000000..40986bbeb --- /dev/null +++ b/src/test/ui/generator/xcrate.rs @@ -0,0 +1,30 @@ +// run-pass + +// aux-build:xcrate.rs + +#![feature(generators, generator_trait)] + +extern crate xcrate; + +use std::ops::{GeneratorState, Generator}; +use std::pin::Pin; + +fn main() { + let mut foo = xcrate::foo(); + + match Pin::new(&mut foo).resume(()) { + GeneratorState::Complete(()) => {} + s => panic!("bad state: {:?}", s), + } + + let mut foo = xcrate::bar(3); + + match Pin::new(&mut foo).resume(()) { + GeneratorState::Yielded(3) => {} + s => panic!("bad state: {:?}", s), + } + match Pin::new(&mut foo).resume(()) { + GeneratorState::Complete(()) => {} + s => panic!("bad state: {:?}", s), + } +} diff --git a/src/test/ui/generator/yield-in-args-rev.rs b/src/test/ui/generator/yield-in-args-rev.rs new file mode 100644 index 000000000..4c99bb3ef --- /dev/null +++ b/src/test/ui/generator/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(generators)] + +fn foo(_a: (), _b: &bool) {} + +fn bar() { + || { //~ WARN unused generator that must be used + let b = true; + foo(yield, &b); + }; +} + +fn main() { } diff --git a/src/test/ui/generator/yield-in-args-rev.stderr b/src/test/ui/generator/yield-in-args-rev.stderr new file mode 100644 index 000000000..a575bf886 --- /dev/null +++ b/src/test/ui/generator/yield-in-args-rev.stderr @@ -0,0 +1,14 @@ +warning: unused generator that must be used + --> $DIR/yield-in-args-rev.rs:13:5 + | +LL | / || { +LL | | let b = true; +LL | | foo(yield, &b); +LL | | }; + | |______^ + | + = note: `#[warn(unused_must_use)]` on by default + = note: generators are lazy and do nothing unless resumed + +warning: 1 warning emitted + diff --git a/src/test/ui/generator/yield-in-args.rs b/src/test/ui/generator/yield-in-args.rs new file mode 100644 index 000000000..80110af55 --- /dev/null +++ b/src/test/ui/generator/yield-in-args.rs @@ -0,0 +1,10 @@ +#![feature(generators)] + +fn foo(_b: &bool, _a: ()) {} + +fn main() { + || { + let b = true; + foo(&b, yield); //~ ERROR + }; +} diff --git a/src/test/ui/generator/yield-in-args.stderr b/src/test/ui/generator/yield-in-args.stderr new file mode 100644 index 000000000..ee6d22c27 --- /dev/null +++ b/src/test/ui/generator/yield-in-args.stderr @@ -0,0 +1,9 @@ +error[E0626]: borrow may still be in use when generator 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/src/test/ui/generator/yield-in-box.rs b/src/test/ui/generator/yield-in-box.rs new file mode 100644 index 000000000..dd6fa7c15 --- /dev/null +++ b/src/test/ui/generator/yield-in-box.rs @@ -0,0 +1,24 @@ +// run-pass +// Test that box-statements with yields in them work. + +#![feature(generators, box_syntax, generator_trait)] +use std::pin::Pin; +use std::ops::Generator; +use std::ops::GeneratorState; + +fn main() { + let x = 0i32; + || { //~ WARN unused generator that must be used + let y = 2u32; + { + let _t = box (&x, yield 0, &y); + } + match box (&x, yield 0, &y) { + _t => {} + } + }; + + let mut g = |_| box yield; + assert_eq!(Pin::new(&mut g).resume(1), GeneratorState::Yielded(())); + assert_eq!(Pin::new(&mut g).resume(2), GeneratorState::Complete(box 2)); +} diff --git a/src/test/ui/generator/yield-in-box.stderr b/src/test/ui/generator/yield-in-box.stderr new file mode 100644 index 000000000..7602e8039 --- /dev/null +++ b/src/test/ui/generator/yield-in-box.stderr @@ -0,0 +1,17 @@ +warning: unused generator that must be used + --> $DIR/yield-in-box.rs:11:5 + | +LL | / || { +LL | | let y = 2u32; +LL | | { +LL | | let _t = box (&x, yield 0, &y); +... | +LL | | } +LL | | }; + | |______^ + | + = note: `#[warn(unused_must_use)]` on by default + = note: generators are lazy and do nothing unless resumed + +warning: 1 warning emitted + diff --git a/src/test/ui/generator/yield-in-const.rs b/src/test/ui/generator/yield-in-const.rs new file mode 100644 index 000000000..fe5ca822c --- /dev/null +++ b/src/test/ui/generator/yield-in-const.rs @@ -0,0 +1,6 @@ +#![feature(generators)] + +const A: u8 = { yield 3u8; 3u8}; +//~^ ERROR yield expression outside + +fn main() {} diff --git a/src/test/ui/generator/yield-in-const.stderr b/src/test/ui/generator/yield-in-const.stderr new file mode 100644 index 000000000..dcf4fe63e --- /dev/null +++ b/src/test/ui/generator/yield-in-const.stderr @@ -0,0 +1,9 @@ +error[E0627]: yield expression outside of generator 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/src/test/ui/generator/yield-in-function.rs b/src/test/ui/generator/yield-in-function.rs new file mode 100644 index 000000000..29b811621 --- /dev/null +++ b/src/test/ui/generator/yield-in-function.rs @@ -0,0 +1,4 @@ +#![feature(generators)] + +fn main() { yield; } +//~^ ERROR yield expression outside diff --git a/src/test/ui/generator/yield-in-function.stderr b/src/test/ui/generator/yield-in-function.stderr new file mode 100644 index 000000000..51cce198c --- /dev/null +++ b/src/test/ui/generator/yield-in-function.stderr @@ -0,0 +1,9 @@ +error[E0627]: yield expression outside of generator 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/src/test/ui/generator/yield-in-initializer.rs b/src/test/ui/generator/yield-in-initializer.rs new file mode 100644 index 000000000..0cab36e5f --- /dev/null +++ b/src/test/ui/generator/yield-in-initializer.rs @@ -0,0 +1,17 @@ +// run-pass + +#![feature(generators)] + +fn main() { + static || { //~ WARN unused generator 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/src/test/ui/generator/yield-in-initializer.stderr b/src/test/ui/generator/yield-in-initializer.stderr new file mode 100644 index 000000000..e79047ae7 --- /dev/null +++ b/src/test/ui/generator/yield-in-initializer.stderr @@ -0,0 +1,17 @@ +warning: unused generator 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: `#[warn(unused_must_use)]` on by default + = note: generators are lazy and do nothing unless resumed + +warning: 1 warning emitted + diff --git a/src/test/ui/generator/yield-in-static.rs b/src/test/ui/generator/yield-in-static.rs new file mode 100644 index 000000000..d27fbb33b --- /dev/null +++ b/src/test/ui/generator/yield-in-static.rs @@ -0,0 +1,6 @@ +#![feature(generators)] + +static B: u8 = { yield 3u8; 3u8}; +//~^ ERROR yield expression outside + +fn main() {} diff --git a/src/test/ui/generator/yield-in-static.stderr b/src/test/ui/generator/yield-in-static.stderr new file mode 100644 index 000000000..d867f3ad3 --- /dev/null +++ b/src/test/ui/generator/yield-in-static.stderr @@ -0,0 +1,9 @@ +error[E0627]: yield expression outside of generator 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/src/test/ui/generator/yield-outside-generator-issue-78653.rs b/src/test/ui/generator/yield-outside-generator-issue-78653.rs new file mode 100644 index 000000000..4e8050c81 --- /dev/null +++ b/src/test/ui/generator/yield-outside-generator-issue-78653.rs @@ -0,0 +1,7 @@ +#![feature(generators)] + +fn main() { + yield || for i in 0 { } + //~^ ERROR yield expression outside of generator literal + //~| ERROR `{integer}` is not an iterator +} diff --git a/src/test/ui/generator/yield-outside-generator-issue-78653.stderr b/src/test/ui/generator/yield-outside-generator-issue-78653.stderr new file mode 100644 index 000000000..ee1afbe5b --- /dev/null +++ b/src/test/ui/generator/yield-outside-generator-issue-78653.stderr @@ -0,0 +1,20 @@ +error[E0627]: yield expression outside of generator literal + --> $DIR/yield-outside-generator-issue-78653.rs:4:5 + | +LL | yield || for i in 0 { } + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: `{integer}` is not an iterator + --> $DIR/yield-outside-generator-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 because of the requirements on the impl of `IntoIterator` for `{integer}` + +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/src/test/ui/generator/yield-subtype.rs b/src/test/ui/generator/yield-subtype.rs new file mode 100644 index 000000000..cb3fc9091 --- /dev/null +++ b/src/test/ui/generator/yield-subtype.rs @@ -0,0 +1,17 @@ +// run-pass +#![allow(dead_code)] +#![allow(dead_code)] + +#![feature(generators)] + +fn bar<'a>() { + let a: &'static str = "hi"; + let b: &'a str = a; + + || { //~ WARN unused generator that must be used + yield a; + yield b; + }; +} + +fn main() {} diff --git a/src/test/ui/generator/yield-subtype.stderr b/src/test/ui/generator/yield-subtype.stderr new file mode 100644 index 000000000..bded36a4c --- /dev/null +++ b/src/test/ui/generator/yield-subtype.stderr @@ -0,0 +1,14 @@ +warning: unused generator that must be used + --> $DIR/yield-subtype.rs:11:5 + | +LL | / || { +LL | | yield a; +LL | | yield b; +LL | | }; + | |______^ + | + = note: `#[warn(unused_must_use)]` on by default + = note: generators are lazy and do nothing unless resumed + +warning: 1 warning emitted + diff --git a/src/test/ui/generator/yield-while-iterating.rs b/src/test/ui/generator/yield-while-iterating.rs new file mode 100644 index 000000000..985e5d8bd --- /dev/null +++ b/src/test/ui/generator/yield-while-iterating.rs @@ -0,0 +1,75 @@ +#![feature(generators, generator_trait)] + +use std::ops::{GeneratorState, Generator}; +use std::cell::Cell; +use std::pin::Pin; + +fn yield_during_iter_owned_data(x: Vec<i32>) { + // The generator 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/src/test/ui/generator/yield-while-iterating.stderr b/src/test/ui/generator/yield-while-iterating.stderr new file mode 100644 index 000000000..b65634752 --- /dev/null +++ b/src/test/ui/generator/yield-while-iterating.stderr @@ -0,0 +1,25 @@ +error[E0626]: borrow may still be in use when generator 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 generator +... +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/src/test/ui/generator/yield-while-local-borrowed.rs b/src/test/ui/generator/yield-while-local-borrowed.rs new file mode 100644 index 000000000..061a64dbc --- /dev/null +++ b/src/test/ui/generator/yield-while-local-borrowed.rs @@ -0,0 +1,49 @@ +#![feature(generators, generator_trait)] + +use std::ops::{GeneratorState, Generator}; +use std::cell::Cell; +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 generator 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 generator yields + yield(); + println!("{}", b); + } + }; + Pin::new(&mut b).resume(()); +} + +fn main() { } diff --git a/src/test/ui/generator/yield-while-local-borrowed.stderr b/src/test/ui/generator/yield-while-local-borrowed.stderr new file mode 100644 index 000000000..c1513ef9b --- /dev/null +++ b/src/test/ui/generator/yield-while-local-borrowed.stderr @@ -0,0 +1,21 @@ +error[E0626]: borrow may still be in use when generator 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 generator 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/src/test/ui/generator/yield-while-ref-reborrowed.rs b/src/test/ui/generator/yield-while-ref-reborrowed.rs new file mode 100644 index 000000000..a03ef945d --- /dev/null +++ b/src/test/ui/generator/yield-while-ref-reborrowed.rs @@ -0,0 +1,40 @@ +#![feature(generators, generator_trait)] + +use std::ops::{GeneratorState, Generator}; +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 generator. + 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 generator. + 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/src/test/ui/generator/yield-while-ref-reborrowed.stderr b/src/test/ui/generator/yield-while-ref-reborrowed.stderr new file mode 100644 index 000000000..47147f9c0 --- /dev/null +++ b/src/test/ui/generator/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 = || { + | -- generator construction occurs here +LL | let a = &mut *x; + | -- first borrow occurs due to use of `x` in generator +... +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/src/test/ui/generator/yielding-in-match-guards.rs b/src/test/ui/generator/yielding-in-match-guards.rs new file mode 100644 index 000000000..4e89fc975 --- /dev/null +++ b/src/test/ui/generator/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 generator. + +#![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); +} |