Skip to content

Commit fca614e

Browse files
Add tests for generator resume arguments
1 parent 5b2059b commit fca614e

File tree

3 files changed

+135
-0
lines changed

3 files changed

+135
-0
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//! This test ensures that a mutable reference cannot be passed as a resume argument twice.
2+
3+
#![feature(generators, generator_trait)]
4+
5+
use std::marker::Unpin;
6+
use std::ops::{
7+
Generator,
8+
GeneratorState::{self, *},
9+
};
10+
use std::pin::Pin;
11+
12+
fn main() {
13+
let mut thing = String::from("hello");
14+
15+
let mut gen = |r| {
16+
if false {
17+
yield r;
18+
}
19+
};
20+
21+
let mut gen = Pin::new(&mut gen);
22+
gen.as_mut().resume(&mut thing);
23+
gen.as_mut().resume(&mut thing);
24+
//~^ cannot borrow `thing` as mutable more than once at a time
25+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
error[E0499]: cannot borrow `thing` as mutable more than once at a time
2+
--> $DIR/retain-resume-ref.rs:23:25
3+
|
4+
LL | gen.as_mut().resume(&mut thing);
5+
| ---------- first mutable borrow occurs here
6+
LL | gen.as_mut().resume(&mut thing);
7+
| ------ ^^^^^^^^^^ second mutable borrow occurs here
8+
| |
9+
| first borrow later used by call
10+
11+
error: aborting due to previous error
12+
13+
For more information about this error, try `rustc --explain E0499`.
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// run-pass
2+
3+
#![feature(generators, generator_trait)]
4+
5+
use std::fmt::Debug;
6+
use std::marker::Unpin;
7+
use std::ops::{
8+
Generator,
9+
GeneratorState::{self, *},
10+
};
11+
use std::pin::Pin;
12+
use std::sync::atomic::{AtomicUsize, Ordering};
13+
14+
fn drain<G: Generator<R, Yield = Y> + Unpin, R, Y>(
15+
gen: &mut G,
16+
inout: Vec<(R, GeneratorState<Y, G::Return>)>,
17+
) where
18+
Y: Debug + PartialEq,
19+
G::Return: Debug + PartialEq,
20+
{
21+
let mut gen = Pin::new(gen);
22+
23+
for (input, out) in inout {
24+
assert_eq!(gen.as_mut().resume(input), out);
25+
}
26+
}
27+
28+
static DROPS: AtomicUsize = AtomicUsize::new(0);
29+
30+
#[derive(Debug, PartialEq)]
31+
struct DropMe;
32+
33+
impl Drop for DropMe {
34+
fn drop(&mut self) {
35+
DROPS.fetch_add(1, Ordering::SeqCst);
36+
}
37+
}
38+
39+
fn expect_drops<T>(expected_drops: usize, f: impl FnOnce() -> T) -> T {
40+
DROPS.store(0, Ordering::SeqCst);
41+
42+
let res = f();
43+
44+
let actual_drops = DROPS.load(Ordering::SeqCst);
45+
assert_eq!(actual_drops, expected_drops);
46+
res
47+
}
48+
49+
fn main() {
50+
drain(
51+
&mut |mut b| {
52+
while b != 0 {
53+
b = yield (b + 1);
54+
}
55+
-1
56+
},
57+
vec![(1, Yielded(2)), (-45, Yielded(-44)), (500, Yielded(501)), (0, Complete(-1))],
58+
);
59+
60+
expect_drops(2, || drain(&mut |a| yield a, vec![(DropMe, Yielded(DropMe))]));
61+
62+
expect_drops(6, || {
63+
drain(
64+
&mut |a| yield yield a,
65+
vec![(DropMe, Yielded(DropMe)), (DropMe, Yielded(DropMe)), (DropMe, Complete(DropMe))],
66+
)
67+
});
68+
69+
#[allow(unreachable_code)]
70+
expect_drops(2, || drain(&mut |a| yield return a, vec![(DropMe, Complete(DropMe))]));
71+
72+
expect_drops(2, || {
73+
drain(
74+
&mut |a: DropMe| {
75+
if false { yield () } else { a }
76+
},
77+
vec![(DropMe, Complete(DropMe))],
78+
)
79+
});
80+
81+
expect_drops(4, || {
82+
drain(
83+
#[allow(unused_assignments, unused_variables)]
84+
&mut |mut a: DropMe| {
85+
a = yield;
86+
a = yield;
87+
a = yield;
88+
},
89+
vec![
90+
(DropMe, Yielded(())),
91+
(DropMe, Yielded(())),
92+
(DropMe, Yielded(())),
93+
(DropMe, Complete(())),
94+
],
95+
)
96+
});
97+
}

0 commit comments

Comments
 (0)