Skip to content

Commit d8a0dd7

Browse files
committed
Auto merge of #55704 - Nemo157:pinned-generators, r=Zoxc
Use pinning for generators to make trait safe I'm unsure whether there needs to be any changes to the actual generator transform. Tests are passing so the fact that `Pin<&mut T>` is fundamentally the same as `&mut T` seems to allow it to still work, but maybe there's something subtle here that could go wrong. This is specified in [RFC 2349 § Immovable generators](https://github.com/rust-lang/rfcs/blob/master/text/2349-pin.md#immovable-generators) (although, since that RFC it has become safe to create an immovable generator, and instead it's unsafe to resume any generator; with these changes both are now safe and instead the unsafety is moved to creating a `Pin<&mut [static generator]>` which there are safe APIs for). CC #43122
2 parents a21bd75 + a21c95f commit d8a0dd7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+395
-170
lines changed

Diff for: src/doc/unstable-book/src/language-features/generators.md

+18-13
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,19 @@ A syntactical example of a generator is:
2929
#![feature(generators, generator_trait)]
3030

3131
use std::ops::{Generator, GeneratorState};
32+
use std::pin::Pin;
3233

3334
fn main() {
3435
let mut generator = || {
3536
yield 1;
3637
return "foo"
3738
};
3839

39-
match unsafe { generator.resume() } {
40+
match Pin::new(&mut generator).resume() {
4041
GeneratorState::Yielded(1) => {}
4142
_ => panic!("unexpected value from resume"),
4243
}
43-
match unsafe { generator.resume() } {
44+
match Pin::new(&mut generator).resume() {
4445
GeneratorState::Complete("foo") => {}
4546
_ => panic!("unexpected value from resume"),
4647
}
@@ -60,6 +61,7 @@ prints all numbers in order:
6061
#![feature(generators, generator_trait)]
6162

6263
use std::ops::Generator;
64+
use std::pin::Pin;
6365

6466
fn main() {
6567
let mut generator = || {
@@ -69,9 +71,9 @@ fn main() {
6971
};
7072

7173
println!("1");
72-
unsafe { generator.resume() };
74+
Pin::new(&mut generator).resume();
7375
println!("3");
74-
unsafe { generator.resume() };
76+
Pin::new(&mut generator).resume();
7577
println!("5");
7678
}
7779
```
@@ -86,13 +88,14 @@ Feedback on the design and usage is always appreciated!
8688
The `Generator` trait in `std::ops` currently looks like:
8789

8890
```
89-
# #![feature(generator_trait)]
91+
# #![feature(arbitrary_self_types, generator_trait)]
9092
# use std::ops::GeneratorState;
93+
# use std::pin::Pin;
9194
9295
pub trait Generator {
9396
type Yield;
9497
type Return;
95-
unsafe fn resume(&mut self) -> GeneratorState<Self::Yield, Self::Return>;
98+
fn resume(self: Pin<&mut Self>) -> GeneratorState<Self::Yield, Self::Return>;
9699
}
97100
```
98101

@@ -167,6 +170,7 @@ Let's take a look at an example to see what's going on here:
167170
#![feature(generators, generator_trait)]
168171

169172
use std::ops::Generator;
173+
use std::pin::Pin;
170174

171175
fn main() {
172176
let ret = "foo";
@@ -175,17 +179,18 @@ fn main() {
175179
return ret
176180
};
177181

178-
unsafe { generator.resume() };
179-
unsafe { generator.resume() };
182+
Pin::new(&mut generator).resume();
183+
Pin::new(&mut generator).resume();
180184
}
181185
```
182186

183187
This generator literal will compile down to something similar to:
184188

185189
```rust
186-
#![feature(generators, generator_trait)]
190+
#![feature(arbitrary_self_types, generators, generator_trait)]
187191

188192
use std::ops::{Generator, GeneratorState};
193+
use std::pin::Pin;
189194

190195
fn main() {
191196
let ret = "foo";
@@ -200,9 +205,9 @@ fn main() {
200205
type Yield = i32;
201206
type Return = &'static str;
202207

203-
unsafe fn resume(&mut self) -> GeneratorState<i32, &'static str> {
208+
fn resume(mut self: Pin<&mut Self>) -> GeneratorState<i32, &'static str> {
204209
use std::mem;
205-
match mem::replace(self, __Generator::Done) {
210+
match mem::replace(&mut *self, __Generator::Done) {
206211
__Generator::Start(s) => {
207212
*self = __Generator::Yield1(s);
208213
GeneratorState::Yielded(1)
@@ -223,8 +228,8 @@ fn main() {
223228
__Generator::Start(ret)
224229
};
225230

226-
unsafe { generator.resume() };
227-
unsafe { generator.resume() };
231+
Pin::new(&mut generator).resume();
232+
Pin::new(&mut generator).resume();
228233
}
229234
```
230235

Diff for: src/liballoc/boxed.rs

+16-7
Original file line numberDiff line numberDiff line change
@@ -873,13 +873,22 @@ impl<T: ?Sized> AsMut<T> for Box<T> {
873873
impl<T: ?Sized> Unpin for Box<T> { }
874874

875875
#[unstable(feature = "generator_trait", issue = "43122")]
876-
impl<T> Generator for Box<T>
877-
where T: Generator + ?Sized
878-
{
879-
type Yield = T::Yield;
880-
type Return = T::Return;
881-
unsafe fn resume(&mut self) -> GeneratorState<Self::Yield, Self::Return> {
882-
(**self).resume()
876+
impl<G: ?Sized + Generator + Unpin> Generator for Box<G> {
877+
type Yield = G::Yield;
878+
type Return = G::Return;
879+
880+
fn resume(mut self: Pin<&mut Self>) -> GeneratorState<Self::Yield, Self::Return> {
881+
G::resume(Pin::new(&mut *self))
882+
}
883+
}
884+
885+
#[unstable(feature = "generator_trait", issue = "43122")]
886+
impl<G: ?Sized + Generator> Generator for Pin<Box<G>> {
887+
type Yield = G::Yield;
888+
type Return = G::Return;
889+
890+
fn resume(mut self: Pin<&mut Self>) -> GeneratorState<Self::Yield, Self::Return> {
891+
G::resume((*self).as_mut())
883892
}
884893
}
885894

Diff for: src/libcore/marker.rs

+1
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,7 @@ unsafe impl<T: ?Sized> Freeze for &mut T {}
627627
/// [`Pin`]: ../pin/struct.Pin.html
628628
/// [`pin module`]: ../../std/pin/index.html
629629
#[stable(feature = "pin", since = "1.33.0")]
630+
#[cfg_attr(not(stage0), lang = "unpin")]
630631
pub auto trait Unpin {}
631632

632633
/// A marker type which does not implement `Unpin`.

Diff for: src/libcore/ops/generator.rs

+23-14
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
use crate::marker::Unpin;
2+
use crate::pin::Pin;
3+
14
/// The result of a generator resumption.
25
///
36
/// This enum is returned from the `Generator::resume` method and indicates the
@@ -39,18 +42,19 @@ pub enum GeneratorState<Y, R> {
3942
/// #![feature(generators, generator_trait)]
4043
///
4144
/// use std::ops::{Generator, GeneratorState};
45+
/// use std::pin::Pin;
4246
///
4347
/// fn main() {
4448
/// let mut generator = || {
4549
/// yield 1;
4650
/// return "foo"
4751
/// };
4852
///
49-
/// match unsafe { generator.resume() } {
53+
/// match Pin::new(&mut generator).resume() {
5054
/// GeneratorState::Yielded(1) => {}
5155
/// _ => panic!("unexpected return from resume"),
5256
/// }
53-
/// match unsafe { generator.resume() } {
57+
/// match Pin::new(&mut generator).resume() {
5458
/// GeneratorState::Complete("foo") => {}
5559
/// _ => panic!("unexpected return from resume"),
5660
/// }
@@ -88,10 +92,6 @@ pub trait Generator {
8892
/// generator will continue executing until it either yields or returns, at
8993
/// which point this function will return.
9094
///
91-
/// The function is unsafe because it can be used on an immovable generator.
92-
/// After such a call, the immovable generator must not move again, but
93-
/// this is not enforced by the compiler.
94-
///
9595
/// # Return value
9696
///
9797
/// The `GeneratorState` enum returned from this function indicates what
@@ -110,16 +110,25 @@ pub trait Generator {
110110
/// been returned previously. While generator literals in the language are
111111
/// guaranteed to panic on resuming after `Complete`, this is not guaranteed
112112
/// for all implementations of the `Generator` trait.
113-
unsafe fn resume(&mut self) -> GeneratorState<Self::Yield, Self::Return>;
113+
fn resume(self: Pin<&mut Self>) -> GeneratorState<Self::Yield, Self::Return>;
114+
}
115+
116+
#[unstable(feature = "generator_trait", issue = "43122")]
117+
impl<G: ?Sized + Generator> Generator for Pin<&mut G> {
118+
type Yield = G::Yield;
119+
type Return = G::Return;
120+
121+
fn resume(mut self: Pin<&mut Self>) -> GeneratorState<Self::Yield, Self::Return> {
122+
G::resume((*self).as_mut())
123+
}
114124
}
115125

116126
#[unstable(feature = "generator_trait", issue = "43122")]
117-
impl<T> Generator for &mut T
118-
where T: Generator + ?Sized
119-
{
120-
type Yield = T::Yield;
121-
type Return = T::Return;
122-
unsafe fn resume(&mut self) -> GeneratorState<Self::Yield, Self::Return> {
123-
(**self).resume()
127+
impl<G: ?Sized + Generator + Unpin> Generator for &mut G {
128+
type Yield = G::Yield;
129+
type Return = G::Return;
130+
131+
fn resume(mut self: Pin<&mut Self>) -> GeneratorState<Self::Yield, Self::Return> {
132+
G::resume(Pin::new(&mut *self))
124133
}
125134
}

Diff for: src/libcore/pin.rs

+1
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ use ops::{Deref, DerefMut, Receiver, CoerceUnsized, DispatchFromDyn};
117117
// implementations, are allowed because they all only use `&P`, so they cannot move
118118
// the value behind `pointer`.
119119
#[stable(feature = "pin", since = "1.33.0")]
120+
#[cfg_attr(not(stage0), lang = "pin")]
120121
#[fundamental]
121122
#[repr(transparent)]
122123
#[derive(Copy, Clone, Hash, Eq, Ord)]

Diff for: src/librustc/middle/lang_items.rs

+2
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,8 @@ language_item_table! {
299299

300300
GeneratorStateLangItem, "generator_state", gen_state, Target::Enum;
301301
GeneratorTraitLangItem, "generator", gen_trait, Target::Trait;
302+
UnpinTraitLangItem, "unpin", unpin_trait, Target::Trait;
303+
PinTypeLangItem, "pin", pin_type, Target::Struct;
302304

303305
EqTraitLangItem, "eq", eq_trait, Target::Trait;
304306
PartialOrdTraitLangItem, "partial_ord", partial_ord_trait, Target::Trait;

Diff for: src/librustc/traits/select.rs

+18
Original file line numberDiff line numberDiff line change
@@ -2017,6 +2017,24 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
20172017
// the auto impl might apply, we don't know
20182018
candidates.ambiguous = true;
20192019
}
2020+
ty::Generator(_, _, movability)
2021+
if self.tcx().lang_items().unpin_trait() == Some(def_id) =>
2022+
{
2023+
match movability {
2024+
hir::GeneratorMovability::Static => {
2025+
// Immovable generators are never `Unpin`, so
2026+
// suppress the normal auto-impl candidate for it.
2027+
}
2028+
hir::GeneratorMovability::Movable => {
2029+
// Movable generators are always `Unpin`, so add an
2030+
// unconditional builtin candidate.
2031+
candidates.vec.push(BuiltinCandidate {
2032+
has_nested: false,
2033+
});
2034+
}
2035+
}
2036+
}
2037+
20202038
_ => candidates.vec.push(AutoImplCandidate(def_id.clone())),
20212039
}
20222040
}

Diff for: src/librustc/ty/instance.rs

+5
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@ impl<'a, 'tcx> Instance<'tcx> {
7676
let env_region = ty::ReLateBound(ty::INNERMOST, ty::BrEnv);
7777
let env_ty = tcx.mk_mut_ref(tcx.mk_region(env_region), ty);
7878

79+
let pin_did = tcx.lang_items().pin_type().unwrap();
80+
let pin_adt_ref = tcx.adt_def(pin_did);
81+
let pin_substs = tcx.intern_substs(&[env_ty.into()]);
82+
let env_ty = tcx.mk_adt(pin_adt_ref, pin_substs);
83+
7984
sig.map_bound(|sig| {
8085
let state_did = tcx.lang_items().gen_state().unwrap();
8186
let state_adt_ref = tcx.adt_def(state_did);

Diff for: src/librustc_codegen_ssa/mir/mod.rs

+7
Original file line numberDiff line numberDiff line change
@@ -586,10 +586,17 @@ fn arg_local_refs<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>(
586586
return;
587587
}
588588

589+
let pin_did = tcx.lang_items().pin_type();
589590
// Or is it the closure environment?
590591
let (closure_layout, env_ref) = match arg.layout.ty.sty {
591592
ty::RawPtr(ty::TypeAndMut { ty, .. }) |
592593
ty::Ref(_, ty, _) => (bx.layout_of(ty), true),
594+
ty::Adt(def, substs) if Some(def.did) == pin_did => {
595+
match substs.type_at(0).sty {
596+
ty::Ref(_, ty, _) => (bx.layout_of(ty), true),
597+
_ => (arg.layout, false),
598+
}
599+
}
593600
_ => (arg.layout, false)
594601
};
595602

Diff for: src/librustc_mir/diagnostics.rs

+15-10
Original file line numberDiff line numberDiff line change
@@ -2119,14 +2119,15 @@ This error occurs because a borrow in a generator persists across a
21192119
yield point.
21202120
21212121
```compile_fail,E0626
2122-
# #![feature(generators, generator_trait)]
2122+
# #![feature(generators, generator_trait, pin)]
21232123
# use std::ops::Generator;
2124+
# use std::pin::Pin;
21242125
let mut b = || {
21252126
let a = &String::new(); // <-- This borrow...
21262127
yield (); // ...is still in scope here, when the yield occurs.
21272128
println!("{}", a);
21282129
};
2129-
unsafe { b.resume() };
2130+
Pin::new(&mut b).resume();
21302131
```
21312132
21322133
At present, it is not permitted to have a yield that occurs while a
@@ -2137,14 +2138,15 @@ resolve the previous example by removing the borrow and just storing
21372138
the integer by value:
21382139
21392140
```
2140-
# #![feature(generators, generator_trait)]
2141+
# #![feature(generators, generator_trait, pin)]
21412142
# use std::ops::Generator;
2143+
# use std::pin::Pin;
21422144
let mut b = || {
21432145
let a = 3;
21442146
yield ();
21452147
println!("{}", a);
21462148
};
2147-
unsafe { b.resume() };
2149+
Pin::new(&mut b).resume();
21482150
```
21492151
21502152
This is a very simple case, of course. In more complex cases, we may
@@ -2154,37 +2156,40 @@ in those cases, something like the `Rc` or `Arc` types may be useful.
21542156
This error also frequently arises with iteration:
21552157
21562158
```compile_fail,E0626
2157-
# #![feature(generators, generator_trait)]
2159+
# #![feature(generators, generator_trait, pin)]
21582160
# use std::ops::Generator;
2161+
# use std::pin::Pin;
21592162
let mut b = || {
21602163
let v = vec![1,2,3];
21612164
for &x in &v { // <-- borrow of `v` is still in scope...
21622165
yield x; // ...when this yield occurs.
21632166
}
21642167
};
2165-
unsafe { b.resume() };
2168+
Pin::new(&mut b).resume();
21662169
```
21672170
21682171
Such cases can sometimes be resolved by iterating "by value" (or using
21692172
`into_iter()`) to avoid borrowing:
21702173
21712174
```
2172-
# #![feature(generators, generator_trait)]
2175+
# #![feature(generators, generator_trait, pin)]
21732176
# use std::ops::Generator;
2177+
# use std::pin::Pin;
21742178
let mut b = || {
21752179
let v = vec![1,2,3];
21762180
for x in v { // <-- Take ownership of the values instead!
21772181
yield x; // <-- Now yield is OK.
21782182
}
21792183
};
2180-
unsafe { b.resume() };
2184+
Pin::new(&mut b).resume();
21812185
```
21822186
21832187
If taking ownership is not an option, using indices can work too:
21842188
21852189
```
2186-
# #![feature(generators, generator_trait)]
2190+
# #![feature(generators, generator_trait, pin)]
21872191
# use std::ops::Generator;
2192+
# use std::pin::Pin;
21882193
let mut b = || {
21892194
let v = vec![1,2,3];
21902195
let len = v.len(); // (*)
@@ -2193,7 +2198,7 @@ let mut b = || {
21932198
yield x; // <-- Now yield is OK.
21942199
}
21952200
};
2196-
unsafe { b.resume() };
2201+
Pin::new(&mut b).resume();
21972202
21982203
// (*) -- Unfortunately, these temporaries are currently required.
21992204
// See <https://github.com/rust-lang/rust/issues/43122>.

0 commit comments

Comments
 (0)