Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generator support #43076

Merged
merged 128 commits into from
Aug 28, 2017
Merged
Show file tree
Hide file tree
Changes from 110 commits
Commits
Show all changes
128 commits
Select commit Hold shift + click to select a range
d861982
Generator literal support
Zoxc Dec 26, 2016
17c749f
Fix tidy warnings
alexcrichton Jul 5, 2017
8103017
std: Add forwarding impls of `Generator` trait
alexcrichton Jul 5, 2017
b5449b7
Ban explicit arguments on generators
Zoxc Jul 6, 2017
0b5b012
Consider all implementations of Generator, not just built in ones
Zoxc Jul 6, 2017
cc40f58
Added some tests
Zoxc Jul 7, 2017
51bb31a
Fix tests
alexcrichton Jul 7, 2017
5efb0cb
Add some generator pass/fail tests
alexcrichton Jul 7, 2017
bc9b4de
Make yield and gen arg outside generator literals an error and update…
Zoxc Jul 8, 2017
55bb1c0
Add documentation for generators
alexcrichton Jul 10, 2017
facabcb
Fill in generator tracking issue in a few more locations
alexcrichton Jul 10, 2017
075fd36
Ensure upvars are dropped when generators have never been resumed
Zoxc Jul 10, 2017
057b0ad
Don't print `gen` in HIR
Zoxc Jul 10, 2017
66fc6df
Update error codes
Zoxc Jul 10, 2017
df608e7
Rename suspend to yield
Zoxc Jul 10, 2017
8cd0595
Use FIXME instead of TODO
Zoxc Jul 10, 2017
2afd04c
Add some comments
Zoxc Jul 10, 2017
a88e750
Fix whitespace
Zoxc Jul 10, 2017
e62d9d5
Fix tests
Zoxc Jul 10, 2017
5d174f0
Clarifying documentation for generator
alexcrichton Jul 11, 2017
8bcafc5
Fix hash impl for SimplifiedType
Zoxc Jul 11, 2017
bfd71c5
Fix examples
Zoxc Jul 11, 2017
66b8a12
Fix upstream changes
Zoxc Jul 11, 2017
098d6f0
Move a FIXME around
Zoxc Jul 13, 2017
75eb598
Copy a comment
Zoxc Jul 13, 2017
21f2d25
Consider StorageDead and StorageLive as gens for liveness analysis
Zoxc Jul 13, 2017
05fcef0
Fix printing
Zoxc Jul 13, 2017
6e66dcc
Remove a FIXME
Zoxc Jul 13, 2017
7eae848
Touch up unstable docs for generators
alexcrichton Jul 13, 2017
77bf6e4
Add a test
Zoxc Jul 14, 2017
12bbcd3
Add a test case for conditional drop
alexcrichton Jul 14, 2017
8e82b19
Convert tabs to spaces
alexcrichton Jul 14, 2017
264c3f4
add some tests of yielding with outstanding borrows
nikomatsakis Jul 14, 2017
d71fa93
fix yields-in-args test and add a reverse one
nikomatsakis Jul 14, 2017
2ad0f89
Tweak docs
Zoxc Jul 14, 2017
10def9a
Revert borrowck changes
Zoxc Jul 14, 2017
a602fc0
Revert some whitespace changes
Zoxc Jul 14, 2017
dcddd80
Remove debug code
Zoxc Jul 15, 2017
9317204
Fix a bug with yielding subtypes of the yield type.
Zoxc Jul 15, 2017
09a5d31
Remove support for `gen arg`
alexcrichton Jul 11, 2017
eea290d
Convert to spaces
Zoxc Jul 15, 2017
cbdb186
Add some comments
Zoxc Jul 15, 2017
b744b1c
Convert to spaces
Zoxc Jul 15, 2017
e4fdbcb
Update FIXME
Zoxc Jul 15, 2017
14c7476
Fix a string literal
Zoxc Jul 15, 2017
5a6e069
Remove a FIXME and apply the same hack as closures
Zoxc Jul 15, 2017
188cdf4
combine `bckerr_to_diag` and `note_and_explain_bckerr`
nikomatsakis Jul 15, 2017
3fdc3fa
change how we report `err_out_of_scope` borrowck errors
nikomatsakis Jul 15, 2017
f5ec503
Fix tidy errors
Zoxc Jul 15, 2017
4628f85
Fix error message tests
Zoxc Jul 15, 2017
0a8b81a
Ignore drop check bools when testing for legal generator types
Zoxc Jul 15, 2017
6106f6c
Fix error message tests
Zoxc Jul 15, 2017
b2d931a
Fix error message tests again
Zoxc Jul 15, 2017
d32428b
Remove tabs
Zoxc Jul 16, 2017
39478e8
update references due to removing tabs
nikomatsakis Jul 16, 2017
62e210f
Derive traits for State.
Zoxc Jul 19, 2017
df0496a
Rename State to GeneratorState
Zoxc Jul 19, 2017
5da9a8a
Rename some variables in lowering.rs and add an error number for the …
Zoxc Jul 20, 2017
9a6a866
Remove visit_body from YieldFinder
Zoxc Jul 20, 2017
0f8897c
Fix error code
Zoxc Jul 20, 2017
9556fac
Fix error message string
Zoxc Jul 20, 2017
93b9b1a
Fix tests
Zoxc Jul 20, 2017
ae1856c
Update rename State in docs
Zoxc Jul 21, 2017
be0a9b8
Fix tests
Zoxc Jul 22, 2017
ff99685
Fix more tests with `GeneratorState` rename
alexcrichton Jul 22, 2017
416096d
Update comment
Zoxc Jul 25, 2017
06ce77c
Merge remote-tracking branch 'origin/master' into gen
alexcrichton Jul 28, 2017
9285a61
Remove `IsGenerator` in favor of a boolean
alexcrichton Jul 28, 2017
cc4ff8f
Use a free error code
alexcrichton Jul 29, 2017
c25ddf2
Merge remote-tracking branch 'origin/master' into gen
alexcrichton Aug 8, 2017
352577f
Initial pass review comments
alexcrichton Aug 9, 2017
f72724e
Fix iterator over indexed sets
alexcrichton Aug 9, 2017
e181060
Add UI test outputs
alexcrichton Aug 9, 2017
be95ca4
Add a failing xcrate generator test
alexcrichton Aug 9, 2017
b8aa595
Merge remote-tracking branch 'origin/master' into gen
alexcrichton Aug 10, 2017
2d30e7e
Fix merge of scope.rs
Zoxc Aug 11, 2017
ffcd32c
Remove code related to the dropping of generator arguments
Zoxc Aug 11, 2017
bbe1552
Some tweaks and comments
Zoxc Aug 11, 2017
91dde3e
Typo
Zoxc Aug 11, 2017
be9c64b
Store generator interior in MIR literals
Zoxc Aug 11, 2017
6601ab9
Remove allocations
Zoxc Aug 11, 2017
54db165
Implement a cache for looking up yields
Zoxc Aug 11, 2017
2e3eaca
Please tidy
Zoxc Aug 11, 2017
2b04afb
Fix xcrate generator test
alexcrichton Aug 11, 2017
3971a3d
Merge remote-tracking branch 'origin/master' into gen
alexcrichton Aug 11, 2017
84af3d8
Fix an error in diagnostic codes
alexcrichton Aug 11, 2017
fa18aa3
Strip landing pads in the state transformation
Zoxc Aug 12, 2017
d020ff0
Merge branch 'master' of https://github.com/rust-lang/rust into gen
Zoxc Aug 12, 2017
1787d4e
Fix errors
Zoxc Aug 12, 2017
6eab1ca
Fix UI test
alexcrichton Aug 12, 2017
eff2884
Make generator interior types deterministic
Zoxc Aug 14, 2017
b045c20
Use a DepNode for the generator signature
Zoxc Aug 14, 2017
1413253
Merge remote-tracking branch 'origin/master' into gen
alexcrichton Aug 15, 2017
22ebcac
Choose another error code
alexcrichton Aug 15, 2017
b6b9690
Fix merge conflicts with `gen` branch
alexcrichton Aug 15, 2017
17197c6
Remove fixme
Zoxc Aug 15, 2017
1210ebf
Merge remote-tracking branch 'origin/master' into gen
alexcrichton Aug 16, 2017
0697e4b
Fix merge conflict
alexcrichton Aug 16, 2017
5f4a99f
Merge remote-tracking branch 'origin/master' into gen
alexcrichton Aug 16, 2017
63477fd
Fix merge conflicts
alexcrichton Aug 16, 2017
4b5f330
Merge remote-tracking branch 'origin/master' into gen
alexcrichton Aug 17, 2017
a47038f
Fix UI tests
alexcrichton Aug 17, 2017
9620d26
Fix more UI tests
alexcrichton Aug 18, 2017
e70cc27
Fix a diagnostic
alexcrichton Aug 18, 2017
bf3ebcc
Make gathering generator interior types O(n log n)
Zoxc Aug 20, 2017
b31998e
Merge remote-tracking branch 'origin/master' into gen
alexcrichton Aug 21, 2017
35b8dba
Merge remote-tracking branch 'origin/master' into gen
alexcrichton Aug 21, 2017
5dc9d71
Merge remote-tracking branch 'origin/master' into gen
alexcrichton Aug 21, 2017
04c66c3
Merge remote-tracking branch 'origin/master' into gen
alexcrichton Aug 22, 2017
c74ff32
More merge conflicts
alexcrichton Aug 22, 2017
0727ade
Avoid looking at `closure_kinds` for generators
alexcrichton Aug 24, 2017
b44e51c
Touch up a comment on `LocalDecl::internal`
alexcrichton Aug 24, 2017
c872f47
Merge remote-tracking branch 'origin/master' into gen
alexcrichton Aug 25, 2017
b28aeeb
Fix indentation
alexcrichton Aug 25, 2017
303a264
Reorder slightly in `visit_basic_block_data`
alexcrichton Aug 25, 2017
8c97ffa
Fix merge conflict
alexcrichton Aug 25, 2017
74b93f3
Add a `self_arg` convenience
alexcrichton Aug 25, 2017
ed5bc60
Remove unneeded FIXME for now
alexcrichton Aug 25, 2017
1917ffe
Add some suggested comments around drops
alexcrichton Aug 25, 2017
b4c5471
Comment about generators and drops
alexcrichton Aug 25, 2017
c711531
Add some comments and fix a typo
Zoxc Aug 24, 2017
5366135
Clean up YieldFinder
Zoxc Aug 25, 2017
7c5780b
Update rls
Zoxc Aug 28, 2017
d29af37
Merge branch 'master' of https://github.com/rust-lang/rust into gen
Zoxc Aug 28, 2017
8a9e32b
Update lock file
alexcrichton Aug 28, 2017
9eff522
Update the rls submodule again
alexcrichton Aug 28, 2017
876278f
Ignore a threaded test on emscripten
alexcrichton Aug 28, 2017
a996d5e
Tweak rls submodule again
alexcrichton Aug 28, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
245 changes: 245 additions & 0 deletions src/doc/unstable-book/src/language-features/generators.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
# `generators`

The tracking issue for this feature is: [#43122]

[#34511]: https://github.com/rust-lang/rust/issues/43122

------------------------

The `generators` feature gate in Rust allows you to define generator or
coroutine literals. A generator is a "resumable function" that syntactically
resembles a closure but compiles to much different semantics in the compiler
itself. The primary feature of a generator is that it can be suspended during
execution to be resumed at a later date. Generators use the `yield` keyword to
"return", and then the caller can `resume` a generator to resume execution just
after the `yield` keyword.

Generators are an extra-unstable feature in the compiler right now. Added in
[RFC 2033] they're mostly intended right now as a information/constraint
gathering phase. The intent is that experimentation can happen on the nightly
compiler before actual stabilization. A further RFC will be required to
stabilize generators/coroutines and will likely contain at least a few small
tweaks to the overall design.

[RFC 2033]: https://github.com/rust-lang/rfcs/pull/2033

A syntactical example of a generator is:

```rust
#![feature(generators, generator_trait)]

use std::ops::{Generator, GeneratorState};

fn main() {
let mut generator = || {
yield 1;
return "foo"
};

match generator.resume() {
GeneratorState::Yielded(1) => {}
_ => panic!("unexpected value from resume"),
}
match generator.resume() {
GeneratorState::Complete("foo") => {}
_ => panic!("unexpected value from resume"),
}
}
```

Generators are closure-like literals which can contain a `yield` statement. The
`yield` statement takes an optional expression of a value to yield out of the
generator. All generator literals implement the `Generator` trait in the
`std::ops` module. The `Generator` trait has one main method, `resume`, which
resumes execution of the generator at the previous suspension point.

An example of the control flow of generators is that the following example
prints all numbers in order:

```rust
#![feature(generators, generator_trait)]

use std::ops::Generator;

fn main() {
let mut generator = || {
println!("2");
yield;
println!("4");
};

println!("1");
generator.resume();
println!("3");
generator.resume();
println!("5");
}
```

At this time the main intended use case of generators is an implementation
primitive for async/await syntax, but generators will likely be extended to
ergonomic implementations of iterators and other primitives in the future.
Feedback on the design and usage is always appreciated!

### The `Generator` trait

The `Generator` trait in `std::ops` currently looks like:

```
# #![feature(generator_trait)]
# use std::ops::GeneratorState;

pub trait Generator {
type Yield;
type Return;
fn resume(&mut self) -> GeneratorState<Self::Yield, Self::Return>;
}
```

The `Generator::Yield` type is the type of values that can be yielded with the
`yield` statement. The `Generator::Return` type is the returned type of the
generator. This is typically the last expression in a generator's definition or
any value passed to `return` in a generator. The `resume` function is the entry
point for executing the `Generator` itself.

The return value of `resume`, `GeneratorState`, looks like:

```
pub enum GeneratorState<Y, R> {
Yielded(Y),
Complete(R),
}
```

The `Yielded` variant indicates that the generator can later be resumed. This
corresponds to a `yield` point in a generator. The `Complete` variant indicates
that the generator is complete and cannot be resumed again. Calling `resume`
after a generator has returned `Complete` will likely result in a panic of the
program.

### Closure-like semantics

The closure-like syntax for generators alludes to the fact that they also have
closure-like semantics. Namely:

* When created, a generator executes no code. A closure literal does not
actually execute any of the closure's code on construction, and similarly a
generator literal does not execute any code inside the generator when
constructed.

* Generators can capture outer variables by reference or by move, and this can
be tweaked with the `move` keyword at the beginning of the closure. Like
closures all generators will have an implicit environment which is inferred by
the compiler. Outer variables can be moved into a generator for use as the
generator progresses.

* Generator literals produce a value with a unique type which implements the
`std::ops::Generator` trait. This allows actual execution of the generator
through the `Generator::resume` method as well as also naming it in return
types and such.

* Traits like `Send` and `Sync` are automatically implemented for a `Generator`
depending on the captured variables of the environment. Unlike closures though
generators also depend on variables live across suspension points. This means
that although the ambient environment may be `Send` or `Sync`, the generator
itself may not be due to internal variables live across `yield` points being
not-`Send` or not-`Sync`. Note, though, that generators, like closures, do
not implement traits like `Copy` or `Clone` automatically.

* Whenever a generator is dropped it will drop all captured environment
variables.

Note that unlike closures generators at this time cannot take any arguments.
That is, generators must always look like `|| { ... }`. This restriction may be
lifted at a future date, the design is ongoing!

### Generators as state machines

In the compiler generators are currently compiled as state machines. Each
`yield` expression will correspond to a different state that stores all live
variables over that suspension point. Resumption of a generator will dispatch on
the current state and then execute internally until a `yield` is reached, at
which point all state is saved off in the generator and a value is returned.

Let's take a look at an example to see what's going on here:

```rust
#![feature(generators, generator_trait)]

use std::ops::Generator;

fn main() {
let ret = "foo";
let mut generator = move || {
yield 1;
return ret
};

generator.resume();
generator.resume();
}
```

This generator literal will compile down to something similar to:

```rust
#![feature(generators, generator_trait)]

use std::ops::{Generator, GeneratorState};

fn main() {
let ret = "foo";
let mut generator = {
enum __Generator {
Start(&'static str),
Yield1(&'static str),
Done,
}

impl Generator for __Generator {
type Yield = i32;
type Return = &'static str;

fn resume(&mut self) -> GeneratorState<i32, &'static str> {
use std::mem;
match mem::replace(self, __Generator::Done) {
__Generator::Start(s) => {
*self = __Generator::Yield1(s);
GeneratorState::Yielded(1)
}

__Generator::Yield1(s) => {
*self = __Generator::Done;
GeneratorState::Complete(s)
}

__Generator::Done => {
panic!("generator resumed after completion")
}
}
}
}

__Generator::Start(ret)
};

generator.resume();
generator.resume();
}
```

Notably here we can see that the compiler is generating a fresh type,
`__Generator` in this case. This type has a number of states (represented here
as an `enum`) corresponding to each of the conceptual states of the generator.
At the beginning we're closing over our outer variable `foo` and then that
variable is also live over the `yield` point, so it's stored in both states.

When the generator starts it'll immediately yield 1, but it saves off its state
just before it does so indicating that it has reached the yield point. Upon
resuming again we'll execute the `return ret` which returns the `Complete`
state.

Here we can also note that the `Done` state, if resumed, panics immediately as
it's invalid to resume a completed generator. It's also worth noting that this
is just a rough desugaring, not a normative specification for what the compiler
does.
13 changes: 12 additions & 1 deletion src/liballoc/boxed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ use core::hash::{self, Hash};
use core::iter::FusedIterator;
use core::marker::{self, Unsize};
use core::mem;
use core::ops::{CoerceUnsized, Deref, DerefMut};
use core::ops::{CoerceUnsized, Deref, DerefMut, Generator, GeneratorState};
use core::ops::{BoxPlace, Boxed, InPlace, Place, Placer};
use core::ptr::{self, Unique};
use core::convert::From;
Expand Down Expand Up @@ -784,3 +784,14 @@ impl<T: ?Sized> AsMut<T> for Box<T> {
&mut **self
}
}

#[unstable(feature = "generator_trait", issue = "43122")]
impl<T> Generator for Box<T>
where T: Generator + ?Sized
{
type Yield = T::Yield;
type Return = T::Return;
fn resume(&mut self) -> GeneratorState<Self::Yield, Self::Return> {
(**self).resume()
}
}
1 change: 1 addition & 0 deletions src/liballoc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
#![cfg_attr(not(test), feature(core_float))]
#![cfg_attr(not(test), feature(exact_size_is_empty))]
#![cfg_attr(not(test), feature(slice_rotate))]
#![cfg_attr(not(test), feature(generator_trait))]
#![cfg_attr(test, feature(rand, test))]
#![feature(allow_internal_unstable)]
#![feature(box_patterns)]
Expand Down
Loading